Merge branch 'master' of ../controller
authorAnil Belur <abelur@linuxfoundation.org>
Fri, 1 Nov 2019 06:59:39 +0000 (12:29 +0530)
committerAnil Belur <abelur@linuxfoundation.org>
Fri, 1 Nov 2019 06:59:39 +0000 (12:29 +0530)
- Pull in classes for src/test

./node
./node/utils
./node/utils/QNameFactoryTest.java
./node/utils/transformer
./node/utils/transformer/NormalizedNodePrunerTest.java
./node/utils/stream
./node/utils/stream/NormalizedNodeStreamReaderWriterTest.java
./node/utils/stream/SerializationUtilsTest.java
./node/utils/stream/LithiumWriteObjectMappingTest.java
./node/utils/stream/SampleNormalizedNodeSerializable.java

Change-Id: I896ea34d6cad7167669f5d9f1be7cb1307a9b9e0

3426 files changed:
.gitignore [new file with mode: 0644]
.gitreview [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
artifacts/pom.xml [new file with mode: 0644]
benchmarks/pom.xml [new file with mode: 0644]
benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/BenchmarkModel.java [new file with mode: 0644]
benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/InMemoryDataTreeBenchmark.java [new file with mode: 0644]
benchmarks/src/main/resources/odl-datastore-test.yang [new file with mode: 0644]
bundle-parent/pom.xml [new file with mode: 0644]
common/concepts/pom.xml [new file with mode: 0644]
common/concepts/src/main/java/module-info.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractCodec.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractDelegator.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractIdentifiable.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractIllegalArgumentCodec.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractListenerRegistration.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractObjectRegistration.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractRegistration.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractUncheckedCodec.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Builder.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/CheckedBuilder.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/CheckedValue.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ClassBasedPropertyBuilder.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Codec.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Delegator.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Deserializer.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ExtensibleObject.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Identifiable.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Identifier.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/IllegalArgumentCodec.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Immutable.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ListenerRegistration.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Mutable.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/MutationBehaviour.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/NoOpListenerRegistration.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/NoOpObjectRegistration.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ObjectExtension.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ObjectExtensions.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ObjectRegistration.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Path.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ProductAwareBuilder.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Registration.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/SemVer.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Serializer.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/UncheckedCodec.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/UncheckedDeserializer.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/UncheckedSerializer.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ValueWrapper.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Variant.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/WritableIdentifier.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/WritableObject.java [new file with mode: 0644]
common/concepts/src/main/java/org/opendaylight/yangtools/concepts/WritableObjects.java [new file with mode: 0644]
common/concepts/src/test/java/org/opendaylight/yangtools/concepts/AbstractIllegalArgumentCodecTest.java [new file with mode: 0644]
common/concepts/src/test/java/org/opendaylight/yangtools/concepts/CheckedValueTest.java [new file with mode: 0644]
common/concepts/src/test/java/org/opendaylight/yangtools/concepts/SemVerTest.java [new file with mode: 0644]
common/concepts/src/test/java/org/opendaylight/yangtools/concepts/WritableObjectsTest.java [new file with mode: 0644]
common/mockito-configuration/pom.xml [new file with mode: 0644]
common/mockito-configuration/src/main/java/org/mockito/configuration/ArgumentsExtractorVerifier.java [new file with mode: 0644]
common/mockito-configuration/src/main/java/org/mockito/configuration/MockitoConfiguration.java [new file with mode: 0644]
common/mockito-configuration/src/main/java/org/mockito/configuration/ThrowsUnstubbedMethodException.java [new file with mode: 0644]
common/mockito-configuration/src/main/java/org/mockito/configuration/UnstubbedMethodException.java [new file with mode: 0644]
common/mockito-configuration/src/main/java/org/mockito/configuration/package-info.java [new file with mode: 0644]
common/mockito-configuration/src/main/resources/logback-test.xml [new file with mode: 0644]
common/mockito-configuration/src/test/java/org/mockito/configuration/ArgumentsExtractorVerifierTest.java [new file with mode: 0644]
common/mockito-configuration/src/test/java/org/mockito/configuration/DefaultAnswerTest.java [new file with mode: 0644]
common/pom.xml [new file with mode: 0644]
common/testutils/pom.xml [new file with mode: 0644]
common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/CallsRealOrExceptionAnswer.java [new file with mode: 0644]
common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/MethodExtensions.java [new file with mode: 0644]
common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/MoreAnswers.java [new file with mode: 0644]
common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/ThrowsMethodExceptionAnswer.java [new file with mode: 0644]
common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/UnstubbedMethodException.java [new file with mode: 0644]
common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MethodExtensionsTest.java [new file with mode: 0644]
common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MikitoTest.java [new file with mode: 0644]
common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MockitoExampleTutorialTest.java [new file with mode: 0644]
common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MockitoUnstubbedMethodExceptionAnswerTest.java [new file with mode: 0644]
common/util/pom.xml [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/AbstractIdentifier.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/AbstractStringIdentifier.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/AbstractUUIDIdentifier.java [new file with mode: 0644]
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/CollectionWrappers.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ConcurrentDurationStatisticsTracker.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ConstantArrayCollection.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/DurationStatisticsTracker.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/DurationWithTime.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/EmptyDeque.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/EvenMoreObjects.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ExecutorServiceUtil.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ForwardingIdentityObject.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/HashCodeBuilder.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableMapTemplate.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableOffsetMap.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableOffsetMapTemplate.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/Immutables.java [new file with mode: 0644]
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/MapAdaptor.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ModifiableMapPhase.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/MutableOffsetMap.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ObjectRegistry.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/OffsetMapCache.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/OptionalBoolean.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 [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/ReadWriteTrieMap.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/RecursiveObjectLeaker.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/SharedSingletonMap.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/SharedSingletonMapTemplate.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/SingletonSet.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/SingletonSpliterators.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/SynchronizedDurationStatsTracker.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/TopologicalSort.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/UnmodifiableCollection.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/UnmodifiableMapPhase.java [new file with mode: 0644]
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/CountingRejectedExecutionHandler.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorService.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ExceptionMapper.java [new file with mode: 0644]
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/FluentFutures.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ListenerNotificationQueueStats.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/LoggingThreadUncaughtExceptionHandler.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/QueuedNotificationManagerMXBean.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManagerMXBeanImpl.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/SettableBoolean.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SettableBooleanThreadLocal.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SpecialExecutors.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ThreadFactoryProvider.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueue.java [new file with mode: 0644]
common/util/src/main/java/org/opendaylight/yangtools/util/xml/UntrustedXML.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/ConstantArrayCollectionTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/DurationStatisticsTrackerTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/EmptyDequeTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/EvenMoreObjectsTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/HashCodeBuilderTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/ImmutableMapTemplateTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/LazyCollectionsTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/ListenerRegistryTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/MapAdaptorTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/OffsetMapTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/PropertyUtilsTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/ReadWriteTrieMapTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/SharedSingletonMapTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/SingletonSetTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/SynchronizedDurationStatsTrackerTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/TopologicalSortTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/UnmodifiableCollectionTest.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/CountingRejectedExecutionHandlerTest.java [new file with mode: 0644]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorServiceTest.java [new file with mode: 0644]
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]
common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueueTest.java [new file with mode: 0644]
distribution-karaf/pom.xml [new file with mode: 0644]
docs/assembly/guides.xml [new file with mode: 0644]
docs/pom.xml [new file with mode: 0644]
docs/src/main/asciidoc/contributing.adoc [new file with mode: 0644]
docs/src/main/asciidoc/developing.adoc [new file with mode: 0644]
docs/src/main/asciidoc/images/models/yang-data-api.png [new file with mode: 0644]
docs/src/main/asciidoc/images/models/yang-model-api.png [new file with mode: 0644]
docs/src/main/asciidoc/yang-1-1.adoc [new file with mode: 0644]
features/features-yangtools-experimental/pom.xml [new file with mode: 0644]
features/features-yangtools/pom.xml [new file with mode: 0644]
features/odl-yangtools-codec/pom.xml [new file with mode: 0644]
features/odl-yangtools-codec/src/main/feature/feature.xml [new file with mode: 0644]
features/odl-yangtools-common/pom.xml [new file with mode: 0644]
features/odl-yangtools-data-api/pom.xml [new file with mode: 0644]
features/odl-yangtools-data/pom.xml [new file with mode: 0644]
features/odl-yangtools-data/src/main/feature/feature.xml [new file with mode: 0644]
features/odl-yangtools-exp-data-xpath-api/pom.xml [new file with mode: 0644]
features/odl-yangtools-exp-data-xpath/pom.xml [new file with mode: 0644]
features/odl-yangtools-export/pom.xml [new file with mode: 0644]
features/odl-yangtools-parser-api/pom.xml [new file with mode: 0644]
features/odl-yangtools-parser/pom.xml [new file with mode: 0644]
features/odl-yangtools-parser/src/main/feature/feature.xml [new file with mode: 0644]
features/odl-yangtools-util/pom.xml [new file with mode: 0644]
features/odl-yangtools-util/src/main/feature/feature.xml [new file with mode: 0644]
features/odl-yangtools-xpath-api/pom.xml [new file with mode: 0644]
features/odl-yangtools-xpath/pom.xml [new file with mode: 0644]
features/pom.xml [new file with mode: 0644]
integration-test/yang-runtime-tests/src/test/java/org/opendaylight/yangtools/it/yang/runtime/stmt/parser/retest/MultipleRevisionsSupportTest.java [new file with mode: 0644]
pom.xml [new file with mode: 0644]
yang-validation-tool/pom.xml [new file with mode: 0644]
yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/Main.java [new file with mode: 0644]
yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/Params.java [new file with mode: 0644]
yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/ParamsUtil.java [new file with mode: 0644]
yang/odlext-model-api/pom.xml [new file with mode: 0644]
yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/AnyxmlSchemaLocationEffectiveStatement.java [new file with mode: 0644]
yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/AnyxmlSchemaLocationStatement.java [new file with mode: 0644]
yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/OpenDaylightExtensionsConstants.java [new file with mode: 0644]
yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/OpenDaylightExtensionsStatements.java [new file with mode: 0644]
yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/YangModeledAnyxmlSchemaNode.java [new file with mode: 0644]
yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/package-info.java [new file with mode: 0644]
yang/odlext-parser-support/pom.xml [new file with mode: 0644]
yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlSchemaLocationEffectiveStatementImpl.java [new file with mode: 0644]
yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlSchemaLocationNamespace.java [new file with mode: 0644]
yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlSchemaLocationStatementImpl.java [new file with mode: 0644]
yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlSchemaLocationStatementSupport.java [new file with mode: 0644]
yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlStatementSupportOverride.java [new file with mode: 0644]
yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/YangModeledAnyxmlEffectiveStatementImpl.java [new file with mode: 0644]
yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/package-info.java [new file with mode: 0644]
yang/odlext-parser-support/src/test/java/org/opendaylight/yangtools/odlext/parser/Bug3874ExtensionTest.java [new file with mode: 0644]
yang/odlext-parser-support/src/test/resources/bugs/bug3874/foo.yang [new file with mode: 0644]
yang/odlext-parser-support/src/test/resources/bugs/bug3874/yang-ext.yang [new file with mode: 0644]
yang/openconfig-model-api/pom.xml [new file with mode: 0644]
yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigConstants.java [new file with mode: 0644]
yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigHashedValueEffectiveStatement.java [new file with mode: 0644]
yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigHashedValueSchemaNode.java [new file with mode: 0644]
yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigHashedValueStatement.java [new file with mode: 0644]
yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigStatements.java [new file with mode: 0644]
yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigVersionEffectiveStatement.java [new file with mode: 0644]
yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigVersionStatement.java [new file with mode: 0644]
yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/package-info.java [new file with mode: 0644]
yang/openconfig-parser-support/pom.xml [new file with mode: 0644]
yang/openconfig-parser-support/src/main/java/org/opendaylight/yangtools/openconfig/parser/AbstractHashedValueStatementSupport.java [new file with mode: 0644]
yang/openconfig-parser-support/src/main/java/org/opendaylight/yangtools/openconfig/parser/EncryptedValueStatementSupport.java [new file with mode: 0644]
yang/openconfig-parser-support/src/main/java/org/opendaylight/yangtools/openconfig/parser/HashedValueStatementSupport.java [new file with mode: 0644]
yang/openconfig-parser-support/src/main/java/org/opendaylight/yangtools/openconfig/parser/package-info.java [new file with mode: 0644]
yang/pom.xml [new file with mode: 0644]
yang/rfc6241-model-api/pom.xml [new file with mode: 0644]
yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/GetFilterElementAttributesEffectiveStatement.java [new file with mode: 0644]
yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/GetFilterElementAttributesSchemaNode.java [new file with mode: 0644]
yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/GetFilterElementAttributesStatement.java [new file with mode: 0644]
yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/NetconfConstants.java [new file with mode: 0644]
yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/NetconfStatements.java [new file with mode: 0644]
yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/package-info.java [new file with mode: 0644]
yang/rfc6241-parser-support/pom.xml [new file with mode: 0644]
yang/rfc6241-parser-support/src/main/java/org/opendaylight/yangtools/rfc6241/parser/GetFilterElementAttributesStatementSupport.java [new file with mode: 0644]
yang/rfc6241-parser-support/src/main/java/org/opendaylight/yangtools/rfc6241/parser/package-info.java [new file with mode: 0644]
yang/rfc6241-parser-support/src/test/java/org/opendaylight/yangtools/rfc6241/parser/NetconfTest.java [new file with mode: 0644]
yang/rfc6241-parser-support/src/test/resources/ietf-inet-types@2013-07-15.yang [new file with mode: 0644]
yang/rfc6241-parser-support/src/test/resources/ietf-netconf@2011-06-01.yang [new file with mode: 0644]
yang/rfc6536-model-api/pom.xml [new file with mode: 0644]
yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyAllEffectiveStatement.java [new file with mode: 0644]
yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyAllSchemaNode.java [new file with mode: 0644]
yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyAllStatement.java [new file with mode: 0644]
yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyWriteEffectiveStatement.java [new file with mode: 0644]
yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyWriteSchemaNode.java [new file with mode: 0644]
yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyWriteStatement.java [new file with mode: 0644]
yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/NACMConstants.java [new file with mode: 0644]
yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/NACMStatements.java [new file with mode: 0644]
yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/package-info.java [new file with mode: 0644]
yang/rfc6536-parser-support/pom.xml [new file with mode: 0644]
yang/rfc6536-parser-support/src/main/java/org/opendaylight/yangtools/rfc6536/parser/DefaultDenyAllStatementSupport.java [new file with mode: 0644]
yang/rfc6536-parser-support/src/main/java/org/opendaylight/yangtools/rfc6536/parser/DefaultDenyWriteStatementSupport.java [new file with mode: 0644]
yang/rfc6536-parser-support/src/main/java/org/opendaylight/yangtools/rfc6536/parser/package-info.java [new file with mode: 0644]
yang/rfc6536-parser-support/src/test/java/org/opendaylight/yangtools/rfc6536/parser/NACMTest.java [new file with mode: 0644]
yang/rfc6536-parser-support/src/test/resources/ietf-netconf-acm@2012-02-22.yang [new file with mode: 0644]
yang/rfc6536-parser-support/src/test/resources/ietf-yang-types@2013-07-15.yang [new file with mode: 0644]
yang/rfc7952-data-api/pom.xml [new file with mode: 0644]
yang/rfc7952-data-api/src/main/java/org/opendaylight/yangtools/rfc7952/data/api/MetadataNormalizedAnydata.java [new file with mode: 0644]
yang/rfc7952-data-api/src/main/java/org/opendaylight/yangtools/rfc7952/data/api/NormalizedMetadata.java [new file with mode: 0644]
yang/rfc7952-data-api/src/main/java/org/opendaylight/yangtools/rfc7952/data/api/StreamWriterMetadataExtension.java [new file with mode: 0644]
yang/rfc7952-data-util/pom.xml [new file with mode: 0644]
yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/ImmutableMetadataNormalizedAnydata.java [new file with mode: 0644]
yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/ImmutableNormalizedMetadata.java [new file with mode: 0644]
yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/ImmutableNormalizedMetadataStreamWriter.java [new file with mode: 0644]
yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/NormalizedMetadataWriter.java [new file with mode: 0644]
yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/NormalizedNodeStreamWriterMetadataDecorator.java [new file with mode: 0644]
yang/rfc7952-model-api/pom.xml [new file with mode: 0644]
yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationEffectiveStatement.java [new file with mode: 0644]
yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationSchemaNode.java [new file with mode: 0644]
yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationSchemaNodeAware.java [new file with mode: 0644]
yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationSchemaNodeAwareSchemaContext.java [new file with mode: 0644]
yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationStatement.java [new file with mode: 0644]
yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/MetadataConstants.java [new file with mode: 0644]
yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/MetadataStatements.java [new file with mode: 0644]
yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/package-info.java [new file with mode: 0644]
yang/rfc7952-parser-support/pom.xml [new file with mode: 0644]
yang/rfc7952-parser-support/src/main/java/org/opendaylight/yangtools/rfc7952/parser/AnnotationStatementSupport.java [new file with mode: 0644]
yang/rfc7952-parser-support/src/main/java/org/opendaylight/yangtools/rfc7952/parser/package-info.java [new file with mode: 0644]
yang/rfc7952-parser-support/src/test/java/org/opendaylight/yangtools/rfc7952/parser/AnnotationTest.java [new file with mode: 0644]
yang/rfc7952-parser-support/src/test/resources/example-last-modified.yang [new file with mode: 0644]
yang/rfc7952-parser-support/src/test/resources/ietf-yang-metadata@2016-08-05.yang [new file with mode: 0644]
yang/rfc8040-model-api/pom.xml [new file with mode: 0644]
yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataConstants.java [new file with mode: 0644]
yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataEffectiveStatement.java [new file with mode: 0644]
yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataSchemaNode.java [new file with mode: 0644]
yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataStatement.java [new file with mode: 0644]
yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataStatements.java [new file with mode: 0644]
yang/rfc8040-parser-support/pom.xml [new file with mode: 0644]
yang/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataEffectiveStatementImpl.java [new file with mode: 0644]
yang/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java [new file with mode: 0644]
yang/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/package-info.java [new file with mode: 0644]
yang/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YangDataExtensionTest.java [new file with mode: 0644]
yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/bar.yang [new file with mode: 0644]
yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/baz.yang [new file with mode: 0644]
yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foo-invalid-1.yang [new file with mode: 0644]
yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foo-invalid-2.yang [new file with mode: 0644]
yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foo-invalid-3.yang [new file with mode: 0644]
yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foo.yang [new file with mode: 0644]
yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foobar.yang [new file with mode: 0644]
yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/ietf-restconf.yang [new file with mode: 0644]
yang/rfc8528-data-api/pom.xml [new file with mode: 0644]
yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MPIv1.java [new file with mode: 0644]
yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointChild.java [new file with mode: 0644]
yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointContext.java [new file with mode: 0644]
yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointContextFactory.java [new file with mode: 0644]
yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointIdentifier.java [new file with mode: 0644]
yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointNode.java [new file with mode: 0644]
yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/StreamWriterMountPointExtension.java [new file with mode: 0644]
yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/YangLibraryConstants.java [new file with mode: 0644]
yang/rfc8528-data-util/pom.xml [new file with mode: 0644]
yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/AbstractDynamicMountPointContextFactory.java [new file with mode: 0644]
yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/AbstractMountPointContextFactory.java [new file with mode: 0644]
yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/EmptyMountPointContext.java [new file with mode: 0644]
yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/ImmutableMountPointContext.java [new file with mode: 0644]
yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/ImmutableMountPointNode.java [new file with mode: 0644]
yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/MountPointNormalizedNodeWriter.java [new file with mode: 0644]
yang/rfc8528-model-api/pom.xml [new file with mode: 0644]
yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/MountPointEffectiveStatement.java [new file with mode: 0644]
yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/MountPointSchemaNode.java [new file with mode: 0644]
yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/MountPointStatement.java [new file with mode: 0644]
yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/SchemaMountConstants.java [new file with mode: 0644]
yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/SchemaMountStatements.java [new file with mode: 0644]
yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/package-info.java [new file with mode: 0644]
yang/rfc8528-parser-support/pom.xml [new file with mode: 0644]
yang/rfc8528-parser-support/src/main/java/org/opendaylight/yangtools/rfc8528/parser/MountPointStatementSupport.java [new file with mode: 0644]
yang/rfc8528-parser-support/src/main/java/org/opendaylight/yangtools/rfc8528/parser/package-info.java [new file with mode: 0644]
yang/rfc8528-parser-support/src/test/java/org/opendaylight/yangtools/rfc8528/parser/MountPointTest.java [new file with mode: 0644]
yang/rfc8528-parser-support/src/test/resources/example-grp.yang [new file with mode: 0644]
yang/rfc8528-parser-support/src/test/resources/example-uses.yang [new file with mode: 0644]
yang/rfc8528-parser-support/src/test/resources/ietf-inet-types@2013-07-15.yang [new file with mode: 0644]
yang/rfc8528-parser-support/src/test/resources/ietf-yang-schema-mount@2019-01-14.yang [new file with mode: 0644]
yang/rfc8528-parser-support/src/test/resources/ietf-yang-types@2013-07-15.yang [new file with mode: 0644]
yang/yang-common/pom.xml [new file with mode: 0644]
yang/yang-common/src/main/java/module-info.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueImplementationValidator.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueSupport.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueValidator.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractQName.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/BiMapYangNamespaceContext.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CachingDerivedString.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValue.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueSupport.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueValidator.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueViolation.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Decimal64.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/DerivedString.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Empty.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/NSv1.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/OperationFailedException.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QName.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QNameModule.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QNv1.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QQNv1.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QualifiedQName.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Revision.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/RpcError.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/RpcResult.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/RpcResultBuilder.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/UQNv1.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Uint16.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Uint32.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Uint64.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Uint8.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/UintConversions.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/UnqualifiedQName.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangConstants.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangNames.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangNamespaceContext.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangVersion.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/pkg-info.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/BiMapYangNamespaceContextTest.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Decimal64Test.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/DerivedStringTest.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/EmptyTest.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/OperationFailedExceptionTest.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/QNameTest.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/RevisionTest.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/RpcResultBuilderTest.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Uint16Test.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Uint32Test.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Uint64Test.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Uint8Test.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/YangConstantsTest.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/YangNamesTest.java [new file with mode: 0644]
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/YangVersionTest.java [new file with mode: 0644]
yang/yang-data-api/pom.xml [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/AIv1.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/DSIv1.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/DatastoreIdentifier.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/FixedYangInstanceIdentifier.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NIPv1.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NIPv2.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NIVv1.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NIv1.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/PathArgumentList.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/StackedPathArguments.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/StackedReversePathArguments.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/StackedYangInstanceIdentifier.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YIDv1.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifierBuilder.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/BinaryCodec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/BitsCodec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/BooleanCodec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/DecimalCodec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/EmptyCodec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/EnumCodec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/IdentityrefCodec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/InstanceIdentifierCodec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Int16Codec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Int32Codec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Int64Codec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Int8Codec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/LeafrefCodec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/StringCodec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Uint16Codec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Uint32Codec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Uint64Codec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Uint8Codec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/UnionCodec.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AnydataNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AnydataNormalizationException.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AnyxmlNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AugmentationNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ChoiceNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ContainerNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DOMSourceAnyxmlNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DataContainerChild.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DataContainerNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DuplicateEntry.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DuplicateFinder.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ForeignDataNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/LeafNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/LeafSetEntryNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/LeafSetNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/MapEntryNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/MapNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/MixinNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizableAnydata.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedAnydata.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedNodeContainer.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedNodes.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/OrderedLeafSetNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/OrderedMapNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/OrderedNodeContainer.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/UnkeyedListEntryNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/UnkeyedListNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ValueNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/YangModeledAnyXmlNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/package-info.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/ForwardingNormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/LoggingNormalizedNodeStreamWriter.java [new file with mode: 0644]
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-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeStreamWriterExtension.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/ReusableStreamReceiver.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractAvailableLeafCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractDataTreeCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractLeafCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractRecursiveCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractWriteCandidate.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/BackendFailedException.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ConflictingModificationAppliedException.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/CursorAwareDataTreeModification.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/CursorAwareDataTreeSnapshot.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTree.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidate.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateNodes.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateTip.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidates.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeConfiguration.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeFactory.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeModification.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeModificationCursor.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeSnapshot.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeSnapshotCursor.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeTip.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataValidationFailedException.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DefaultDataTreeCandidate.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DeleteLeafCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/EmptyDataTreeCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/IncorrectDataStructureException.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ModificationType.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ModifiedNodeDoesNotExistException.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/NormalizedNodeDataTreeCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ReadOnlyDataTree.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RecursiveDeleteCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RecursiveReplaceCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RecursiveUnmodifiedCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RecursiveWriteCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ReplaceLeafCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RequiredElementCountException.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/StoreTreeNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/StoreTreeNodes.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/SynchronizedDataTreeModification.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/TreeType.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/UnmodifiedLeafCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/WriteLeafCandidateNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/AbstractContainerNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/AbstractModifiedContainerNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/AbstractMutableContainerNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/AbstractTreeNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/LazyContainerNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/LazyMutableContainerNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/MaterializedContainerNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/MaterializedMutableContainerNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/MutableTreeNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/SimpleContainerNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNodeFactory.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/ValueNode.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/Version.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/LazyXPathExpression.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/LazyXPathExpressionException.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/OptimizableXPathExpression.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/PrefixConverters.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/RelocatableXPathExpression.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathBooleanResult.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathDocument.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathExpression.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathNodesetResult.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathNumberResult.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathResult.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathSchemaContext.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathSchemaContextFactory.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathStringResult.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/package-info.java [new file with mode: 0644]
yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/PathArgumentListTest.java [new file with mode: 0644]
yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifierTest.java [new file with mode: 0644]
yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedNodesTest.java [new file with mode: 0644]
yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriterTest.java [new file with mode: 0644]
yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateNodesTest.java [new file with mode: 0644]
yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidatesTest.java [new file with mode: 0644]
yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeConfigurationTest.java [new file with mode: 0644]
yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/NormalizedNodeDataTreeCandidateNodeTest.java [new file with mode: 0644]
yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/DuplicityTest.java [new file with mode: 0644]
yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNodeFactoryTest.java [new file with mode: 0644]
yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/VersionTest.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/pom.xml [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractLithiumDataInput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractLithiumDataOutput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractMagnesiumDataInput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractMagnesiumDataOutput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractNormalizedNodeDataInput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractNormalizedNodeDataOutput.java [new file with mode: 0755]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/ForwardingDataInput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/ForwardingNormalizedNodeDataInput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/InvalidNormalizedNodeStreamException.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumNode.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumNormalizedNodeInputStreamReader.java [new file with mode: 0755]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumNormalizedNodeOutputStreamWriter.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumPathArgument.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumTokens.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumValue.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumDataInput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumDataOutput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumNode.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumPathArgument.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumValue.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NeonSR2NormalizedNodeInputStreamReader.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NeonSR2NormalizedNodeOutputStreamWriter.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NeonSR2Tokens.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NormalizedNodeDataInput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NormalizedNodeDataOutput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NormalizedNodeStreamVersion.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/QNameFactory.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/SodiumSR1DataInput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/SodiumSR1DataOutput.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/TokenTypes.java [new file with mode: 0644]
yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/VersionedNormalizedNodeDataInput.java [new file with mode: 0644]
yang/yang-data-codec-gson/pom.xml [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractJSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/BooleanJSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/EmptyJSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/IdentityrefJSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactorySupplier.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONInstanceIdentifierCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterExclusiveRootContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterInvisibleContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterListContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterNamedObjectContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterObjectContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterQNameContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterRootContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterSharedRootContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterURIContext.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonWriterFactory.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/Lhotka02JSONCodecFactory.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/Lhotka02JSONInstanceIdentifierCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/NullJSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/NumberJSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/QuotedJSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/RFC7951JSONCodecFactory.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/RFC7951JSONInstanceIdentifierCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/UnionJSONCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractComplexJsonTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractYT1027Test.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlSupportTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug4501Test.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug4969Test.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug5446Test.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug6112Test.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug7246Test.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug8083Test.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonStreamToNormalizedNodeTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Lhotka02YT1027Test.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/NormalizedNodeToJsonStreamTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/RFC7951YT1027Test.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/StreamToNormalizedNodeTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestUtils.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestingNormalizedNodeStructuresCreator.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YT1029Test.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YangModeledAnyXmlSupportTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug-4501/json/foo-correct.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug-4501/json/foo-incorrect.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug-4501/yang/foo.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug-4969/json/foo.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug-4969/yang/bar.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug-4969/yang/foo.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug-6112/json/data-identityref.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug-6112/json/data-uint8.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug-6112/yang/union-with-identityref.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug5446/json/foo.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug5446/yang/foo.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug7246/json/expected-output.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug7246/yang/rpc-test.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8083/json/baz.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8083/json/foo.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8083/json/foobar.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8083/json/zab.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8083/yang/bar.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8083/yang/baz.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8083/yang/foo.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8083/yang/foobar.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8083/yang/zab.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8927/json/complex_array.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8927/json/complex_array_with_other_elements.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8927/json/composite.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8927/json/expected.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8927/json/scalar_array.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8927/json/scalar_array_with_other_elements.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8927/xml/complex_array.xml [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8927/xml/complex_array_with_other_elements.xml [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8927/xml/input.xml [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8927/xml/input4.xml [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8927/xml/scalar_array.xml [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug8927/xml/scalar_array_with_other_elements.xml [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/anyxml-node-with-composite-value-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/anyxml-node-with-simple-value-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-augmentation-in-choice-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-external-augmentation-in-choice-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/choice-node-augmentation-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/choice-node-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/complex-json.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/keyed-list-node-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/keyed-list-restconf-behaviour.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/keyed-list-yang-json-behaviour.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/leaf-node-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/leaf-node-via-augmentation-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/leaflist-node-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/missing-module-in-top-level.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/multiple-choice-augmentation-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/namesakes.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/not-existing-element.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/type-empty.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/unkeyed-node-in-container.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/yang/bug8927.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation-namesake.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/leafref/json/data.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/leafref/yang/augment-leafref-module.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/leafref/yang/leafref-module.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/json/baz.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/xml/baz.xml [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/yang/baz.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/yang/yang-ext.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/yt1027/yt1027.yang [new file with mode: 0644]
yang/yang-data-codec-xml/pom.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractXmlCodec.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/BooleanXmlCodec.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceAnydata.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceMountPointChild.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceXMLStreamReader.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/EmptyXmlCodec.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/IdentityrefXmlCodec.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/NullXmlCodec.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/NumberXmlCodec.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/QuotedXmlCodec.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/RandomPrefix.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/RandomPrefixInstanceIdentifierSerializer.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamWriterUtils.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamWriterUtils.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/StreamWriterFacade.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/UnionXmlCodec.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/ValueWriter.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamNormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamWriterUtils.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodec.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractAnydataTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnyXmlWithParamsParsingTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataNormalizeToContainerTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataParseTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataSerializeTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug5396Test.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug5446Test.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug8083Test.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug8675Test.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug8745Test.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug8803Test.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug890Test.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceXMLStreamReaderTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/NormalizedNodeXmlTranslationTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/NormalizedNodesToXmlTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/RandomPrefixTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaOrderedNormalizedNodeWriterTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriterTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/StrictParsingModeTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/TestFactories.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStreamUtilsTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlToNormalizedNodesTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YangModeledAnyXMLDeserializationTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YangModeledAnyXMLSerializationTest.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools821Test.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools891Test.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools892Test.java [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/anyxml-support/params/edit.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/anyxml-support/params/ietf-netconf@2011-06-01.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/anyxml-support/serialization/baz.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/anyxml-support/serialization/baz.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/anyxml-support/serialization/yang-ext.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/anyxml-support/xml/bar.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/anyxml-support/xml/foo.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/anyxml-support/yang/bar.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/anyxml-support/yang/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/anyxml-support/yang/yang-ext.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/baz.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/baz.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug1848/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug1848/order.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo2.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo3.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo4.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo5.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug5396/xml/invalid-foo.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug5396/yang/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug5446/xml/foo.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug5446/yang/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8083/xml/baz.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8083/xml/foobar.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8083/xml/zab.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8083/yang/baz.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8083/yang/foobar.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8083/yang/zab.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8675/foo-2.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8675/foo-3.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8675/foo-4.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8675/foo-5.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8675/foo-6.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8675/foo.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8675/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8745/foo.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8745/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8803/bar.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8803/baz.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8803/foo.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug8803/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug890/xml/foo.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/bug890/yang/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/dom-reader-test/bar.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/dom-reader-test/baz.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/dom-reader-test/foo.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/dom-reader-test/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/dom-reader-test/rab.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/dom-reader-test/zab.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/foo.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/foobar.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/invalid-baz-2.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/invalid-baz-3.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/invalid-baz-4.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/invalid-baz.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/invalid-foo-2.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/invalid-foo-3.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/invalid-foo.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/leafref-test.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/schema/augment_choice_hell.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/schema/augment_choice_hell_ok.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/schema/augment_choice_hell_ok2.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/schema/augment_choice_hell_ok3.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/schema/rpc-test-model.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/schema/rpc-test-payload1.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/schema/rpc-test-payload2.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/schema/simple.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/schema/simple2.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/schema/simple_xml_with_attributes.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/schema/test.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/strict-parsing-mode-test/foo.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/strict-parsing-mode-test/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/test-anydata.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/test-anydata.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools821/foo-invalid.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools821/foo-ref-in-container-invalid.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools821/foo-ref-in-container-valid.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools821/foo-valid.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools821/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/bar.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/baz-top.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/baz.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/foo.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-invalid.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-valid.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-invalid.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-valid.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools892/bgp-test-extensions.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools892/neighbour.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools892/peer-groups.xml [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools892/test-bgp-multiprotocol.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools892/test-bgp-types.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools892/test-bgp.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools892/test-network-instance.yang [new file with mode: 0644]
yang/yang-data-codec-xml/src/test/resources/yangtools892/test-policy-types.yang [new file with mode: 0644]
yang/yang-data-impl/pom.xml [new file with mode: 0644]
yang/yang-data-impl/src/main/antlr4/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathLexer.g4 [new file with mode: 0644]
yang/yang-data-impl/src/main/antlr4/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParser.g4 [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/AbstractDataStringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/AbstractIntegerStringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/BinaryStringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/BitsStringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/BooleanStringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/CompiledPatternContext.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/DataStringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/DecimalStringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/DeserializationException.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/EmptyStringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/EnumStringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Int16StringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Int32StringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Int64StringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Int8StringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/SchemaTracker.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/StringPatternCheckingCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/StringStringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/TypeDefinitionAwareCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Uint16StringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Uint32StringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Uint64StringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Uint8StringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/UnionStringCodec.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/ValueWithQName.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/AbstractQNameWithPredicate.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefContext.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefContextBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefContextTreeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefContextUtils.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefDataValidationFailedException.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPath.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathErrorListener.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParserImpl.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParserListenerImpl.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathSyntaxErrorException.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefUtils.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefValidation.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefYangSyntaxErrorException.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNamePredicate.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNamePredicateBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNamePredicateImpl.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNameWithPredicate.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNameWithPredicateBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNameWithPredicateImpl.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/SimpleQNameWithPredicate.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/osgi/Activator.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/osgi/package-info.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/AbstractMountPointChild.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/AbstractNormalizableAnydata.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/Builders.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableMetadataNormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableMountPointNormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java [new file with mode: 0644]
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/InstanceIdToCompositeNodes.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToNodes.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToSimpleNodes.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InterningLeafNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InterningLeafSetNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeMetadataResult.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/NormalizedNodeResultBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ResultAlreadySetException.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ReusableImmutableNormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaOrderedNormalizedNodeWriter.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaUtils.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/CollectionNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/DataContainerNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/ListNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/NormalizedNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/NormalizedNodeContainerBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/AbstractImmutableDataContainerNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/AbstractImmutableNormalizedNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAnyXmlNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAnyXmlNodeSchemaAwareBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAnydataNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAugmentationNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAugmentationNodeSchemaAwareBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableChoiceNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableChoiceNodeSchemaAwareBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableContainerNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableContainerNodeSchemaAwareBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafNodeSchemaAwareBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetEntryNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetEntryNodeSchemaAwareBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetNodeSchemaAwareBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapEntryNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapEntryNodeSchemaAwareBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapNodeSchemaAwareBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedLeafSetNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedLeafSetNodeSchemaAwareBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedMapNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedMapNodeSchemaAwareBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableUnkeyedListEntryNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableUnkeyedListNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableYangModeledAnyXmlNodeBuilder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/valid/DataNodeContainerValidator.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/valid/DataValidationException.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableDataContainerNode.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedNode.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedSimpleValueNode.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueNode.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/CloneableMap.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/LazyLeafOperations.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/LazyValues.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/UnmodifiableChildrenMap.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractCursor.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractCursorAware.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractDataTreeCandidate.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractDataTreeTip.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractModifiedNodeBasedCandidateNode.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractNodeContainerModificationStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractReadyIterator.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AugmentationModificationStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AutomaticLifecycleMixin.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseEnforcer.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ChildTrackingPolicy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ChoiceModificationStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ContainerModificationStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataNodeContainerModificationStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataTreeState.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTree.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeCandidate.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeFactory.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModification.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModificationCursor.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeSnapshot.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeSnapshotCursor.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/LatestOperationHolder.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/LeafSetModificationStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ListEntryModificationStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/LogicalOperation.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MandatoryLeafEnforcer.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MapModificationStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MinMaxElementsValidation.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationApplyOperation.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationPath.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModifiedNode.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NodeModification.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NoopDataTreeCandidate.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NormalizedNodeContainerSupport.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NotUpgradableRootApplyStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/OperationWithModification.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/RootApplyStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaAwareApplyOperation.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaValidationFailedException.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/StructuralContainerModificationStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/UnkeyedListModificationStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/UpgradableRootApplyStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ValueNodeModificationStrategy.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/IgnoreWhiteCharsDiffListener.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/MemoryConsumption.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/BinaryCodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/BitsCodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/BooleanCodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/DecimalCodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/EmptyCodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/EnumCodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Int16CodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Int32CodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Int64CodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Int8CodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/StringCodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/StringPatternCheckingCodecTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/TypeDefinitionAwareCodecTestHelper.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Uint16CodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Uint32CodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Uint64CodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Uint8CodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/UnionCodecStringTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/Bug7844Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/Bug8713Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/DataTreeCandidateValidatorTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/DataTreeCandidateValidatorTest2.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/DataTreeCandidateValidatorTest3.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/LeafRefContextTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/LeafRefContextTreeBuilderTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/BuilderTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriterTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToNodesTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedDataBuilderTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeUtilsTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaUtilsTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueAttrNodeTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractTestModelTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug2690Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug3674Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug4295Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug4454Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug5830Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug5968MergeTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug5968Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug8291Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseAugmentTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseExclusionTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ConcurrentTreeModificationTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ConfigStatementValidationTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataTreeCandidatesTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataTreeTransactionTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ErrorReportingTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ListConstraintsValidation.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MandatoryLeafTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MapEntryRootTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationMetadataTreeTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/OrderedListTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/StoreTreeNodesTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/StructuralApplyModificationTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/TestModel.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/YT776Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/MyXmlGenerator.groovy [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/anyxml-support/serialization/baz.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/anyxml-support/serialization/baz.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/anyxml-support/serialization/yang-ext.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/anyxml-support/xml/bar.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/anyxml-support/xml/foo.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/anyxml-support/yang/bar.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/anyxml-support/yang/foo.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/anyxml-support/yang/yang-ext.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug-4295/foo.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug-4454-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5396/xml/foo.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5396/xml/foo2.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5396/xml/foo3.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5396/xml/foo4.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5396/xml/foo5.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5396/xml/invalid-foo.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5396/yang/foo.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5446/xml/foo.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5446/yang/foo.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5830/foo-multiple.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5830/foo-non-presence.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5830/foo-presence.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug5968/foo.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug6392/foo.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug6392/foo.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug7844/bar.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug7844/baz.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug7844/foo.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug8291/foo.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug8713/bar.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/bug8713/foo.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/builder-test/immutable-ordered-map-node.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/case-augment-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/case-exclusion-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/config01.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/config02.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/controller-network.xsd [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/controller-network.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/dom-serializer-test/serializer-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/filter-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/foo.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/ietf-inet-types@2010-09-24.xsd [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/ietf-inet-types@2010-09-24.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/leafref-context-test/correct-modules/import-mod.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/leafref-context-test/correct-modules/leafref-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/leafref-context-test/correct-modules/leafref-test2.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/leafref-context-test/incorrect-modules/leafref-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/leafref-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/leafref-validation/leafref-validation.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/leafref-validation/leafref-validation2.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/leafref-validation/leafref-validation3.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/list-constraints-validation-test-model.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/log4j-test.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/mandatory-leaf-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/odl-datastore-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/ordered-list-modification-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/codec/xml/iid-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/codec/xml/xml-doc-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/codec/xml/xml-doc-test2.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/config02-shadow.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/config02.groovy [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/config02g-shadow.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/mutableNodesConfig.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/augment_choice_hell.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/augment_choice_hell_ok.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/augment_choice_hell_ok2.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/augment_choice_hell_ok3.xml [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-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/simple.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/simple2.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/simple_xml_with_attributes.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/rpc-getDeviceEquipment.xml [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/schema-utils-test/foo.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/schema-utils-test/name-conflicts.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/string-pattern-checking-codec-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/yt776/yt776.yang [new file with mode: 0644]
yang/yang-data-jaxen/pom.xml [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/ConverterNamespaceContext.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/ExprListener.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/ExprWalker.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenDocument.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenSchemaContext.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenSchemaContextFactory.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenXPath.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/LeafrefXPathStringParsingPathArgumentBuilder.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContext.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContextSupport.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeNavigator.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/Operator.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/StepListener.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/YangFunctionContext.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/osgi/Activator.java [new file with mode: 0644]
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/osgi/package-info.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/BitIsSetXPathFunctionTest.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/DerefXPathFunctionTest.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/DerivedFromXPathFunctionTest.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/EnumValueXPathFunctionTest.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenTest.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/ReMatchXPathFunctionTest.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/TestUtils.java [new file with mode: 0644]
yang/yang-data-jaxen/src/test/resources/test/documentTest/test2.yang [new file with mode: 0644]
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/bit-is-set-function/foo-invalid.yang [new file with mode: 0644]
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/bit-is-set-function/foo.yang [new file with mode: 0644]
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/deref-function-iid/foo.yang [new file with mode: 0644]
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/deref-function-leafref/foo.yang [new file with mode: 0644]
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/derived-from-function/bar-invalid.yang [new file with mode: 0644]
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/derived-from-function/bar.yang [new file with mode: 0644]
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/derived-from-function/foo.yang [new file with mode: 0644]
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/enum-value-function/foo-invalid.yang [new file with mode: 0644]
yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/enum-value-function/foo.yang [new file with mode: 0644]
yang/yang-data-transform/pom.xml [new file with mode: 0644]
yang/yang-data-transform/src/main/java/org/opendaylight/yangtools/transform/NormalizedNodeTransformations.java [new file with mode: 0644]
yang/yang-data-transform/src/main/java/org/opendaylight/yangtools/transform/QNameModuleReplacementFunction.java [new file with mode: 0644]
yang/yang-data-transform/src/main/java/org/opendaylight/yangtools/transform/QNameReplacementFunction.java [new file with mode: 0644]
yang/yang-data-transform/src/main/java/org/opendaylight/yangtools/transform/QNameTransformingStreamWriter.java [new file with mode: 0644]
yang/yang-data-util/pom.xml [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractInteriorContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractLeafContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractLeafNodeContext.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractMixinContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractModuleStringIdentityrefCodec.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractModuleStringInstanceIdentifierCodec.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractMountPointDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractNamespaceCodec.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractOpaqueContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringIdentityrefCodec.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringInstanceIdentifierCodec.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringUnionCodec.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnyXmlContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnyXmlNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnydataContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnydataNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AugmentationContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CaseNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ChoiceNodeContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ChoiceNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CompositeNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerSchemaNodes.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/DataContainerContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/DataSchemaContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/DataSchemaContextTree.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ImmutableNormalizedAnydata.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafInterner.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListEntryContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListEntryNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafsetEntryInterner.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListEntryNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListItemContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ModuleStringIdentityrefCodec.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/MountPointData.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/OperationAsContainer.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/OrderedLeafListMixinContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/OrderedMapMixinContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ParserStreamUtils.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/SimpleNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/SingleChildDataNodeContainer.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/UnkeyedListItemContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/UnkeyedListMixinContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/UnorderedLeafListMixinContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/UnorderedMapMixinContextNode.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/XpathStringParsingPathArgumentBuilder.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/YangModeledAnyXmlNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/AbstractCodecFactory.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/CodecCache.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/IdentityCodecUtil.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/LazyCodecCache.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/NoopCodecCache.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/PrecomputedCodecCache.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/QNameCodecUtil.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/SharedCodecCache.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/TypeAwareCodec.java [new file with mode: 0644]
yang/yang-data-util/src/test/java/org/opendaylight/yangtools/yang/data/util/codec/DataSchemaContextTreeTest.java [new file with mode: 0644]
yang/yang-data-util/src/test/java/org/opendaylight/yangtools/yang/data/util/codec/IdentityCodecUtilTest.java [new file with mode: 0644]
yang/yang-data-util/src/test/resources/dataschemacontext.yang [new file with mode: 0644]
yang/yang-data-util/src/test/resources/yangtools846.yang [new file with mode: 0644]
yang/yang-data-xpath-api/pom.xml [new file with mode: 0644]
yang/yang-data-xpath-api/src/main/java/org/opendaylight/yangtools/yang/data/api/xpath/DataTreeCandidateDocument.java [new file with mode: 0644]
yang/yang-data-xpath-api/src/main/java/org/opendaylight/yangtools/yang/data/api/xpath/NormalizedNodeDocument.java [new file with mode: 0644]
yang/yang-data-xpath-api/src/main/java/org/opendaylight/yangtools/yang/data/api/xpath/XPathDocument.java [new file with mode: 0644]
yang/yang-data-xpath-api/src/main/java/org/opendaylight/yangtools/yang/data/api/xpath/package-info.java [new file with mode: 0644]
yang/yang-maven-plugin-it/.gitignore [new file with mode: 0644]
yang/yang-maven-plugin-it/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/it/YangToSourcesPluginTestIT.java [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/AdditionalConfig/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/Correct/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/src/main/resources/testfile1.yang [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/src/main/resources/testfile2.yang [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/src/main/resources/testfile3.yang [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest2/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest2/yang/private.yang [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/Generator/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/InvalidVersion/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/MissingYangInDep/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/MissingYangInDep/yang/private.yang [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/NoGenerators/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/NoOutputDir/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/NoYangFiles/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/UnknownGenerator/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/YangRootNotExist/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/files/testfile1.yang [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/files/testfile2.yang [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/files/testfile3.yang [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/resources/test-parent/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-spi/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin-spi/src/main/java/org/opendaylight/yangtools/yang2sources/spi/BasicCodeGenerator.java [new file with mode: 0644]
yang/yang-maven-plugin-spi/src/main/java/org/opendaylight/yangtools/yang2sources/spi/BuildContextAware.java [new file with mode: 0644]
yang/yang-maven-plugin-spi/src/main/java/org/opendaylight/yangtools/yang2sources/spi/MavenProjectAware.java [new file with mode: 0644]
yang/yang-maven-plugin-spi/src/test/java/org/opendaylight/yangtools/yang2sources/spi/CodeGeneratorTestImpl.java [new file with mode: 0644]
yang/yang-maven-plugin/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ConfigArg.java [new file with mode: 0644]
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ContextHolder.java [new file with mode: 0644]
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/GeneratedDirectories.java [new file with mode: 0644]
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ProcessorModuleReactor.java [new file with mode: 0644]
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ScannedDependency.java [new file with mode: 0644]
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/Util.java [new file with mode: 0644]
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangProvider.java [new file with mode: 0644]
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangSourceFromDependency.java [new file with mode: 0644]
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesMojo.java [new file with mode: 0644]
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java [new file with mode: 0644]
yang/yang-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml [new file with mode: 0644]
yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/GenerateSourcesTest.java [new file with mode: 0644]
yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/ScannedDependencyTest.java [new file with mode: 0644]
yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/UtilTest.java [new file with mode: 0644]
yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesMojoTest.java [new file with mode: 0644]
yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessorTest.java [new file with mode: 0644]
yang/yang-maven-plugin/src/test/resources/test.yang [new file with mode: 0644]
yang/yang-maven-plugin/src/test/resources/test2.yang [new file with mode: 0644]
yang/yang-maven-plugin/src/test/resources/tests/META-INF/yang/test.yang [new file with mode: 0644]
yang/yang-maven-plugin/src/test/resources/tests/META-INF/yang/test2.yang [new file with mode: 0644]
yang/yang-maven-plugin/src/test/resources/yang-source-from-file.yang [new file with mode: 0644]
yang/yang-maven-plugin/src/test/resources/yang/excluded-file.yang [new file with mode: 0644]
yang/yang-maven-plugin/src/test/resources/yang/mock.yang [new file with mode: 0644]
yang/yang-model-api/pom.xml [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ActionDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ActionNodeContainer.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AddedByUsesAware.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AnydataSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AnyxmlSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AugmentationSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AugmentationTarget.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/CaseSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ChoiceSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ConstraintMetaDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ContainerSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/CopyableNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DataNodeContainer.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DataSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DerivableSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DeviateDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DeviateKind.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Deviation.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DocumentedNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/EffectiveModelContext.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/EffectiveModelContextListener.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/EffectiveModelContextProvider.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ElementCountConstraint.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ElementCountConstraintAware.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ExtensionDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/FeatureDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/GroupingDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/HelperMethods.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/IdentitySchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/LeafListSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/LeafSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ListSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/MandatoryAware.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Module.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ModuleImport.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/MustConstraintAware.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/MustDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NamespaceRevisionAware.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NotificationDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NotificationNodeContainer.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/OperationDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/PathExpression.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/RevisionAwareXPath.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/RpcDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaContext.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaContextListener.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaContextProvider.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaPath.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Status.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/TypeAware.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/TypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/TypedDataSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/UniqueConstraint.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/UnknownSchemaNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/UsesNode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/WhenConditionAware.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/YangStmtMapping.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/AbstractStatementDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/ArgumentDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DefaultStatementDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/EffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/ForwardingEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/IdentifierNamespace.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/ModelStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/StatementDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/StatementSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/package-info.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/package-info.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ActionEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ActionStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ActionStatementAwareDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AnydataEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AnydataStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AnyxmlEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AnyxmlStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ArgumentEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ArgumentStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AugmentEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AugmentStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BaseEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BaseStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BelongsToEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BelongsToStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BitEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BitStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BodyDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/CaseEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/CaseEffectiveStatementNamespace.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/CaseStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ChoiceEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ChoiceStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ConfigEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ConfigStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ConfigStatementAwareDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ConstrainedDocumentedDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ContactEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ContactStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ContainerEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ContainerStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataDefinitionAwareDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataDefinitionStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataTreeAwareEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataTreeEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DefaultEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DefaultStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DefaultStatementAwareDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DescriptionEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DescriptionStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DeviateEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DeviateStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DeviationEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DeviationStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DocumentedDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/EffectiveStatementNamespace.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/EnumEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/EnumStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ErrorAppTagEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ErrorAppTagStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ErrorMessageEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ErrorMessageStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ExtensionEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ExtensionEffectiveStatementNamespace.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ExtensionStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FeatureEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FeatureEffectiveStatementNamespace.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FeatureStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FractionDigitsEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FractionDigitsStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/GroupingEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/GroupingStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IdentityEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IdentityEffectiveStatementNamespace.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IdentityStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IfFeatureAwareDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IfFeatureEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IfFeatureExpr.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IfFeatureStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ImportEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ImportStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IncludeEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IncludeStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/InputEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/InputStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/KeyEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/KeyStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LeafEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LeafListEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LeafListStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LeafStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LengthEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LengthStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LinkageDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ListEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ListStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MandatoryEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MandatoryStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MandatoryStatementAwareDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MaxElementsEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MaxElementsStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MetaDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MinElementsEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MinElementsStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModifierEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModifierStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModuleEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModuleHeaderGroup.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModuleStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MultipleElementsDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MustEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MustStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MustStatementAwareDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NamespaceEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NamespaceStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NamespacedEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NotificationEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NotificationStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NotificationStatementAwareDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OperationDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OrderedByEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OrderedByStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OrganizationEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OrganizationStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OutputEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OutputStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PathEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PathStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PatternEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PatternStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PositionEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PositionStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PrefixEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PrefixStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PresenceEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PresenceStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RangeEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RangeStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ReferenceEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ReferenceStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RefineEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RefineStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RequireInstanceEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RequireInstanceStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionAwareDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionDateEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionDateStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/Rfc6020AbnfRule.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RpcEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RpcStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SchemaNodeIdentifier.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SchemaTreeAwareEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SchemaTreeEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/StatusEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/StatusStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SubmoduleEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SubmoduleStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypeAwareDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypeDefinitionAware.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypeEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypeStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypedefEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypedefStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UniqueEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UniqueStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnitsEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnitsStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnknownEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnknownStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnrecognizedEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnrecognizedStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnresolvedNumber.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UsesEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UsesStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ValueEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ValueRange.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ValueStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/WhenEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/WhenStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/WhenStatementAwareDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/YangVersionEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/YangVersionStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/YinElementEffectiveStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/YinElementStatement.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/compat/ActionNodeContainerCompat.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/compat/NotificationNodeContainerCompat.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/compat/package-info.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/BinaryTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/BitsTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/BooleanTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/DecimalTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/EmptyTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/EnumTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/IdentityTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/IdentityrefTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/InstanceIdentifierTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Int16TypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Int32TypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Int64TypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Int8TypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/LeafrefTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/LengthConstraint.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/LengthRestrictedTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/ModifierKind.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/PatternConstraint.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/RangeConstraint.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/RangeRestrictedTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/RequireInstanceRestrictedTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/StringTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/TypeDefinitions.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Uint16TypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Uint32TypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Uint64TypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Uint8TypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/UnionTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/UnknownTypeDefinition.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/DelegatedYangTextSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/DelegatedYinTextSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/EffectiveModelContextFactory.java [new file with mode: 0644]
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/ResourceYangTextSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/ResourceYinTextSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/RevisionSourceIdentifier.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactoryConfiguration.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceException.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SemVerSourceIdentifier.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/StatementParserMode.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangSchemaSourceRepresentation.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextFileSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinDomSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSourceRepresentation.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinTextFileSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinTextSchemaSource.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinXmlSchemaSource.java [new file with mode: 0644]
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 [new file with mode: 0644]
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 [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java [new file with mode: 0644]
yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatement1.java [new file with mode: 0644]
yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatement2.java [new file with mode: 0644]
yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatementTest.java [new file with mode: 0644]
yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/EffectiveStatement1.java [new file with mode: 0644]
yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/EffectiveStatementTest.java [new file with mode: 0644]
yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/Effectivestatement2.java [new file with mode: 0644]
yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/stmt/ActionStatementAwareDeclaredStatementTest.java [new file with mode: 0644]
yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSourceTest.java [new file with mode: 0644]
yang/yang-model-export/pom.xml [new file with mode: 0644]
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/DeclaredStatementFormatter.java [new file with mode: 0644]
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ExportUtils.java [new file with mode: 0644]
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ModuleNamespaceContext.java [new file with mode: 0644]
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YangTextSnippet.java [new file with mode: 0644]
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YangTextSnippetIterator.java [new file with mode: 0644]
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinExportUtils.java [new file with mode: 0644]
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinXMLEventReader.java [new file with mode: 0644]
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinXMLEventReaderFactory.java [new file with mode: 0644]
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/AbstractYinExportTest.java [new file with mode: 0644]
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug2444Test.java [new file with mode: 0644]
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug4504Test.java [new file with mode: 0644]
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug5531Test.java [new file with mode: 0644]
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug6856Test.java [new file with mode: 0644]
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/SchemaContextEmitterTest.java [new file with mode: 0644]
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/SimpleModuleTest.java [new file with mode: 0644]
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/YangTextSnippetTest.java [new file with mode: 0644]
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/YinExportTestUtils.java [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/action.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/anydata.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/binary-spec.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/default.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/deviation-target.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/deviation.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/identities.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/import-yang1.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/import.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/include.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/leafref-yang1.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/leafref.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/modifier-yang1.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/modifier.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/must-yang1.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/must.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/notification.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/submodule.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yang/unique.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin-effective-emitter/notification.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/action.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/anydata.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/binary-spec.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/default.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/deviation-target@2017-01-20.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/deviation@2017-01-20.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/identities.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/import-yang1@1970-01-01.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/import.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/include@2017-06-26.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/leafref-yang1.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/leafref.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/modifier-yang1.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/modifier.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/must-yang1.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/must.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/notification.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/submodule.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug2444/yin/unique.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug4504/bar.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug4504/foo.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug5531/foo.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug6856/bar.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/bugs/bug6856/foo.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/schema-context-emitter-test/foo@2016-08-05.yang [new file with mode: 0644]
yang/yang-model-export/src/test/resources/schema-context-emitter-test/foo@2016-08-05.yin [new file with mode: 0644]
yang/yang-model-export/src/test/resources/yang/ietf-inet-types@2010-09-24.yang [new file with mode: 0644]
yang/yang-model-util/pom.xml [new file with mode: 0644]
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/FilesystemSchemaSourceCache.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/AbstractIdentifiableSchemaContextProvider.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractPathExpression.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractSchemaContext.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractSchemaContextProvider.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/BaseConstraints.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/BaseTypes.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/DataNodeIterator.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/EffectiveAugmentationSchema.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/FilteringSchemaContextProxy.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/ModuleDependencySort.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/ModuleNameNamespaceContext.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PatternConstraintImpl.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/RegexUtils.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/RevisionAwareXPathImpl.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaNodeUtils.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SimpleSchemaContext.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/package-info.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractBaseType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractDerivedType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractLengthRestrictedBaseType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractLengthRestrictedDerivedType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractLengthRestrictedType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRangeRestrictedBaseType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRangeRestrictedDerivedType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRangeRestrictedType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRestrictedType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRestrictedTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractTypeDefinition.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseBinaryType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseBitsType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseBooleanType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseDecimalType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseEmptyType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseEnumerationType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseIdentityrefType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInstanceIdentifierType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInt16Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInt32Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInt64Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInt8Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseLeafrefType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseStringType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseTypes.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUint16Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUint32Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUint64Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUint8Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUnionType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BitBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BitImpl.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BitsTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/CompatUtils.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/ConcreteTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/ConcreteTypes.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DecimalTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedBinaryType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedBitsType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedBooleanType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedDecimalType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedEmptyType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedEnumerationType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedIdentityrefType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInstanceIdentifierType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInt16Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInt32Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInt64Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInt8Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedLeafrefType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedStringType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedTypes.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUint16Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUint32Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUint64Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUint8Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUnionType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/EnumPairBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/EnumPairImpl.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/EnumerationTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/IdentityrefTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InstanceIdentifierTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InvalidBitDefinitionException.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InvalidEnumDefinitionException.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InvalidLengthConstraintException.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InvalidRangeConstraintException.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/JavaLengthConstraints.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/LeafrefTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/LengthRestrictedTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/NumberUtil.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RangeRestrictedTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RangeRestrictedTypeBuilderWithBase.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RequireInstanceRestrictedTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/ResolvedLengthConstraint.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/ResolvedRangeConstraint.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedBinaryType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedBitsType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedBooleanType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedDecimalType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedEmptyType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedEnumerationType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedIdentityrefType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInstanceIdentifierType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInt16Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInt32Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInt64Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInt8Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedLeafrefType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedStringType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedTypes.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUint16Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUint32Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUint64Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUint8Type.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUnionType.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/StringTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/TypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/UnionTypeBuilder.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/repo/util/FilesystemSchemaSourceCacheTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCacheTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistrationTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformerTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/BaseTypesTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/BinaryTypeTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/BitsTypeTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/BooleanTypeTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug4079Test.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug4969Test.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug5410Test.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/DataNodeIteratorTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/EmptyTypeTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Int8Test.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/LeafrefTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/ModuleDependencySortTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/PatternConstraintImplTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextProxyTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtilTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaNodeUtilsTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SimpleSchemaContextTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/type/BitImplTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/type/EnumPairImplTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/type/NumberUtilTest.java [new file with mode: 0644]
yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/type/TypeTest.java [new file with mode: 0644]
yang/yang-model-validator/pom.xml [new file with mode: 0644]
yang/yang-model-validator/src/main/java/org/opendaylight/yangtools/yang/validator/Main.java [new file with mode: 0644]
yang/yang-model-validator/src/main/java/org/opendaylight/yangtools/yang/validator/SystemTestUtils.java [new file with mode: 0644]
yang/yang-model-validator/src/main/yang/example.yang [new file with mode: 0644]
yang/yang-parser-api/pom.xml [new file with mode: 0644]
yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParser.java [new file with mode: 0644]
yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParserException.java [new file with mode: 0644]
yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParserFactory.java [new file with mode: 0644]
yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangSyntaxErrorException.java [new file with mode: 0644]
yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/package-info.java [new file with mode: 0644]
yang/yang-parser-impl/pom.xml [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/DefaultReactors.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserFactoryImpl.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/osgi/Activator.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/osgi/package-info.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/package-info.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractYangTextSchemaSourceRegistration.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/RevisionDependencyResolver.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SemVerDependencyResolver.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/YangTextSchemaContextResolver.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/YangTextSchemaSourceRegistration.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolverTest.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/MultipleRevImportBug6875Test.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/OpenconfigVerSharedSchemaRepositoryTest.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SchemaContextFactoryDeviationsTest.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SettableSchemaProvider.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactoryTest.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepositoryTest.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepositoryWithFeaturesTest.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/YangTextSchemaContextResolverTest.java [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/bug9195/bar-invalid.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/bug9195/bar.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/bug9195/baz-invalid.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/bug9195/baz.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/bug9195/foo-invalid.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/bug9195/foo.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/bug9195/foobar.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/ietf/iana-timezones@2012-07-09.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/ietf/ietf-inet-types@2010-09-24.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/ietf/network-topology@2013-10-21.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/if-feature-resolution-test/shared-schema-repository/foobar.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/model/bar.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/model/baz.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/model/foo.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/model/subfoo.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/no-revision/imported.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/no-revision/imported@2012-12-12.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/no-revision/module-without-revision.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/no-revision/top@2012-10-10.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/openconfig-version/openconfigver-shared-schema-repository/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/openconfig-version/openconfigver-shared-schema-repository/foo.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/openconfig-version/openconfigver-shared-schema-repository/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/openconfig-version/shared-schema-repository/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/openconfig-version/shared-schema-repository/foo.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/openconfig-version/shared-schema-repository/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/parse-methods/dependencies/m2@2013-09-30.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-0/bar@1999-01-01.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-0/bar@2017-02-06.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-0/foo.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-1/bar@1970-01-01.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-1/bar@1999-01-01.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-1/bar@2017-02-06.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-1/foo.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/yang-text-schema-context-resolver-test/bar.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/yang-text-schema-context-resolver-test/baz.yang [new file with mode: 0644]
yang/yang-parser-impl/src/test/resources/yang-text-schema-context-resolver-test/foo.yang [new file with mode: 0644]
yang/yang-parser-reactor/pom.xml [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/BuildGlobalContext.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/CrossSourceStatementReactor.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/EffectiveSchemaContext.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ModifierImpl.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/NamespaceBehaviourWithListeners.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/NamespaceStorageSupport.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReactorDeclaredModel.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/RootStatementContext.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SimpleNamespaceContext.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SourceSpecificContext.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextWriter.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementDefinitionContext.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementMap.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SubstatementContext.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/VirtualNamespaceContext.java [new file with mode: 0644]
yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/package-info.java [new file with mode: 0644]
yang/yang-parser-rfc7950/pom.xml [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/IfFeatureExpressionLexer.g4 [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/IfFeatureExpressionParser.g4 [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/LeafRefPathLexer.g4 [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/LeafRefPathParser.g4 [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/YangStatementLexer.g4 [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/YangStatementParser.g4 [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/openconfig/stmt/OpenConfigVersionEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/openconfig/stmt/OpenConfigVersionStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/openconfig/stmt/OpenConfigVersionSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/antlr/AbstractParserErrorListener.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/antlr/SourceExceptionParser.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/ChildSchemaNodeNamespace.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/ModuleQNameToPrefix.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/URIStringToImportPrefix.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/package-info.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/CustomCrossSourceStatementReactorBuilder.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/RFC7950Reactors.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/YangValidationBundles.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/package-info.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/ASTSchemaSource.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/ArgumentContextUtils.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/StatementContextVisitor.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/StatementSourceReferenceHandler.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/TextToASTTransformer.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangErrorListener.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangModelDependencyInfo.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangStatementStreamSource.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YinStatementStreamSource.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YinTextToDomTransformer.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractConstraintEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveContainerSchemaNode.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveDataSchemaNode.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveDocumentedDataNodeContainer.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveDocumentedNode.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveModule.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveOperationContainerSchemaNode.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveOperationDefinition.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveSchemaNode.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveSimpleDataNodeContainer.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractListConstraintEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractRootStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractSchemaEffectiveDocumentedNode.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ArgumentUtils.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/DeclaredEffectiveStatementBase.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStatementBase.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStmtUtils.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/StmtNamespaceContext.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/UndeclaredEffectiveStatementBase.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/UnknownEffectiveStatementBase.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/WithExpressionImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/XPathSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/action/ActionEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/action/ActionStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/action/ActionStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anydata/AnydataEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anydata/AnydataStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anydata/AnydataStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anyxml/AnyxmlEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anyxml/AnyxmlStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anyxml/AnyxmlStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/argument/ArgumentEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/argument/ArgumentStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/argument/ArgumentStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AbstractAugmentStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentImplicitHandlingNamespace.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/base/BaseEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/base/BaseStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/base/BaseStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/belongs_to/BelongsToEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/belongs_to/BelongsToStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/belongs_to/BelongsToStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/AbstractBitStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/BitEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/BitStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/BitStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/BitStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/AbstractCaseStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/CaseEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/CaseStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/CaseStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/CaseStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/AbstractChoiceStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/ChoiceEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/ChoiceStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/ChoiceStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/ChoiceStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/ConfigEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/ConfigStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/ConfigStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/EmptyConfigEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/EmptyConfigStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/contact/ContactEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/contact/ContactStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/contact/ContactStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/AbstractContainerStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/ContainerEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/ContainerStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/ContainerStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/ContainerStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/default_/DefaultEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/default_/DefaultStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/default_/DefaultStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/description/DescriptionEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/description/DescriptionStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/description/DescriptionStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/AbstractDeviateStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/DeviateEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/DeviateStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/DeviateStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/DeviateStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviation/DeviationEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviation/DeviationStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviation/DeviationStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/AbstractEnumStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/EnumEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/EnumStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/EnumStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/EnumStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_app_tag/ErrorAppTagEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_app_tag/ErrorAppTagStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_app_tag/ErrorAppTagStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_message/ErrorMessageEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_message/ErrorMessageStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_message/ErrorMessageStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ExtensionEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ExtensionStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ExtensionStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ModelDefinedStatementDefinition.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ModelDefinedStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/UnrecognizedEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/UnrecognizedStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/UnrecognizedStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/feature/FeatureEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/feature/FeatureStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/feature/FeatureStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/fraction_digits/FractionDigitsEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/fraction_digits/FractionDigitsStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/fraction_digits/FractionDigitsStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/AbstractGroupingStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/GroupingEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/GroupingStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/GroupingStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/GroupingStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/AbstractIdentityStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/IdentityEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/IdentityStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/IdentityStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/IdentityStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeatureEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeaturePredicateVisitor.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeatureStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeatureStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/AbstractImportStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/ImportEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/ImportStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/ImportStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/ImportStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/RevisionImport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/SemanticVersionImport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/AbstractIncludeStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/IncludeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/IncludeStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/IncludeStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/IncludeStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/AbstractInputStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/InputEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/InputStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/InputStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/InputStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf/LeafEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf/LeafStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf/LeafStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/AbstractLeafListStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/LeafListEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/LeafListStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/LeafListStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/LeafListStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/length/LengthEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/length/LengthStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/length/LengthStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/AbstractListStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/EmptyMandatoryEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/EmptyMandatoryStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/MandatoryEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/MandatoryStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/MandatoryStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/max_elements/MaxElementsEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/max_elements/MaxElementsStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/max_elements/MaxElementsStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/min_elements/MinElementsEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/min_elements/MinElementsStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/min_elements/MinElementsStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/modifier/ModifierEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/modifier/ModifierStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/modifier/ModifierStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/AbstractModuleStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/ModuleEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/ModuleStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/ModuleStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/ModuleStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/must/MustEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/must/MustStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/must/MustStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/namespace/NamespaceEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/namespace/NamespaceStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/namespace/NamespaceStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/AbstractNotificationStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/NotificationEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/NotificationStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/NotificationStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/NotificationStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ordered_by/OrderedByEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ordered_by/OrderedByStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ordered_by/OrderedByStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/organization/OrganizationEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/organization/OrganizationStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/organization/OrganizationStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/AbstractOutputStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/OutputEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/OutputStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/OutputStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/OutputStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/package-info.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/ParsedPathExpression.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathExpressionParser.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/UnparsedPathExpression.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/AbstractPatternStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternConstraintImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/position/PositionEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/position/PositionStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/position/PositionStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/prefix/PrefixEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/prefix/PrefixStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/prefix/PrefixStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/presence/PresenceEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/presence/PresenceStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/presence/PresenceStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/range/RangeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/range/RangeStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/range/RangeStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/reference/ReferenceEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/reference/ReferenceStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/reference/ReferenceStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/AbstractRefineStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/RefineEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/RefineStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/RefineStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/RefineStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/require_instance/RequireInstanceEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/require_instance/RequireInstanceStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/require_instance/RequireInstanceStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision/RevisionEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision/RevisionStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision/RevisionStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision_date/RevisionDateEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision_date/RevisionDateStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision_date/RevisionDateStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/AbstractRpcStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/RpcEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/RpcStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/RpcStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/RpcStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/status/StatusEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/status/StatusStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/status/StatusStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/AbstractSubmoduleStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/SubmoduleEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/SubmoduleStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/SubmoduleStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/SubmoduleStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractIdentityRefSpecificationSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractLeafrefSpecificationSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractTypeEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractTypeStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BinaryTypeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BitsSpecificationEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BitsSpecificationImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BitsSpecificationSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BitsTypeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BooleanTypeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BuiltinEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BuiltinTypeStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/Decimal64SpecificationEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/Decimal64SpecificationImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/Decimal64SpecificationSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/DecimalTypeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EffectiveTypeUtil.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EmptyTypeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EnumSpecificationEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EnumSpecificationImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EnumSpecificationSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EnumTypeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityRefSpecificationEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityRefSpecificationImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityRefSpecificationRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityrefSpecificationRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityrefTypeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/InstanceIdentifierSpecificationEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/InstanceIdentifierSpecificationImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/InstanceIdentifierSpecificationSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/InstanceIdentifierTypeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IntegralTypeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefSpecificationEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefSpecificationImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefSpecificationRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefSpecificationRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefTypeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/NumericalRestrictionsEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/NumericalRestrictionsImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/NumericalRestrictionsSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/StringRestrictionsEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/StringRestrictionsImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/StringRestrictionsSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/StringTypeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/TypeStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/TypeStatementRFC6020Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/TypeStatementRFC7950Support.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/UnionSpecificationEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/UnionSpecificationImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/UnionSpecificationSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/UnionTypeEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/typedef/TypedefEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/typedef/TypedefStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/typedef/TypedefStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/unique/UniqueEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/unique/UniqueStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/unique/UniqueStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/units/UnitsEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/units/UnitsStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/units/UnitsStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/uses/UsesEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/uses/UsesStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/uses/UsesStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/value/ValueEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/value/ValueStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/value/ValueStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/when/WhenEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/when/WhenStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/when/WhenStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yang_version/YangVersionEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yang_version/YangVersionStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yang_version/YangVersionStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yin_element/YinElementEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yin_element/YinElementStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yin_element/YinElementStatementSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/AugmentContextUtilsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/SchemaContextUtilTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangModelDependencyInfoTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/Bug5410Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/YT826Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/ElementCountConstraintsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/ActionStatementTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6867BasicTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6868Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6869Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6870Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6871Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6874Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6876Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6878Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6880Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6883Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6884Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6885Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6886Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6887Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6897Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6901Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug9241Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/IdentityStatementTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/IdentityrefStatementTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/LeafrefStatementTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentArgumentParsingTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentProcessTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentSimplestTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentToExtensionTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug1412Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug1413Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug2291Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug2872Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug3799Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug3859Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug394Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4231Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4410Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4456Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4610Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4623Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4933Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5059Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5101Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5335Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5396Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5437Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5481Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5518Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5550Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5693Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5712Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5884Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5899Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5942Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5946Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6131Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6150Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6180Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6183Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6240Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6316Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6410Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6491Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6669Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6771Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6961Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6972Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7037Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7038Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7146Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7424Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7440Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7480Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7865Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7879Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7954Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8126Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8307Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8597Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8831Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8922Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug9005Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug9242Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug9244Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/CaseStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ChoiceStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ControllerStmtParserTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/DeclaredStatementsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/DeviationResolutionTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/DeviationStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveBuildTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveIdentityTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveModuleTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveModulesAndSubmodulesTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveSchemaContextTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveStatementTypeTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveUsesRefineAndConstraintsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ExtensionStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/GroupingAndUsesStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/GroupingTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IdentityIssueIetfRoutingTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IdentityStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IfFeatureResolutionTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ImportResolutionBasicTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ImportRevisionsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IncludeResolutionTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IncludeRevisionsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IncludedStmtsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/KeyTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ListKeysTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ListTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/MoreRevisionsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/MustAndWhenStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/NameCollisionWithinCaseTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/NotificationStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/OrderingTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ParsingExtensionValueTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/RpcStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/StmtTestUtils.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/SubstatementValidatorTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TestUtils.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TwoRevisionsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TypedefConstraintsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TypedefSubStmtsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TypesResolutionTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/UsesAugmentTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT838Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT841Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT857Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT893Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT911Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT956Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT971Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT983Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangFileStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserIdentityTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserNegativeTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserSimpleTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserWithContextTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangTypes2StmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangTypesStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionBorderCaseTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionComplexTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionDefaultsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionIgnoringRevisionTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionImportTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionMultipleImportTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionPositionTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/yin/YinOpenconfigVersionTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/test/Bug5200Test.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/AbstractYinModulesTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileAugmentStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileChoiceStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileExtensionStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileFeatureStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileGroupingStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileHeaderStmtsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileIdentityStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileImportStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileIncludeStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileLeafListStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileListStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileMetaStmtsTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileRpcStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileTypeDefStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileUsesStmtTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/CustomInferencePipeline.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionEffectiveStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionEffectiveStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionPluginTest.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionStatementImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionsMapping.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyNamespace.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/added-by-uses-leaf-test/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/added-by-uses-leaf-test/import-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/augment-test/augment-in-augment/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/augment-test/augment-in-augment/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/augment-test/augment-in-augment/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/augment-test/augment-in-uses/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/augment-test/rpc/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/augment-test/rpc/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/correct-augment/augment-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/correct-augment/extension-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/correct-path-into-unsupported-target/augment-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/correct-path-into-unsupported-target/extension-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/incorrect-path/augment-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/incorrect-path/extension-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT827/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT838/grouping-post.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT838/grouping.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT838/typedef-post.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT838/typedef.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT841/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT857/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT893/cr.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT893/crlf.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT893/ht.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT893/lf.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT911/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT956/another-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT956/mainmodule.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT956/sub-module-1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT956/sub-module-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT971/test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/YT983/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug1412/bug1412.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug1412/extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug1413/bug1413.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug1413/extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug2291/bug2291-ext.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug2291/bug2291.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug2872/bug2872.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug3799/test_module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug3799/test_submodule.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug3859/bug3859.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug394-retest/bug394-ext.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug394-retest/bug394.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug394-retest/ietf-yang-types@2010-09-24.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug394/bug394-ext.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug394/bug394.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4079/bug4079-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4231/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4410/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4456/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4459/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4610/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4610/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4623/extension-def.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4623/string-with-ext.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4933/correct/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4933/correct/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4933/incorrect/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug4933/incorrect/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5059/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5101/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5200/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-1/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-1/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-1/sub-bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-2/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-2/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-2/sub-bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-3/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-3/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-4/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-1/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-1/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-1/sub-bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-2/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-2/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-3/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-3/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-3/sub-bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5396/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5410/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5437/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5481/module1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5481/module2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5518/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5550/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5693/foo.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5712/bad.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5884/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5899/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5942/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5946/foo-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug5946/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6131/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6150/aug-first.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6150/aug-second.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6150/target.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6180/double-quotes-single-inside.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6180/double-quotes.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6180/single-quotes.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6180/string-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6183/yang10/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6183/yang11/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/correct/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/correct/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/correct/sub-bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/correct/sub-foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/incorrect/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/incorrect/foo-imp.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/incorrect/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/incorrect/sub-foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6316/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6410/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6410/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/importedModuleRevisionOnly/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/importedModuleRevisionOnly/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/moduleRevisionOnly/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/moduleRevisionOnly/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/withRevision/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/withRevision/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/withoutRevision/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/withoutRevision/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test1/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test1/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test2/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test2/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test3/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test3/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test1/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test1/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test2/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test2/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test3/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test3/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6771/augment/typedef-bug.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6771/choice-case/typedef-bug.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6771/grouping/typedef-bug.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/bar-sub1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/foo-sub1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/foo-sub2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6972/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6972/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug6972/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7037/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7037/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7038/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7038/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7038/yang10/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7038/yang11/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7146/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7424/foo-data.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7424/foo-notification.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7424/foo-rpc-uses.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7424/foo-rpc.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7440/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7440/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files-2/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files-2/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files/sub-mod.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/foo-imp-1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/foo-imp-1@2017-01-22.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/foo-imp-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/foo-imp-imp-1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/lib-1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/lib-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/sub-foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/baz-imp@2000-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/baz-imp@2001-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/baz-imp@2002-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/foo-imp-1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/foo-imp-1@2017-01-22.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/foo-imp-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/foo-imp-imp-1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/lib-1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/lib-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/lib-3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/parent-mod.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/sub-foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/main-source-lib-source-conflict-test/child-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/main-source-lib-source-conflict-test/parent-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7865/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7879/my-event-types.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7879/my-model-alarms.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7879/my-model.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7879/my-notification-types.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7954/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7954/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug7954/subbar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8126/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8126/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/bar-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/baz-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/foo-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/foobar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8597/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8597/bar@2010-10-10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8597/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8597/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8831/invalid/inv-model.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8831/invalid/inv-model2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8831/valid/example-model.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8831/valid/example-model2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug8922/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug9005/bar-1@2000-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug9005/bar-2@2000-01-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug9005/foo@2017-07-07.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug9005/sub-foo@2017-07-07.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug9242/imp-module-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug9242/imp-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug9242/root-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug9244/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/bug9244/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/identity-ietf-routing-test/ietf-interfaces@2014-05-08.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/identity-ietf-routing-test/ietf-routing@2016-11-04.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/identity-ietf-routing-test/ietf-yang-types@2013-07-15.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/name-collision-within-case/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/name-collision-within-case/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/name-collision-within-case/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/qnameDuplicity/augment/two-cases.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/qnameDuplicity/two-same-node-in-choice/two-same-nodes-in-choice-case.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/bugs/yangtools826/example.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/case-test/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/case-test/case-test-exceptions/case/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/case-test/case-test-exceptions/choice/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/case-test/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/choice-case-type-test-models/augment-monitoring@2013-07-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/choice-case-type-test-models/choice-monitoring@2013-07-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/constraint-definitions-test/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/context-augment-test/test1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/context-augment-test/test2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/context-augment-test/test3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/context-augment-test/test4.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/context-test/deviation-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/context-test/test1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/context-test/test2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/context-test/test3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/anyxml-declared-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/augment-declared-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/child-module-declared-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/choice-declared-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/container-declared-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/imported-module-declared-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/parent-module-declared-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/root-module-declared-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/depinfo-malformed/malformed-import-rev.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/depinfo-malformed/malformed-import.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/depinfo-malformed/malformed-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/depinfo-malformed/malformed-rev.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar-invalid-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar-invalid-3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar-invalid-4.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar10-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo-invalid-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo-invalid-3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo-invalid-4.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo10-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/bar-invalid-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/bar-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/bar10-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/foo-invalid-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/foo-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/foo10-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-not-supported/imported.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-not-supported/root.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/bar-invalid-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/bar-invalid-3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/bar-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/foo-invalid-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/foo-invalid-3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/foo-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/foo-invalid-deviation-path.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/foo-invalid-deviation-target.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-stmt-test/bar-imp.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-stmt-test/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-stmt-test/foo-imp.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/deviation-stmt-test/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/effective-schema-context-test/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/effective-schema-context-test/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/effective-schema-context-test/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/extensions/ext-typedef.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/extensions/ext-use.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/grouping-test/cascade-uses.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/grouping-test/grouping-definitions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/grouping-test/uses-grouping.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/identity/duplicate-identity-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/identity/identitytest.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/identity/illegal-chained-identity-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/identity/import/dummy.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/identity/import/prefiximportidentitytest.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/identity/legal-chained-identity-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/identity/prefixidentitytest.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/ietf/iana-afn-safi@2012-06-04.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/ietf/iana-if-type@2012-06-05.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/ietf/iana-timezones@2012-07-09.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/ietf/ietf-inet-types@2010-09-24.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/ietf/ietf-yang-types@2010-09-24.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/ietf/network-topology@2013-07-12.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/ietf/network-topology@2013-10-21.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/if-feature-resolution-test/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/if-feature-resolution-test/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/if-feature-resolution-test/shared-schema-repository/foobar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-with-equal-revision-date.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-with-no-revision-date.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-with-revision-date.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-with-unequal-revision-date.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-without-revision-date.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-with-equal-revision-date.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-with-no-revision-date.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-with-revision-date.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-with-unequal-revision-date.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-without-revision-date.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/included-statements-test/child-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/included-statements-test/root-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/list-keys-test/correct-list-keys-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/list-keys-test/incorrect-list-keys-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/list-keys-test/incorrect-list-keys-test2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/list-keys-test/incorrect-list-keys-test3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/list-keys-test/incorrect-list-keys-test4.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/list-test/list-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/mapped-build/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/mapped-build/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/mapped-build/sub-bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/mapped-build/sub-foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/model-new/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/model-new/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/model-new/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/model-new/subfoo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/model/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/model/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/model/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/model/subfoo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/module-source/simple-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/must-when-stmt-test/must-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/must-when-stmt-test/when-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/augment0.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/augment1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/augment2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/container-leaf.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/container-list.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/container.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/identity.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/typedef.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/invalid-list-key-def.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile0.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile4.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile5.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile6.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile7.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile8.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/no-revision/imported.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/no-revision/imported@2012-12-12.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/no-revision/module-without-revision.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/no-revision/top@2012-10-10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-2/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-2/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-2/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-3/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-3/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-1/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-1/bar@2016-01-31.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-1/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-1/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-1/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-1/bar@2016-01-31.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-1/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-1/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-2/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-2/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-2/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/bar@2016-01-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/bar@2016-01-03.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/bar@2016-01-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/bar@2016-01-03.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/bar@2016-01-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/bar@2016-01-03.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/bar@2016-01-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/bar@2016-01-03.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/bar@2016-01-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/bar@2016-01-03.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/bar@2016-01-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/bar@2016-01-03.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/bar@2016-01-03.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/bar@2016-01-04.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/foobar@2016-01-31.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/foobar@2016-02-28.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/bar@2016-01-03.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/bar@2016-01-04.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/foobar@2016-01-31.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/foobar@2016-02-27.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/foobar@2016-02-28.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-invalid/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-invalid/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-invalid/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-valid/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-valid/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-valid/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/defaults/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/defaults/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision-2/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision-2/bar@2016-01-31.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision-2/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision-2/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-deprecated-1/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-deprecated-1/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-deprecated-2/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-deprecated-2/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-notsufficient-1/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-notsufficient-1/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-notsufficient-2/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-notsufficient-2/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-valid/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-valid/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-03.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-04.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-31.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-02-28.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/bar@2016-01-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/bar@2016-02-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/bar@2016-02-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/bar@2016-01-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/bar@2016-02-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/bar@2016-02-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/bar@2016-01-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/bar@2016-02-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/bar@2016-02-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/bar@2016-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/bar@2016-01-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/bar@2016-02-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/bar@2016-02-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/bar@2016-04-04.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/bar@2016-04-05.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/bar@2016-04-06.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/bar@2017-04-06.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-head/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-head/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-head/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-middle/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-middle/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-middle/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-tail/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-tail/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-tail/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import-invalid/bar@2016-01-01.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import-invalid/bar@2016-01-31.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import-invalid/foo@2016-02-01.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import-invalid/openconfig-extensions@2017-01-29.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import/bar@2016-01-01.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import/bar@2016-01-31.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import/foo@2016-02-01.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import/openconfig-extensions@2016-01-29.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic/bar@2016-01-31.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic/foo@2016-02-01.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic/openconfig-extensions@2017-01-29.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/ordering/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m2@2013-09-30.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m2@2013-10-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m4.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m5.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m6.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/unused.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/m1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/m2@2013-09-30.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/m2@2013-10-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/m3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/m4.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/m5.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/m6.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/parse-methods/unused.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/plugin-test/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/revisions/equal-rev.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/revisions/equal-root.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/revisions/mod-only-rev.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/revisions/mod-only-root.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/revisions/nowhere-rev.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/revisions/nowhere-root.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/revisions/submod-only-rev.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/revisions/submod-only-root.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/revisions/unequal-rev.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/revisions/unequal-root.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/bar-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/baz-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/foo-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/foo10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/foobar-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/anydata-10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/anydata-11.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/invalid-10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/invalid-11.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/unsupported-version.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/valid-10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/valid-11.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6868/invalid10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6868/yang11/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6868/yang11/imported.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6869/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6869/invalid10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6870/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6870/invalid10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6870/invalid11.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6871/bar10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6871/baz10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6871/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6871/foo10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-0/bar@1999-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-0/bar@2017-02-06.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-0/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-1/bar@1970-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-1/bar@1999-01-01.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-1/bar@2017-02-06.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-1/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6876/yang10/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6876/yang10/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6876/yang11/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6876/yang11/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6878/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6878/foo10-invalid-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6878/foo10-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6880/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6880/invalid10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6883/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6883/sub-foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/circular-includes/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/circular-includes/sub-foo-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/circular-includes/sub-foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/yang1-1/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/yang1-1/sub-foo-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/yang1-1/sub-foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang10/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang10/foo2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang10/foo3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang11/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang11/foo2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang11/foo3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar-invalid-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar-invalid-3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar-invalid-4.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar10-invalid-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar10-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar10-valid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo-invalid-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo-invalid-3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo-invalid-4.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo10-invalid-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo10-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo10-valid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-10-bit.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-10-enum.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-bit.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-enum-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-enum-3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-enum.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-union.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug9241/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/identity-stmt/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/identity-stmt/foo10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/identityref-stmt/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/identityref-stmt/foo10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/invalid-import-10/import-test-imported.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/invalid-import-10/import-test-root.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/invalid-include-10/child-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/invalid-include-10/root-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/valid-11/child-module-1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/valid-11/child-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/valid-11/imported-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/valid-11/root-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/leafref-stmt/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/leafref-stmt/foo10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/correct-list-keys-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test4.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test5.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/subfoo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/bar-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/baz-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/foo-invalid.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/foo10.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/bar@2016-01-03.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/bar@2016-01-04.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/foobar@2016-01-31.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/foobar@2016-02-28.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/bar@2016-01-03.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/bar@2016-01-04.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/foobar@2016-01-31.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/foobar@2016-02-27.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/foobar@2016-02-28.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/openconfig-extensions.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rpc-stmt-test/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/rpc-stmt-test/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/config.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/ietf-yang-types.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-config-dom-datastore.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-dom-broker-impl.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-md-sal-common.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-md-sal-dom.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-operational-dom-datastore.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-pingpong-broker.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/rpc-context.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/schema-context-util-test/imported-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/schema-context-util-test/my-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/imported.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-abs-no-imp.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-abs.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-empty.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-rel1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-rel2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-xpath.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-valid-aug-args.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augmented.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/bug2649/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/bug2649/import-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/effective-module/imported.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/effective-module/root.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/effective-module/submod.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/ext-typedef.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/ext-use.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/foobar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/identityreftest.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/cycle-yang.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/cycle-yin.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/egocentric.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/human.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/mammal.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/nature.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/importedtest.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/error-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/error-submodule.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/missing-parent.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/root-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/submodule-1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/submodule-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/key-arg-parsing/key-comp-duplicate.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/key-arg-parsing/key-simple-and-comp.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/bar.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/subfoo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-inet-types.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-interfaces.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-netconf-monitoring.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-yang-types.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-yang-types@2013-05-16.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-yang-types@2013-07-15.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-04-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-05-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-06-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-08-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-09-02.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/node-test/bar@2016-04-04.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/node-test/bar@2016-04-05.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/node-test/bar@2016-04-06.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/node-test/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-interfaces.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-netconf-monitoring.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-yang-types.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-yang-types@2013-05-16.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-yang-types@2013-07-15.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/testrev.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/revisions/more-revisions-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/root.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/simple-nodes-semantic.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/simple-types.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/isis-topology@2013-07-12.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/isis-topology@2013-10-21.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/l3-unicast-igp-topology@2013-07-12.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/l3-unicast-igp-topology@2013-10-21.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/ted@2013-07-12.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/ted@2013-10-21.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/types.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/types2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/extension-use.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/extension.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/feature-test/yang-with-features.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/include-belongs-to-test/child.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/include-belongs-to-test/parent.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/incorrect-bar.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/incorrect-foo.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/config.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/iana-if-type.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-inet-types.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-netconf-monitoring.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-yang-types.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-yang-types@2013-05-16.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-yang-types@2013-07-15.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/main-impl.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/opendaylight-md-sal-binding.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/test.yin [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/simple-test/simple-nodes.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sorting-test/m1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sorting-test/m2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sorting-test/m3.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sorting-test/m4.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sorting-test/m5.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sorting-test/m6.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sorting-test/m7.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/sorting-test/m8.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/aug-root.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/augmented.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-imported.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-incorrect.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-incorrect2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-root.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-submodule.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/constraints/foo.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/effective-build/simple-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/extensions/mock.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/extensions/yang-ext.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/identity/cyclic-identity-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/identity/identity-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/imported-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/root-module.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/submodule-1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/submodule-2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/submodule-to-submodule-1.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/stmt-test/uses/refine-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/substatement-validator/bug-4310/bug4310.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/substatement-validator/empty-element/empty.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/substatement-validator/maximal-element/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/substatement-validator/maximal-element/maximal.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/substatement-validator/missing-element/baz.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/substatement-validator/missing-element/missing.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/substatement-validator/undesirable-element/undesirable.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/type-tests/types.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/typedef-substmts-test/typedef-substmts-test.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/types/custom-types-test@2012-04-04.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/types/union-in-list/unioninlisttest.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/types/union-with-bits/union-bits-model.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/types/union-with-ext/extdef.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/types/union-with-ext/unionbug.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/revisions-extension.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/stmtsep-in-statements-sub.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/stmtsep-in-statements.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/stmtsep-in-statements2.yang [new file with mode: 0644]
yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/submodule-header-extension.yang [new file with mode: 0644]
yang/yang-parser-spi/pom.xml [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/ExtensionNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/FeatureNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/GroupingNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/IdentityNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/ModuleNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/NamespaceToModule.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/PreLinkageModuleNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/SchemaNodeIdentifierNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/SubmoduleNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/TypeNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractDeclaredStatement.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractQNameStatementSupport.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractStatementSupport.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractVoidStatementSupport.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/CopyHistory.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/CopyType.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/DerivedIdentitiesNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/DerivedNamespaceBehaviour.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ForwardingStatementSupport.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ImplicitParentAwareStatementSupport.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ImportedNamespaceContext.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/InferenceException.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/InvalidSubstatementException.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/MissingSubstatementException.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ModelActionBuilder.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ModelProcessingPhase.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/MutableStatement.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/NamespaceBehaviour.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/NamespaceKeyCriterion.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/NamespaceNotAvailableException.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/QNameCacheNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ReactorException.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SemanticVersionModuleNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SemanticVersionNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SomeModifiersUnresolvedException.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementDefinitionNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementFactory.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementSupport.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementSupportBundle.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StmtContext.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StmtContextUtils.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SubstatementValidator.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/package-info.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/package-info.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/BelongsToModuleContext.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/BelongsToPrefixToModuleCtx.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/BelongsToPrefixToModuleName.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/DeclarationInTextSource.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImpPrefixToNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImplicitSubstatement.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImportPrefixToModuleCtx.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImportPrefixToSemVerSourceIdentifier.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImportedModuleContext.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/IncludedModuleContext.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/IncludedSubmoduleNameToModuleCtx.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleCtxToModuleQName.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleCtxToSourceIdentifier.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleNameToModuleQName.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleNameToNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleNamespaceForBelongsTo.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleQNameToModuleName.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModulesDeviatedByModules.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/PrefixToModule.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/PrefixToModuleMap.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/QNameToStatementDefinition.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/QNameToStatementDefinitionMap.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/SourceException.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StatementSourceReference.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StatementStreamSource.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StatementWriter.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StmtOrderingNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/SupportedFeaturesNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/package-info.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/validation/ValidationBundlesNamespace.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/validation/package-info.java [new file with mode: 0644]
yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/NamespaceTest.java [new file with mode: 0644]
yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/meta/CopyHistoryTest.java [new file with mode: 0644]
yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/meta/ModelProcessingPhaseTest.java [new file with mode: 0644]
yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/meta/NamespaceTest.java [new file with mode: 0644]
yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/meta/QNameCacheNamespaceTest.java [new file with mode: 0644]
yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/DeclarationInTextSourceTest.java [new file with mode: 0644]
yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/ImplicitStatementTest.java [new file with mode: 0644]
yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/NamespaceTest.java [new file with mode: 0644]
yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/QNameToStatementDefinitionMapTest.java [new file with mode: 0644]
yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/SourceExceptionTest.java [new file with mode: 0644]
yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/validation/ValidationBundlesNamespaceTest.java [new file with mode: 0644]
yang/yang-test-util/pom.xml [new file with mode: 0644]
yang/yang-test-util/src/main/java/org/opendaylight/yangtools/yang/test/util/YangParserTestUtils.java [new file with mode: 0644]
yang/yang-xpath-api/pom.xml [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/AbstractYangXPathMathSupport.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalNumberExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalXPathMathSupport.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleNumberExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleXPathMathSupport.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/QNameReferent.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/QNameReferentBehavior.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/ResolvedQNameReferent.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/UnresolvedQNameReferent.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangBinaryExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangBinaryOperator.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangBooleanConstantExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangConstantExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFilterExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFunction.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFunctionCallExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangLiteralExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangLocationPath.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryOperator.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNegateExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNumberExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangPathExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangPredicateAware.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangQNameExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangVariableReferenceExpr.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathAxis.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathExpression.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathMode.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathSupport.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathNodeType.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParser.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParserFactory.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/package-info.java [new file with mode: 0644]
yang/yang-xpath-impl/pom.xml [new file with mode: 0644]
yang/yang-xpath-impl/src/main/antlr4/org/opendaylight/yangtools/yang/xpath/impl/instanceIdentifier.g4 [new file with mode: 0644]
yang/yang-xpath-impl/src/main/antlr4/org/opendaylight/yangtools/yang/xpath/impl/xpath.g4 [new file with mode: 0644]
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/Activator.java [new file with mode: 0644]
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParser.java [new file with mode: 0644]
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParserFactory.java [new file with mode: 0644]
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrYangXPathExpression.java [new file with mode: 0644]
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/CapturingErrorListener.java [new file with mode: 0644]
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/FunctionSupport.java [new file with mode: 0644]
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/InstanceIdentifierParser.java [new file with mode: 0644]
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/ParseTreeUtils.java [new file with mode: 0644]
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/Utils.java [new file with mode: 0644]
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/package-info.java [new file with mode: 0644]
yang/yang-xpath-impl/src/test/java/org/opendaylight/yangtools/yang/xpath/impl/XPathParserTest.java [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..c6c7ae2
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# 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
+#
+.classpath
+.project
+.settings
+target
+target-ide
+*.iml
+.idea
+bin
+xtend-gen
+target
+.DS_Store
+META-INF
+.checkstyle
+.pmd
+.factorypath
+.fbExcludeFilterFile
+
+# Vim
+.*.sw?
diff --git a/.gitreview b/.gitreview
new file mode 100644 (file)
index 0000000..6f0deb8
--- /dev/null
@@ -0,0 +1,5 @@
+[gerrit]
+host=git.opendaylight.org
+port=29418
+project=yangtools.git
+defaultbranch=master
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..3d967ae
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,70 @@
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+i) changes to the Program, and
+ii) additions to the Program;
+where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
+b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
+c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
+d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+b) its license agreement:
+i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
+ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
+iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
+iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+b) a copy of this Agreement must be included with each copy of the Program.
+Contributors may not remove or alter any copyright notices contained within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..e2ed2a2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,25 @@
+[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.opendaylight.yangtools/yangtools-artifacts/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.opendaylight.yangtools/yangtools-artifacts)
+[![Javadocs](https://www.javadoc.io/badge/org.opendaylight.yangtools/yangtools-aggregator.svg)](https://www.javadoc.io/doc/org.opendaylight.yangtools/yangtools-aggregator)
+[![License](https://img.shields.io/badge/License-EPL%201.0-blue.svg)](https://opensource.org/licenses/EPL-1.0)
+
+# YANG Tools
+
+YANG Tools is to develop necessary tooling and libraries to provide Java runtime
+and support for [YANG modeling language][RFC6020], data structures modeled by YANG and their
+serialization and deserialization as defined in IETF drafts and standards.
+
+## Current Features
+
+* parsing of [YANG sources][RFC6020] and semantic inference of relationship across YANG models as defined in [RFC6020]
+* representation of YANG-modeled data in Java
+  * **DOM-like APIs** - DOM-like tree model, which uses conceptual meta-model
+  * **Java Bindings** - Concrete data model generated from YANG models
+* serialization / deserialization of YANG-modeled data driven by YANG models
+  * XML - as defined in [RFC6020]
+  * JSON - as defined in [draft-lhotka-netmod-yang-json-01]
+* Integration of YANG model parsing into Maven build lifecycle and
+  support for third-party generators processing  YANG models.
+
+
+[RFC6020]:https://tools.ietf.org/html/rfc6020
+[draft-lhotka-netmod-yang-json-01]:https://tools.ietf.org/html/draft-lhotka-netmod-yang-json-01
diff --git a/artifacts/pom.xml b/artifacts/pom.xml
new file mode 100644 (file)
index 0000000..85d2412
--- /dev/null
@@ -0,0 +1,375 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+      <artifactId>odlparent-lite</artifactId>
+      <version>6.0.1</version>
+      <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>yangtools-artifacts</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-data-util</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>concepts</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-common</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-data-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-data-xpath-api</artifactId>
+                <version>0.2.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-data-impl</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-data-transform</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-data-jaxen</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-data-codec-gson</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-data-codec-xml</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-model-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-model-export</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-model-util</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-parser-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-parser-spi</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-parser-reactor</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-parser-impl</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-parser-rfc7950</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-test-util</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin-spi</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odlext-model-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odlext-parser-support</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>openconfig-model-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>openconfig-parser-support</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc6241-model-api</artifactId>
+                <version>2.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc6241-parser-support</artifactId>
+                <version>2.0.3-SNAPSHOT</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc6536-model-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc6536-parser-support</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc7952-data-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc7952-data-util</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc7952-model-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc7952-parser-support</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc8040-model-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc8040-parser-support</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc8528-data-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc8528-data-util</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc8528-model-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>rfc8528-parser-support</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-xpath-api</artifactId>
+                <version>2.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-xpath-impl</artifactId>
+                <version>2.0.3-SNAPSHOT</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>util</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+
+            <!-- Test utilities, not packaged in features -->
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>mockito-configuration</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>testutils</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <scope>test</scope>
+            </dependency>
+
+            <!-- Stable/Experimental feature repositories -->
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>features-yangtools</artifactId>
+                <classifier>features</classifier>
+                <type>xml</type>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>features-yangtools-experimental</artifactId>
+                <classifier>features</classifier>
+                <type>xml</type>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+
+            <!-- Stable features -->
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odl-yangtools-util</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <type>xml</type>
+                <classifier>features</classifier>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odl-yangtools-common</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <type>xml</type>
+                <classifier>features</classifier>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odl-yangtools-parser-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <type>xml</type>
+                <classifier>features</classifier>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odl-yangtools-parser</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <type>xml</type>
+                <classifier>features</classifier>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odl-yangtools-data-api</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <type>xml</type>
+                <classifier>features</classifier>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odl-yangtools-data</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <type>xml</type>
+                <classifier>features</classifier>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odl-yangtools-codec</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <type>xml</type>
+                <classifier>features</classifier>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odl-yangtools-export</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <type>xml</type>
+                <classifier>features</classifier>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odl-yangtools-xpath-api</artifactId>
+                <version>2.0.3-SNAPSHOT</version>
+                <type>xml</type>
+                <classifier>features</classifier>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odl-yangtools-xpath</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <type>xml</type>
+                <classifier>features</classifier>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odl-yangtools-exp-data-xpath-api</artifactId>
+                <version>0.2.3-SNAPSHOT</version>
+                <type>xml</type>
+                <classifier>features</classifier>
+            </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>odl-yangtools-exp-data-xpath</artifactId>
+                <version>0.2.3-SNAPSHOT</version>
+                <type>xml</type>
+                <classifier>features</classifier>
+            </dependency>
+
+            <!-- Experimental features -->
+
+            <!-- Documentation -->
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-docs</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+</project>
diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml
new file mode 100644 (file)
index 0000000..66e66f3
--- /dev/null
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.opendaylight.odlparent</groupId>
+        <artifactId>odlparent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>benchmarks</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+
+    <properties>
+        <!-- JMH-generated code does not pass SB (very noisily)-->
+        <odlparent.spotbugs.skip>true</odlparent.spotbugs.skip>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-impl</artifactId>
+            <version>4.0.3-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-test-util</artifactId>
+            <version>4.0.3-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-core</artifactId>
+            <version>1.21</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-generator-annprocess</artifactId>
+            <version>1.21</version>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <activation>
+                <property>
+                    <name>benchmarks</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>exec-maven-plugin</artifactId>
+                        <configuration>
+                            <classpathScope>test</classpathScope>
+                            <executable>java</executable>
+                            <arguments>
+                                <argument>-classpath</argument>
+                                <classpath/>
+                                <argument>org.openjdk.jmh.Main</argument>
+                                <argument>.*</argument>
+                            </arguments>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <id>run-benchmarks</id>
+                                <phase>integration-test</phase>
+                                <goals>
+                                    <goal>exec</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/BenchmarkModel.java b/benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/BenchmarkModel.java
new file mode 100644 (file)
index 0000000..9faadd0
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.tree;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+/**
+ * Benchmark model constants.
+ *
+ * @author Lukas Sedlak &lt;lsedlak@cisco.com&gt;
+ */
+final class BenchmarkModel {
+    private static final QName TEST_QNAME = QName.create(
+        "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13", "test").intern();
+    static final NodeIdentifier TEST = NodeIdentifier.create(TEST_QNAME);
+    static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.create(TEST);
+
+    static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list").intern();
+    static final NodeIdentifier OUTER_LIST = NodeIdentifier.create(OUTER_LIST_QNAME);
+    static final YangInstanceIdentifier OUTER_LIST_PATH = YangInstanceIdentifier.create(TEST, OUTER_LIST);
+
+    static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list").intern();
+    static final NodeIdentifier INNER_LIST = NodeIdentifier.create(INNER_LIST_QNAME);
+
+    static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice").intern();
+    static final QName ID_QNAME = QName.create(TEST_QNAME, "id").intern();
+    static final QName NAME_QNAME = QName.create(TEST_QNAME, "name").intern();
+    static final QName VALUE_QNAME = QName.create(TEST_QNAME, "value").intern();
+
+    private BenchmarkModel() {
+
+    }
+
+    static SchemaContext createTestContext() {
+        return YangParserTestUtils.parseYangResource("/odl-datastore-test.yang");
+    }
+}
diff --git a/benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/InMemoryDataTreeBenchmark.java b/benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/InMemoryDataTreeBenchmark.java
new file mode 100644 (file)
index 0000000..49e85b7
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.Streams;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.CursorAwareDataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModificationCursor;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+/**
+ * Benchmarking of InMemoryDataTree performance. JMH is used for microbenchmarking.
+ *
+ * @author Lukas Sedlak &lt;lsedlak@cisco.com&gt;
+ * @see <a href="http://openjdk.java.net/projects/code-tools/jmh/">JMH</a>
+ */
+@State(Scope.Thread)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@Fork(1)
+public class InMemoryDataTreeBenchmark {
+
+    private static final int WARMUP_ITERATIONS = 10;
+    private static final int MEASUREMENT_ITERATIONS = 10;
+
+    private static final int OUTER_LIST_100K = 100000;
+    private static final int OUTER_LIST_50K = 50000;
+    private static final int OUTER_LIST_10K = 10000;
+
+    private static final NodeIdentifierWithPredicates[] OUTER_LIST_IDS = Streams.mapWithIndex(
+        IntStream.range(0, OUTER_LIST_100K),
+        (i, index) -> NodeIdentifierWithPredicates.of(BenchmarkModel.OUTER_LIST_QNAME, BenchmarkModel.ID_QNAME, i))
+            .collect(Collectors.toList()).toArray(new NodeIdentifierWithPredicates[0]);
+
+    private static final YangInstanceIdentifier[] OUTER_LIST_PATHS = Arrays.stream(OUTER_LIST_IDS)
+            .map(id -> BenchmarkModel.OUTER_LIST_PATH.node(id).toOptimized())
+            .collect(Collectors.toList()).toArray(new YangInstanceIdentifier[0]);
+
+    private static final MapNode EMPTY_OUTER_LIST = ImmutableNodes.mapNodeBuilder(BenchmarkModel.OUTER_LIST).build();
+    private static final MapNode ONE_ITEM_INNER_LIST = initInnerListItems(1);
+    private static final MapNode TWO_ITEM_INNER_LIST = initInnerListItems(2);
+    private static final MapNode TEN_ITEM_INNER_LIST = initInnerListItems(10);
+
+    private static MapNode initInnerListItems(final int count) {
+        final CollectionNodeBuilder<MapEntryNode, MapNode> mapEntryBuilder = ImmutableNodes
+            .mapNodeBuilder(BenchmarkModel.INNER_LIST);
+
+        for (int i = 0; i < count; ++i) {
+            mapEntryBuilder
+                .withChild(ImmutableNodes.mapEntry(BenchmarkModel.INNER_LIST_QNAME, BenchmarkModel.NAME_QNAME, i));
+        }
+
+        return mapEntryBuilder.build();
+    }
+
+    private static final NormalizedNode<?, ?>[] OUTER_LIST_ONE_ITEM_INNER_LIST = initOuterListItems(OUTER_LIST_100K,
+        ONE_ITEM_INNER_LIST);
+    private static final NormalizedNode<?, ?>[] OUTER_LIST_TWO_ITEM_INNER_LIST = initOuterListItems(OUTER_LIST_50K,
+        TWO_ITEM_INNER_LIST);
+    private static final NormalizedNode<?, ?>[] OUTER_LIST_TEN_ITEM_INNER_LIST = initOuterListItems(OUTER_LIST_10K,
+        TEN_ITEM_INNER_LIST);
+
+    private static NormalizedNode<?,?>[] initOuterListItems(final int outerListItemsCount, final MapNode innerList) {
+        return Arrays.stream(OUTER_LIST_IDS).limit(outerListItemsCount)
+                .map(id -> ImmutableNodes.mapEntryBuilder().withNodeIdentifier(id).withChild(innerList).build())
+                .collect(Collectors.toList()).toArray(new NormalizedNode[0]);
+    }
+
+    private DataTree datastore;
+
+    public static void main(final String... args) throws RunnerException {
+        Options opt = new OptionsBuilder()
+            .include(".*" + InMemoryDataTreeBenchmark.class.getSimpleName() + ".*")
+            .forks(1)
+            .build();
+
+        new Runner(opt).run();
+    }
+
+    @Setup(Level.Trial)
+    public void setup() throws DataValidationFailedException {
+        datastore = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION,
+            BenchmarkModel.createTestContext());
+
+        final DataTreeModification modification = begin();
+        modification.write(BenchmarkModel.TEST_PATH, ImmutableContainerNodeBuilder.create()
+            .withNodeIdentifier(BenchmarkModel.TEST).withChild(EMPTY_OUTER_LIST).build());
+        commit(modification);
+    }
+
+    @TearDown
+    public void tearDown() {
+        datastore = null;
+    }
+
+    @Benchmark
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    public void write100KSingleNodeWithOneInnerItemInOneCommitBenchmark() throws DataValidationFailedException {
+        final DataTreeModification modification = begin();
+        for (int outerListKey = 0; outerListKey < OUTER_LIST_100K; ++outerListKey) {
+            modification.write(OUTER_LIST_PATHS[outerListKey], OUTER_LIST_ONE_ITEM_INNER_LIST[outerListKey]);
+        }
+        commit(modification);
+    }
+
+    @Benchmark
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    public void write100KSingleNodeWithOneInnerItemInOneCommitCursorBenchmark() throws DataValidationFailedException {
+        final CursorAwareDataTreeModification modification = begin();
+        try (DataTreeModificationCursor cursor = modification.openCursor(BenchmarkModel.OUTER_LIST_PATH).get()) {
+            for (int outerListKey = 0; outerListKey < OUTER_LIST_100K; ++outerListKey) {
+                cursor.write(OUTER_LIST_IDS[outerListKey], OUTER_LIST_ONE_ITEM_INNER_LIST[outerListKey]);
+            }
+        }
+        commit(modification);
+    }
+
+    @Benchmark
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    public void write100KSingleNodeWithOneInnerItemInCommitPerWriteBenchmark() throws DataValidationFailedException {
+        for (int outerListKey = 0; outerListKey < OUTER_LIST_100K; ++outerListKey) {
+            final DataTreeModification modification = begin();
+            modification.write(OUTER_LIST_PATHS[outerListKey], OUTER_LIST_ONE_ITEM_INNER_LIST[outerListKey]);
+            commit(modification);
+        }
+    }
+
+    @Benchmark
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    public void write50KSingleNodeWithTwoInnerItemsInOneCommitBenchmark() throws DataValidationFailedException {
+        final DataTreeModification modification = begin();
+        for (int outerListKey = 0; outerListKey < OUTER_LIST_50K; ++outerListKey) {
+            modification.write(OUTER_LIST_PATHS[outerListKey], OUTER_LIST_TWO_ITEM_INNER_LIST[outerListKey]);
+        }
+        commit(modification);
+    }
+
+    @Benchmark
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    public void write50KSingleNodeWithTwoInnerItemsInOneCommitCursorBenchmark() throws DataValidationFailedException {
+        final CursorAwareDataTreeModification modification = begin();
+        try (DataTreeModificationCursor cursor = modification.openCursor(BenchmarkModel.OUTER_LIST_PATH).get()) {
+            for (int outerListKey = 0; outerListKey < OUTER_LIST_50K; ++outerListKey) {
+                cursor.write(OUTER_LIST_IDS[outerListKey], OUTER_LIST_TWO_ITEM_INNER_LIST[outerListKey]);
+            }
+        }
+        commit(modification);
+    }
+
+    @Benchmark
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    public void write50KSingleNodeWithTwoInnerItemsInCommitPerWriteBenchmark() throws DataValidationFailedException {
+        for (int outerListKey = 0; outerListKey < OUTER_LIST_50K; ++outerListKey) {
+            final DataTreeModification modification = begin();
+            modification.write(OUTER_LIST_PATHS[outerListKey], OUTER_LIST_TWO_ITEM_INNER_LIST[outerListKey]);
+            commit(modification);
+        }
+    }
+
+    @Benchmark
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    public void write10KSingleNodeWithTenInnerItemsInOneCommitBenchmark() throws DataValidationFailedException {
+        final DataTreeModification modification = begin();
+        for (int outerListKey = 0; outerListKey < OUTER_LIST_10K; ++outerListKey) {
+            modification.write(OUTER_LIST_PATHS[outerListKey], OUTER_LIST_TEN_ITEM_INNER_LIST[outerListKey]);
+        }
+        commit(modification);
+    }
+
+    @Benchmark
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    public void write10KSingleNodeWithTenInnerItemsInOneCommitCursorBenchmark() throws DataValidationFailedException {
+        final CursorAwareDataTreeModification modification = begin();
+        try (DataTreeModificationCursor cursor = modification.openCursor(BenchmarkModel.OUTER_LIST_PATH).get()) {
+            for (int outerListKey = 0; outerListKey < OUTER_LIST_10K; ++outerListKey) {
+                cursor.write(OUTER_LIST_IDS[outerListKey], OUTER_LIST_TEN_ITEM_INNER_LIST[outerListKey]);
+            }
+        }
+        commit(modification);
+    }
+
+    @Benchmark
+    @Warmup(iterations = WARMUP_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    @Measurement(iterations = MEASUREMENT_ITERATIONS, timeUnit = TimeUnit.MILLISECONDS)
+    public void write10KSingleNodeWithTenInnerItemsInCommitPerWriteBenchmark() throws DataValidationFailedException {
+        for (int outerListKey = 0; outerListKey < OUTER_LIST_10K; ++outerListKey) {
+            final DataTreeModification modification = begin();
+            modification.write(OUTER_LIST_PATHS[outerListKey], OUTER_LIST_TEN_ITEM_INNER_LIST[outerListKey]);
+            commit(modification);
+        }
+    }
+
+    private CursorAwareDataTreeModification begin() {
+        return (CursorAwareDataTreeModification) datastore.takeSnapshot().newModification();
+    }
+
+    private void commit(final DataTreeModification modification) throws DataValidationFailedException {
+        modification.ready();
+        datastore.validate(modification);
+        datastore.commit(datastore.prepare(modification));
+    }
+}
diff --git a/benchmarks/src/main/resources/odl-datastore-test.yang b/benchmarks/src/main/resources/odl-datastore-test.yang
new file mode 100644 (file)
index 0000000..730ca17
--- /dev/null
@@ -0,0 +1,42 @@
+module odl-datastore-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+    prefix "store-test";
+    
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    container test {
+        list outer-list {
+            key id;
+            leaf id {
+                type int32;
+            }
+            choice outer-choice {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+               }
+           }
+           list inner-list {
+                key name;
+                leaf name {
+                    type int32;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/bundle-parent/pom.xml b/bundle-parent/pom.xml
new file mode 100644 (file)
index 0000000..308080c
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2018 Pantheon Technologies, s.r.o. 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>bundle-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>bundle-parent</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <name>${project.artifactId}</name>
+    <description>Bundle parent</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
diff --git a/common/concepts/pom.xml b/common/concepts/pom.xml
new file mode 100644 (file)
index 0000000..8a7bd32
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>concepts</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>Common concepts</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>mockito-configuration</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/common/concepts/src/main/java/module-info.java b/common/concepts/src/main/java/module-info.java
new file mode 100644 (file)
index 0000000..eb8e059
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module org.opendaylight.yangtools.concepts {
+    exports org.opendaylight.yangtools.concepts;
+
+    requires transitive com.google.common;
+    requires org.slf4j;
+
+    // Annotations
+    requires static com.github.spotbugs.annotations;
+    requires static org.eclipse.jdt.annotation;
+    requires static org.checkerframework.checker.qual;
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractCodec.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractCodec.java
new file mode 100644 (file)
index 0000000..47d4f3d
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * An abstract base class enforcing nullness contract around {@link Codec} interface.
+ *
+ * @param <P> Product type
+ * @param <I> Input type
+ * @param <X> Error exception type
+ */
+@Beta
+@NonNullByDefault
+public abstract class AbstractCodec<P, I, X extends Exception> implements Codec<P, I, X> {
+    @Override
+    public final @NonNull I deserialize(@NonNull final P input) throws X {
+        return verifyNotNull(deserializeImpl(requireNonNull(input)), "Codec %s returned null on %s", this, input);
+    }
+
+    @Override
+    public final @NonNull P serialize(@NonNull final I input) throws X {
+        return verifyNotNull(serializeImpl(requireNonNull(input)), "Codec %s returned null on %s", this, input);
+    }
+
+    // implementation is guarded from nulls and verified not to return null
+    protected abstract @NonNull I deserializeImpl(@NonNull P product) throws X;
+
+    // implementation is guarded from nulls and verified not to return null
+    protected abstract @NonNull P serializeImpl(@NonNull I input) throws X;
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractDelegator.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractDelegator.java
new file mode 100644 (file)
index 0000000..531be25
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Simple base class for classes which wish to implement {@link Delegator} interface and are not otherwise constrained
+ * in their class hierarchy.
+ *
+ * @param <T> Type of delegate
+ */
+@Beta
+@NonNullByDefault
+public abstract class AbstractDelegator<T> implements Delegator<T> {
+    private final @NonNull T delegate;
+
+    protected AbstractDelegator(final T delegate) {
+        this.delegate = requireNonNull(delegate);
+    }
+
+    @Override
+    public final T getDelegate() {
+        return delegate;
+    }
+
+    @Override
+    public final String toString() {
+        return addToString(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+    }
+
+    protected ToStringHelper addToString(final ToStringHelper helper) {
+        return helper.add("delegate", delegate);
+    }
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractIdentifiable.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractIdentifiable.java
new file mode 100644 (file)
index 0000000..2d33dc1
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+@Beta
+@NonNullByDefault
+public abstract class AbstractIdentifiable<T> implements Identifiable<T> {
+    private final @NonNull T identifier;
+
+    protected AbstractIdentifiable(final T identifier) {
+        this.identifier = requireNonNull(identifier);
+    }
+
+    @Override
+    public final T getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("identifier", getIdentifier());
+    }
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractIllegalArgumentCodec.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractIllegalArgumentCodec.java
new file mode 100644 (file)
index 0000000..eed595a
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * An abstract base class enforcing nullness contract around {@link IllegalArgumentCodec} interface.
+ *
+ * @param <P> Product type
+ * @param <I> Input type
+ */
+@Beta
+@NonNullByDefault
+public abstract class AbstractIllegalArgumentCodec<P, I> extends AbstractUncheckedCodec<P, I, IllegalArgumentException>
+        implements IllegalArgumentCodec<P, I> {
+
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractListenerRegistration.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractListenerRegistration.java
new file mode 100644 (file)
index 0000000..872816a
--- /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.concepts;
+
+import java.util.EventListener;
+import org.eclipse.jdt.annotation.NonNull;
+
+public abstract class AbstractListenerRegistration<T extends EventListener> extends AbstractObjectRegistration<T>
+        implements ListenerRegistration<T> {
+
+    protected AbstractListenerRegistration(final @NonNull T listener) {
+        super(listener);
+    }
+}
+
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractObjectRegistration.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractObjectRegistration.java
new file mode 100644 (file)
index 0000000..1fec43c
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Utility registration handle. It is a convenience for register-style method which can return an AutoCloseable realized
+ * by a subclass of this class. Invoking the close() method triggers unregistration of the state the method installed.
+ */
+public abstract class AbstractObjectRegistration<T> extends AbstractRegistration implements ObjectRegistration<T> {
+    private final T instance;
+
+    protected AbstractObjectRegistration(final @NonNull T instance) {
+        this.instance = requireNonNull(instance);
+    }
+
+    @Override
+    public final T getInstance() {
+        return instance;
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return super.addToStringAttributes(toStringHelper).add("instance", instance);
+    }
+}
+
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractRegistration.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractRegistration.java
new file mode 100644 (file)
index 0000000..7f43bc9
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+/**
+ * Utility registration handle. It is a convenience for register-style method which can return an AutoCloseable realized
+ * by a subclass of this class. Invoking the close() method triggers unregistration of the state the method installed.
+ */
+public abstract class AbstractRegistration implements Registration {
+    private static final VarHandle CLOSED;
+
+    // All access needs to go through this handle
+    @SuppressWarnings("unused")
+    private volatile byte closed;
+
+    static {
+        try {
+            CLOSED = MethodHandles.lookup().findVarHandle(AbstractRegistration.class, "closed", byte.class);
+        } catch (ReflectiveOperationException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    /**
+     * Remove the state referenced by this registration. This method is guaranteed to be called at most once.
+     * Referenced state must be retained until this method is invoked.
+     */
+    protected abstract void removeRegistration();
+
+    /**
+     * Query the state of this registration. Returns true if it was closed.
+     *
+     * @return true if the registration was closed, false otherwise.
+     */
+    public final boolean isClosed() {
+        return (byte) CLOSED.getAcquire(this) != 0;
+    }
+
+    @Override
+    public final void close() {
+        // We want full setVolatile() memory semantics here, as all state before calling this method
+        // needs to be visible
+        if (CLOSED.compareAndSet(this, (byte) 0, (byte) 1)) {
+            removeRegistration();
+        }
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("closed", isClosed());
+    }
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractUncheckedCodec.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractUncheckedCodec.java
new file mode 100644 (file)
index 0000000..0edb216
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * An abstract base class enforcing nullness contract around {@link UncheckedCodec} interface.
+ *
+ * @param <P> Product type
+ * @param <I> Input type
+ * @param <X> Error exception type
+ */
+@Beta
+@NonNullByDefault
+public abstract class AbstractUncheckedCodec<P, I, X extends RuntimeException> extends AbstractCodec<P, I, X>
+        implements UncheckedCodec<P, I, X> {
+    @Override
+    protected abstract @NonNull I deserializeImpl(@NonNull P product);
+
+    // implementation is guarded from nulls and verified not to return null
+    @Override
+    protected abstract @NonNull P serializeImpl(@NonNull I input);
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Builder.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Builder.java
new file mode 100644 (file)
index 0000000..4b37e0b
--- /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.concepts;
+
+/**
+ * Builder object which produces a product.
+ *
+ * @param <P> Product of builder
+ *
+ * @author Tony Tkacik &lt;ttkacik@cisco.com&gt;
+ */
+public interface Builder<P> extends CheckedBuilder<P, IllegalArgumentException> {
+    @Override
+    P build();
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/CheckedBuilder.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/CheckedBuilder.java
new file mode 100644 (file)
index 0000000..e3e46e5
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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;
+
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Builder object which produces a product.
+ *
+ * @param <P> Product of builder
+ */
+public interface CheckedBuilder<P, E extends Exception> extends Mutable {
+    /**
+     * Returns instance of the product. Multiple calls to this method are not required to return same instance if
+     * the state of the builder has changed.
+     *
+     * @return A newly-built instance
+     * @throws E if the builder's state is not sufficiently initialized
+     */
+    @NonNull P build() throws E;
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/CheckedValue.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/CheckedValue.java
new file mode 100644 (file)
index 0000000..45fb7fe
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Utility holder similar to {@link java.util.Optional}, except the empty case contains an Exception, which should be
+ * reported, for example via throwing it. It provides analogous methods such as {@link #isPresent()},
+ * {@link #ifPresent(Consumer)}, {@link #get()}, {@link #orElse(Object)}, {@link #orElseGet(Supplier)},
+ * {@link #orElseThrow(Function)}.
+ *
+ * @param <T> Value type
+ * @param <E> Exception type
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public class CheckedValue<T, E extends Exception> extends Variant<T, E> {
+    protected CheckedValue(final T value) {
+        super(value);
+    }
+
+    protected CheckedValue(final E violation, final @Nullable Void dummy) {
+        super(violation, dummy);
+    }
+
+    /**
+     * Create a new instance containing an {@link Exception}.
+     *
+     * @param cause Throwable
+     * @param <T> Value type
+     * @param <E> Exception type
+     * @return A new instance
+     * @throws NullPointerException if {@code cause} is null
+     */
+    public static <T, E extends Exception> CheckedValue<T, E> ofException(final E cause) {
+        return new CheckedValue<>(cause, null);
+    }
+
+    /**
+     * Create a new instance containing specified value.
+     *
+     * @param value Value
+     * @param <T> Value type
+     * @param <E> Exception type
+     * @return A new instance
+     * @throws NullPointerException if {@code value} is null
+     */
+    public static <T, E extends Exception> CheckedValue<T, E> ofValue(final T value) {
+        return new CheckedValue<>(value);
+    }
+
+    /**
+     * Convert a Variant into a {@link CheckedValue}, converting the second value into an exception.
+     *
+     * @param variant Input variant
+     * @param mapper Mapping function from second alternative to an exception
+     * @param <T> First alternative type
+     * @param <U> Second alternative type
+     * @param <E> Exception type
+     * @return Resulting {@link CheckedValue}
+     */
+    public static <T, U, E extends Exception> CheckedValue<T, E> ofVariant(final Variant<T, U> variant,
+            final Function<U, E> mapper) {
+        requireNonNull(mapper);
+        return variant.isFirst() ? new CheckedValue<>(variant.first())
+                : new CheckedValue<>(mapper.apply(variant.second()), null);
+    }
+
+    /**
+     * Return the contained value if {@link #isPresent()} would return true, throws {@link IllegalStateException}
+     * otherwise.
+     *
+     * @return Contained value
+     * @throws IllegalStateException if an error string is present.
+     */
+    public final T get() {
+        if (isFirst()) {
+            return first();
+        }
+        throw new IllegalStateException("Value is not present", second());
+    }
+
+    /**
+     * Return the contained error string if {@link #isPresent()} would return false, throws
+     * {@link IllegalStateException} otherwise.
+     *
+     * @return Throwable which was used to instantiate this object, or absent if it was instantiated using an error
+     *         string.
+     * @throws IllegalStateException if a value is present.
+     */
+    public final E getException() {
+        if (isSecond()) {
+            return second();
+        }
+        throw new IllegalStateException("Value " + first() + " is present");
+    }
+
+    /**
+     * Return true if a value is present.
+     *
+     * @return True if a value is present.
+     */
+    public final boolean isPresent() {
+        return isFirst();
+    }
+
+    /**
+     * If a value is present, invoke the specified consumer with the value, otherwise do nothing.
+     *
+     * @param consumer block to be executed if a value is present
+     * @throws NullPointerException if value is present and {@code consumer} is null
+     */
+    public final void ifPresent(final Consumer<? super T> consumer) {
+        if (isFirst()) {
+            consumer.accept(first());
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public <U> CheckedValue<U, E> map(final Function<? super T, U> mapper) {
+        requireNonNull(mapper);
+        return isFirst() ? new CheckedValue<>(mapper.apply(first())) : (CheckedValue<U, E>) this;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <X extends Exception> CheckedValue<T, X> mapException(final Function<? super E, X> mapper) {
+        requireNonNull(mapper);
+        if (isFirst()) {
+            return (CheckedValue<T, X>) this;
+        }
+        return new CheckedValue<>(mapper.apply(second()), null);
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public <U> CheckedValue<U, E> flatMap(final Function<? super T, CheckedValue<U, E>> mapper) {
+        requireNonNull(mapper);
+        return isFirst() ? requireNonNull(mapper.apply(first())) : (CheckedValue<U, E>) this;
+    }
+
+    /**
+     * Return contained value if present, otherwise return supplied value.
+     *
+     * @param other Replacement value
+     * @return Contained value or {code other}
+     */
+    public final T orElse(final T other) {
+        return isFirst() ? first() : other;
+    }
+
+    /**
+     * Return contained value if present, otherwise return the value produced by a supplier.
+     *
+     * @param supplier Replacement value supplier
+     * @return Contained value or supplier's value
+     * @throws NullPointerException if {@code supplier} is null
+     */
+    public final T orElseGet(final Supplier<T> supplier) {
+        requireNonNull(supplier);
+        return isFirst() ? first() : supplier.get();
+    }
+
+    /**
+     * Return contained value if present or throw the exception alternative.
+     *
+     * @return Contained value
+     * @throws E When there is no contained value
+     */
+    public final T orElseThrow() throws E {
+        if (isFirst()) {
+            return first();
+        }
+        throw second();
+    }
+
+    /**
+     * Return contained value if present or throw the exception alternative mapped through provided mapper.
+     *
+     * @param exceptionMapper Exception mapper
+     * @param <X> Thrown exception type
+     * @return Contained value
+     * @throws NullPointerException if {@code exceptionMapper} is null
+     * @throws X When there is no contained value
+     */
+    public final <X extends Throwable> T orElseThrow(final Function<E, X> exceptionMapper) throws X {
+        requireNonNull(exceptionMapper);
+        if (isFirst()) {
+            return first();
+        }
+        throw exceptionMapper.apply(second());
+    }
+
+    /**
+     * Return contained value if present or throw the exception supplied by supplier.
+     *
+     * @param supplier Exception supplier
+     * @param <X> Thrown exception type
+     * @return Contained value
+     * @throws NullPointerException if {@code exceptionMapper} is null
+     * @throws X When there is no contained value
+     */
+    public final <X extends Throwable> T orElseThrow(final Supplier<X> supplier) throws X {
+        requireNonNull(supplier);
+        if (isFirst()) {
+            return first();
+        }
+        throw supplier.get();
+    }
+
+    /**
+     * Complete target {@link CompletableFuture} either successfully or exceptionally based on the state of this object.
+     *
+     * @param future Future to complete
+     * @return True if this call has transitioned the future to a completed state, false otherwise.
+     * @throws NullPointerException if {code future} is null
+     */
+    public final boolean completeFuture(final CompletableFuture<T> future) {
+        return isFirst() ? future.complete(first()) : future.completeExceptionally(second());
+    }
+
+    /**
+     * Complete target {@link SettableFuture} either successfully or exceptionally based on the state of this object.
+     *
+     * @param future Future to complete
+     * @return True if this call has transitioned the future to a completed state, false otherwise.
+     * @throws NullPointerException if {code future} is null
+     */
+    public final boolean completeFuture(final SettableFuture<T> future) {
+        return isFirst() ? future.set(first()) : future.setException(second());
+    }
+
+    /**
+     * Transform this object into an immediately-completed {@link CompletableFuture}. The future will be successful
+     * if this object has a contained value or unsuccessful if this objects contains an exception.
+     *
+     * @return A {@link CompletableFuture}.
+     */
+    public final CompletableFuture<T> toCompletableFuture() {
+        if (isFirst()) {
+            return CompletableFuture.completedFuture(first());
+        }
+        // FIXME: Java 9: use CompletableFuture.failedFuture()
+        final CompletableFuture<T> future = new CompletableFuture<>();
+        future.completeExceptionally(second());
+        return future;
+    }
+
+    /**
+     * Transform this object into an immediately-completed {@link FluentFuture}. The future will be successful
+     * if this object has a contained value or unsuccessful if this objects contains an exception.
+     *
+     * @return A {@link FluentFuture}.
+     */
+    public final FluentFuture<T> toFluentFuture() {
+        final ListenableFuture<T> future;
+        if (isFirst()) {
+            future = Futures.immediateFuture(first());
+        } else {
+            future = Futures.immediateFailedFuture(second());
+        }
+        return FluentFuture.from(future);
+    }
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ClassBasedPropertyBuilder.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ClassBasedPropertyBuilder.java
new file mode 100644 (file)
index 0000000..1c80540
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+public interface ClassBasedPropertyBuilder<P, T extends ClassBasedPropertyBuilder<P, T>> extends Builder<P> {
+    /**
+     * Sets a value of property uniquely identified by its
+     * class.
+     *
+     * @param <V> value type
+     * @param type Type of property to set
+     * @param value Value of property
+     * @return Builder instance
+     */
+    <V> T set(Class<V> type, V value);
+
+    /**
+     * Gets a value of property based on its type.
+     *
+     * @param <V> value type
+     * @param type Type of property to get
+     * @return Builder instance
+     */
+    <V> V get(Class<V> type);
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Codec.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Codec.java
new file mode 100644 (file)
index 0000000..91060a1
--- /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.concepts;
+
+/**
+ * The concept of a combined {@link Serializer} and {@link Deserializer}, which produces an object from some input.
+ * Implementations should consider subclassing {@link AbstractCodec}.
+ *
+ * @param <P> Product type
+ * @param <I> Input type
+ * @param <X> Error exception type
+ */
+public interface Codec<P, I, X extends Exception> extends Serializer<P, I, X>, Deserializer<I, P, X> {
+    @Override
+    I deserialize(P input) throws X;
+
+    @Override
+    P serialize(I input) throws X;
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Delegator.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Delegator.java
new file mode 100644 (file)
index 0000000..78325fb
--- /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.concepts;
+
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Implementation of this interface delegates all its calls to the delegate if not specified otherwise.
+ *
+ * @param <T> Type of delegate
+ */
+public interface Delegator<T> {
+    /**
+     * Return underlying delegate.
+     *
+     * @return underlying delegate.
+     */
+    @NonNull T getDelegate();
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Deserializer.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Deserializer.java
new file mode 100644 (file)
index 0000000..d327bea
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * The concept of a deserializer, which produces an object from some input.
+ *
+ * @param <P> Product type
+ * @param <I> Input type
+ * @param <X> Error exception type
+ */
+public interface Deserializer<P, I, X extends Exception> {
+    /**
+     * Produce an object base on input.
+     *
+     * @param input Input object
+     * @return Product derived from input
+     * @throws NullPointerException if input is null
+     * @throws X when input is not valid
+     */
+    @NonNull P deserialize(@NonNull I input) throws X;
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ExtensibleObject.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ExtensibleObject.java
new file mode 100644 (file)
index 0000000..2b94aea
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Interface specifying access to extensions attached to a particular object. This functionality is loosely based on
+ * <a href="https://docs.microsoft.com/en-us/dotnet/framework/wcf/extending/extensible-objects">Extensible Object</a>
+ * pattern.
+ *
+ * @param <T> Type of extensible object
+ * @param <E> Extension marker interface
+ * @author Robert Varga
+ */
+@Beta
+public interface ExtensibleObject<T extends ExtensibleObject<T, E>, E extends ObjectExtension<T, E>> {
+    /**
+     * Return a map of currently-supported extensions, along with accessor objects which provide access to the specific
+     * functionality bound to this object.
+     *
+     * @return A map of supported functionality.
+     */
+    default @NonNull ClassToInstanceMap<E> getExtensions() {
+        return ImmutableClassToInstanceMap.of();
+    }
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Identifiable.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Identifiable.java
new file mode 100644 (file)
index 0000000..5473f2e
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * A generalized contract of an object which has an identifier. The identifier, unlike the object carrying it, must
+ * generally comply to the interface contract outlined in {@link Identifier}, but this is not enforced in this API's
+ * design, as there are external classes, such as {@link String}, which comply to the contract without implementing it.
+ *
+ * @param <T> Identifier class, must conform to API contract outlined by {@link Identifier}, even if it does not
+ *            implement it.
+ */
+// FIXME: 5.0.0: consider requiring "T extends Serializable"
+public interface Identifiable<T> {
+    /**
+     * Return this objects Identifier.
+     *
+     * @return Object's identifier, must not be null.
+     */
+    @NonNull T getIdentifier();
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Identifier.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Identifier.java
new file mode 100644 (file)
index 0000000..6947105
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+import java.io.Serializable;
+
+/**
+ * General identifier interface. It is primarily a marker for all things that identify concepts -- such as names,
+ * addresses, classes, etc. We do not require too much, just that the identifiers are serializable (and thus
+ * transferable).
+ *
+ * <p>Implementations are expected to implement {@link #hashCode()} and {@link #equals(Object)} methods in a way,
+ * which ensures that objects before and after serialization are considered equal.
+ *
+ * <p>Implementations are advised to use the {@link java.io.Externalizable} Proxy pattern to allow future evolution
+ * of their serialization format. For further efficiency, implementations should consider implementing
+ * {@link WritableObject}, so they can be efficiently embedded in other {@link Serializable} and {@code WritableObject}s
+ * objects.
+ */
+public interface Identifier extends Serializable, Immutable {
+    @Override
+    boolean equals(Object obj);
+
+    @Override
+    int hashCode();
+
+    @Override
+    String toString();
+}
+
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/IllegalArgumentCodec.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/IllegalArgumentCodec.java
new file mode 100644 (file)
index 0000000..e2b072a
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Utility interface, which specializes {@link UncheckedCodec} to {@link IllegalArgumentException}. This is useful
+ * for migration purposes. Implementations should consider subclassing {@link AbstractIllegalArgumentCodec}.
+ *
+ * @param <P> Product type
+ * @param <I> Input type
+ */
+@Beta
+public interface IllegalArgumentCodec<P, I> extends UncheckedCodec<P, I, IllegalArgumentException> {
+
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Immutable.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Immutable.java
new file mode 100644 (file)
index 0000000..1f17aeb
--- /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.concepts;
+
+/**
+ * Immutable Object - object does not change its externally-observable state during its lifetime.
+ *
+ * <p>
+ * Marker interface for objects which are immutable. This interface should be used directly on objects, preferably
+ * final, which are eligible for the JSR-305 {@code javax.annotation.concurrent.Immutable} annotation and objects
+ * implementing this interface are required to abide to interface contract specified by {@code @Immutable}.
+ *
+ * <p>
+ * The reason for the existence of this interface is twofold: unlike {@code @Immutable}, it is visible at runtime and
+ * objects can be quickly checked for compliance using an 'instanceof' check. This is useful for code which needs to
+ * capture a point-in-time snapshot of otherwise unknown objects -- a typical example being logging/tracing systems.
+ * Such systems would normally have to rely on serializing the object to get a stable checkpoint. Objects marked with
+ * this interface are guaranteed to remain stable, thus already being a checkpoint for all intents and purposes, so
+ * aside from retaining a reference no further action on them is necessary.
+ *
+ * <p>
+ * Implementations of this interface must not change any public state during their entire lifetime.
+ *
+ * <p>
+ * This interface is mutually exclusive with {@link Mutable} and other {@link MutationBehaviour}s.
+ *
+ * @author Robert Varga
+ * @author Tony Tkacik
+ */
+public interface Immutable extends MutationBehaviour<Immutable> {
+
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ListenerRegistration.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ListenerRegistration.java
new file mode 100644 (file)
index 0000000..02d0d60
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+import java.util.EventListener;
+
+/**
+ * Class representing a {@link Registration} of an {@link EventListener}. This interface provides the additional
+ * guarantee that the process of unregistration cannot fail for predictable reasons.
+ *
+ * @param <T> Type of registered listener
+ */
+public interface ListenerRegistration<T extends EventListener> extends ObjectRegistration<T> {
+    /**
+     * Unregister the listener. Note that invocations enqueued to the listener are not subject to synchronization
+     * rules, and events may be delivered to the listener after this method completes.
+     *
+     * <p>
+     * While the interface contract allows an implementation to ignore the occurrence of RuntimeExceptions,
+     * implementations are strongly encouraged to deal with such exceptions internally and to ensure invocations of
+     * this method do not fail in such circumstances.
+     */
+    @Override
+    void close();
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Mutable.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Mutable.java
new file mode 100644 (file)
index 0000000..f6f2893
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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;
+
+/**
+ * Mutable object - object may change it's state during lifecycle. This interface is mutually exclusive
+ * with {@link Immutable} and other {@link MutationBehaviour}s.
+ *
+ * @author Tony Tkacik
+ */
+public interface Mutable extends MutationBehaviour<Mutable> {
+
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/MutationBehaviour.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/MutationBehaviour.java
new file mode 100644 (file)
index 0000000..2004062
--- /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.concepts;
+
+/**
+ * Mutation behavior. This interface is used to prevent same class extends multiple types of MutationBehaviour
+ * such as {@link Immutable} and {@link Mutable} which are mutually exclusive.
+ *
+ * @author Tony Tkacik
+ *
+ * @param <T> Mutation Type
+ */
+public interface MutationBehaviour<T extends MutationBehaviour<T>> {
+
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/NoOpListenerRegistration.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/NoOpListenerRegistration.java
new file mode 100644 (file)
index 0000000..9780715
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+import java.util.EventListener;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Implementation of {@link ListenerRegistration} which does nothing in its {@link #close()} method.
+ *
+ * @param <T> Type of registered listener
+ */
+@Beta
+@NonNullByDefault
+public final class NoOpListenerRegistration<T extends EventListener> extends NoOpObjectRegistration<T>
+        implements ListenerRegistration<T> {
+    private NoOpListenerRegistration(final T instance) {
+        super(instance);
+    }
+
+    public static <T extends EventListener> ListenerRegistration<T> of(final T instance) {
+        return new NoOpListenerRegistration<>(instance);
+    }
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/NoOpObjectRegistration.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/NoOpObjectRegistration.java
new file mode 100644 (file)
index 0000000..8610680
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Implementation of {@link ObjectRegistration} which does nothing in its {@link #close()} method.
+ *
+ * @param <T> Type of registered object
+ */
+@Beta
+@NonNullByDefault
+public class NoOpObjectRegistration<T> implements Immutable, ObjectRegistration<T> {
+    private final T instance;
+
+    NoOpObjectRegistration(final T instance) {
+        this.instance = requireNonNull(instance);
+    }
+
+    public static <T> ObjectRegistration<T> of(final T instance) {
+        return new NoOpObjectRegistration<>(instance);
+    }
+
+    @Override
+    public final T getInstance() {
+        return instance;
+    }
+
+    @Override
+    public final void close() {
+        // No-op
+    }
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(this).add("instance", instance).toString();
+    }
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ObjectExtension.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ObjectExtension.java
new file mode 100644 (file)
index 0000000..2da775e
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * An extension to a concrete {@link ExtensibleObject}. This is a marker interface to introduce type safety and unlike
+ * full Extensible Objects, does not specify how extensions are attached to an extensible object.
+ *
+ * <p>
+ * {@link ObjectExtension} instances are attached to their host object and share its state, which means they work in
+ * concert and care must be taken to ensure consistency, such as thread safety and observable effects.
+ *
+ * @param <T> Extensible object type
+ * @param <E> Extension type
+ * @author Robert Varga
+ */
+@Beta
+public interface ObjectExtension<T extends ExtensibleObject<T, E>, E extends ObjectExtension<T, E>> {
+
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ObjectExtensions.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ObjectExtensions.java
new file mode 100644 (file)
index 0000000..f7e8423
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterators;
+import java.util.AbstractList;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Utility {@link ClassToInstanceMap} implementation for implementing {@link ExtensibleObject#getExtensions()} method
+ * by objects which are themselves implementing the extension.
+ *
+ * @param <O> Type of extensible object
+ * @param <E> Extension marker interface
+ * @author Robert Varga
+ */
+@Beta
+public final class ObjectExtensions<O extends ExtensibleObject<O, E>, E extends ObjectExtension<O, E>>
+        extends AbstractMap<Class<? extends E>, E> implements ClassToInstanceMap<E> {
+    private final class EntrySet extends AbstractSet<Entry<Class<? extends E>, E>> {
+        @Override
+        public Iterator<Entry<Class<? extends E>, E>> iterator() {
+            return Iterators.transform(extensions.iterator(), ext -> new SimpleImmutableEntry<>(ext, ext.cast(object)));
+        }
+
+        @Override
+        public int size() {
+            return extensions.size();
+        }
+    }
+
+    private static final class Values<E> extends AbstractList<E> {
+        private final @NonNull E instance;
+        private final int size;
+
+        @SuppressWarnings("unchecked")
+        Values(final @NonNull Object instance, final int size) {
+            this.instance = (E) instance;
+            this.size = size;
+        }
+
+        @Override
+        public E get(final int index) {
+            if (index < 0 || index >= size) {
+                throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
+            }
+            return instance;
+        }
+
+        @Override
+        public int size() {
+            return size;
+        }
+    }
+
+    public static final class Factory<T, O extends ExtensibleObject<O, E>, E extends ObjectExtension<O, E>> {
+        private final @NonNull ImmutableSet<Class<? extends E>> extensions;
+
+        Factory(final ImmutableSet<Class<? extends E>> extensions) {
+            this.extensions = requireNonNull(extensions);
+        }
+
+        public @NonNull ClassToInstanceMap<E> newInstance(final T object) {
+            return new ObjectExtensions<>(extensions, object);
+        }
+    }
+
+    private final @NonNull ImmutableSet<Class<? extends E>> extensions;
+    private final @NonNull Object object;
+
+    ObjectExtensions(final ImmutableSet<Class<? extends E>> extensions, final Object object) {
+        this.extensions = requireNonNull(extensions);
+        this.object = requireNonNull(object);
+    }
+
+    @SafeVarargs
+    public static <T, O extends ExtensibleObject<O, E>, E extends ObjectExtension<O, E>> @NonNull Factory<T, O, E>
+            factory(final Class<T> objClass, final Class<? extends E>... extensions) {
+        final ImmutableSet<Class<? extends E>> set = ImmutableSet.copyOf(extensions);
+        for (Class<? extends E> extension : set) {
+            checkArgument(extension.isAssignableFrom(objClass), "%s is not a valid extension %s", objClass, extension);
+        }
+        return new Factory<>(set);
+    }
+
+    @Override
+    public int size() {
+        return extensions.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return extensions.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(final Object key) {
+        return extensions.contains(key);
+    }
+
+    @Override
+    public boolean containsValue(final Object value) {
+        return object.equals(value);
+    }
+
+    @Override
+    public E get(final Object key) {
+        return containsKey(key) ? ((Class<? extends E>) key).cast(object) : null;
+    }
+
+    @Override
+    public E put(final Class<? extends E> key, final E value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public E remove(final Object key) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public void putAll(final Map<? extends Class<? extends E>, ? extends E> m) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Set<Class<? extends E>> keySet() {
+        return extensions;
+    }
+
+    @Override
+    public Collection<E> values() {
+        return new Values<>(object, extensions.size());
+    }
+
+    @Override
+    public Set<Entry<Class<? extends E>, E>> entrySet() {
+        return new EntrySet();
+    }
+
+    @Override
+    public <T extends E> T getInstance(final Class<T> type) {
+        return extensions.contains(requireNonNull(type)) ? type.cast(object) : null;
+    }
+
+    @Override
+    public <T extends E> T putInstance(final Class<T> type, final T value) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ObjectRegistration.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ObjectRegistration.java
new file mode 100644 (file)
index 0000000..124ea43
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Class representing a registration of an object. Such a registration is a proper resource and should be cleaned up
+ * when no longer required, so references to the object can be removed. This mechanism lies above the usual Java
+ * reference mechanism, as the entity where the object is registered may reside outside of the Java Virtual Machine.
+ *
+ * @param <T> Type of registered object
+ */
+public interface ObjectRegistration<T> extends Registration {
+    /**
+     * Return the object instance.
+     *
+     * @return Registered object.
+     */
+    @NonNull T getInstance();
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Path.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Path.java
new file mode 100644 (file)
index 0000000..c0e6621
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Conceptual representation of a logical path in a tree-like structure, similar to a
+ * {@link java.nio.file.Path}, but more general in terms of what objects it can be applied to.
+ * Paths have an equivalence class, which is captured in the defining type. Paths also have the
+ * notion of containment, where one path is said to contain another path if it the data set
+ * identified by the former contains all elements of the data set represented by later.
+ *
+ * @param <P> Path equivalence class
+ */
+public interface Path<P extends Path<P>> {
+    /**
+     * Check if this path contains some other.
+     *
+     * @param other Other path, may not be null.
+     * @return True if this path contains the other.
+     */
+    boolean contains(@NonNull P other);
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ProductAwareBuilder.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ProductAwareBuilder.java
new file mode 100644 (file)
index 0000000..0ac9fda
--- /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.concepts;
+
+/**
+ * An extension of the {@link Builder} concept which allows an implementation
+ * of this interface to be used in collections instead of the product. Given
+ * the mutable nature of Builders, this has to be done very carefully.
+ *
+ * @param <P> Product type
+ */
+public interface ProductAwareBuilder<P> extends Builder<P> {
+    /**
+     * Return the hash code of the product. This has to be equivalent
+     * of calling {@link #build()}.{@link Object#hashCode()}.
+     *
+     * @return the hash code of the product.
+     */
+    int productHashCode();
+
+    /**
+     * Check whether an instance of the product that would be created
+     * by the builder is equal to an existing instance. This has to
+     * be equivalent of calling {@link #build()}.{@link Object#equals(Object)}.
+     *
+     * @param product Product instance
+     * @return Return true if the product is equal to the would-be
+     *         product of the builder.
+     */
+    boolean productEquals(Object product);
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Registration.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Registration.java
new file mode 100644 (file)
index 0000000..c166eb6
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+/**
+ * Class representing a registration. Such a registration is a proper resource and should be cleaned up when no longer
+ * required.
+ */
+public interface Registration extends AutoCloseable {
+    /**
+     * Unregisters the object. This operation is required not to invoke blocking operations. Implementations which
+     * require interaction with outside world must provide guarantees that any work is done behind the scenes and
+     * the unregistration process looks as if it has already succeeded once this method returns.
+     */
+    @Override
+    void close();
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/SemVer.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/SemVer.java
new file mode 100644 (file)
index 0000000..7290be8
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import java.io.Serializable;
+import java.util.Objects;
+import org.checkerframework.checker.index.qual.NonNegative;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * A single version according to <a href="http://semver.org/">Semantic Versioning</a>.
+ */
+@Beta
+public final class SemVer implements Comparable<SemVer>, Serializable {
+    private static final long serialVersionUID = 1L;
+    private final int major;
+    private final int minor;
+    private final int patch;
+
+    private SemVer(final int major, final int minor, final int patch) {
+        checkArgument(major >= 0);
+        this.major = major;
+        checkArgument(minor >= 0);
+        this.minor = minor;
+        checkArgument(patch >= 0);
+        this.patch = patch;
+    }
+
+    public static @NonNull SemVer create(final @NonNegative int major) {
+        return create(major, 0);
+    }
+
+    public static @NonNull SemVer create(final @NonNegative int major, final @NonNegative int minor) {
+        return create(major, minor, 0);
+    }
+
+    public static @NonNull  SemVer create(final @NonNegative int major, final @NonNegative int minor,
+            final @NonNegative int patch) {
+        return new SemVer(major, minor, patch);
+    }
+
+    public static @NonNull SemVer valueOf(final @NonNull String str) {
+        final int minorIdx = str.indexOf('.');
+        if (minorIdx == -1) {
+            return create(Integer.parseInt(str));
+        }
+
+        final String minorStr;
+        final int patchIdx = str.indexOf('.', minorIdx + 1);
+        if (patchIdx == -1) {
+            minorStr = str.substring(minorIdx + 1);
+            return create(Integer.parseInt(str.substring(0, minorIdx), 10), Integer.parseInt(minorStr, 10));
+        }
+
+        minorStr = str.substring(minorIdx + 1, patchIdx);
+        return create(Integer.parseInt(str.substring(0, minorIdx), 10), Integer.parseInt(minorStr, 10),
+            Integer.parseInt(str.substring(patchIdx + 1), 10));
+    }
+
+    /**
+     * Return the major version number.
+     *
+     * @return major version number
+     */
+    public int getMajor() {
+        return major;
+    }
+
+    /**
+     * Return the minor version number.
+     *
+     * @return minor version number
+     */
+    public int getMinor() {
+        return minor;
+    }
+
+    /**
+     * Return the patch version number.
+     *
+     * @return patch version number
+     */
+    public int getPatch() {
+        return patch;
+    }
+
+    @Override
+    public int compareTo(final SemVer other) {
+        int cmp = Integer.compare(major, other.major);
+        if (cmp == 0) {
+            cmp = Integer.compare(minor, other.minor);
+            if (cmp == 0) {
+                return Integer.compare(patch, other.patch);
+            }
+        }
+
+        return cmp;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(major, minor, patch);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof SemVer)) {
+            return false;
+        }
+
+        final SemVer o = (SemVer) obj;
+        return major == o.major && minor == o.minor && patch == o.patch;
+    }
+
+    @Override
+    public String toString() {
+        return major + "." + minor + "." + patch;
+    }
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Serializer.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Serializer.java
new file mode 100644 (file)
index 0000000..f7a85c0
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * An entity which is able to convert some input into a product.
+ *
+ * @param <P> Product type
+ * @param <I> Input type
+ * @param <X> Error exception type
+ */
+public interface Serializer<P, I, X extends Exception> {
+    /**
+     * Convert an input into a product.
+     *
+     * @param input Input
+     * @return A product
+     * @throws NullPointerException if input is null
+     * @throws X when input is not valid
+     */
+    @NonNull P serialize(@NonNull I input) throws X;
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/UncheckedCodec.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/UncheckedCodec.java
new file mode 100644 (file)
index 0000000..c1196a7
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * The concept of a combined {@link UncheckedSerializer} and {@link UncheckedDeserializer}, which produces an object
+ * from some input. Implementations should consider subclassing {@link AbstractUncheckedCodec}.
+ *
+ * @param <P> Product type
+ * @param <I> Input type
+ * @param <X> Error exception type
+ */
+@Beta
+public interface UncheckedCodec<P, I, X extends RuntimeException>
+    extends UncheckedSerializer<I, P, X>, UncheckedDeserializer<P, I, X>, Codec<P, I, X> {
+
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/UncheckedDeserializer.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/UncheckedDeserializer.java
new file mode 100644 (file)
index 0000000..e6ffd6c
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019 PANTHEoN.tech, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * A specialization of {@link Serializer}, which is guaranteed to throws unchecked exceptions.
+ *
+ * @param <P> Product type
+ * @param <I> Input type
+ * @param <X> Error exception type
+ */
+@Beta
+public interface UncheckedDeserializer<P, I, X extends RuntimeException> extends Serializer<P, I, X> {
+    @Override
+    P serialize(I input);
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/UncheckedSerializer.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/UncheckedSerializer.java
new file mode 100644 (file)
index 0000000..a8683e5
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * A specialization of {@link Deserializer}, which is guaranteed to throws unchecked exceptions.
+ *
+ * @param <P> Product type
+ * @param <I> Input type
+ * @param <X> Error exception type
+ */
+@Beta
+public interface UncheckedSerializer<P, I, X extends RuntimeException> extends Deserializer<P, I, X> {
+    @Override
+    P deserialize(I input);
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ValueWrapper.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/ValueWrapper.java
new file mode 100644 (file)
index 0000000..450c3c2
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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.concepts;
+
+/**
+ * Wraps around a value object.
+ *
+ * @param <T> Value type
+ */
+public interface ValueWrapper<T> {
+    /**
+     * Return the wrapped value object.
+     *
+     * @return wrapped value.
+     */
+    T getValue();
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Variant.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/Variant.java
new file mode 100644 (file)
index 0000000..094f9b0
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Utility holder of a two-variant value. The class design treats both variants as equal.
+ *
+ * @param <T> First alternative type
+ * @param <U> Second alternative type
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public class Variant<T, U> implements Immutable {
+    private final @Nullable T first;
+    private final @Nullable U second;
+
+    @SuppressFBWarnings("NP_STORE_INTO_NONNULL_FIELD")
+    protected Variant(final T first) {
+        this.first = requireNonNull(first);
+        second = null;
+    }
+
+    @SuppressFBWarnings("NP_STORE_INTO_NONNULL_FIELD")
+    protected Variant(final U second, final @Nullable Void dummy) {
+        first = null;
+        this.second = requireNonNull(second);
+    }
+
+    protected final T first() {
+        return verifyNotNull(first);
+    }
+
+    protected final U second() {
+        return verifyNotNull(second);
+    }
+
+    /**
+     * Create a new instance containing specified value.
+     *
+     * @param value Value
+     * @param <T> First alternative type
+     * @param <U> Second alternative type
+     * @return A new instance
+     * @throws NullPointerException if {@code value} is null
+     */
+    public static <T, U> Variant<T, U> ofFirst(final T value) {
+        return new Variant<>(value);
+    }
+
+    /**
+     * Create a new instance containing specified value.
+     *
+     * @param value Value
+     * @param <T> First alternative type
+     * @param <U> Second alternative type
+     * @return A new instance
+     * @throws NullPointerException if {@code value} is null
+     */
+    public static <T, U> Variant<T, U> ofSecond(final U value) {
+        return new Variant<>(value, null);
+    }
+
+    public final boolean isFirst() {
+        return first != null;
+    }
+
+    public final T getFirst() {
+        return tryFirst().get();
+    }
+
+    public final Optional<T> tryFirst() {
+        return Optional.ofNullable(first);
+    }
+
+    public final boolean isSecond() {
+        return second != null;
+    }
+
+    public final U getSecond() {
+        return trySecond().get();
+    }
+
+    public final Optional<U> trySecond() {
+        return Optional.ofNullable(second);
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(first, second);
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj == null || !getClass().equals(obj.getClass())) {
+            return false;
+        }
+        final Variant<?, ?> other = (Variant<?, ?>) obj;
+        return Objects.equals(first, other.first) && Objects.equals(second, other.second);
+    }
+
+    @Override
+    public final String toString() {
+        return addToString(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+    }
+
+    protected ToStringHelper addToString(final ToStringHelper helper) {
+        return helper.add("first", first).add("second", second);
+    }
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/WritableIdentifier.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/WritableIdentifier.java
new file mode 100644 (file)
index 0000000..0176299
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2016 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;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * A combination of both {@link Identifier} and {@link WritableObject}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface WritableIdentifier extends Identifier, WritableObject {
+
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/WritableObject.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/WritableObject.java
new file mode 100644 (file)
index 0000000..763fbca
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 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;
+
+import com.google.common.annotations.Beta;
+import java.io.DataOutput;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Marker interface for an object which can be written out to an {@link DataOutput}. Classes implementing this
+ * interface should declare a corresponding
+ *
+ * <pre>
+ *      public static CLASS readFrom(DataInput in) throws IOException;
+ * </pre>
+ *
+ * <p>The serialization format provided by this abstraction does not guarantee versioning. Callers are responsible
+ * for ensuring the source stream is correctly positioned.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface WritableObject {
+    /**
+     * Serialize this object into a {@link DataOutput} as a fixed-format stream.
+     *
+     * @param out Data output
+     * @throws IOException if an I/O error occurs
+     * @throws NullPointerException if out is null
+     */
+    void writeTo(@NonNull DataOutput out) throws IOException;
+}
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/WritableObjects.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/WritableObjects.java
new file mode 100644 (file)
index 0000000..4d1b604
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Utility methods for working with {@link WritableObject}s.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public final class WritableObjects {
+    private WritableObjects() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Shorthand for {@link #writeLong(DataOutput, long, int)} with zero flags.
+     *
+     * @param out Data output
+     * @param value long value to write
+     * @throws IOException if an I/O error occurs
+     * @throws NullPointerException if output is null
+     */
+    public static void writeLong(final DataOutput out, final long value) throws IOException {
+        writeLong(out, value, 0);
+    }
+
+    /**
+     * Write a long value into a {@link DataOutput}, compressing potential zero bytes. This method is useful for
+     * serializing counters and similar, which have a wide range, but typically do not use it. The value provided is
+     * treated as unsigned.
+     *
+     * <p>This methods writes the number of trailing non-zero in the value. It then writes the minimum required bytes
+     * to reconstruct the value by left-padding zeroes. Inverse operation is performed by {@link #readLong(DataInput)}
+     * or a combination of {@link #readLongHeader(DataInput)} and {@link #readLongBody(DataInput, byte)}.
+     *
+     * <p>Additionally the caller can use the top four bits (i.e. 0xF0) for caller-specific flags. These will be
+     * ignored by {@link #readLong(DataInput)}, but can be extracted via {@link #readLongHeader(DataInput)}.
+     *
+     * @param out Data output
+     * @param value long value to write
+     * @param flags flags to store
+     * @throws IOException if an I/O error occurs
+     * @throws NullPointerException if output is null
+     */
+    public static void writeLong(final DataOutput out, final long value, final int flags) throws IOException {
+        checkArgument((flags & 0xFFFFFF0F) == 0, "Invalid flags %s", flags);
+        final int bytes = valueBytes(value);
+        out.writeByte(bytes | flags);
+        writeValue(out, value, bytes);
+    }
+
+    /**
+     * Read a long value from a {@link DataInput} which was previously written via {@link #writeLong(DataOutput, long)}.
+     *
+     * @param in Data input
+     * @return long value extracted from the data input
+     * @throws IOException if an I/O error occurs
+     * @throws NullPointerException if input is null
+     */
+    public static long readLong(final DataInput in) throws IOException {
+        return readLongBody(in, readLongHeader(in));
+    }
+
+    /**
+     * Read the header of a compressed long value. The header may contain user-defined flags, which can be extracted
+     * via {@link #longHeaderFlags(byte)}.
+     *
+     * @param in Data input
+     * @return Header of next value
+     * @throws IOException if an I/O error occurs
+     * @throws NullPointerException if input is null
+     */
+    public static byte readLongHeader(final DataInput in) throws IOException {
+        return in.readByte();
+    }
+
+    /**
+     * Extract user-defined flags from a compressed long header. This will return 0 if the long value originates from
+     * {@link #writeLong(DataOutput, long)}.
+     *
+     * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
+     * @return User-defined flags
+     */
+    public static int longHeaderFlags(final byte header) {
+        return header & 0xF0;
+    }
+
+    /**
+     * Read a long value from a {@link DataInput} as hinted by the result of {@link #readLongHeader(DataInput)}.
+     *
+     * @param in Data input
+     * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
+     * @return long value
+     * @throws IOException if an I/O error occurs
+     * @throws NullPointerException if input is null
+     */
+    public static long readLongBody(final DataInput in, final byte header) throws IOException {
+        int bytes = header & 0xF;
+        if (bytes >= 8) {
+            return in.readLong();
+        }
+
+        if (bytes <= 0) {
+            return 0;
+        }
+
+        long value = 0;
+        if (bytes >= 4) {
+            bytes -= 4;
+            value = (in.readInt() & 0xFFFFFFFFL) << bytes * Byte.SIZE;
+        }
+        if (bytes >= 2) {
+            bytes -= 2;
+            value |= in.readUnsignedShort() << bytes * Byte.SIZE;
+        }
+        if (bytes > 0) {
+            value |= in.readUnsignedByte();
+        }
+        return value;
+    }
+
+    /**
+     * Write two consecutive long values. These values can be read back using {@link #readLongHeader(DataInput)},
+     * {@link #readFirstLong(DataInput, byte)} and {@link #readSecondLong(DataInput, byte)}.
+     *
+     * <p>This is a more efficient way of serializing two longs than {@link #writeLong(DataOutput, long)}. This is
+     * achieved by using the flags field to hold the length of the second long -- hence saving one byte.
+     *
+     * @param out Data output
+     * @param value0 first long value to write
+     * @param value1 second long value to write
+     * @throws IOException if an I/O error occurs
+     * @throws NullPointerException if output is null
+     */
+    public static void writeLongs(final DataOutput out, final long value0, final long value1) throws IOException {
+        final int clen = WritableObjects.valueBytes(value1);
+        writeLong(out, value0, clen << 4);
+        WritableObjects.writeValue(out, value1, clen);
+    }
+
+    /**
+     * Read first long value from an input.
+     *
+     * @param in Data input
+     * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
+     * @return First long specified in {@link #writeLongs(DataOutput, long, long)}
+     * @throws IOException if an I/O error occurs
+     * @throws NullPointerException if input is null
+     */
+    public static long readFirstLong(final DataInput in, final byte header) throws IOException {
+        return WritableObjects.readLongBody(in, header);
+    }
+
+    /**
+     * Read second long value from an input.
+     *
+     * @param in Data input
+     * @param header Value header, as returned by {@link #readLongHeader(DataInput)}
+     * @return Second long specified in {@link #writeLongs(DataOutput, long, long)}
+     * @throws IOException if an I/O error occurs
+     * @throws NullPointerException if input is null
+     */
+    public static long readSecondLong(final DataInput in, final byte header) throws IOException {
+        return WritableObjects.readLongBody(in, (byte)(header >> 4));
+    }
+
+    private static void writeValue(final DataOutput out, final long value, final int bytes) throws IOException {
+        if (bytes < 8) {
+            int left = bytes;
+            if (left >= 4) {
+                left -= 4;
+                out.writeInt((int)(value >>> left * Byte.SIZE));
+            }
+            if (left >= 2) {
+                left -= 2;
+                out.writeShort((int)(value >>> left * Byte.SIZE));
+            }
+            if (left > 0) {
+                out.writeByte((int)(value & 0xFF));
+            }
+        } else {
+            out.writeLong(value);
+        }
+    }
+
+    private static int valueBytes(final long value) {
+        // This is a binary search for the first match. Note that we need to mask bits from the most significant one.
+        // It completes completes in three to four mask-and-compare operations.
+        if ((value & 0xFFFFFFFF00000000L) != 0) {
+            if ((value & 0xFFFF000000000000L) != 0) {
+                return (value & 0xFF00000000000000L) != 0 ? 8 : 7;
+            }
+            return (value & 0x0000FF0000000000L) != 0 ? 6 : 5;
+        } else if ((value & 0x00000000FFFFFFFFL) != 0) {
+            if ((value & 0x00000000FFFF0000L) != 0) {
+                return (value & 0x00000000FF000000L) != 0 ? 4 : 3;
+            }
+            return (value & 0x000000000000FF00L) != 0 ? 2 : 1;
+        } else {
+            return 0;
+        }
+    }
+}
diff --git a/common/concepts/src/test/java/org/opendaylight/yangtools/concepts/AbstractIllegalArgumentCodecTest.java b/common/concepts/src/test/java/org/opendaylight/yangtools/concepts/AbstractIllegalArgumentCodecTest.java
new file mode 100644 (file)
index 0000000..da8b624
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import org.junit.Test;
+
+public class AbstractIllegalArgumentCodecTest {
+    private static final class TestCodec extends AbstractIllegalArgumentCodec<String, String> {
+        @Override
+        protected String deserializeImpl(final String product) {
+            throw new AssertionError("Should never be invoked");
+        }
+
+        @Override
+        protected String serializeImpl(final String input) {
+            throw new AssertionError("Should never be invoked");
+        }
+    }
+
+    private final TestCodec codec = new TestCodec();
+
+    @Test(expected = NullPointerException.class)
+    public void testNullDeserialize() {
+        codec.deserialize(null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testNullSerialize() {
+        codec.serialize(null);
+    }
+}
diff --git a/common/concepts/src/test/java/org/opendaylight/yangtools/concepts/CheckedValueTest.java b/common/concepts/src/test/java/org/opendaylight/yangtools/concepts/CheckedValueTest.java
new file mode 100644 (file)
index 0000000..d39de3b
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import org.junit.Test;
+
+public class CheckedValueTest {
+    @Test(expected = NullPointerException.class)
+    public void testNullValue() {
+        CheckedValue.ofValue(null);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testExceptionGet() {
+        CheckedValue.ofException(new Exception()).get();
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testValueException() {
+        CheckedValue.ofValue("foo").getException();
+    }
+
+    @Test
+    public void testGet() {
+        final String value = "foo";
+        final CheckedValue<String, ?> val = CheckedValue.ofValue(value);
+        assertTrue(val.isPresent());
+        assertSame(value, val.get());
+    }
+
+    @Test
+    public void testThrowableGetThrowable() {
+        final Exception cause = new Exception();
+        final CheckedValue<?, ?> val = CheckedValue.ofException(cause);
+        assertFalse(val.isPresent());
+        assertSame(cause, val.getException());
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals("CheckedValue{first=foo}", CheckedValue.ofValue("foo").toString());
+        assertEquals("CheckedValue{second=java.lang.NullPointerException: foo}",
+            CheckedValue.ofException(new NullPointerException("foo")).toString());
+    }
+
+    @Test
+    public void testEqualsHashCode() {
+        final CheckedValue<String, ?> fooVal = CheckedValue.ofValue("foo");
+        final CheckedValue<String, ?> fooVal2 = CheckedValue.ofValue("foo");
+        final CheckedValue<Integer, ?> oneVal = CheckedValue.ofValue(1);
+        final CheckedValue<?, ?> errBar = CheckedValue.ofException(new NullPointerException("bar"));
+        final CheckedValue<?, ?> errFoo = CheckedValue.ofException(new NullPointerException("foo"));
+        final CheckedValue<?, ?> errFoo2 = CheckedValue.ofException(new NullPointerException("foo"));
+
+        assertFalse(fooVal.equals(null));
+        assertFalse(fooVal.equals("foo"));
+        assertTrue(fooVal.equals(fooVal));
+        assertTrue(fooVal.equals(fooVal2));
+        assertFalse(fooVal.equals(oneVal));
+        assertEquals(fooVal.hashCode(), fooVal2.hashCode());
+
+        assertFalse(errFoo.equals(null));
+        assertFalse(errFoo.equals("foo"));
+        assertTrue(errFoo.equals(errFoo));
+
+        assertFalse(errBar.equals(errFoo));
+    }
+
+    @Test
+    public void testIfPresent() {
+        final String foo = "foo";
+        @SuppressWarnings("unchecked")
+        final Consumer<Object> consumer = mock(Consumer.class);
+        doNothing().when(consumer).accept(any(Object.class));
+        CheckedValue.ofValue(foo).ifPresent(consumer);
+        verify(consumer).accept(foo);
+    }
+
+    @Test
+    public void testThrowableIfPresent() {
+        @SuppressWarnings("unchecked")
+        final Consumer<Object> consumer = mock(Consumer.class);
+        doNothing().when(consumer).accept(any(Object.class));
+        CheckedValue.ofException(new NullPointerException()).ifPresent(consumer);
+        verifyZeroInteractions(consumer);
+    }
+
+    @Test
+    public void testOrElse() {
+        final String foo = "foo";
+        final String bar = "bar";
+        assertSame(foo, CheckedValue.ofValue(foo).orElse(bar));
+        assertSame(bar, CheckedValue.ofException(new NullPointerException()).orElse(bar));
+    }
+
+    @Test
+    public void testMap() {
+        final String foo = "foo";
+        final String bar = "bar";
+        final CheckedValue<Object, ?> errVal = CheckedValue.ofValue(foo);
+        @SuppressWarnings("unchecked")
+        final Function<Object, Object> mapper = mock(Function.class);
+        doReturn(bar).when(mapper).apply(any(Object.class));
+        assertSame(bar, errVal.map(mapper).get());
+        verify(mapper).apply(foo);
+    }
+
+    @Test
+    public void testExceptionMap() {
+        final CheckedValue<Object, ?> errVal = CheckedValue.ofException(new NullPointerException());
+        @SuppressWarnings("unchecked")
+        final Function<Object, Object> mapper = mock(Function.class);
+        doReturn(null).when(mapper).apply(any(Object.class));
+        assertSame(errVal, errVal.map(mapper));
+        verifyZeroInteractions(mapper);
+    }
+
+    @Test
+    public void testOrElseThrow() {
+        final String foo = "foo";
+        assertSame(foo, CheckedValue.ofValue(foo)
+            .orElseThrow((Supplier<NullPointerException>)NullPointerException::new));
+    }
+
+    @Test(expected = InterruptedException.class)
+    public void testThrowableOrElseThrow() throws InterruptedException {
+        final String foo = "foo";
+        final Exception cause = new NullPointerException(foo);
+        CheckedValue.ofException(cause).orElseThrow((Supplier<InterruptedException>)InterruptedException::new);
+    }
+
+    @Test
+    public void testOrElseGet() {
+        final String foo = "foo";
+        @SuppressWarnings("unchecked")
+        final Supplier<String> supplier = mock(Supplier.class);
+        doReturn(null).when(supplier).get();
+        assertSame(foo, CheckedValue.ofValue(foo).orElseGet(supplier));
+        verifyZeroInteractions(supplier);
+    }
+
+    @Test
+    public void testExceptionOrElseGet() {
+        final String bar = "bar";
+        @SuppressWarnings("unchecked")
+        final Supplier<Object> supplier = mock(Supplier.class);
+        doReturn(bar).when(supplier).get();
+
+        assertSame(bar, CheckedValue.ofException(new NullPointerException()).orElseGet(supplier));
+        verify(supplier).get();
+    }
+}
diff --git a/common/concepts/src/test/java/org/opendaylight/yangtools/concepts/SemVerTest.java b/common/concepts/src/test/java/org/opendaylight/yangtools/concepts/SemVerTest.java
new file mode 100644 (file)
index 0000000..56e45e4
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class SemVerTest {
+
+    @Test
+    public void testSemVer() {
+        final SemVer semVer = SemVer.create(5);
+        assertNotNull(semVer);
+
+        assertEquals(5, semVer.getMajor());
+        assertEquals(0, semVer.getMinor());
+        assertEquals(0, semVer.getPatch());
+
+        final SemVer semVer2 = SemVer.valueOf("1.2.3");
+        assertNotNull(semVer2);
+
+        assertEquals(1, semVer2.getMajor());
+        assertEquals(2, semVer2.getMinor());
+        assertEquals(3, semVer2.getPatch());
+
+        final SemVer semVer3 = SemVer.valueOf("1");
+        assertNotNull(semVer3);
+
+        assertEquals(1, semVer3.getMajor());
+        assertEquals(0, semVer3.getMinor());
+        assertEquals(0, semVer3.getPatch());
+
+        final SemVer semVer4 = SemVer.valueOf("1.2");
+        assertNotNull(semVer4);
+
+        assertEquals(1, semVer4.getMajor());
+        assertEquals(2, semVer4.getMinor());
+        assertEquals(0, semVer4.getPatch());
+
+        assertEquals(1, semVer2.compareTo(semVer3));
+        assertEquals(-1, semVer3.compareTo(semVer2));
+        assertEquals(0, semVer2.compareTo(semVer2));
+
+        assertTrue(semVer2.equals(semVer2));
+        assertFalse(semVer2.equals("not equal"));
+        assertFalse(semVer2.equals(semVer3));
+
+        assertEquals(semVer2.hashCode(), semVer2.hashCode());
+
+        assertEquals("1.0.0", semVer3.toString());
+    }
+}
diff --git a/common/concepts/src/test/java/org/opendaylight/yangtools/concepts/WritableObjectsTest.java b/common/concepts/src/test/java/org/opendaylight/yangtools/concepts/WritableObjectsTest.java
new file mode 100644 (file)
index 0000000..f019219
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.io.ByteArrayDataOutput;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import org.junit.Test;
+
+public class WritableObjectsTest {
+
+    private static void assertRecovery(final long expected) throws IOException {
+        final ByteArrayDataOutput out = ByteStreams.newDataOutput();
+        WritableObjects.writeLong(out, expected);
+        final long actual = WritableObjects.readLong(ByteStreams.newDataInput(out.toByteArray()));
+        assertEquals(Long.toUnsignedString(expected, 16), Long.toUnsignedString(actual, 16));
+    }
+
+    @Test
+    public void testReadWriteLong() throws IOException {
+        assertRecovery(0L);
+        assertRecovery(1L);
+        assertRecovery(255L);
+        assertRecovery(256L);
+
+        assertRecovery(Long.MAX_VALUE);
+        assertRecovery(Long.MIN_VALUE);
+
+        assertRecovery(0xF000000000000000L);
+        assertRecovery(0x0F00000000000000L);
+        assertRecovery(0x00F0000000000000L);
+        assertRecovery(0x000F000000000000L);
+        assertRecovery(0x0000F00000000000L);
+        assertRecovery(0x00000F0000000000L);
+        assertRecovery(0x000000F000000000L);
+        assertRecovery(0x0000000F00000000L);
+        assertRecovery(0x00000000F0000000L);
+        assertRecovery(0x000000000F000000L);
+        assertRecovery(0x0000000000F00000L);
+        assertRecovery(0x00000000000F0000L);
+        assertRecovery(0x000000000000F000L);
+        assertRecovery(0x0000000000000F00L);
+        assertRecovery(0x00000000000000F0L);
+
+        assertRecovery(0xF0F0F0F0F0F0F0F0L);
+        assertRecovery(0x0FF0F0F0F0F0F0F0L);
+        assertRecovery(0x00F0F0F0F0F0F0F0L);
+        assertRecovery(0x000FF0F0F0F0F0F0L);
+        assertRecovery(0x0000F0F0F0F0F0F0L);
+        assertRecovery(0x00000F00F0F0F0F0L);
+        assertRecovery(0x000000F0F0F0F0F0L);
+        assertRecovery(0x0000000FF0F0F0F0L);
+        assertRecovery(0x00000000F0F0F0F0L);
+        assertRecovery(0x000000000FF0F0F0L);
+        assertRecovery(0x0000000000F0F0F0L);
+        assertRecovery(0x00000000000FF0F0L);
+        assertRecovery(0x000000000000F0F0L);
+        assertRecovery(0x0000000000000FF0L);
+        assertRecovery(0x00000000000000F0L);
+
+        assertRecovery(0x8000000000000000L);
+        assertRecovery(0x0800000000000000L);
+        assertRecovery(0x0080000000000000L);
+        assertRecovery(0x0008000000000000L);
+        assertRecovery(0x0000800000000000L);
+        assertRecovery(0x0000080000000000L);
+        assertRecovery(0x0000008000000000L);
+        assertRecovery(0x0000000800000000L);
+        assertRecovery(0x0000000080000000L);
+        assertRecovery(0x0000000008000000L);
+        assertRecovery(0x0000000000800000L);
+        assertRecovery(0x0000000000080000L);
+        assertRecovery(0x0000000000008000L);
+        assertRecovery(0x0000000000000800L);
+        assertRecovery(0x0000000000000080L);
+        assertRecovery(0x0000000000000008L);
+    }
+}
diff --git a/common/mockito-configuration/pom.xml b/common/mockito-configuration/pom.xml
new file mode 100644 (file)
index 0000000..35bb686
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <groupId>org.opendaylight.odlparent</groupId>
+        <artifactId>odlparent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>mockito-configuration</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <description>Default mockito configuration</description>
+    <packaging>jar</packaging><!-- not needed in OSGi -->
+    <name>${project.artifactId}</name>
+
+    <dependencies>
+        <!-- all those dependencies will be in test scope as mockito-configuration should be referenced as test scope dependency -->
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/common/mockito-configuration/src/main/java/org/mockito/configuration/ArgumentsExtractorVerifier.java b/common/mockito-configuration/src/main/java/org/mockito/configuration/ArgumentsExtractorVerifier.java
new file mode 100644 (file)
index 0000000..ec7fc8e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.mockito.configuration;
+
+import java.util.List;
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.invocation.InvocationsFinder;
+import org.mockito.internal.verification.VerificationModeFactory;
+import org.mockito.internal.verification.api.VerificationData;
+import org.mockito.invocation.Invocation;
+import org.mockito.verification.VerificationMode;
+
+/**
+ * Verifier that extracts arguments from actual invocation. Useful when deeper validation of arguments is needed.
+ */
+public class ArgumentsExtractorVerifier implements VerificationMode {
+    private Object[] arguments = null;
+
+    @Override
+    public void verify(final VerificationData data) {
+        List<Invocation> actualInvocations =
+            InvocationsFinder.findInvocations(data.getAllInvocations(), data.getTarget());
+        if (actualInvocations.size() != 1) {
+            throw new MockitoException("This verifier can only be used with 1 invocation, got "
+                    + actualInvocations.size());
+        }
+        Invocation invocation = actualInvocations.get(0);
+        arguments = invocation.getArguments();
+        invocation.markVerified();
+    }
+
+    @Override
+    public VerificationMode description(final String description) {
+        return VerificationModeFactory.description(this, description);
+    }
+
+    public Object[] getArguments() {
+        return arguments == null ? null : arguments.clone();
+    }
+}
diff --git a/common/mockito-configuration/src/main/java/org/mockito/configuration/MockitoConfiguration.java b/common/mockito-configuration/src/main/java/org/mockito/configuration/MockitoConfiguration.java
new file mode 100644 (file)
index 0000000..b6909b5
--- /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.mockito.configuration;
+
+import org.mockito.stubbing.Answer;
+
+/**
+ * Configuration customization for Mockito. Change default answer to {@link ThrowsUnstubbedMethodException}.
+ */
+public class MockitoConfiguration extends DefaultMockitoConfiguration {
+    @Override
+    public Answer<Object> getDefaultAnswer() {
+        return new ThrowsUnstubbedMethodException();
+    }
+}
diff --git a/common/mockito-configuration/src/main/java/org/mockito/configuration/ThrowsUnstubbedMethodException.java b/common/mockito-configuration/src/main/java/org/mockito/configuration/ThrowsUnstubbedMethodException.java
new file mode 100644 (file)
index 0000000..e08b755
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.mockito.configuration;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.Serializable;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Answer that throws {@link UnstubbedMethodException}.
+ */
+@SuppressFBWarnings("NM_CLASS_NOT_EXCEPTION")
+public class ThrowsUnstubbedMethodException implements Answer<Object>, Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public Object answer(final InvocationOnMock invocation) {
+        throw new UnstubbedMethodException(invocation.toString() + " was not stubbed");
+    }
+}
diff --git a/common/mockito-configuration/src/main/java/org/mockito/configuration/UnstubbedMethodException.java b/common/mockito-configuration/src/main/java/org/mockito/configuration/UnstubbedMethodException.java
new file mode 100644 (file)
index 0000000..5432e7c
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * 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.mockito.configuration;
+
+/**
+ * Exception to be thrown on unstubbed method call.
+ */
+public class UnstubbedMethodException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    public UnstubbedMethodException(final String message) {
+        super(message);
+    }
+}
diff --git a/common/mockito-configuration/src/main/java/org/mockito/configuration/package-info.java b/common/mockito-configuration/src/main/java/org/mockito/configuration/package-info.java
new file mode 100644 (file)
index 0000000..de51c9a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 Red Hat 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
+ */
+/**
+ * Black magic Mockito utility.
+ *
+ * <p>This artifact is "black magic" because by merely depending on it (by its
+ * presence on the classpath, without ever using any of its classes), all usages
+ * of core Mockito.mock() in your existing tests will suddenly behave
+ * differently automagically all by themselves: They will throw
+ * ThrowsUnstubbedMethodException instead of returning null etc. as in default
+ * Mockito. (This "magic" is also the technical reason why this is package
+ * org.mockito.configuration instead of org.opendaylight.*)
+ *
+ * <p>TODO Point to the "other/new artifact with more sane Mockito helper, with
+ * explicit API.
+ *
+ * @see org.mockito.configuration.MockitoConfiguration
+ * @see org.mockito.configuration.IMockitoConfiguration
+ */
+package org.mockito.configuration;
diff --git a/common/mockito-configuration/src/main/resources/logback-test.xml b/common/mockito-configuration/src/main/resources/logback-test.xml
new file mode 100644 (file)
index 0000000..7ede3b2
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<configuration>
+
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
+    </encoder>
+  </appender>
+
+  <root level="DEBUG">
+    <appender-ref ref="STDOUT" />
+  </root>
+</configuration>
diff --git a/common/mockito-configuration/src/test/java/org/mockito/configuration/ArgumentsExtractorVerifierTest.java b/common/mockito-configuration/src/test/java/org/mockito/configuration/ArgumentsExtractorVerifierTest.java
new file mode 100644 (file)
index 0000000..4c26d8b
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.mockito.configuration;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import java.util.List;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class ArgumentsExtractorVerifierTest {
+
+    @Mock
+    List<String> mockedList;
+
+    @Test
+    public void test() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(Boolean.TRUE).when(this.mockedList).add(any(String.class));
+        final String argument = "something";
+        this.mockedList.add(argument);
+        // retrieve argument
+        final ArgumentsExtractorVerifier argumentsExtractorVerifier = new ArgumentsExtractorVerifier();
+        verify(this.mockedList, argumentsExtractorVerifier).add(any(String.class));
+        assertArrayEquals(new Object[] { argument }, argumentsExtractorVerifier.getArguments());
+    }
+}
diff --git a/common/mockito-configuration/src/test/java/org/mockito/configuration/DefaultAnswerTest.java b/common/mockito-configuration/src/test/java/org/mockito/configuration/DefaultAnswerTest.java
new file mode 100644 (file)
index 0000000..c8c290f
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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.mockito.configuration;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import java.io.Closeable;
+import java.io.IOException;
+import org.junit.Test;
+
+public class DefaultAnswerTest {
+    @Test
+    public void testAnswering() throws IOException {
+        Closeable mock = mock(Closeable.class);
+        try {
+            mock.close();
+            fail();
+        } catch (UnstubbedMethodException e) {
+            assertEquals("closeable.close(); was not stubbed", e.getMessage());
+        }
+    }
+}
diff --git a/common/pom.xml b/common/pom.xml
new file mode 100644 (file)
index 0000000..68c7812
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+      <groupId>org.opendaylight.odlparent</groupId>
+      <artifactId>odlparent-lite</artifactId>
+      <version>6.0.1</version>
+      <relativePath/>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>common-aggregator</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>concepts</module>
+        <module>mockito-configuration</module>
+        <module>util</module>
+        <module>testutils</module>
+    </modules>
+
+</project>
diff --git a/common/testutils/pom.xml b/common/testutils/pom.xml
new file mode 100644 (file)
index 0000000..b1ead6b
--- /dev/null
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>bundle-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>testutils</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <!-- Currently not needed, maybe later: <packaging>bundle</packaging> -->
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <!-- NOTE: The use of <scope> here is a little particular, compared to other standard projects...
+
+             As this test helper project is intended to itself be used as a <scope>test
+             <dependency> so that the utility code in src/main/java of this project can
+             be used to write src/test/java code in projects using it, all <dependencies>
+             here are <scope>compile here (the default, don't mention it), and NOT <scope>test.
+             (Only a <dependency> which only this project would want to use in its own src/test/java code
+             but not expose to projects depending on it would be <scope>test.  However that kind of against
+             the whole point of this project, and currently there no such dependencies here.
+         -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.truth</groupId>
+            <artifactId>truth</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/CallsRealOrExceptionAnswer.java b/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/CallsRealOrExceptionAnswer.java
new file mode 100644 (file)
index 0000000..4f706df
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2016 Red Hat, 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.testutils.mockito;
+
+import com.google.common.annotations.Beta;
+import java.io.Serializable;
+import java.lang.reflect.Modifier;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Mockito Answer which for un-stubbed methods forwards the call to the real
+ * method if it is implemented on the mocked object (i.e. not an interface or
+ * abstract method), and otherwise throws an {@link UnstubbedMethodException}, like the
+ * {@link ThrowsMethodExceptionAnswer}.
+ *
+ * <p>
+ * This can be useful to create light-weight <a href=
+ * "http://googletesting.blogspot.ch/2013/07/testing-on-toilet-know-your-test-doubles.html">Fake Doubles</a>
+ * (in particular some with state). For example:
+ *
+ * <pre>
+ * import static ...testutils.mockito.MoreAnswers.realOrException;
+ *
+ * interface Service {
+ *     List&lt;Thing&gt; getThings();
+ *     boolean installThing(Thing thing);
+ * }
+ *
+ * abstract class FakeService implements Service {
+ *     // Ignore getThings() - we don't need that for this test
+ *     boolean installThing(Thing thing) {
+ *         LOGGER.log("not really installed");
+ *         return false;
+ *     }
+ * }
+ *
+ * Service fake = Mockito.mock(FakeService.class, realOrException())
+ * </pre>
+ *
+ * <p>
+ * TIP: An impact of Mockito is that, just like in standard Mockito, constructors
+ * (and thus field initializers) are not called. So in your abstract fake class,
+ * instead of:
+ *
+ * <pre>
+ * abstract class FakeService implements Service {
+ *     private final List&lt;Thing&gt; things = new ArrayList&lt;&gt;();
+ *
+ *     public List&lt;Thing&gt; getThings() {
+ *         return things;
+ *     }
+ *
+ *     &#64;Override
+ *     public boolean installThing(Thing thing) {
+ *         return things.add(thing);
+ *     }
+ * }
+ * </pre>
+ *
+ * <p>
+ * you'll just need to do:
+ *
+ * <pre>
+ * abstract class FakeService implements Service {
+ *     private List&lt;Thing&gt; things;
+ *
+ *     public List&lt;Thing&gt; getThings() {
+ *         if (things == null)
+ *             things = new ArrayList&lt;&gt;()
+ *         return things;
+ *     }
+ *
+ *     &#64;Override
+ *     public boolean installThing(Thing thing) {
+ *         return getThings().add(thing);
+ *     }
+ * }
+ * </pre>
+ *
+ * <p>
+ * The big advantage of Mikitos versus just writing classes implementing service
+ * interfaces without using Mockito at all is that you don't have to implement a
+ * lot of methods you don't care about - you can just make an abstract fake
+ * class (incl. e.g. an inner class in your Test) and implement only one or some
+ * methods. This keeps code shorter and thus more readable.
+ *
+ * <p>
+ * The advantage of Mikitos VS pure Mockito's when/thenAnswer are that they:
+ * <ul>
+ *
+ * <li>are fully type safe and refactoring resistant; whereas Mockito is not,
+ * e.g. for return values with doReturn(...).when(), and uses runtime instead of
+ * compile time error reporting for this.</li>
+ * <li>avoid confusion re. the alternative doReturn(...).when() syntax required
+ * with ThrowsMethodExceptionAnswer instead of when(...).thenReturn()</li>
+ * <li>enforce the ThrowsMethodExceptionAnswer by default for
+ * non-implemented methods (which is possible with Mockito by explicitly passing
+ * this, but is easily forgotten)</li>
+ * </ul>
+ *
+ * @see Mockito#mock(Class, Answer)
+ * @see ThrowsMethodExceptionAnswer
+ * @see Mockito#CALLS_REAL_METHODS
+ * @see Mockito#CALLS_REAL_METHODS
+ *
+ * @author Michael Vorburger
+ */
+@Beta
+public final class CallsRealOrExceptionAnswer implements Answer<Object>, Serializable {
+    private static final long serialVersionUID = -3730024662402964588L;
+    static final CallsRealOrExceptionAnswer INSTANCE = new CallsRealOrExceptionAnswer();
+
+    private CallsRealOrExceptionAnswer() {
+
+    }
+
+    @Override
+    public Object answer(final InvocationOnMock invocation) throws Throwable {
+        if (Modifier.isAbstract(invocation.getMethod().getModifiers())) {
+            throw new UnstubbedMethodException(invocation.getMethod(), invocation.getMock());
+        }
+        return invocation.callRealMethod();
+    }
+
+    Object readResolve() {
+        return INSTANCE;
+    }
+}
diff --git a/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/MethodExtensions.java b/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/MethodExtensions.java
new file mode 100644 (file)
index 0000000..25e9c51
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 Red Hat, 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.testutils.mockito;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
+import java.util.regex.Pattern;
+import org.checkerframework.checker.regex.qual.Regex;
+
+/**
+ * Nicer shorter toString() for {@link Method} than it's default.
+ *
+ * <p>Without modifiers, return type, FQN of class and exceptions; instead with parameter names (if javac -parameters).
+ *
+ * @author Michael Vorburger
+ */
+public final class MethodExtensions {
+    @Regex
+    private static final String PARAM_PATTERN_STR = "\\[\\]$";
+    private static final Pattern PARAM_PATTERN = Pattern.compile(PARAM_PATTERN_STR);
+
+    private MethodExtensions() {
+    }
+
+    public static String toString(final Method method) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(method.getName());
+
+        // copy/paste from java.lang.reflect.Executable.sharedToGenericString(int, boolean)
+        sb.append('(');
+        final Type[] params = method.getGenericParameterTypes();
+        // NEW
+        final Parameter[] parameters = method.getParameters();
+        for (int j = 0; j < params.length; j++) {
+            String param = params[j].getTypeName();
+            if (method.isVarArgs() && j == params.length - 1) {
+                // replace T[] with T...
+                param = PARAM_PATTERN.matcher(param).replaceFirst("...");
+            }
+            sb.append(param);
+            // NEW
+            if (parameters[j].isNamePresent()) {
+                sb.append(' ');
+                sb.append(parameters[j].getName());
+            }
+            // NEW END
+            if (j < params.length - 1) {
+                // NEW ", " instead of ','
+                sb.append(", ");
+            }
+        }
+        sb.append(')');
+
+        return sb.toString();
+    }
+
+}
diff --git a/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/MoreAnswers.java b/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/MoreAnswers.java
new file mode 100644 (file)
index 0000000..8decbb3
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 Red Hat, 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.testutils.mockito;
+
+import org.mockito.AdditionalAnswers;
+import org.mockito.Answers;
+import org.mockito.stubbing.Answer;
+
+/**
+ * More {@link Answer} variants.
+ *
+ * @see Answers
+ * @see AdditionalAnswers
+ *
+ * @author Michael Vorburger
+ */
+@SuppressWarnings("unchecked")
+public final class MoreAnswers {
+    private MoreAnswers() {
+    }
+
+    /**
+     * Returns Mockito Answer (default) which forwards method calls or throws an UnstubbedMethodException.
+     *
+     * @param <T> answer type
+     * @return An answer
+     * @see CallsRealOrExceptionAnswer
+     */
+    public static <T> Answer<T> realOrException() {
+        return (Answer<T>) CallsRealOrExceptionAnswer.INSTANCE;
+    }
+
+    /**
+     * Returns Mockito Answer (default) which throws an UnstubbedMethodException.
+     *
+     * @param <T> answer type
+     * @return An answer
+     * @see ThrowsMethodExceptionAnswer
+     */
+    public static <T> Answer<T> exception() {
+        return (Answer<T>) ThrowsMethodExceptionAnswer.INSTANCE;
+    }
+}
diff --git a/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/ThrowsMethodExceptionAnswer.java b/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/ThrowsMethodExceptionAnswer.java
new file mode 100644 (file)
index 0000000..271d5a0
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016 Red Hat, 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.testutils.mockito;
+
+import com.google.common.annotations.Beta;
+import java.io.Serializable;
+import org.mockito.Mockito;
+import org.mockito.internal.stubbing.answers.ThrowsException;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Mockito Answer which for un-stubbed methods throws an
+ * UnstubbedMethodException (instead of Mockito's default of returning null).
+ *
+ * <p>
+ * Usage:
+ *
+ * <pre>
+ * import static ...testutils.mockito.MoreAnswers.exception;
+ *
+ * Mockito.mock(YourInterface.class, exception())
+ * </pre>
+ *
+ * @see Mockito#mock(Class, Answer)
+ *
+ * @see ThrowsException
+ *
+ * @author Michael Vorburger
+ */
+@Beta
+public final class ThrowsMethodExceptionAnswer implements Answer<Object>, Serializable {
+    private static final long serialVersionUID = -7316574192253912318L;
+    static final ThrowsMethodExceptionAnswer INSTANCE = new ThrowsMethodExceptionAnswer();
+
+    private ThrowsMethodExceptionAnswer() {
+
+    }
+
+    @Override
+    public Void answer(final InvocationOnMock invocation) {
+        throw new UnstubbedMethodException(invocation.getMethod());
+    }
+
+    Object readResolve() {
+        return INSTANCE;
+    }
+}
diff --git a/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/UnstubbedMethodException.java b/common/testutils/src/main/java/org/opendaylight/yangtools/testutils/mockito/UnstubbedMethodException.java
new file mode 100644 (file)
index 0000000..f4f6b64
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016 Red Hat 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.testutils.mockito;
+
+import java.lang.reflect.Method;
+import org.mockito.internal.util.MockUtil;
+
+/**
+ * Exception to be thrown on unstubbed method calls.
+ *
+ * @author Michael Vorburger
+ */
+public class UnstubbedMethodException extends UnsupportedOperationException {
+    private static final long serialVersionUID = 1L;
+
+    public UnstubbedMethodException(final Method method) {
+        super(MethodExtensions.toString(method) + " is not stubbed in mock of " + method.getDeclaringClass().getName());
+    }
+
+    public UnstubbedMethodException(final Method method, final Object mockAbstractFakeObject) {
+        super(MethodExtensions.toString(method) + " is not implemented in "
+                + MockUtil.getMockName(mockAbstractFakeObject).toString());
+    }
+}
diff --git a/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MethodExtensionsTest.java b/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MethodExtensionsTest.java
new file mode 100644 (file)
index 0000000..a48538c
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 Red Hat, 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.testutils.mockito.tests;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.lang.reflect.Method;
+import org.junit.Test;
+import org.opendaylight.yangtools.testutils.mockito.MethodExtensions;
+
+public class MethodExtensionsTest {
+
+    public <T> void fooBar(int index, T element) {
+    }
+
+    @Test
+    public void betterToString() throws Exception {
+        Method method = MethodExtensionsTest.class.getMethod("fooBar", Integer.TYPE, Object.class);
+        assertThat(MethodExtensions.toString(method)).isEqualTo("fooBar(int index, T element)");
+    }
+
+}
diff --git a/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MikitoTest.java b/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MikitoTest.java
new file mode 100644 (file)
index 0000000..346dd3c
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 Red Hat, 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.testutils.mockito.tests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.opendaylight.yangtools.testutils.mockito.MoreAnswers.realOrException;
+
+import java.io.File;
+import org.junit.Test;
+import org.opendaylight.yangtools.testutils.mockito.UnstubbedMethodException;
+
+/**
+ * Test to illustrate the use of the REAL_OR_EXCEPTION.
+ *
+ * <p>Also useful as example to contrast this approach illustrated in the MockitoExampleTutorialTest.
+ *
+ * @see MockitoExampleTutorialTest
+ *
+ * @author Michael Vorburger
+ */
+public class MikitoTest {
+
+    interface SomeService {
+
+        void foo();
+
+        String bar(String arg);
+
+        // Most methods on real world services have complex input (and output objects), not just int or String
+        int foobar(File file);
+    }
+
+    @Test
+    public void usingMikitoToCallStubbedMethod() {
+        SomeService service = mock(MockSomeService.class, realOrException());
+        assertEquals(123, service.foobar(new File("hello.txt")));
+        assertEquals(0, service.foobar(new File("belo.txt")));
+    }
+
+    @Test
+    public void usingMikitoToCallUnstubbedMethodAndExpectException() {
+        MockSomeService service = mock(MockSomeService.class, realOrException());
+        try {
+            service.foo();
+            fail();
+        } catch (UnstubbedMethodException e) {
+            assertThat(e.getMessage()).isEqualTo("foo() is not implemented in mockSomeService");
+        }
+    }
+
+    abstract static class MockSomeService implements SomeService {
+        @Override
+        public int foobar(final File file) {
+            return "hello.txt".equals(file.getName()) ? 123 : 0;
+        }
+    }
+}
diff --git a/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MockitoExampleTutorialTest.java b/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MockitoExampleTutorialTest.java
new file mode 100644 (file)
index 0000000..aad4655
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2016 Red Hat, 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.testutils.mockito.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.opendaylight.yangtools.testutils.mockito.MoreAnswers.exception;
+
+import java.io.File;
+import org.junit.Test;
+import org.opendaylight.yangtools.testutils.mockito.UnstubbedMethodException;
+
+/**
+ * Test to illustrate the basic use of Mockito VS the EXCEPTION_ANSWER.
+ *
+ * <p>Also useful as example to contrast this approach with the REAL_OR_EXCEPTION
+ * approach illustrated in the MikitoTest.
+ *
+ * @see MikitoTest
+ *
+ * @author Michael Vorburger
+ */
+public class MockitoExampleTutorialTest {
+
+    interface SomeService {
+
+        void foo();
+
+        String bar(String arg);
+
+        // Most methods on real world services have complex input (and output objects), not just int or String
+        int foobar(File file);
+    }
+
+    @Test
+    public void usingMockitoWithoutStubbing() {
+        SomeService service = mock(SomeService.class);
+        assertNull(service.bar("hulo"));
+    }
+
+    @Test
+    public void usingMockitoToStubSimpleCase() {
+        SomeService service = mock(SomeService.class);
+        when(service.foobar(any())).thenReturn(123);
+        assertEquals(123, service.foobar(new File("hello.txt")));
+    }
+
+    @Test
+    public void usingMockitoToStubComplexCase() {
+        SomeService service = mock(SomeService.class);
+        when(service.foobar(any())).thenAnswer(invocation -> {
+            File file = invocation.getArgument(0);
+            return "hello.txt".equals(file.getName()) ? 123 : 0;
+        });
+        assertEquals(0, service.foobar(new File("belo.txt")));
+    }
+
+    @Test(expected = UnstubbedMethodException.class)
+    public void usingMockitoExceptionException() {
+        SomeService service = mock(SomeService.class, exception());
+        service.foo();
+    }
+
+    @Test
+    public void usingMockitoNoExceptionIfStubbed() {
+        SomeService service = mock(SomeService.class, exception());
+        // NOT when(s.foobar(any())).thenReturn(123) BUT must be like this:
+        doReturn(123).when(service).foobar(any());
+        assertEquals(123, service.foobar(new File("hello.txt")));
+        try {
+            service.foo();
+            fail("expected NotImplementedException");
+        } catch (UnstubbedMethodException e) {
+            // OK
+        }
+    }
+
+    @Test
+    public void usingMockitoToStubComplexCaseAndExceptionIfNotStubbed() {
+        SomeService service = mock(SomeService.class, exception());
+        doAnswer(invocation -> {
+            File file = invocation.getArgument(0);
+            return "hello.txt".equals(file.getName()) ? 123 : 0;
+        }).when(service).foobar(any());
+        assertEquals(123, service.foobar(new File("hello.txt")));
+        assertEquals(0, service.foobar(new File("belo.txt")));
+    }
+
+}
diff --git a/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MockitoUnstubbedMethodExceptionAnswerTest.java b/common/testutils/src/test/java/org/opendaylight/yangtools/testutils/mockito/tests/MockitoUnstubbedMethodExceptionAnswerTest.java
new file mode 100644 (file)
index 0000000..2b1296f
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016 Red Hat, 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.testutils.mockito.tests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.testutils.mockito.MoreAnswers.exception;
+
+import java.io.Closeable;
+import java.io.IOException;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.yangtools.testutils.mockito.UnstubbedMethodException;
+
+public class MockitoUnstubbedMethodExceptionAnswerTest {
+
+    @Test
+    public void testAnswering() throws IOException {
+        Closeable mock = Mockito.mock(Closeable.class, exception());
+        try {
+            mock.close();
+            fail();
+        } catch (UnstubbedMethodException e) {
+            assertThat(e.getMessage()).isEqualTo("close() is not stubbed in mock of java.io.Closeable");
+        }
+
+    }
+
+}
diff --git a/common/util/pom.xml b/common/util/pom.xml
new file mode 100644 (file)
index 0000000..7b08724
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>util</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>Common utilities</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>tech.pantheon.triemap</groupId>
+            <artifactId>triemap</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.immutables</groupId>
+            <artifactId>value</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>mockito-configuration</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava-testlib</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.util</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/AbstractIdentifier.java b/common/util/src/main/java/org/opendaylight/yangtools/util/AbstractIdentifier.java
new file mode 100644 (file)
index 0000000..93c2da9
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016 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 java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Identifier;
+
+/**
+ * An abstract {@link Identifier} backed by an immutable object. Subclasses have no control over {@link #hashCode()}
+ * and {@link #equals(Object)}, hence they should not add any fields.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Object type
+ */
+public abstract class AbstractIdentifier<T> implements Identifier {
+    private static final long serialVersionUID = 1L;
+
+    private final @NonNull T value;
+
+    public AbstractIdentifier(final @NonNull T value) {
+        this.value = requireNonNull(value);
+    }
+
+    protected final @NonNull T getValue() {
+        return value;
+    }
+
+    @Override
+    public final int hashCode() {
+        return value.hashCode();
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+
+        return getClass().equals(obj.getClass()) && value.equals(((AbstractIdentifier<?>)obj).value);
+    }
+
+    @Override
+    public final @NonNull String toString() {
+        return MoreObjects.toStringHelper(this).add("value", value).toString();
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/AbstractStringIdentifier.java b/common/util/src/main/java/org/opendaylight/yangtools/util/AbstractStringIdentifier.java
new file mode 100644 (file)
index 0000000..18f9ddc
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import com.google.common.annotations.Beta;
+import java.util.UUID;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Identifier;
+
+/**
+ * Utility {@link Identifier} backed by a {@link UUID}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class AbstractStringIdentifier<T extends AbstractStringIdentifier<T>>
+        extends AbstractIdentifier<String> implements Comparable<T> {
+    private static final long serialVersionUID = 1L;
+
+    protected AbstractStringIdentifier(final @NonNull String string) {
+        super(string);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final int compareTo(final T o) {
+        return getValue().compareTo(o.getValue());
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/AbstractUUIDIdentifier.java b/common/util/src/main/java/org/opendaylight/yangtools/util/AbstractUUIDIdentifier.java
new file mode 100644 (file)
index 0000000..3251004
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import com.google.common.annotations.Beta;
+import java.util.UUID;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Identifier;
+
+/**
+ * Utility {@link Identifier} backed by a {@link UUID}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class AbstractUUIDIdentifier<T extends AbstractUUIDIdentifier<T>> extends AbstractIdentifier<UUID>
+        implements Comparable<T> {
+    private static final long serialVersionUID = 1L;
+
+    protected AbstractUUIDIdentifier(final @NonNull UUID uuid) {
+        super(uuid);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final int compareTo(final T o) {
+        return getValue().compareTo(o.getValue());
+    }
+}
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..0dcbc05
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * 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 java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import org.eclipse.jdt.annotation.NonNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility methods for working with ClassLoaders and classes.
+ */
+public final class ClassLoaderUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderUtils.class);
+    private static final Joiner DOT_JOINER = Joiner.on(".");
+    private static final Splitter DOT_SPLITTER = Splitter.on('.');
+
+    private ClassLoaderUtils() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    /**
+     * Immediately call {@link Function#apply(Object)} with provided {@link ClassLoader}. This method safely switches
+     * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
+     * method.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param function Function to be applied.
+     * @param input Function input
+     * @throws NullPointerException if class loader or function is null
+     */
+    @Beta
+    public static <T, R> R applyWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Function<T, R> function,
+            final T input) {
+        final Thread currentThread = Thread.currentThread();
+        final ClassLoader oldCls = currentThread.getContextClassLoader();
+        currentThread.setContextClassLoader(requireNonNull(cls));
+        try {
+            return requireNonNull(function).apply(input);
+        } finally {
+            currentThread.setContextClassLoader(oldCls);
+        }
+    }
+
+    /**
+     * Immediately call {@link Callable#call()} with provided {@link ClassLoader}. This method safely switches
+     * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
+     * method.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param callable Function to be executed.
+     * @return Result of callable invocation.
+     * @throws NullPointerException if class loader or callable is null
+     */
+    @Beta
+    public static <V> V callWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Callable<V> callable)
+            throws Exception {
+        final Thread currentThread = Thread.currentThread();
+        final ClassLoader oldCls = currentThread.getContextClassLoader();
+        currentThread.setContextClassLoader(requireNonNull(cls));
+        try {
+            return requireNonNull(callable).call();
+        } finally {
+            currentThread.setContextClassLoader(oldCls);
+        }
+    }
+
+    /**
+     * Immediately call {@link Supplier#get()} with provided {@link ClassLoader}. This method safely switches
+     * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
+     * method.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param supplier Function to be executed.
+     * @return Result of supplier invocation.
+     * @throws NullPointerException if class loader or supplier is null
+     */
+    @Beta
+    public static <V> V getWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Supplier<V> supplier) {
+        final Thread currentThread = Thread.currentThread();
+        final ClassLoader oldCls = currentThread.getContextClassLoader();
+        currentThread.setContextClassLoader(requireNonNull(cls));
+        try {
+            return requireNonNull(supplier).get();
+        } finally {
+            currentThread.setContextClassLoader(oldCls);
+        }
+    }
+
+    /**
+     * Immediately call {@link Runnable#run()} with provided {@link ClassLoader}. This method safely switches
+     * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
+     * method.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param runnable Function to be executed.
+     * @throws NullPointerException if class loader or runnable is null
+     */
+    @Beta
+    public static void runWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Runnable runnable) {
+        final Thread currentThread = Thread.currentThread();
+        final ClassLoader oldCls = currentThread.getContextClassLoader();
+        currentThread.setContextClassLoader(requireNonNull(cls));
+        try {
+            requireNonNull(runnable).run();
+        } finally {
+            currentThread.setContextClassLoader(oldCls);
+        }
+    }
+
+    /**
+     * Loads class using this supplied classloader.
+     *
+     * @param name String name of class.
+     */
+    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;
+        }
+        return loadClass0(cls,name);
+    }
+
+    private static Class<?> loadClass0(final ClassLoader cls, final String name) throws ClassNotFoundException {
+        try {
+            return cls.loadClass(name);
+        } catch (final ClassNotFoundException e) {
+            final List<String> components = DOT_SPLITTER.splitToList(name);
+
+            if (isInnerClass(components)) {
+                final int length = components.size() - 1;
+                final String outerName = DOT_JOINER.join(Iterables.limit(components, length));
+                final String innerName = outerName + "$" + components.get(length);
+                return cls.loadClass(innerName);
+            }
+
+            throw e;
+        }
+    }
+
+    private static boolean isInnerClass(final List<String> components) {
+        final int length = components.size();
+        if (length < 2) {
+            return false;
+        }
+
+        final String potentialOuter = components.get(length - 2);
+        if (potentialOuter == null) {
+            return false;
+        }
+        return Character.isUpperCase(potentialOuter.charAt(0));
+    }
+
+    public static Class<?> loadClassWithTCCL(final String name) throws ClassNotFoundException {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader tccl = thread.getContextClassLoader();
+        if (tccl == null) {
+            throw new ClassNotFoundException("Thread " + thread + " does not have a Context Class Loader, cannot load "
+                    + name);
+        }
+        return loadClass(tccl, name);
+    }
+
+    public static Optional<Class<?>> tryToLoadClassWithTCCL(final String fullyQualifiedClassName) {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader tccl = thread.getContextClassLoader();
+        if (tccl == null) {
+            LOG.debug("Thread {} does not have a Context Class Loader, not loading class {}", thread,
+                fullyQualifiedClassName);
+            return Optional.empty();
+        }
+
+        try {
+            return Optional.of(loadClass(tccl, fullyQualifiedClassName));
+        } catch (final ClassNotFoundException e) {
+            LOG.debug("Failed to load class {}", fullyQualifiedClassName, e);
+            return Optional.empty();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <S, G, P> Optional<Class<P>> findFirstGenericArgument(final Class<S> scannedClass,
+            final Class<G> genericType) {
+        return getWithClassLoader(scannedClass.getClassLoader(), () -> {
+            return findParameterizedType(scannedClass, genericType)
+                    .map(ptype -> (Class<P>) ptype.getActualTypeArguments()[0]);
+        });
+    }
+
+    /**
+     * Find the parameterized instantiation of a particular interface implemented by a class.
+     *
+     * @param subclass Implementing class
+     * @param genericType Interface to search for
+     * @return Parameterized interface as implemented by the class, if present
+     */
+    public static Optional<ParameterizedType> findParameterizedType(final Class<?> subclass,
+            final Class<?> genericType) {
+        requireNonNull(genericType);
+
+        for (final Type type : subclass.getGenericInterfaces()) {
+            if (type instanceof ParameterizedType) {
+                final ParameterizedType ptype = (ParameterizedType) type;
+                if (genericType.equals(ptype.getRawType())) {
+                    return Optional.of(ptype);
+                }
+            }
+        }
+
+        LOG.debug("Class {} does not declare interface {}", subclass, genericType);
+        return Optional.empty();
+    }
+
+    /**
+     * Extract the first generic type argument for a Type. If the type is not parameterized, this method returns empty.
+     *
+     * @param type Type to examine
+     * @return First generic type argument, if present
+     * @throws NullPointerException if {@code type} is null
+     */
+    public static Optional<Type> getFirstGenericParameter(final Type type) {
+        requireNonNull(type);
+        return type instanceof ParameterizedType ? Optional.of(((ParameterizedType) type).getActualTypeArguments()[0])
+                : Optional.empty();
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/CollectionWrappers.java b/common/util/src/main/java/org/opendaylight/yangtools/util/CollectionWrappers.java
new file mode 100644 (file)
index 0000000..0693d87
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import java.util.AbstractList;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.stream.Stream;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Utility class for adapting a {@link Collection}s to {@link Set}s and {@link List}s.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public final class CollectionWrappers {
+    private static final class ListWrapper<E> extends AbstractList<E> implements Delegator<Collection<E>> {
+        private final Collection<E> delegate;
+
+        ListWrapper(final Collection<E> delegate) {
+            this.delegate = requireNonNull(delegate);
+        }
+
+        @Override
+        public Collection<E> getDelegate() {
+            return delegate;
+        }
+
+        @Override
+        public Iterator<E> iterator() {
+            return Iterators.unmodifiableIterator(delegate.iterator());
+        }
+
+        @Override
+        public int size() {
+            return delegate.size();
+        }
+
+        @Override
+        public Spliterator<E> spliterator() {
+            return delegate.spliterator();
+        }
+
+        @Override
+        public Stream<E> parallelStream() {
+            return delegate.parallelStream();
+        }
+
+        @Override
+        public Stream<E> stream() {
+            return delegate.stream();
+        }
+
+        @Override
+        public E get(final int index) {
+            return Iterables.get(delegate, index);
+        }
+    }
+
+    private static final class SetWrapper<E> extends AbstractSet<E> implements Delegator<Collection<E>> {
+        private final Collection<E> delegate;
+
+        SetWrapper(final Collection<E> delegate) {
+            this.delegate = requireNonNull(delegate);
+        }
+
+        @Override
+        public Collection<E> getDelegate() {
+            return delegate;
+        }
+
+        @Override
+        public Iterator<E> iterator() {
+            return Iterators.unmodifiableIterator(delegate.iterator());
+        }
+
+        @Override
+        public int size() {
+            return delegate.size();
+        }
+
+        @Override
+        public Spliterator<E> spliterator() {
+            return delegate.spliterator();
+        }
+
+        @Override
+        public Stream<E> parallelStream() {
+            return delegate.parallelStream();
+        }
+
+        @Override
+        public Stream<E> stream() {
+            return delegate.stream();
+        }
+    }
+
+    private CollectionWrappers() {
+
+    }
+
+    /**
+     * Wrap the specified {@link Collection} as a {@link List}. If the collection is already a List, it is wrapped in
+     * a {@link Collections#unmodifiableList(List)} to prevent mutability leaking. If the collection is determined
+     * to be empty, an empty list is returned instead. If the collection is a known-immutable implementation of List
+     * interface, it is returned unwrapped. Backing collection is required to be effectively immutable. If this
+     * requirement is violated, the returned object may behave in unpredictable ways.
+     *
+     * @param collection Collection to be wrapped
+     * @return An effectively-immutable wrapper of the collection.
+     * @throws NullPointerException if collection is null
+     */
+    public static <E> List<E> wrapAsList(final Collection<E> collection) {
+        if (collection.isEmpty()) {
+            return ImmutableList.of();
+        }
+        if (collection instanceof SetWrapper) {
+            return wrapAsList(((SetWrapper<E>) collection).getDelegate());
+        }
+        if (collection instanceof List) {
+            final List<E> cast = (List<E>) collection;
+            return cast instanceof ListWrapper || cast instanceof Immutable || cast instanceof ImmutableList
+                    ? cast : Collections.unmodifiableList(cast);
+        }
+
+        return new ListWrapper<>(collection);
+    }
+
+    /**
+     * Wrap the specified {@link Collection} as a {@link Set}. If the collection is already a Set, it is wrapped in
+     * a {@link Collections#unmodifiableSet(Set)} to prevent mutability leaking. If the collection is determined
+     * to be empty, an empty set is returned instead. If the collection is a known-immutable implementation of Set
+     * interface, it is returned unwrapped. The collection is checked for duplicates at instantiation time, such that
+     * it effectively implements the Set contract. Backing collection is required to be effectively immutable. If this
+     * requirement is violated, the returned object may behave in unpredictable ways.
+     *
+     * @param collection Collection to be wrapped
+     * @return An effectively-immutable wrapper of the collection.
+     * @throws NullPointerException if collection is null or any of its elements is null
+     * @throws IllegalArgumentException if the collection's contents do not conform to the Set contract
+     */
+    public static <E> Set<E> wrapAsSet(final Collection<E> collection) {
+        if (collection.isEmpty()) {
+            return ImmutableSet.of();
+        }
+        if (collection instanceof ListWrapper) {
+            return wrapAsSet(((ListWrapper<E>) collection).getDelegate());
+        }
+        if (collection instanceof Set) {
+            final Set<E> cast = (Set<E>) collection;
+            return cast instanceof SetWrapper || cast instanceof Immutable || cast instanceof SingletonSet
+                    || cast instanceof ImmutableSet ? cast : Collections.unmodifiableSet(cast);
+        }
+
+        final Set<E> check = ImmutableSet.copyOf(collection);
+        checkArgument(collection.size() == check.size(), "Supplied collection %s has duplicate elements", collection);
+        return new SetWrapper<>(collection);
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ConcurrentDurationStatisticsTracker.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ConcurrentDurationStatisticsTracker.java
new file mode 100644 (file)
index 0000000..085805a
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import com.google.common.primitives.UnsignedLong;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+/**
+ * Concurrent version of {@link DurationStatisticsTracker}.
+ */
+// TODO: once DurationStatsTracker is gone make this class final
+class ConcurrentDurationStatisticsTracker extends DurationStatisticsTracker {
+
+    private static final AtomicReferenceFieldUpdater<ConcurrentDurationStatisticsTracker, DurationWithTime>
+        LONGEST_UPDATER = AtomicReferenceFieldUpdater.newUpdater(ConcurrentDurationStatisticsTracker.class,
+                DurationWithTime.class, "longest");
+
+    private static final AtomicReferenceFieldUpdater<ConcurrentDurationStatisticsTracker, DurationWithTime>
+        SHORTEST_UPDATER = AtomicReferenceFieldUpdater.newUpdater(ConcurrentDurationStatisticsTracker.class,
+                DurationWithTime.class, "shortest");
+
+    private static final AtomicLongFieldUpdater<ConcurrentDurationStatisticsTracker> COUNT_UPDATER =
+            AtomicLongFieldUpdater.newUpdater(ConcurrentDurationStatisticsTracker.class, "count");
+    private static final AtomicLongFieldUpdater<ConcurrentDurationStatisticsTracker> SUM_UPDATER =
+            AtomicLongFieldUpdater.newUpdater(ConcurrentDurationStatisticsTracker.class, "sum");
+
+    private volatile long sum = 0;
+    private volatile long count = 0;
+    private volatile DurationWithTime longest = null;
+    private volatile DurationWithTime shortest = null;
+
+    ConcurrentDurationStatisticsTracker() {
+        // Hidden on purpose
+    }
+
+    @Override
+    public final void addDuration(final long duration) {
+        // First update the quick stats
+        SUM_UPDATER.addAndGet(this, duration);
+        COUNT_UPDATER.incrementAndGet(this);
+
+        /*
+         * Now the hairy 'min/max' things. The notion of "now" we cache,
+         * so the first time we use it, we do not call it twice. We populate
+         * it lazily, though.
+         *
+         * The longest/shortest stats both are encapsulated in an object,
+         * so we update them atomically and we minimize the number of volatile
+         * operations.
+         */
+        DurationWithTime current = shortest;
+        if (current == null || duration < current.getDuration()) {
+            final DurationWithTime newObj = new DurationWithTime(duration, System.currentTimeMillis());
+            while (!SHORTEST_UPDATER.weakCompareAndSet(this, current, newObj)) {
+                current = shortest;
+                if (current != null && duration >= current.getDuration()) {
+                    break;
+                }
+            }
+        }
+
+        current = longest;
+        if (current == null || duration > current.getDuration()) {
+            final DurationWithTime newObj = new DurationWithTime(duration, System.currentTimeMillis());
+            while (!LONGEST_UPDATER.weakCompareAndSet(this, current, newObj)) {
+                current = longest;
+                if (current != null && duration <= current.getDuration()) {
+                    break;
+                }
+            }
+        }
+    }
+
+    @Override
+    public final long getTotalDurations() {
+        return count;
+    }
+
+    @Override
+    public final double getAverageDuration() {
+        final long myCount = count;
+        return myCount == 0 ? 0 : UnsignedLong.fromLongBits(sum).doubleValue() / myCount;
+    }
+
+    @Override
+    public final synchronized void reset() {
+        // Synchronized is just to make sure we do not have concurrent resets :)
+        longest = null;
+        shortest = null;
+        count = 0;
+        sum = 0;
+    }
+
+    @Override
+    protected final DurationWithTime getLongest() {
+        return longest;
+    }
+
+    @Override
+    protected final DurationWithTime getShortest() {
+        return shortest;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ConstantArrayCollection.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ConstantArrayCollection.java
new file mode 100644 (file)
index 0000000..d843a36
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2015 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 java.util.Objects.requireNonNull;
+
+import com.google.common.collect.UnmodifiableIterator;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Internal array-backed {@link List}. It assumes the array does not contain nulls and it does not get modified
+ * externally. These assumptions are not checked. It does not allow modification of the underlying array -- thus it
+ * is very useful for use with {@link ImmutableOffsetMap}.
+ *
+ * @param <E> the type of elements in this list
+ */
+final class ConstantArrayCollection<E> implements Collection<E>, Serializable {
+    private static final long serialVersionUID = 1L;
+    private final E @NonNull[] array;
+
+    ConstantArrayCollection(final E @NonNull[] array) {
+        this.array = requireNonNull(array);
+    }
+
+    @Override
+    public int size() {
+        return array.length;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return array.length == 0;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean contains(final Object o) {
+        for (Object wlk : array) {
+            if (o.equals(wlk)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public @NonNull Iterator<E> iterator() {
+        return new Itr<>(array);
+    }
+
+    @Override
+    public Object @NonNull[] toArray() {
+        return array.clone();
+    }
+
+    @SuppressWarnings({ "unchecked", "checkstyle:parameterName" })
+    @Override
+    public <T> T[] toArray(final T[] a) {
+        if (a.length < array.length) {
+            return Arrays.copyOf(array, array.length, (Class<T[]>)a.getClass().getComponentType());
+        }
+
+        System.arraycopy(array, 0, a, 0, array.length);
+        if (a.length > array.length) {
+            a[array.length] = null;
+        }
+        return a;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean add(final E e) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean remove(final Object o) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean containsAll(final Collection<?> c) {
+        for (Object o : c) {
+            if (!contains(o)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean addAll(final Collection<? extends E> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean removeAll(final Collection<?> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean retainAll(final Collection<?> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 1;
+        for (E e : array) {
+            result = 31 * result + e.hashCode();
+        }
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return obj == this || obj instanceof ConstantArrayCollection
+                && Arrays.equals(array, ((ConstantArrayCollection<?>) obj).array);
+    }
+
+    @Override
+    public @NonNull String toString() {
+        if (array.length == 0) {
+            return "[]";
+        }
+
+        final StringBuilder sb = new StringBuilder("[");
+        int offset = 0;
+        while (offset < array.length - 1) {
+            sb.append(String.valueOf(array[offset++])).append(", ");
+        }
+        return sb.append(String.valueOf(array[offset])).append(']').toString();
+    }
+
+    private static final class Itr<E> extends UnmodifiableIterator<E> {
+        private final E @NonNull[] array;
+        private int offset = 0;
+
+        Itr(final E @NonNull[] array) {
+            this.array = array;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return offset < array.length;
+        }
+
+        @Override
+        public E next() {
+            if (offset >= array.length) {
+                throw new NoSuchElementException();
+            }
+            return array[offset++];
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/DurationStatisticsTracker.java b/common/util/src/main/java/org/opendaylight/yangtools/util/DurationStatisticsTracker.java
new file mode 100644 (file)
index 0000000..e3d121d
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import static java.util.concurrent.TimeUnit.MICROSECONDS;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import com.google.common.annotations.Beta;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract class that calculates and tracks time duration statistics.
+ *
+ * @author Thomas Pantelis
+ * @author Robert Varga
+ */
+@Beta
+public abstract class DurationStatisticsTracker {
+    private static final Logger LOG = LoggerFactory.getLogger(DurationStatisticsTracker.class);
+    private static final DecimalFormat DECIMAL_FORMAT;
+
+    static {
+        final DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
+        symbols.setDecimalSeparator('.');
+        DECIMAL_FORMAT = new DecimalFormat("0.00", symbols);
+    }
+
+    /**
+     * Create a concurrent {@link DurationStatisticsTracker}, which performs well
+     * in very contended environments.
+     *
+     * @return A new instance.
+     */
+    public static DurationStatisticsTracker createConcurrent() {
+        return new ConcurrentDurationStatisticsTracker();
+    }
+
+    /**
+     * Create a synchronized {@link DurationStatisticsTracker}, which performs well
+     * in non-contended environments.
+     *
+     * @return A new instance.
+     */
+    public static DurationStatisticsTracker createSynchronized() {
+        return new SynchronizedDurationStatsTracker();
+    }
+
+    /**
+     * Add a duration to track.
+     *
+     * @param duration
+     *            non-negative duration in nanoseconds.
+     */
+    public abstract void addDuration(long duration);
+
+    /**
+     * Returns the average duration in nanoseconds.
+     */
+    public abstract double getAverageDuration();
+
+    /**
+     * Returns the total number of tracked durations.
+     *
+     * @return Total number of measurements accumulated since last
+     *         {@link #reset()}.
+     */
+    public abstract long getTotalDurations();
+
+    /**
+     * Resets all statistics back to their defaults.
+     */
+    public abstract void reset();
+
+    /**
+     * Get the shortest recorded duration and the time when it was recorded.
+     *
+     * @return Duration and timestamp.
+     */
+    protected abstract DurationWithTime getShortest();
+
+    /**
+     * Get the longest recorded duration and the time when it was recorded.
+     *
+     * @return Duration and timestamp.
+     */
+    protected abstract DurationWithTime getLongest();
+
+    /**
+     * Returns the longest duration in nanoseconds.
+     */
+    public final long getLongestDuration() {
+        return getDuration(getLongest());
+    }
+
+    /**
+     * Returns the shortest duration in nanoseconds.
+     */
+    public final long getShortestDuration() {
+        return getDuration(getShortest());
+    }
+
+    /**
+     * Returns the average duration as a displayable String with units, e.g.
+     * "12.34 ms".
+     */
+    public final String getDisplayableAverageDuration() {
+        return formatDuration(getAverageDuration(), null);
+    }
+
+    /**
+     * Returns the longest duration as a displayable String with units and the
+     * date/time at which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
+     */
+    public final String getDisplayableLongestDuration() {
+        return formatDuration(getLongest());
+    }
+
+    /**
+     * Returns the shortest duration as a displayable String with units and the
+     * date/time at which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
+     */
+    public final String getDisplayableShortestDuration() {
+        return formatDuration(getShortest());
+    }
+
+    /**
+     * Returns the time stamp of the longest duration.
+     */
+    public final long getTimeOfLongestDuration() {
+        return getTimeMillis(getLongest());
+    }
+
+    /**
+     * Returns the time stamp of the shortest duration.
+     */
+    public final long getTimeOfShortestDuration() {
+        return getTimeMillis(getShortest());
+    }
+
+    /**
+     * Returns formatted value of number, e.g. "12.34". Always is used dot as
+     * decimal separator.
+     */
+    private static synchronized String formatDecimalValue(final double value) {
+        return DECIMAL_FORMAT.format(value);
+    }
+
+    private static long getDuration(final DurationWithTime current) {
+        return current == null ? 0L : current.getDuration();
+    }
+
+    private static long getTimeMillis(final DurationWithTime current) {
+        return current == null ? 0L : current.getTimeMillis();
+    }
+
+    private static String formatDuration(final double duration, final Long timeStamp) {
+        final TimeUnit unit = chooseUnit((long) duration);
+        final double value = duration / NANOSECONDS.convert(1, unit);
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append(formatDecimalValue(value));
+        sb.append(' ');
+        sb.append(abbreviate(unit));
+
+        if (timeStamp != null) {
+            sb.append(String.format(" at %1$tD %1$tT", new Date(timeStamp)));
+        }
+
+        return sb.toString();
+    }
+
+    private static String formatDuration(final DurationWithTime current) {
+        if (current == null) {
+            return formatDuration(0, null);
+        }
+        return formatDuration(current.getDuration(), current.getTimeMillis());
+    }
+
+    private static TimeUnit chooseUnit(final long nanos) {
+        // TODO: this could be inlined, as we are doing needless divisions
+        if (NANOSECONDS.toSeconds(nanos) > 0) {
+            return SECONDS;
+        }
+        if (NANOSECONDS.toMillis(nanos) > 0) {
+            return MILLISECONDS;
+        }
+        if (NANOSECONDS.toMicros(nanos) > 0) {
+            return MICROSECONDS;
+        }
+        return NANOSECONDS;
+    }
+
+    private static String abbreviate(final TimeUnit unit) {
+        switch (unit) {
+            case NANOSECONDS:
+                return "ns";
+            case MICROSECONDS:
+                return "μs";
+            case MILLISECONDS:
+                return "ms";
+            case SECONDS:
+                return "s";
+            case MINUTES:
+                return "m";
+            case HOURS:
+                return "h";
+            case DAYS:
+                return "d";
+            default:
+                LOG.warn("Unhandled time unit {}", unit);
+                return "";
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/DurationWithTime.java b/common/util/src/main/java/org/opendaylight/yangtools/util/DurationWithTime.java
new file mode 100644 (file)
index 0000000..aa77678
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+/**
+ * Utility holder for a duration/time of occurance.
+ */
+final class DurationWithTime {
+    private final long duration;
+    private final long timeMillis;
+
+    DurationWithTime(final long duration, final long timeMillis) {
+        this.duration = duration;
+        this.timeMillis = timeMillis;
+    }
+
+    long getDuration() {
+        return duration;
+    }
+
+    long getTimeMillis() {
+        return timeMillis;
+    }
+}
\ No newline at end of file
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/EmptyDeque.java b/common/util/src/main/java/org/opendaylight/yangtools/util/EmptyDeque.java
new file mode 100644 (file)
index 0000000..27e40ed
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2016 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 java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.AbstractQueue;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * A specialized always-empty implementation of {@link java.util.Deque}. This implementation will always refuse new
+ * elements in its {@link #offer(Object)} method.
+
+ * @author Robert Varga
+ *
+ * @param <E> the type of elements held in this collection
+ */
+@Beta
+public final class EmptyDeque<E> extends AbstractQueue<E> implements Deque<E>, Immutable {
+    private static final EmptyDeque<?> INSTANCE = new EmptyDeque<>();
+    private static final Object[] EMPTY_ARRAY = new Object[0];
+
+    private EmptyDeque() {
+        // No instances
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> EmptyDeque<T> instance() {
+        return (EmptyDeque<T>) INSTANCE;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean offer(final E e) {
+        return false;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean offerFirst(final E e) {
+        return false;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean offerLast(final E e) {
+        return false;
+    }
+
+    @Override
+    public E poll() {
+        return null;
+    }
+
+    @Override
+    public E pollFirst() {
+        return null;
+    }
+
+    @Override
+    public E pollLast() {
+        return null;
+    }
+
+    @Override
+    public E peek() {
+        return null;
+    }
+
+    @Override
+    public E peekFirst() {
+        return null;
+    }
+
+    @Override
+    public E peekLast() {
+        return null;
+    }
+
+    @Override
+    public Iterator<E> iterator() {
+        return Collections.emptyIterator();
+    }
+
+    @Override
+    public Spliterator<E> spliterator() {
+        return Spliterators.emptySpliterator();
+    }
+
+    @Override
+    public int size() {
+        return 0;
+    }
+
+    @Override
+    public Object[] toArray() {
+        return EMPTY_ARRAY;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public <T> T[] toArray(final T[] a) {
+        return requireNonNull(a);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public void addFirst(final E e) {
+        add(e);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public void addLast(final E e) {
+        add(e);
+    }
+
+    @Override
+    public E removeFirst() {
+        return remove();
+    }
+
+    @Override
+    public E removeLast() {
+        return remove();
+    }
+
+    @Override
+    public E getFirst() {
+        return element();
+    }
+
+    @Override
+    public E getLast() {
+        return element();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean removeFirstOccurrence(final Object o) {
+        return false;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean removeLastOccurrence(final Object o) {
+        return false;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public void push(final E e) {
+        addFirst(e);
+    }
+
+    @Override
+    public E pop() {
+        return removeFirst();
+    }
+
+    @Override
+    public Iterator<E> descendingIterator() {
+        return Collections.emptyIterator();
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/EvenMoreObjects.java b/common/util/src/main/java/org/opendaylight/yangtools/util/EvenMoreObjects.java
new file mode 100644 (file)
index 0000000..1923c2b
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import java.util.function.BiFunction;
+
+/**
+ * Utility helping to implement readable equals() methods.
+ *
+ * <p>Usage:
+ * <pre>
+ *{@literal @}Override
+ * public boolean equals(Object obj) {
+ *     return EvenMoreObjects.equalsHelper(this, obj,
+ *        (one, another) -&gt; Objects.equals(one.name, another.name) &amp;&amp; Objects.equals(one.age, another.age));
+ * }
+ * </pre>
+ *
+ * <p>See <a href="https://github.com/google/guava/issues/2521">Guava issue proposing contributing this</a>.
+ *
+ * @see MoreObjects
+ *
+ * @author Michael Vorburger, Red Hat
+ */
+@Beta
+public final class EvenMoreObjects {
+
+    @SuppressWarnings("unchecked")
+    public static <T> boolean equalsHelper(final T self, final Object other, final BooleanEqualsFunction<T> equals) {
+        if (other == self) {
+            return true;
+        }
+        if (other == null) {
+            return false;
+        }
+        if (self.getClass() != other.getClass()) {
+            return false;
+        }
+        return equals.apply(self, (T) other).booleanValue();
+    }
+
+    @FunctionalInterface
+    public interface BooleanEqualsFunction<T> extends BiFunction<T, T, Boolean> { }
+
+    private EvenMoreObjects() {
+
+    }
+}
+
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ExecutorServiceUtil.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ExecutorServiceUtil.java
new file mode 100644 (file)
index 0000000..b438a96
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import com.google.common.util.concurrent.ForwardingBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+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.eclipse.jdt.annotation.NonNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility methods for dealing with {@link ExecutorService}s.
+ */
+public final class ExecutorServiceUtil {
+    private static final class WaitInQueueExecutionHandler implements RejectedExecutionHandler {
+        @Override
+        @SuppressWarnings("checkstyle:parameterName")
+        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("Interrupted while attempting to put to the queue", e);
+                throw new RejectedExecutionException("Interrupted while attempting to put to the queue", e);
+            }
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(ExecutorServiceUtil.class);
+    private static final @NonNull RejectedExecutionHandler WAIT_IN_QUEUE_HANDLER = new WaitInQueueExecutionHandler();
+
+    private ExecutorServiceUtil() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    /**
+     * 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.
+     *
+     * @param delegate Backing blocking queue.
+     * @return A new blocking queue backed by the delegate
+     */
+    public static <E> @NonNull BlockingQueue<E> offerFailingBlockingQueue(final BlockingQueue<E> delegate) {
+        return new ForwardingBlockingQueue<E>() {
+            @Override
+            @SuppressWarnings("checkstyle:parameterName")
+            public boolean offer(final E o) {
+                return false;
+            }
+
+            @Override
+            protected BlockingQueue<E> delegate() {
+                return delegate;
+            }
+        };
+    }
+
+    /**
+     * 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 static @NonNull 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 @NonNull ExecutorService executor, final long timeout,
+            final @NonNull TimeUnit unit) {
+        executor.shutdown();
+
+        try {
+            if (!executor.awaitTermination(timeout, unit)) {
+                executor.shutdownNow();
+            }
+        } catch (InterruptedException e) {
+            executor.shutdownNow();
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ForwardingIdentityObject.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ForwardingIdentityObject.java
new file mode 100644 (file)
index 0000000..f775562
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ForwardingObject;
+import java.util.concurrent.ConcurrentHashMap;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Delegator;
+
+/**
+ * A {@link ForwardingObject} which additionally masks {@link #hashCode()}/{@link #equals(Object)} of a delegate object,
+ * so that it can be a data transfer object with data-dependent implementations of those contracts can be use in
+ * collections and maps which need to work on identity. This is useful in situations where identity equality needs to
+ * be used with the conjunction with the collections library, for example {@link ConcurrentHashMap}.  All instances are
+ * considered equal if they refer to the same delegate object.
+ *
+ * <p>
+ * Note this class forms its own equality domain, and its use may lead to surprising results, especially where
+ * {@link #toString()} is involved. For example a {@code Map.toString()} may end up emitting two keys which have the
+ * same String representation.
+ *
+ * @param <T> Type of wrapped object
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public abstract class ForwardingIdentityObject<T> extends ForwardingObject implements Delegator<T> {
+    protected ForwardingIdentityObject() {
+        // Mask public constructor
+    }
+
+    public static <T> ForwardingIdentityObject<T> of(final T obj) {
+        return checkedOf(requireNonNull(obj));
+    }
+
+    @Override
+    public final @NonNull T getDelegate() {
+        return delegate();
+    }
+
+    @Override
+    public final int hashCode() {
+        return System.identityHashCode(delegate());
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        return obj == this || obj instanceof ForwardingIdentityObject
+                && delegate() == ((ForwardingIdentityObject<?>) obj).delegate();
+    }
+
+    @Override
+    protected abstract @NonNull T delegate();
+
+    private static <T> ForwardingIdentityObject<T> checkedOf(final @NonNull T delegate) {
+        return new ForwardingIdentityObject<T>() {
+            @Override
+            protected @NonNull T delegate() {
+                return delegate;
+            }
+        };
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/HashCodeBuilder.java b/common/util/src/main/java/org/opendaylight/yangtools/util/HashCodeBuilder.java
new file mode 100644 (file)
index 0000000..5bd0233
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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 org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Builder;
+
+/**
+ * Utility class for incrementally building object hashCode by hashing together component objects, one by one.
+ *
+ * @param <T> Component object type
+ */
+public final class HashCodeBuilder<T> implements Builder<Integer> {
+    /**
+     * The value 31 was chosen because it is an odd prime. If it were even and the multiplication overflowed,
+     * information would be lost, as multiplication by 2 is equivalent to shifting. The advantage of using a prime is
+     * less clear, but it is traditional. A nice property of 31 is that the multiplication can be replaced by a shift
+     * and a subtraction for better performance: 31 * i == (i << 5) - i. Modern VMs do this sort of optimization
+     * automatically.
+     *
+     * <p>(from Joshua Bloch's Effective Java, Chapter 3, Item 9: Always override hashcode when you override equals,
+     * page 48)
+     */
+    private static final int PRIME = 31;
+    private int currentHash;
+
+    /**
+     * Create a new instance, with internal hash initialized to 1, equivalent of <code>HashCodeBuilder(1)</code>.
+     */
+    public HashCodeBuilder() {
+        this(1);
+    }
+
+    /**
+     * Create a new instance, with internal hash set to specified seed.
+     *
+     * @param seedHash Seed hash value
+     */
+    public HashCodeBuilder(final int seedHash) {
+        this.currentHash = seedHash;
+    }
+
+    /**
+     * Determine the next hash code combining a base hash code and the hash code of an object.
+     *
+     * @param hashCode base hash code
+     * @param obj Object to be added
+     * @return Combined hash code
+     */
+    public static int nextHashCode(final int hashCode, final Object obj) {
+        return PRIME * hashCode + obj.hashCode();
+    }
+
+    /**
+     * Update the internal hash code with the hash code of a component object.
+     *
+     * @param obj Component object
+     */
+    public void addArgument(final T obj) {
+        currentHash = nextHashCode(currentHash, obj);
+    }
+
+    @Override
+    public @NonNull Integer build() {
+        return currentHash;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableMapTemplate.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableMapTemplate.java
new file mode 100644 (file)
index 0000000..4f9d4b0
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.checkArgument;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Template for instantiating {@link UnmodifiableMapPhase} instances with a fixed set of keys. The template can then be
+ * used as a factory for instances via using {@link #instantiateTransformed(Map, BiFunction)} or, more efficiently,
+ * using {@link #instantiateWithValues(Object[])} where the argument array has values ordered corresponding to the key
+ * order defined by {@link #keySet()}.
+ *
+ * <p>
+ * If the keySet is static known to contain only a single key, consider using {@link SharedSingletonMapTemplate}. If
+ * it is statically known to contain multiple keys, consider using {@link ImmutableOffsetMapTemplate}.
+ *
+ * @param <K> the type of keys maintained by this template
+ */
+@Beta
+public abstract class ImmutableMapTemplate<K> implements Immutable {
+    ImmutableMapTemplate() {
+        // Hidden on purpose
+    }
+
+    /**
+     * Create a template which produces Maps with specified keys, with iteration order matching the iteration order
+     * of {@code keys}. {@link #keySet()} will return these keys in exactly the same order. The resulting map will
+     * retain insertion order through {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
+     *
+     * @param keys Keys in requested iteration order.
+     * @param <K> the type of keys maintained by resulting template
+     * @return A template object.
+     * @throws NullPointerException if {@code keys} or any of its elements is null
+     * @throws IllegalArgumentException if {@code keys} is empty
+     */
+    public static <K> @NonNull ImmutableMapTemplate<K> ordered(final Collection<K> keys) {
+        switch (keys.size()) {
+            case 0:
+                throw new IllegalArgumentException("Proposed keyset must not be empty");
+            case 1:
+                return SharedSingletonMapTemplate.ordered(keys.iterator().next());
+            default:
+                return ImmutableOffsetMapTemplate.ordered(keys);
+        }
+    }
+
+    /**
+     * Create a template which produces Maps with specified keys, with unconstrained iteration order. Produced maps
+     * will have the iteration order matching the order returned by {@link #keySet()}.  The resulting map will
+     * NOT retain ordering through {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
+     *
+     * @param keys Keys in any iteration order.
+     * @param <K> the type of keys maintained by resulting template
+     * @return A template object.
+     * @throws NullPointerException if {@code keys} or any of its elements is null
+     * @throws IllegalArgumentException if {@code keys} is empty
+     */
+    public static <K> @NonNull ImmutableMapTemplate<K> unordered(final Collection<K> keys) {
+        switch (keys.size()) {
+            case 0:
+                throw new IllegalArgumentException("Proposed keyset must not be empty");
+            case 1:
+                return SharedSingletonMapTemplate.unordered(keys.iterator().next());
+            default:
+                return ImmutableOffsetMapTemplate.unordered(keys);
+        }
+    }
+
+    /**
+     * Instantiate an immutable map by applying specified {@code transformer} to values of {@code fromMap}.
+     *
+     * @param fromMap Input map
+     * @param keyValueTransformer Transformation to apply to values
+     * @param <T> the type of input values
+     * @param <V> the type of mapped values
+     * @return An immutable map
+     * @throws NullPointerException if any of the arguments is null or if the transformer produces a {@code null} value
+     * @throws IllegalArgumentException if {@code fromMap#keySet()} does not match this template's keys
+     */
+    public abstract <T, V> @NonNull UnmodifiableMapPhase<K, V> instantiateTransformed(Map<K, T> fromMap,
+            BiFunction<K, T, V> keyValueTransformer);
+
+    /**
+     * Instantiate an immutable map by filling values from provided array. The array MUST be ordered to match key order
+     * as returned by {@link #keySet()}.
+     *
+     * @param values Values to use
+     * @param <V> the type of mapped values
+     * @return An immutable map
+     * @throws NullPointerException if {@code values} or any of its elements is null
+     * @throws IllegalArgumentException if {@code values.length} does not match the number of keys in this template
+     */
+    @SuppressWarnings("unchecked")
+    public abstract <V> @NonNull UnmodifiableMapPhase<K, V> instantiateWithValues(V... values);
+
+    /**
+     * Returns the set of keys expected by this template, in the iteration order Maps resulting from instantiation
+     * will have.
+     *
+     * @return This template's key set
+     * @see Map#keySet()
+     */
+    public abstract Set<K> keySet();
+
+    final <T, V> @NonNull V transformValue(final K key, final T input, final BiFunction<K, T, V> transformer) {
+        final V value = transformer.apply(key, input);
+        checkArgument(value != null, "Transformer returned null for input %s at key %s", input, key);
+        return value;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableOffsetMap.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableOffsetMap.java
new file mode 100644 (file)
index 0000000..be0bcbf
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2015 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.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.UnmodifiableIterator;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Implementation of the {@link Map} interface which stores a set of immutable mappings using a key-to-offset map and
+ * a backing array. This is useful for situations where the same key set is shared across a multitude of maps, as this
+ * class uses a global cache to share the key-to-offset mapping.
+ *
+ * <p>
+ * In case the set of keys is statically known, you can use {@link ImmutableOffsetMapTemplate} to efficiently create
+ * {@link ImmutableOffsetMap} instances.
+ *
+ * @param <K> the type of keys maintained by this map
+ * @param <V> the type of mapped values
+ */
+@Beta
+public abstract class ImmutableOffsetMap<K, V> implements UnmodifiableMapPhase<K, V>, Serializable {
+    static final class Ordered<K, V> extends ImmutableOffsetMap<K, V> {
+        private static final long serialVersionUID = 1L;
+
+        Ordered(final ImmutableMap<K, Integer> offsets, final V[] objects) {
+            super(offsets, objects);
+        }
+
+        @Override
+        public @NonNull MutableOffsetMap<K, V> toModifiableMap() {
+            return MutableOffsetMap.orderedCopyOf(this);
+        }
+
+        @Override
+        void setFields(final List<K> keys, final V[] values) throws IOException {
+            setField(this, OFFSETS_FIELD, OffsetMapCache.orderedOffsets(keys));
+            setField(this, ARRAY_FIELD, values);
+        }
+    }
+
+    static final class Unordered<K, V> extends ImmutableOffsetMap<K, V> {
+        private static final long serialVersionUID = 1L;
+
+        Unordered(final ImmutableMap<K, Integer> offsets, final V[] objects) {
+            super(offsets, objects);
+        }
+
+        @Override
+        public @NonNull MutableOffsetMap<K, V> toModifiableMap() {
+            return MutableOffsetMap.unorderedCopyOf(this);
+        }
+
+        @Override
+        void setFields(final List<K> keys, final V[] values) throws IOException {
+            final Map<K, Integer> newOffsets = OffsetMapCache.unorderedOffsets(keys);
+
+            setField(this, OFFSETS_FIELD, newOffsets);
+            setField(this, ARRAY_FIELD, OffsetMapCache.adjustedArray(newOffsets, keys, values));
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+
+    private final transient @NonNull ImmutableMap<K, Integer> offsets;
+    private final transient @NonNull V[] objects;
+    private transient int hashCode;
+
+    /**
+     * Construct a new instance backed by specified key-to-offset map and array of objects.
+     *
+     * @param offsets Key-to-offset map, may not be null
+     * @param objects Array of value object, may not be null. The array is stored as is, the caller
+     *              is responsible for ensuring its contents remain unmodified.
+     */
+    ImmutableOffsetMap(final ImmutableMap<K, Integer> offsets, final V[] objects) {
+        this.offsets = requireNonNull(offsets);
+        this.objects = requireNonNull(objects);
+        checkArgument(offsets.size() == objects.length);
+    }
+
+    @Override
+    public abstract @NonNull MutableOffsetMap<K, V> toModifiableMap();
+
+    abstract void setFields(List<K> keys, V[] values) throws IOException;
+
+    /**
+     * Create an {@link ImmutableOffsetMap} as a copy of an existing map. This is actually not completely true, as this
+     * method returns an {@link ImmutableMap} for empty and singleton inputs, as those are more memory-efficient. This
+     * method also recognizes {@link ImmutableOffsetMap} and {@link SharedSingletonMap} on input, and returns it back
+     * without doing anything else. It also recognizes {@link MutableOffsetMap} (as returned by
+     * {@link #toModifiableMap()}) and makes an efficient copy of its contents. All other maps are converted to an
+     * {@link ImmutableOffsetMap} with the same iteration order as input.
+     *
+     * @param map Input map, may not be null.
+     * @return An isolated, immutable copy of the input map
+     * @throws NullPointerException if {@code map} or any of its elements is null.
+     */
+    public static <K, V> @NonNull Map<K, V> orderedCopyOf(final @NonNull Map<K, V> map) {
+        final Map<K, V> common = commonCopy(map);
+        if (common != null) {
+            return common;
+        }
+
+        final int size = map.size();
+        if (size == 1) {
+            // Efficient single-entry implementation
+            final Entry<K, V> e = map.entrySet().iterator().next();
+            return SharedSingletonMap.orderedOf(e.getKey(), e.getValue());
+        }
+
+        final ImmutableMap<K, Integer> offsets = OffsetMapCache.orderedOffsets(map.keySet());
+        @SuppressWarnings("unchecked")
+        final V[] array = (V[]) new Object[offsets.size()];
+        for (Entry<K, V> e : map.entrySet()) {
+            array[offsets.get(e.getKey())] = e.getValue();
+        }
+
+        return new Ordered<>(offsets, array);
+    }
+
+    /**
+     * Create an {@link ImmutableOffsetMap} as a copy of an existing map. This is actually not completely true, as this
+     * method returns an {@link ImmutableMap} for empty and singleton inputs, as those are more memory-efficient. This
+     * method also recognizes {@link ImmutableOffsetMap} and {@link SharedSingletonMap} on input, and returns it back
+     * without doing anything else. It also recognizes {@link MutableOffsetMap} (as returned by
+     * {@link #toModifiableMap()}) and makes an efficient copy of its contents. All other maps are converted to an
+     * {@link ImmutableOffsetMap}. Iterator order is not guaranteed to be retained.
+     *
+     * @param map Input map, may not be null.
+     * @return An isolated, immutable copy of the input map
+     * @throws NullPointerException if {@code map} or any of its elements is null.
+     */
+    public static <K, V> @NonNull Map<K, V> unorderedCopyOf(final @NonNull Map<K, V> map) {
+        final Map<K, V> common = commonCopy(map);
+        if (common != null) {
+            return common;
+        }
+
+        if (map.size() == 1) {
+            // Efficient single-entry implementation
+            final Entry<K, V> e = map.entrySet().iterator().next();
+            return SharedSingletonMap.unorderedOf(e.getKey(), e.getValue());
+        }
+
+        final ImmutableMap<K, Integer> offsets = OffsetMapCache.unorderedOffsets(map.keySet());
+        @SuppressWarnings("unchecked")
+        final V[] array = (V[]) new Object[offsets.size()];
+        for (Entry<K, V> e : map.entrySet()) {
+            array[offsets.get(e.getKey())] = e.getValue();
+        }
+
+        return new Unordered<>(offsets, array);
+    }
+
+    private static <K, V> @Nullable Map<K, V> commonCopy(final @NonNull Map<K, V> map) {
+        // Prevent a copy. Note that ImmutableMap is not listed here because of its potentially larger keySet overhead.
+        if (map instanceof ImmutableOffsetMap || map instanceof SharedSingletonMap) {
+            return map;
+        }
+
+        // Familiar and efficient to copy
+        if (map instanceof MutableOffsetMap) {
+            return ((MutableOffsetMap<K, V>) map).toUnmodifiableMap();
+        }
+
+        if (map.isEmpty()) {
+            // Shares a single object
+            return ImmutableMap.of();
+        }
+
+        return null;
+    }
+
+    @Override
+    public final int size() {
+        return offsets.size();
+    }
+
+    @Override
+    public final boolean isEmpty() {
+        return offsets.isEmpty();
+    }
+
+    @Override
+    public final int hashCode() {
+        if (hashCode != 0) {
+            return hashCode;
+        }
+
+        int result = 0;
+        for (Entry<K, Integer> e : offsets.entrySet()) {
+            result += e.getKey().hashCode() ^ objects[e.getValue()].hashCode();
+        }
+
+        hashCode = result;
+        return result;
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof Map)) {
+            return false;
+        }
+
+        if (obj instanceof ImmutableOffsetMap) {
+            final ImmutableOffsetMap<?, ?> om = (ImmutableOffsetMap<?, ?>) obj;
+
+            // If the offset match, the arrays have to match, too
+            if (offsets.equals(om.offsets)) {
+                return Arrays.deepEquals(objects, om.objects);
+            }
+        } else if (obj instanceof MutableOffsetMap) {
+            // Let MutableOffsetMap do the actual work.
+            return obj.equals(this);
+        }
+
+        final Map<?, ?> other = (Map<?, ?>)obj;
+
+        // Size and key sets have to match
+        if (size() != other.size() || !keySet().equals(other.keySet())) {
+            return false;
+        }
+
+        try {
+            // Ensure all objects are present
+            for (Entry<K, Integer> e : offsets.entrySet()) {
+                if (!objects[e.getValue()].equals(other.get(e.getKey()))) {
+                    return false;
+                }
+            }
+        } catch (ClassCastException e) {
+            // Can be thrown by other.get() indicating we have incompatible key types
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public final boolean containsKey(final Object key) {
+        return offsets.containsKey(key);
+    }
+
+    @Override
+    public final boolean containsValue(final Object value) {
+        for (Object o : objects) {
+            if (value.equals(o)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public final V get(final Object key) {
+        Integer offset;
+        return (offset = offsets.get(key)) == null ? null : objects[offset];
+    }
+
+    @Override
+    public final V remove(final Object key) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final V put(final K key, final V value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final void putAll(final Map<? extends K, ? extends V> m) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final Set<K> keySet() {
+        return offsets.keySet();
+    }
+
+    @Override
+    public final @NonNull Collection<V> values() {
+        return new ConstantArrayCollection<>(objects);
+    }
+
+    @Override
+    public final @NonNull Set<Entry<K, V>> entrySet() {
+        return new EntrySet();
+    }
+
+    @Override
+    public final String toString() {
+        final StringBuilder sb = new StringBuilder("{");
+        final Iterator<K> it = offsets.keySet().iterator();
+        int offset = 0;
+        while (it.hasNext()) {
+            sb.append(it.next()).append('=').append(objects[offset++]);
+
+            if (it.hasNext()) {
+                sb.append(", ");
+            }
+        }
+
+        return sb.append('}').toString();
+    }
+
+    final @NonNull ImmutableMap<K, Integer> offsets() {
+        return offsets;
+    }
+
+    final @NonNull V[] objects() {
+        return objects;
+    }
+
+    private final class EntrySet extends AbstractSet<Entry<K, V>> {
+        @Override
+        public @NonNull Iterator<Entry<K, V>> iterator() {
+            final Iterator<Entry<K, Integer>> it = offsets.entrySet().iterator();
+            return new UnmodifiableIterator<Entry<K, V>>() {
+                @Override
+                public boolean hasNext() {
+                    return it.hasNext();
+                }
+
+                @Override
+                public Entry<K, V> next() {
+                    final Entry<K, Integer> e = it.next();
+                    return new SimpleImmutableEntry<>(e.getKey(), objects[e.getValue()]);
+                }
+            };
+        }
+
+        @Override
+        public int size() {
+            return offsets.size();
+        }
+    }
+
+    private void writeObject(final ObjectOutputStream out) throws IOException {
+        out.writeInt(offsets.size());
+        for (Entry<K, V> e : entrySet()) {
+            out.writeObject(e.getKey());
+            out.writeObject(e.getValue());
+        }
+    }
+
+    private static final Field OFFSETS_FIELD = fieldFor("offsets");
+    private static final Field ARRAY_FIELD = fieldFor("objects");
+
+    private static @NonNull Field fieldFor(final @NonNull String name) {
+        final Field f;
+        try {
+            f = ImmutableOffsetMap.class.getDeclaredField(name);
+        } catch (NoSuchFieldException | SecurityException e) {
+            throw new IllegalStateException("Failed to lookup field " + name, e);
+        }
+
+        f.setAccessible(true);
+        return f;
+    }
+
+    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+            justification = "https://github.com/spotbugs/spotbugs/issues/811")
+    private static void setField(final @NonNull ImmutableOffsetMap<?, ?> map, final @NonNull Field field,
+            final Object value) throws IOException {
+        try {
+            field.set(map, value);
+        } catch (IllegalArgumentException | IllegalAccessException e) {
+            throw new IOException("Failed to set field " + field, e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void readObject(final @NonNull ObjectInputStream in) throws IOException, ClassNotFoundException {
+        final int s = in.readInt();
+
+        final List<K> keys = new ArrayList<>(s);
+        final V[] values = (V[]) new Object[s];
+
+        for (int i = 0; i < s; ++i) {
+            keys.add((K)in.readObject());
+            values[i] = (V)in.readObject();
+        }
+
+        setFields(keys, values);
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableOffsetMapTemplate.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ImmutableOffsetMapTemplate.java
new file mode 100644 (file)
index 0000000..091b40a
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.BiFunction;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Template for instantiating {@link ImmutableOffsetMap} instances with a fixed set of keys. The template can then be
+ * used as a factory for instances via using {@link #instantiateTransformed(Map, BiFunction)} or, more efficiently,
+ * using {@link #instantiateWithValues(Object[])} where the argument array has values ordered corresponding to the key
+ * order defined by {@link #keySet()}.
+ *
+ * @param <K> the type of keys maintained by this template
+ */
+public abstract class ImmutableOffsetMapTemplate<K> extends ImmutableMapTemplate<K> {
+    private static final class Ordered<K> extends ImmutableOffsetMapTemplate<K> {
+        Ordered(final Collection<K> keys) {
+            super(OffsetMapCache.orderedOffsets(keys));
+        }
+
+        @Override
+        <V> @NonNull ImmutableOffsetMap<K, V> createMap(final ImmutableMap<K, Integer> offsets, final V[] objects) {
+            return new ImmutableOffsetMap.Ordered<>(offsets, objects);
+        }
+    }
+
+    private static final class Unordered<K> extends ImmutableOffsetMapTemplate<K> {
+        Unordered(final Collection<K> keys) {
+            super(OffsetMapCache.unorderedOffsets(keys));
+        }
+
+        @Override
+        <V> @NonNull ImmutableOffsetMap<K, V> createMap(final ImmutableMap<K, Integer> offsets, final V[] objects) {
+            return new ImmutableOffsetMap.Unordered<>(offsets, objects);
+        }
+    }
+
+    private final @NonNull ImmutableMap<K, Integer> offsets;
+
+    ImmutableOffsetMapTemplate(final ImmutableMap<K, Integer> offsets) {
+        this.offsets = requireNonNull(offsets);
+    }
+
+    /**
+     * Create a template which produces Maps with specified keys, with iteration order matching the iteration order
+     * of {@code keys}. {@link #keySet()} will return these keys in exactly the same order. The resulting map will
+     * retain insertion order through {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
+     *
+     * @param keys Keys in requested iteration order.
+     * @param <K> the type of keys maintained by resulting template
+     * @return A template object.
+     * @throws NullPointerException if {@code keys} or any of its elements is null
+     * @throws IllegalArgumentException if {@code keys} is does not have at least two keys
+     */
+    public static <K> @NonNull ImmutableOffsetMapTemplate<K> ordered(final Collection<K> keys) {
+        checkArgument(keys.size() > 1);
+        return new Ordered<>(keys);
+    }
+
+    /**
+     * Create a template which produces Maps with specified keys, with unconstrained iteration order. Produced maps
+     * will have the iteration order matching the order returned by {@link #keySet()}.  The resulting map will
+     * NOT retain ordering through {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
+     *
+     * @param keys Keys in any iteration order.
+     * @param <K> the type of keys maintained by resulting template
+     * @return A template object.
+     * @throws NullPointerException if {@code keys} or any of its elements is null
+     * @throws IllegalArgumentException if {@code keys} is does not have at least two keys
+     */
+    public static <K> @NonNull ImmutableOffsetMapTemplate<K> unordered(final Collection<K> keys) {
+        checkArgument(keys.size() > 1);
+        return new Unordered<>(keys);
+    }
+
+    @Override
+    public final Set<K> keySet() {
+        return offsets.keySet();
+    }
+
+    @Override
+    public final <T, V> @NonNull ImmutableOffsetMap<K, V> instantiateTransformed(final Map<K, T> fromMap,
+            final BiFunction<K, T, V> valueTransformer) {
+        final int size = offsets.size();
+        checkArgument(fromMap.size() == size);
+
+        @SuppressWarnings("unchecked")
+        final V[] objects = (V[]) new Object[size];
+        for (Entry<K, T> entry : fromMap.entrySet()) {
+            final K key = requireNonNull(entry.getKey());
+            final Integer offset = offsets.get(key);
+            checkArgument(offset != null, "Key %s present in input, but not in offsets %s", key, offsets);
+
+            objects[offset.intValue()] = transformValue(key, entry.getValue(), valueTransformer);
+        }
+
+        return createMap(offsets, objects);
+    }
+
+    @Override
+    @SafeVarargs
+    public final <V> @NonNull ImmutableOffsetMap<K, V> instantiateWithValues(final V... values) {
+        checkArgument(values.length == offsets.size());
+        final V[] copy = values.clone();
+        Arrays.stream(copy).forEach(Objects::requireNonNull);
+        return createMap(offsets, values);
+    }
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(this).add("offsets", offsets).toString();
+    }
+
+    abstract <V> @NonNull ImmutableOffsetMap<K, V> createMap(ImmutableMap<K, Integer> offsets, V[] objects);
+}
\ No newline at end of file
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/Immutables.java b/common/util/src/main/java/org/opendaylight/yangtools/util/Immutables.java
new file mode 100644 (file)
index 0000000..47bf5ab
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.checkArgument;
+
+import com.google.common.collect.ImmutableSet;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Mutable;
+
+public final class Immutables {
+
+    private Immutables() {
+        throw new UnsupportedOperationException("Helper class");
+    }
+
+    private static final ImmutableSet<Class<?>> KNOWN_IMMUTABLES = ImmutableSet.of(
+            Integer.class, Short.class, BigDecimal.class, BigInteger.class, Byte.class, Character.class, Double.class,
+            Float.class, String.class, Boolean.class, Void.class);
+
+    /**
+     * Determines if object is known to be immutable
+     *
+     * <p>Note: This method may return false to immutable objects which
+     * immutability is not known, was defined not using concepts term.
+     *
+     * @param obj
+     *            Reference to check
+     * @return true if object is known to be immutable false otherwise.
+     */
+    public static boolean isImmutable(final Object obj) {
+        checkArgument(obj != null,"Object should not be null");
+        if (obj instanceof Mutable) {
+            return false;
+        } else if (obj instanceof Immutable) {
+            return true;
+        } else if (obj instanceof String) {
+            return true;
+        } else if (KNOWN_IMMUTABLES.contains(obj.getClass())) {
+            return true;
+        }
+        return false;
+    }
+}
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..1aa2c24
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 {
+
+    private LazyCollections() {
+        throw new UnsupportedOperationException("Utility class should not be instantiated");
+    }
+
+    /**
+     * 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<>(2);
+                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..598b96d
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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 static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import java.util.Collections;
+import java.util.EventListener;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Mutable;
+
+/**
+ * A registry of EventListeners, maintaining a set of registrations. This class is thread-safe.
+ *
+ * @param <T> Type of listeners this registry handles
+ */
+public final class ListenerRegistry<T extends EventListener> implements Mutable {
+
+    private final Set<ListenerRegistration<? extends T>> listeners = ConcurrentHashMap.newKeySet();
+    // This conversion is known to be safe.
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private final Set<ListenerRegistration<T>> unmodifiableView = (Set) Collections.unmodifiableSet(listeners);
+
+    private final String name;
+
+    private ListenerRegistry(final String name) {
+        this.name = name;
+    }
+
+    public static <T extends EventListener> @NonNull ListenerRegistry<T> create() {
+        return new ListenerRegistry<>(null);
+    }
+
+    public static <T extends EventListener> @NonNull ListenerRegistry<T> create(final @NonNull String name) {
+        return new ListenerRegistry<>(requireNonNull(name));
+    }
+
+    public @NonNull Set<? extends ListenerRegistration<? extends T>> getRegistrations() {
+        return unmodifiableView;
+    }
+
+    public boolean isEmpty() {
+        return listeners.isEmpty();
+    }
+
+    public Stream<? extends T> streamListeners() {
+        return listeners.stream().map(ListenerRegistration::getInstance);
+    }
+
+    public <L extends T> @NonNull  ListenerRegistration<L> register(final L listener) {
+        final ListenerRegistration<L> ret = new ListenerRegistrationImpl<>(listener, listeners::remove);
+        listeners.add(ret);
+        return ret;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).omitNullValues()
+                .add("name", name)
+                .add("size", listeners.size())
+                .toString();
+    }
+
+    private static final class ListenerRegistrationImpl<T extends EventListener>
+            extends AbstractListenerRegistration<T> {
+        private Consumer<ListenerRegistration<? super T>> removeCall;
+
+        ListenerRegistrationImpl(final T instance, final Consumer<ListenerRegistration<? super T>> removeCall) {
+            super(instance);
+            this.removeCall = requireNonNull(removeCall);
+        }
+
+        @Override
+        protected void removeRegistration() {
+            removeCall.accept(this);
+            // Do not retain reference to that state
+            removeCall = null;
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/MapAdaptor.java b/common/util/src/main/java/org/opendaylight/yangtools/util/MapAdaptor.java
new file mode 100644 (file)
index 0000000..3aacb07
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * 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.checkArgument;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import tech.pantheon.triemap.MutableTrieMap;
+import tech.pantheon.triemap.TrieMap;
+
+/**
+ * A simple layer on top of maps, which performs snapshot mediation and optimization of
+ * what the underlying implementation is.
+ */
+public final class MapAdaptor {
+    public static final int DEFAULT_COPY_MAX_ITEMS = 100;
+    public static final String COPY_MAX_ITEMS_MAX_PROP = "org.opendaylight.yangtools.util.mapadaptor.maxcopy";
+
+    public static final int DEFAULT_PERSIST_MIN_ITEMS = 50;
+    public static final String PERSIST_MIN_ITEMS_PROP = "org.opendaylight.yangtools.util.mapadaptor.minpersist";
+
+    private static final Logger LOG = LoggerFactory.getLogger(MapAdaptor.class);
+    private static final MapAdaptor DEFAULT_INSTANCE;
+
+    private final boolean useSingleton;
+    private final int persistMinItems;
+    private final int copyMaxItems;
+
+    static {
+        DEFAULT_INSTANCE = new MapAdaptor(true,
+                getProperty(COPY_MAX_ITEMS_MAX_PROP, DEFAULT_COPY_MAX_ITEMS),
+                getProperty(PERSIST_MIN_ITEMS_PROP, DEFAULT_PERSIST_MIN_ITEMS));
+        LOG.debug("Configured HashMap/TrieMap cutoff at {}/{} entries",
+                DEFAULT_INSTANCE.persistMinItems, DEFAULT_INSTANCE.copyMaxItems);
+    }
+
+    private static int getProperty(final String name, final int defaultValue) {
+        final int val = Integer.getInteger(name, defaultValue).intValue();
+        if (val > 0) {
+            return val;
+        }
+
+        LOG.warn("Ignoring illegal value of {}: has to be a positive number", name);
+        return defaultValue;
+    }
+
+    private MapAdaptor(final boolean useSingleton, final int copyMaxItems, final int persistMinItems) {
+        this.useSingleton = useSingleton;
+        this.copyMaxItems = copyMaxItems;
+        this.persistMinItems = persistMinItems;
+    }
+
+    /**
+     * Return the default-configured instance.
+     *
+     * @return the singleton global instance
+     */
+    public static MapAdaptor getDefaultInstance() {
+        return DEFAULT_INSTANCE;
+    }
+
+    public static MapAdaptor getInstance(final boolean useSingleton, final int copyMaxItems,
+            final int persistMinItems) {
+        checkArgument(copyMaxItems >= 0, "copyMaxItems has to be a non-negative integer");
+        checkArgument(persistMinItems >= 0, "persistMinItems has to be a positive integer");
+        checkArgument(persistMinItems <= copyMaxItems, "persistMinItems must be less than or equal to copyMaxItems");
+        return new MapAdaptor(useSingleton, copyMaxItems, persistMinItems);
+    }
+
+    /**
+     * Creates an initial snapshot. The backing map is selected according to the expected size.
+     *
+     * @param expectedSize Expected map size
+     * @return An empty mutable map.
+     */
+    public <K, V> Map<K, V> initialSnapshot(final int expectedSize) {
+        checkArgument(expectedSize >= 0);
+        if (expectedSize > persistMinItems) {
+            return new ReadWriteTrieMap<>();
+        }
+
+        if (expectedSize < 2) {
+            return new HashMap<>(1);
+        }
+        if (expectedSize == 2) {
+            return new HashMap<>(2);
+        }
+        return Maps.newHashMapWithExpectedSize(expectedSize);
+    }
+
+    /**
+     * Input is treated is supposed to be left unmodified, result must be mutable.
+     *
+     * @param input input map
+     * @return An isolated, read-write snapshot of input map
+     * @throws NullPointerException if input is null
+     */
+    @SuppressWarnings("static-method")
+    public <K, V> Map<K, V> takeSnapshot(final Map<K, V> input) {
+        if (input instanceof ReadOnlyTrieMap) {
+            return ((ReadOnlyTrieMap<K, V>)input).toReadWrite();
+        }
+
+        LOG.trace("Converting input {} to a HashMap", input);
+
+        /*
+         * The default HashMap copy constructor performs a bad thing for small maps, using the default capacity of 16
+         * as the minimum sizing hint, which can lead to wasted memory. Since the HashMap grows in powers-of-two, we
+         * only kick this in if we are storing 6 entries or less, as that results in 8-entry map -- the next power is
+         * 16, which is the default.
+         */
+        final Map<K, V> ret;
+        final int size = input.size();
+        if (size <= 6) {
+            final int target;
+            switch (size) {
+                case 0:
+                case 1:
+                    target = 1;
+                    break;
+                case 2:
+                    target = 2;
+                    break;
+                case 3:
+                    target = 4;
+                    break;
+                default:
+                    target = 8;
+            }
+
+            ret = new HashMap<>(target);
+            ret.putAll(input);
+        } else if (input instanceof HashMap) {
+            // HashMap supports cloning, but we want to make sure we trim it down if entries were removed, so we do
+            // this only after having checked for small sizes.
+            @SuppressWarnings("unchecked")
+            final Map<K, V> tmp = (Map<K, V>) ((HashMap<K, V>) input).clone();
+            ret = tmp;
+        } else {
+            ret = new HashMap<>(input);
+        }
+
+        LOG.trace("Read-write HashMap is {}", ret);
+        return ret;
+    }
+
+    /**
+     * Input will be thrown away, result will be retained for read-only access or
+     * {@link #takeSnapshot(Map)} purposes.
+     *
+     * @param input non-optimized (read-write) map
+     * @return optimized read-only map
+     * @throws NullPointerException if input is null
+     */
+    public <K, V> Map<K, V> optimize(final Map<K, V> input) {
+        if (input instanceof ReadOnlyTrieMap) {
+            LOG.warn("Optimizing read-only map {}", input);
+        }
+
+        final int size = input.size();
+
+        /*
+         * No-brainer :)
+         */
+        if (size == 0) {
+            LOG.trace("Reducing input {} to an empty map", input);
+            return ImmutableMap.of();
+        }
+
+        /*
+         * We retain the persistent map as long as it holds at least
+         * persistMinItems
+         */
+        if (input instanceof ReadWriteTrieMap && size >= persistMinItems) {
+            return ((ReadWriteTrieMap<K, V>)input).toReadOnly();
+        }
+
+        /*
+         * If the user opted to use singleton maps, use them. Except for the case
+         * when persistMinItems dictates we should not move off of the persistent
+         * map.
+         */
+        if (useSingleton && size == 1) {
+            final Entry<K, V> e = input.entrySet().iterator().next();
+            final Map<K, V> ret = Collections.singletonMap(e.getKey(), e.getValue());
+            LOG.trace("Reducing input {} to singleton map {}", input, ret);
+            return ret;
+        }
+
+        if (size <= copyMaxItems) {
+            /*
+             * Favor access speed: use a HashMap and copy it on modification.
+             */
+            if (input instanceof HashMap) {
+                return input;
+            }
+
+            LOG.trace("Copying input {} to a HashMap ({} entries)", input, size);
+            final Map<K, V> ret = new HashMap<>(input);
+            LOG.trace("Read-only HashMap is {}", ret);
+            return ret;
+        }
+
+        /*
+         * Favor isolation speed: use a TrieMap and perform snapshots
+         *
+         * This one is a bit tricky, as the TrieMap is concurrent and does not
+         * keep an uptodate size. Updating it requires a full walk -- which is
+         * O(N) and we want to avoid that. So we wrap it in an interceptor,
+         * which will maintain the size for us.
+         */
+        LOG.trace("Copying input {} to a TrieMap ({} entries)", input, size);
+        final MutableTrieMap<K, V> map = TrieMap.create();
+        map.putAll(input);
+        final Map<K, V> ret = new ReadOnlyTrieMap<>(map, size);
+        LOG.trace("Read-only TrieMap is {}", ret);
+        return ret;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ModifiableMapPhase.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ModifiableMapPhase.java
new file mode 100644 (file)
index 0000000..cd4ba69
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Mutable;
+
+/**
+ * A {@link Map} which can be modified and supports efficient conversion to an
+ * unmodifiable map. This interface is the logical counterpart to
+ * {@link UnmodifiableMapPhase}, but it does not require implementations of
+ * {@link #toUnmodifiableMap()} to return an implementation of that interface.
+ * The reason for that empty and singleton mappings are efficiently represented
+ * as {@link ImmutableMap}, which does not implement
+ * {@link UnmodifiableMapPhase}.
+ *
+ * @param <K>
+ *            the type of keys maintained by this map
+ * @param <V>
+ *            the type of mapped values
+ */
+@Beta
+public interface ModifiableMapPhase<K, V> extends Map<K, V>, Mutable {
+    /**
+     * Return an isolated unmodifiable version of this map. Returned object must not allow removal, addition or changing
+     * of mappings. Its mappings must match the mappings currently present in this map, but must not be affected by any
+     * subsequent changes to this map.
+     *
+     * @return An unmodifiable version of this map.
+     */
+    @NonNull Map<K, V> toUnmodifiableMap();
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/MutableOffsetMap.java b/common/util/src/main/java/org/opendaylight/yangtools/util/MutableOffsetMap.java
new file mode 100644 (file)
index 0000000..d71b3aa
--- /dev/null
@@ -0,0 +1,706 @@
+/*
+ * Copyright (c) 2015 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.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * A mutable version of {@link ImmutableOffsetMap}. It inherits the set of mappings from the immutable version and
+ * allows updating/removing existing mappings. New mappings are stored in a dedicated {@link LinkedHashMap} to preserve
+ * insertion order. It also tracks the need to duplicate the backing array, so the sequence of
+ * <code>
+ * ImmutableOffsetMap&lt;K, V&gt; source;
+ * ImmutableOffsetMap&lt;K, V&gt; result = source.createMutableClone().immutableCopy();
+ * </code>
+ * results in source and result sharing the backing objects.
+ *
+ * <p>This map does not support null keys nor values.
+ *
+ * @param <K> the type of keys maintained by this map
+ * @param <V> the type of mapped values
+ */
+@Beta
+public abstract class MutableOffsetMap<K, V> extends AbstractMap<K, V> implements Cloneable, ModifiableMapPhase<K, V> {
+    static final class Ordered<K, V> extends MutableOffsetMap<K, V> {
+        Ordered() {
+        }
+
+        Ordered(final Map<K, V> source) {
+            super(OffsetMapCache.orderedOffsets(source.keySet()), source);
+        }
+
+        Ordered(final ImmutableMap<K, Integer> offsets, final V[] objects) {
+            super(offsets, objects);
+        }
+
+        @Override
+        Object removedObject() {
+            return REMOVED;
+        }
+
+        @Override
+        UnmodifiableMapPhase<K, V> modifiedMap(final List<K> keys, final V[] values) {
+            return new ImmutableOffsetMap.Ordered<>(OffsetMapCache.orderedOffsets(keys), values);
+        }
+
+        @Override
+        UnmodifiableMapPhase<K, V> unmodifiedMap(final ImmutableMap<K, Integer> offsetMap, final V[] values) {
+            return new ImmutableOffsetMap.Ordered<>(offsetMap, values);
+        }
+
+        @Override
+        SharedSingletonMap<K, V> singletonMap() {
+            return SharedSingletonMap.orderedCopyOf(this);
+        }
+
+        @Override
+        HashMap<K, V> createNewKeys() {
+            return new LinkedHashMap<>();
+        }
+    }
+
+    static final class Unordered<K, V> extends MutableOffsetMap<K, V> {
+        Unordered() {
+        }
+
+        Unordered(final Map<K, V> source) {
+            super(OffsetMapCache.unorderedOffsets(source.keySet()), source);
+        }
+
+        Unordered(final ImmutableMap<K, Integer> offsets, final V[] objects) {
+            super(offsets, objects);
+        }
+
+        @Override
+        Object removedObject() {
+            return null;
+        }
+
+        @Override
+        UnmodifiableMapPhase<K, V> modifiedMap(final List<K> keys, final V[] values) {
+            final ImmutableMap<K, Integer> offsets = OffsetMapCache.unorderedOffsets(keys);
+            return new ImmutableOffsetMap.Unordered<>(offsets, OffsetMapCache.adjustedArray(offsets, keys, values));
+        }
+
+        @Override
+        UnmodifiableMapPhase<K, V> unmodifiedMap(final ImmutableMap<K, Integer> offsetMap, final V[] values) {
+            return new ImmutableOffsetMap.Unordered<>(offsetMap, values);
+        }
+
+        @Override
+        SharedSingletonMap<K, V> singletonMap() {
+            return SharedSingletonMap.unorderedCopyOf(this);
+        }
+
+        @Override
+        HashMap<K, V> createNewKeys() {
+            return new HashMap<>();
+        }
+    }
+
+    private static final Object[] EMPTY_ARRAY = new Object[0];
+    private static final Object REMOVED = new Object();
+
+    private final ImmutableMap<K, Integer> offsets;
+    private HashMap<K, V> newKeys;
+    private Object[] objects;
+    private int removed = 0;
+
+    // Fail-fast iterator guard, see java.util.ArrayList for reference.
+    @SuppressFBWarnings("VO_VOLATILE_INCREMENT")
+    private transient volatile int modCount;
+    private boolean needClone = true;
+
+    MutableOffsetMap(final ImmutableMap<K, Integer> offsets, final Object[] objects) {
+        this.offsets = requireNonNull(offsets);
+        this.objects = requireNonNull(objects);
+    }
+
+    MutableOffsetMap() {
+        this(ImmutableMap.of(), EMPTY_ARRAY);
+    }
+
+    MutableOffsetMap(final ImmutableMap<K, Integer> offsets, final Map<K, V> source) {
+        this(offsets, new Object[offsets.size()]);
+
+        for (Entry<K, V> e : source.entrySet()) {
+            objects[offsets.get(e.getKey())] = requireNonNull(e.getValue());
+        }
+
+        this.needClone = false;
+    }
+
+    /**
+     * Create a {@link MutableOffsetMap} of the specified map, retaining its iteration order.
+     *
+     * @param map input map
+     * @return MutableOffsetMap with the same iteration order
+     * @throws NullPointerException if {@code map} is null
+     */
+    public static <K, V> @NonNull MutableOffsetMap<K, V> orderedCopyOf(final Map<K, V> map) {
+        if (map instanceof Ordered) {
+            return ((Ordered<K, V>) map).clone();
+        }
+        if (map instanceof ImmutableOffsetMap) {
+            final ImmutableOffsetMap<K, V> om = (ImmutableOffsetMap<K, V>) map;
+            return new Ordered<>(om.offsets(), om.objects());
+        }
+
+        return new Ordered<>(map);
+    }
+
+    /**
+     * Create a {@link MutableOffsetMap} of the specified map, potentially with a different iteration order.
+     *
+     * @param map input map
+     * @return MutableOffsetMap with undefined iteration order
+     * @throws NullPointerException if {@code map} is null
+     */
+    public static <K, V> @NonNull MutableOffsetMap<K, V> unorderedCopyOf(final Map<K, V> map) {
+        if (map instanceof Unordered) {
+            return ((Unordered<K, V>) map).clone();
+        }
+        if (map instanceof ImmutableOffsetMap) {
+            final ImmutableOffsetMap<K, V> om = (ImmutableOffsetMap<K, V>) map;
+            return new Unordered<>(om.offsets(), om.objects());
+        }
+
+        return new Unordered<>(map);
+    }
+
+    /**
+     * Create an empty {@link MutableOffsetMap} which has an iteration order matching the insertion order.
+     *
+     * @return MutableOffsetMap which preserves insertion order
+     */
+    public static <K, V> @NonNull MutableOffsetMap<K, V> ordered() {
+        return new MutableOffsetMap.Ordered<>();
+    }
+
+    /**
+     * Create an empty {@link MutableOffsetMap} which has unspecified iteration order.
+     *
+     * @return An MutableOffsetMap
+     */
+    public static <K, V> @NonNull MutableOffsetMap<K, V> unordered() {
+        return new MutableOffsetMap.Unordered<>();
+    }
+
+    abstract Object removedObject();
+
+    abstract UnmodifiableMapPhase<K, V> modifiedMap(List<K> keys, V[] values);
+
+    abstract UnmodifiableMapPhase<K, V> unmodifiedMap(ImmutableMap<K, Integer> offsetMap, V[] values);
+
+    abstract SharedSingletonMap<K, V> singletonMap();
+
+    @Override
+    public final int size() {
+        return offsets.size() - removed + (newKeys == null ? 0 : newKeys.size());
+    }
+
+    @Override
+    public final boolean isEmpty() {
+        return size() == 0;
+    }
+
+    @Override
+    public final boolean containsKey(final Object key) {
+        final Integer offset = offsets.get(key);
+        if (offset != null) {
+            final Object obj = objects[offset];
+            if (!REMOVED.equals(obj)) {
+                return obj != null;
+            }
+        }
+
+        return newKeys != null && newKeys.containsKey(key);
+    }
+
+    @Override
+    public final V get(final Object key) {
+        final Integer offset = offsets.get(key);
+        if (offset != null) {
+            final Object obj = objects[offset];
+
+            /*
+             * This is a bit tricky:  Ordered will put REMOVED to removed objects to retain strict insertion order.
+             * Unordered will add null, indicating that the slot may be reused in future. Hence if we see a REMOVED
+             * marker, we need to fall back to checking with new keys.
+             */
+            if (!REMOVED.equals(obj)) {
+                @SuppressWarnings("unchecked")
+                final V ret = (V)obj;
+                return ret;
+            }
+        }
+
+        return newKeys == null ? null : newKeys.get(key);
+    }
+
+    private void cloneArray() {
+        if (needClone) {
+            needClone = false;
+            if (objects.length != 0) {
+                objects = objects.clone();
+            }
+        }
+    }
+
+    @Override
+    public final V put(final K key, final V value) {
+        requireNonNull(value);
+        final Integer offset = offsets.get(requireNonNull(key));
+        if (offset != null) {
+            final Object obj = objects[offset];
+
+            /*
+             * Put which can potentially replace something in objects. Replacing an object does not cause iterators
+             * to be invalidated and does follow insertion order (since it is not a fresh insert). If the object has
+             * been removed, we fall back to newKeys.
+             */
+            if (!REMOVED.equals(obj)) {
+                @SuppressWarnings("unchecked")
+                final V ret = (V)obj;
+
+                cloneArray();
+                objects[offset] = value;
+                if (ret == null) {
+                    modCount++;
+                    removed--;
+                }
+
+                return ret;
+            }
+        }
+
+        if (newKeys == null) {
+            newKeys = createNewKeys();
+        }
+        final V ret = newKeys.put(key, value);
+        if (ret == null) {
+            modCount++;
+        }
+        return ret;
+    }
+
+    @Override
+    public final V remove(final Object key) {
+        final Integer offset = offsets.get(key);
+        if (offset != null) {
+            final Object obj = objects[offset];
+
+            /*
+             * A previous remove() may have indicated that the objects slot cannot be reused. In that case we need
+             * to fall back to checking with newKeys.
+             */
+            if (!REMOVED.equals(obj)) {
+                cloneArray();
+
+                @SuppressWarnings("unchecked")
+                final V ret = (V)obj;
+                objects[offset] = removedObject();
+                if (ret != null) {
+                    modCount++;
+                    removed++;
+                }
+                return ret;
+            }
+        }
+
+        if (newKeys == null) {
+            return null;
+        }
+        final V ret = newKeys.remove(key);
+        if (ret != null) {
+            modCount++;
+        }
+        return ret;
+    }
+
+    @Override
+    public final void clear() {
+        if (size() != 0) {
+            if (newKeys != null) {
+                newKeys.clear();
+            }
+            cloneArray();
+            Arrays.fill(objects, removedObject());
+            removed = objects.length;
+            modCount++;
+        }
+    }
+
+    @Override
+    public final @NonNull Set<Entry<K, V>> entrySet() {
+        return new EntrySet();
+    }
+
+    @Override
+    public @NonNull Map<K, V> toUnmodifiableMap() {
+        if (removed == 0 && noNewKeys()) {
+            // Make sure next modification clones the array, as we leak it to the map we return.
+            needClone = true;
+
+            // We have ended up with no removed objects, hence this cast is safe
+            @SuppressWarnings("unchecked")
+            final V[] values = (V[])objects;
+
+            /*
+             * TODO: we could track the ImmutableOffsetMap from which this one was instantiated and if we do not
+             *       perform any modifications, just return the original instance. The trade-off is increased complexity
+             *       and an additional field in this class.
+             */
+            return unmodifiedMap(offsets, values);
+        }
+
+        final int s = size();
+        if (s == 0) {
+            return ImmutableMap.of();
+        }
+        if (s == 1) {
+            return singletonMap();
+        }
+
+        // Construct the set of keys
+        final List<K> keyset = new ArrayList<>(s);
+        if (removed != 0) {
+            if (removed != offsets.size()) {
+                for (Entry<K, Integer> e : offsets.entrySet()) {
+                    final Object o = objects[e.getValue()];
+                    if (o != null && !REMOVED.equals(o)) {
+                        keyset.add(e.getKey());
+                    }
+                }
+            }
+        } else {
+            keyset.addAll(offsets.keySet());
+        }
+        if (newKeys != null) {
+            keyset.addAll(newKeys.keySet());
+        }
+
+        // Construct the values
+        @SuppressWarnings("unchecked")
+        final V[] values = (V[])new Object[keyset.size()];
+        int offset = 0;
+        if (removed != 0) {
+            if (removed != offsets.size()) {
+                for (Entry<K, Integer> e : offsets.entrySet()) {
+                    final Object o = objects[e.getValue()];
+                    if (o != null && !REMOVED.equals(o)) {
+                        @SuppressWarnings("unchecked")
+                        final V v = (V) o;
+                        values[offset++] = v;
+                    }
+                }
+            }
+        } else {
+            System.arraycopy(objects, 0, values, 0, offsets.size());
+            offset = offsets.size();
+        }
+        if (newKeys != null) {
+            for (V v : newKeys.values()) {
+                values[offset++] = v;
+            }
+        }
+
+        return modifiedMap(keyset, values);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public MutableOffsetMap<K, V> clone() {
+        final MutableOffsetMap<K, V> ret;
+
+        try {
+            ret = (MutableOffsetMap<K, V>) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new IllegalStateException("Clone is expected to work", e);
+        }
+
+        ret.newKeys = newKeys == null ? null : (HashMap<K, V>) newKeys.clone();
+        ret.needClone = true;
+        return ret;
+    }
+
+    @Override
+    public final int hashCode() {
+        int result = 0;
+
+        for (Entry<K, Integer> e : offsets.entrySet()) {
+            final Object v = objects[e.getValue()];
+            if (v != null) {
+                result += e.getKey().hashCode() ^ v.hashCode();
+            }
+        }
+
+        return newKeys != null ? result + newKeys.hashCode() : result;
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof Map)) {
+            return false;
+        }
+
+        if (obj instanceof ImmutableOffsetMap) {
+            final ImmutableOffsetMap<?, ?> om = (ImmutableOffsetMap<?, ?>) obj;
+
+            if (noNewKeys() && offsets.equals(om.offsets())) {
+                return Arrays.deepEquals(objects, om.objects());
+            }
+        } else if (obj instanceof MutableOffsetMap) {
+            final MutableOffsetMap<?, ?> om = (MutableOffsetMap<?, ?>) obj;
+
+            if (offsets.equals(om.offsets)) {
+                return Arrays.deepEquals(objects, om.objects) && equalNewKeys(om);
+            }
+        }
+
+        // Fall back to brute map compare
+        return mapEquals((Map<?, ?>)obj);
+    }
+
+    private boolean equalNewKeys(final MutableOffsetMap<?, ?> other) {
+        return noNewKeys() ? other.noNewKeys() : newKeys.equals(other.newKeys());
+    }
+
+    private boolean mapEquals(final Map<?, ?> other) {
+        // Size and key sets have to match
+        if (size() != other.size() || !keySet().equals(other.keySet())) {
+            return false;
+        }
+
+        try {
+            if (newKeys != null) {
+                // Ensure all newKeys are present. Note newKeys is guaranteed to not contain a null value.
+                for (Entry<K, V> e : newKeys.entrySet()) {
+                    if (!e.getValue().equals(other.get(e.getKey()))) {
+                        return false;
+                    }
+                }
+            }
+
+            // Ensure all objects are present
+            for (Entry<K, Integer> e : offsets.entrySet()) {
+                final Object val = objects[e.getValue()];
+                if (val != null && !REMOVED.equals(val) && !val.equals(other.get(e.getKey()))) {
+                    return false;
+                }
+            }
+        } catch (ClassCastException e) {
+            // Can be thrown by other.get() and indicate we have incompatible key types
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public final @NonNull Set<K> keySet() {
+        return new KeySet();
+    }
+
+    @VisibleForTesting
+    final boolean needClone() {
+        return needClone;
+    }
+
+    @VisibleForTesting
+    final Object array() {
+        return objects;
+    }
+
+    @VisibleForTesting
+    final Object newKeys() {
+        return newKeys != null ? newKeys : ImmutableMap.of();
+    }
+
+    abstract HashMap<K, V> createNewKeys();
+
+    private boolean noNewKeys() {
+        return newKeys == null || newKeys.isEmpty();
+    }
+
+    private final class EntrySet extends AbstractSet<Entry<K, V>> {
+        @Override
+        public @NonNull Iterator<Entry<K, V>> iterator() {
+            return new AbstractSetIterator<Entry<K, V>>() {
+                @Override
+                public Entry<K, V> next() {
+                    final K key = nextKey();
+                    return new SimpleEntry<>(key, get(key));
+                }
+            };
+        }
+
+        @Override
+        public int size() {
+            return MutableOffsetMap.this.size();
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:parameterName")
+        public boolean contains(final Object o) {
+            if (!(o instanceof Entry)) {
+                return false;
+            }
+
+            @SuppressWarnings("unchecked")
+            final Entry<K,V> e = (Entry<K,V>) o;
+            if (e.getValue() == null) {
+                return false;
+            }
+
+            return e.getValue().equals(MutableOffsetMap.this.get(e.getKey()));
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:parameterName")
+        public boolean add(final Entry<K, V> e) {
+            final V v = requireNonNull(e.getValue());
+            final V p = MutableOffsetMap.this.put(e.getKey(), v);
+            return !v.equals(p);
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:parameterName")
+        public boolean remove(final Object o) {
+            if (!(o instanceof Entry)) {
+                return false;
+            }
+
+            @SuppressWarnings("unchecked")
+            final Entry<K,V> e = (Entry<K,V>) o;
+            if (e.getValue() == null) {
+                return false;
+            }
+
+            final V v = MutableOffsetMap.this.get(e.getKey());
+            if (e.getValue().equals(v)) {
+                MutableOffsetMap.this.remove(e.getKey());
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public void clear() {
+            MutableOffsetMap.this.clear();
+        }
+    }
+
+    private final class KeySet extends AbstractSet<K> {
+        @Override
+        public @NonNull Iterator<K> iterator() {
+            return new AbstractSetIterator<K>() {
+                @Override
+                public K next() {
+                    return nextKey();
+                }
+            };
+        }
+
+        @Override
+        public int size() {
+            return MutableOffsetMap.this.size();
+        }
+    }
+
+    private abstract class AbstractSetIterator<E> implements Iterator<E> {
+        private final Iterator<Entry<K, Integer>> oldIterator = offsets.entrySet().iterator();
+        private final Iterator<K> newIterator = newKeys == null ? Collections.emptyIterator()
+                : newKeys.keySet().iterator();
+        private int expectedModCount = modCount;
+        private @Nullable K currentKey = null;
+        private @Nullable K nextKey;
+
+        AbstractSetIterator() {
+            updateNextKey();
+        }
+
+        private void updateNextKey() {
+            while (oldIterator.hasNext()) {
+                final Entry<K, Integer> e = oldIterator.next();
+                final Object obj = objects[e.getValue()];
+                if (obj != null && !REMOVED.equals(obj)) {
+                    nextKey = e.getKey();
+                    return;
+                }
+            }
+
+            nextKey = newIterator.hasNext() ? newIterator.next() : null;
+        }
+
+        private void checkModCount() {
+            if (modCount != expectedModCount) {
+                throw new ConcurrentModificationException();
+            }
+        }
+
+        @Override
+        public final boolean hasNext() {
+            checkModCount();
+            return nextKey != null;
+        }
+
+        @Override
+        public final void remove() {
+            checkModCount();
+            checkState(currentKey != null);
+            final Integer offset = offsets.get(currentKey);
+            if (offset != null) {
+                cloneArray();
+                objects[offset] = removedObject();
+                removed++;
+            } else {
+                newIterator.remove();
+            }
+
+            expectedModCount = ++modCount;
+            currentKey = null;
+        }
+
+        protected final K nextKey() {
+            if (nextKey == null) {
+                throw new NoSuchElementException();
+            }
+
+            checkModCount();
+            currentKey = nextKey;
+            updateNextKey();
+
+            return currentKey;
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ObjectRegistry.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ObjectRegistry.java
new file mode 100644 (file)
index 0000000..6e834f0
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+
+@Beta
+@NonNullByDefault
+public final class ObjectRegistry<T> {
+    private final Set<ObjectRegistration<? extends T>> objects;
+    private final Set<ObjectRegistration<? extends T>> unmodifiableView;
+    private final String name;
+
+    private ObjectRegistry(final String name, final Set<ObjectRegistration<? extends T>> objects) {
+        this.name = requireNonNull(name);
+        this.objects = requireNonNull(objects);
+        this.unmodifiableView = Collections.unmodifiableSet(objects);
+    }
+
+    public static <T> ObjectRegistry<T> createConcurrent(final String name) {
+        return new ObjectRegistry<>(name, ConcurrentHashMap.newKeySet());
+    }
+
+    public static <T> ObjectRegistry<T> createSimple(final String name) {
+        return new ObjectRegistry<>(name, new HashSet<>(1));
+    }
+
+    public boolean isEmpty() {
+        return objects.isEmpty();
+    }
+
+    public Stream<? extends T> streamObjects() {
+        return streamRegistrations().map(ObjectRegistration::getInstance);
+    }
+
+    public Set<ObjectRegistration<? extends T>> getRegistrations() {
+        return unmodifiableView;
+    }
+
+    public Stream<ObjectRegistration<? extends T>> streamRegistrations() {
+        return objects.stream();
+    }
+
+    public <O extends T> ObjectRegistration<O> register(final O object) {
+        final ObjectRegistration<O> ret = new Reg<>(object, objects::remove);
+        objects.add(ret);
+        return ret;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("name", name).add("objects", objects.size()).toString();
+    }
+
+    private static final class Reg<T> extends AbstractObjectRegistration<T> {
+        private @Nullable Consumer<ObjectRegistration<? super T>> removeCall;
+
+        Reg(final T instance, final Consumer<ObjectRegistration<? super T>> removeCall) {
+            super(instance);
+            this.removeCall = requireNonNull(removeCall);
+        }
+
+        @Override
+        @SuppressFBWarnings("NP_STORE_INTO_NONNULL_FIELD")
+        protected void removeRegistration() {
+            removeCall.accept(this);
+            // Do not retail reference to that state
+            removeCall = null;
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/OffsetMapCache.java b/common/util/src/main/java/org/opendaylight/yangtools/util/OffsetMapCache.java
new file mode 100644 (file)
index 0000000..450f017
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Verify;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableSet;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+final class OffsetMapCache {
+    /*
+     * Cache for offsets where order matters. The key is a List, which defines the iteration order. Since we want
+     * to retain this order, it is okay to use a simple LoadingCache.
+     */
+    private static final LoadingCache<List<?>, ImmutableMap<?, Integer>> ORDERED_CACHE =
+            CacheBuilder.newBuilder().weakValues().build(new CacheLoader<List<?>, ImmutableMap<?, Integer>>() {
+                @Override
+                public ImmutableMap<?, Integer> load(final List<?> key) {
+                    return createMap(key);
+                }
+            });
+    /*
+     * Cache for offsets where order does not mapper. The key is a Set of elements. We use manual two-stage loading
+     * because of the nature of the objects we store as values, which is ImmutableMaps. An ImmutableMap, when queried
+     * for keys (as is done in ImmutableOffsetMap.keySet()), will instantiate an ImmutableSet to hold these keys. It
+     * would be wasteful to use one Set for lookup only to have the map have an exact copy.
+     *
+     * We perform the first look up using a Set (which may come from the user, for example via
+     * ImmutableOffsetMap.unorderedCopyOf()), hence potentially saving a copy operation. If we fail to find an entry,
+     * we construct the map and put it conditionally with Map.keySet() as the key. This will detect concurrent loading
+     * and also lead to the cache and the map sharing the same Set.
+     */
+    private static final Cache<Set<?>, ImmutableMap<?, Integer>> UNORDERED_CACHE =
+            CacheBuilder.newBuilder().weakValues().build();
+
+    private OffsetMapCache() {
+        throw new UnsupportedOperationException();
+    }
+
+    @VisibleForTesting
+    static void invalidateCache() {
+        ORDERED_CACHE.invalidateAll();
+        UNORDERED_CACHE.invalidateAll();
+    }
+
+    @SuppressWarnings("unchecked")
+    static <T> ImmutableMap<T, Integer> orderedOffsets(final Collection<T> args) {
+        if (args.size() == 1) {
+            return unorderedOffsets(args);
+        }
+
+        return (ImmutableMap<T, Integer>) ORDERED_CACHE.getUnchecked(ImmutableList.copyOf(args));
+    }
+
+    static <T> ImmutableMap<T, Integer> unorderedOffsets(final Collection<T> args) {
+        return unorderedOffsets(args instanceof Set ? (Set<T>)args : ImmutableSet.copyOf(args));
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> ImmutableMap<T, Integer> unorderedOffsets(final Set<T> args) {
+        final ImmutableMap<T, Integer> existing = (ImmutableMap<T, Integer>) UNORDERED_CACHE.getIfPresent(args);
+        if (existing != null) {
+            return existing;
+        }
+
+        final ImmutableMap<T, Integer> newMap = createMap(args);
+        final ImmutableMap<?, Integer> raced = UNORDERED_CACHE.asMap().putIfAbsent(newMap.keySet(), newMap);
+        return raced == null ? newMap : (ImmutableMap<T, Integer>)raced;
+    }
+
+    static <K, V> V[] adjustedArray(final Map<K, Integer> offsets, final List<K> keys, final V[] array) {
+        Verify.verify(offsets.size() == keys.size(), "Offsets %s do not match keys %s", offsets, keys);
+
+        // This relies on the fact that offsets has an ascending iterator
+        final Iterator<K> oi = offsets.keySet().iterator();
+        final Iterator<K> ki = keys.iterator();
+
+        while (oi.hasNext()) {
+            final K o = oi.next();
+            final K k = ki.next();
+            if (!k.equals(o)) {
+                return adjustArray(offsets, keys, array);
+            }
+        }
+
+        return array;
+    }
+
+    private static <T> ImmutableMap<T, Integer> createMap(final Collection<T> keys) {
+        final Builder<T, Integer> b = ImmutableMap.builder();
+        int counter = 0;
+
+        for (T arg : keys) {
+            b.put(arg, counter++);
+        }
+
+        return b.build();
+    }
+
+    private static <K, V> V[] adjustArray(final Map<K, Integer> offsets, final List<K> keys, final V[] array) {
+        @SuppressWarnings("unchecked")
+        final V[] ret = (V[]) Array.newInstance(array.getClass().getComponentType(), array.length);
+
+        int offset = 0;
+        for (final K k : keys) {
+            final Integer o = Verify.verifyNotNull(offsets.get(k), "Key %s not present in offsets %s", k, offsets);
+            ret[o] = array[offset++];
+        }
+
+        return ret;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/OptionalBoolean.java b/common/util/src/main/java/org/opendaylight/yangtools/util/OptionalBoolean.java
new file mode 100644 (file)
index 0000000..84bb6a2
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import com.google.common.annotations.Beta;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Utility class for storing an optional boolean in a single byte value. This cuts down the memory requirement quite
+ * at very small computational cost.
+ *
+ * <p>
+ * Note: fields do not have to be explicitly initialized, as default initialization value for 'byte', 0, is used to
+ *       represent 'absent' condition.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class OptionalBoolean {
+    private static final @NonNull Optional<Boolean> FALSE_OPTIONAL = Optional.of(Boolean.FALSE);
+    private static final @NonNull Optional<Boolean> TRUE_OPTIONAL = Optional.of(Boolean.TRUE);
+
+    private static final byte ABSENT = 0;
+    private static final byte FALSE = 1;
+    private static final byte TRUE = 2;
+
+    private OptionalBoolean() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Check if a field value has been set, just like {@link Optional#isPresent()}.
+     *
+     * @param value field value
+     * @return True if the value is set.
+     * @throws IllegalArgumentException if value is invalid
+     */
+    public static boolean isPresent(final byte value) {
+        switch (value) {
+            case ABSENT:
+                return false;
+            case FALSE:
+            case TRUE:
+                return true;
+            default:
+                throw invalidValue(value);
+        }
+    }
+
+    /**
+     * Decode boolean from a field value, just like {@link Optional#get()}.
+     *
+     * @param value Field value
+     * @return Decoded boolean.
+     * @throws IllegalArgumentException if value is invalid
+     * @throws IllegalStateException if value has not been set
+     */
+    public static boolean get(final byte value) {
+        switch (value) {
+            case ABSENT:
+                throw new IllegalStateException("Field has not been initialized");
+            case FALSE:
+                return false;
+            case TRUE:
+                return true;
+            default:
+                throw invalidValue(value);
+        }
+    }
+
+    /**
+     * Encode a boolean to a field value, just like {@link Optional#of(Object)}.
+     *
+     * @param bool Boolean value.
+     * @return Field value.
+     */
+    public static byte of(final boolean bool) {
+        return bool ? TRUE : FALSE;
+    }
+
+    /**
+     * Convert a nullable {@link Boolean} into a field value, just like {@link Optional#ofNullable(Object)}.
+     *
+     * @param bool Boolean value.
+     * @return Field value.
+     */
+    public static byte ofNullable(final @Nullable Boolean bool) {
+        return bool == null ? ABSENT : of(bool.booleanValue());
+    }
+
+    /**
+     * Convert a field value to a nullable {@link Boolean}. Similar to {@code Optional.orElse(null)}.
+     *
+     * @param value Field value.
+     * @return Nullable Boolean.
+     */
+    @SuppressFBWarnings("NP_BOOLEAN_RETURN_NULL")
+    public static @Nullable Boolean toNullable(final byte value) {
+        switch (value) {
+            case ABSENT:
+                return null;
+            case FALSE:
+                return Boolean.FALSE;
+            case TRUE:
+                return Boolean.TRUE;
+            default:
+                throw invalidValue(value);
+        }
+    }
+
+    /**
+     * Convert an {@link Optional} {@link Boolean} into a field value.
+     *
+     * @param value Optional {@link Boolean}.
+     * @return Field value.
+     * @throws NullPointerException if value is null.
+     */
+    public static byte ofOptional(final Optional<Boolean> value) {
+        return ofNullable(value.orElse(null));
+    }
+
+    /**
+     * Convert a field value into an {@link Optional} {@link Boolean}.
+     *
+     * @param value Field value.
+     * @return Optional {@link Boolean}.
+     * @throws IllegalArgumentException if value is invalid.
+     */
+    public static @NonNull Optional<Boolean> toOptional(final byte value) {
+        switch (value) {
+            case ABSENT:
+                return Optional.empty();
+            case FALSE:
+                return FALSE_OPTIONAL;
+            case TRUE:
+                return TRUE_OPTIONAL;
+            default:
+                throw invalidValue(value);
+        }
+    }
+
+    /**
+     * Convert a field value into a String representation.
+     *
+     * @param value Field value.
+     * @return Boolean-compatible string, or "absent".
+     * @throws IllegalArgumentException if value is invalid.
+     */
+    public static @NonNull String toString(final byte value) {
+        switch (value) {
+            case ABSENT:
+                return "absent";
+            case FALSE:
+                return Boolean.toString(false);
+            case TRUE:
+                return Boolean.toString(true);
+            default:
+                throw invalidValue(value);
+        }
+    }
+
+    private static IllegalArgumentException invalidValue(final byte value) {
+        throw new IllegalArgumentException("Invalid field value " + value);
+    }
+}
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..222d5f0
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 com.google.common.base.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 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;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ReadOnlyTrieMap.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ReadOnlyTrieMap.java
new file mode 100644 (file)
index 0000000..a993b85
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ForwardingMap;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import tech.pantheon.triemap.ImmutableTrieMap;
+import tech.pantheon.triemap.MutableTrieMap;
+
+/**
+ * A read-only facade in front of a TrieMap. This is what we give out from
+ * MapAdaptor.optimize(). The idea is that we want our read-only users to
+ * share a single snapshot. That snapshot is instantiated lazily either on
+ * first access. Since we never leak the TrieMap and track its size as it
+ * changes, we can cache it for future reference.
+ */
+final class ReadOnlyTrieMap<K, V> extends ForwardingMap<K, V> {
+    private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTrieMap.class);
+    private static final VarHandle READ_ONLY;
+
+    static {
+        try {
+            READ_ONLY = MethodHandles.lookup().findVarHandle(ReadOnlyTrieMap.class, "readOnly", ImmutableTrieMap.class);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    private final MutableTrieMap<K, V> readWrite;
+    private final int size;
+
+    // Used via the varhandle
+    @SuppressWarnings("unused")
+    private volatile ImmutableTrieMap<K, V> readOnly;
+
+    ReadOnlyTrieMap(final MutableTrieMap<K, V> map, final int size) {
+        this.readWrite = requireNonNull(map);
+        this.size = size;
+    }
+
+    ReadWriteTrieMap<K, V> toReadWrite() {
+        final ReadWriteTrieMap<K, V> ret = new ReadWriteTrieMap<>(readWrite.mutableSnapshot(), size);
+        LOG.trace("Converted read-only TrieMap {} to read-write {}", this, ret);
+        return ret;
+    }
+
+    @Override
+    protected ImmutableTrieMap<K, V> delegate() {
+        final ImmutableTrieMap<K, V> ret = (ImmutableTrieMap<K, V>) READ_ONLY.getAcquire(this);
+        return ret != null ? ret : loadReadOnly();
+    }
+
+    @Override
+    public int size() {
+        return size;
+    }
+
+    @SuppressWarnings("unchecked")
+    private ImmutableTrieMap<K, V> loadReadOnly() {
+        final ImmutableTrieMap<K, V> ret = readWrite.immutableSnapshot();
+        final Object witness = READ_ONLY.compareAndExchangeRelease(this, null, ret);
+        return witness == null ? ret : (ImmutableTrieMap<K, V>) witness;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/ReadWriteTrieMap.java b/common/util/src/main/java/org/opendaylight/yangtools/util/ReadWriteTrieMap.java
new file mode 100644 (file)
index 0000000..25025b3
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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 java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ForwardingMap;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import tech.pantheon.triemap.MutableTrieMap;
+import tech.pantheon.triemap.TrieMap;
+
+/**
+ * A TrieMap facade tracking modifications. Since we change structures based on
+ * their size, and determining the size of a TrieMap is expensive, we make sure
+ * to update it as we go.
+ *
+ * <p>
+ * FIXME: this map does not support modification view the keySet()/values()/entrySet() methods.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ */
+final class ReadWriteTrieMap<K, V> extends ForwardingMap<K, V> {
+    private static final Logger LOG = LoggerFactory.getLogger(ReadWriteTrieMap.class);
+
+    private final MutableTrieMap<K, V> delegate;
+
+    private int size;
+
+    ReadWriteTrieMap() {
+        this.delegate = TrieMap.create();
+        this.size = 0;
+    }
+
+    ReadWriteTrieMap(final MutableTrieMap<K, V> delegate, final int size) {
+        this.delegate = requireNonNull(delegate);
+        this.size = size;
+    }
+
+    @Override
+    protected Map<K, V> delegate() {
+        return delegate;
+    }
+
+    Map<K, V> toReadOnly() {
+        final Map<K, V> ret = new ReadOnlyTrieMap<>(delegate, size);
+        LOG.trace("Converted read-write TrieMap {} to read-only {}", this, ret);
+        return ret;
+    }
+
+    @Override
+    public int size() {
+        return size;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    @Override
+    public V put(final K key, final V value) {
+        final V ret = delegate.put(key, value);
+        if (ret == null) {
+            size++;
+        }
+        return ret;
+    }
+
+    @Override
+    public V remove(final Object key) {
+        final V ret = delegate.remove(key);
+        if (ret != null) {
+            size--;
+        }
+        return ret;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public void putAll(final Map<? extends K, ? extends V> m) {
+        for (Entry<? extends K, ? extends V> e : m.entrySet()) {
+            put(e.getKey(), e.getValue());
+        }
+    }
+
+    @Override
+    public void clear() {
+        delegate.clear();
+        size = 0;
+    }
+
+    @Override
+    public Set<K> keySet() {
+        return Collections.unmodifiableSet(delegate.keySet());
+    }
+
+    @Override
+    public Collection<V> values() {
+        return Collections.unmodifiableCollection(delegate.values());
+    }
+
+    @Override
+    public Set<Entry<K, V>> entrySet() {
+        return Collections.unmodifiableSet(delegate.entrySet());
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/RecursiveObjectLeaker.java b/common/util/src/main/java/org/opendaylight/yangtools/util/RecursiveObjectLeaker.java
new file mode 100644 (file)
index 0000000..4c8b9af
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2015 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.checkState;
+
+import com.google.common.annotations.Beta;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Map.Entry;
+import org.eclipse.jdt.annotation.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Thread-local hack to make recursive extensions work without too much hassle. The idea is that prior to instantiating
+ * an extension, the definition object checks whether it is already present on the stack, recorded object is returned.
+ *
+ * <p>
+ * If it is not, it will push itself to the stack as unresolved and invoke the constructor. The constructor's lowermost
+ * class calls to this class and if the topmost entry is not resolved, it will leak itself.
+ *
+ * <p>
+ * Upon return from the constructor, the topmost entry is removed and if the queue is empty, the thread-local variable
+ * will be cleaned up.
+ *
+ * <p>
+ * WARNING: BE CAREFUL WHEN USING THIS CLASS. IT LEAKS OBJECTS WHICH ARE NOT COMPLETELY INITIALIZED.
+ *
+ * <p>
+ * WARNING: THIS CLASS LEAVES THREAD-LOCAL RESIDUE. MAKE SURE IT IS OKAY OR CALL {@link #cleanup()} IN APPROPRIATE
+ *          PLACES.
+ *
+ * <p>
+ * THIS CLASS IS EXTREMELY DANGEROUS (okay, not as much as sun.misc.unsafe). YOU HAVE BEEN WARNED. IF SOMETHING BREAKS
+ * IT IS PROBABLY YOUR FAULT AND YOU ARE ON YOUR OWN.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class RecursiveObjectLeaker {
+    // Logging note. Only keys passed can be logged, as objects beng resolved may not be properly constructed.
+    private static final Logger LOG = LoggerFactory.getLogger(RecursiveObjectLeaker.class);
+
+    // Initial value is set to null on purpose, so we do not allocate anything (aside the map)
+    private static final ThreadLocal<Deque<Entry<?, Object>>> STACK = new ThreadLocal<>();
+
+    private RecursiveObjectLeaker() {
+        throw new UnsupportedOperationException();
+    }
+
+    // Key is checked for identity
+    public static void beforeConstructor(final Object key) {
+        Deque<Entry<?, Object>> stack = STACK.get();
+        if (stack == null) {
+            // Biased: this class is expected to be rarely and shallowly used
+            stack = new ArrayDeque<>(1);
+            STACK.set(stack);
+        }
+
+        LOG.debug("Resolving key {}", key);
+        stack.push(new SimpleEntry<>(key, null));
+    }
+
+    // Can potentially store a 'null' mapping. Make sure cleanup() is called
+    public static void inConstructor(final Object obj) {
+        final Deque<Entry<?, Object>> stack = STACK.get();
+        if (stack != null) {
+            final Entry<?, Object> top = stack.peek();
+            if (top != null) {
+                if (top.getValue() == null) {
+                    LOG.debug("Resolved key {}", top.getKey());
+                    top.setValue(obj);
+                }
+            } else {
+                LOG.info("Cleaned stale empty stack", new Exception());
+                STACK.set(null);
+            }
+        } else {
+            LOG.trace("No thread stack");
+        }
+    }
+
+    // Make sure to call this from a finally block
+    public static void afterConstructor(final Object key) {
+        final Deque<Entry<?, Object>> stack = STACK.get();
+        checkState(stack != null, "No stack allocated when completing %s", key);
+
+        final Entry<?, Object> top = stack.pop();
+        if (stack.isEmpty()) {
+            LOG.trace("Removed empty thread stack");
+            STACK.set(null);
+        }
+
+        checkState(key == top.getKey(), "Expected key %s, have %s", top.getKey(), key);
+        checkState(top.getValue() != null, "");
+    }
+
+    // BEWARE: this method returns incpmpletely-initialized objects (that is the purpose of this class).
+    //
+    //         BE VERY CAREFUL WHAT OBJECT STATE YOU TOUCH
+    public static <T> @Nullable T lookup(final Object key, final Class<T> requiredClass) {
+        final Deque<Entry<?, Object>> stack = STACK.get();
+        if (stack != null) {
+            for (Entry<?, Object> e : stack) {
+                // Keys are treated as identities
+                if (key == e.getKey()) {
+                    checkState(e.getValue() != null, "Object for %s is not resolved", key);
+                    LOG.debug("Looked up key {}", e.getKey());
+                    return requiredClass.cast(e.getValue());
+                }
+            }
+        }
+
+        return null;
+    }
+
+    // Be sure to call this in from a finally block when bulk processing is done, so that this class can be unloaded
+    public static void cleanup() {
+        STACK.remove();
+        LOG.debug("Removed thread state");
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/SharedSingletonMap.java b/common/util/src/main/java/org/opendaylight/yangtools/util/SharedSingletonMap.java
new file mode 100644 (file)
index 0000000..004cbf3
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2015 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.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.io.Serializable;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Iterator;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Implementation of the {@link Map} interface which stores a single mapping. The key set is shared among all instances
+ * which contain the same key. This implementation does not support null keys or values.
+ *
+ * <p>
+ * In case the set of keys is statically known, you can use {@link SharedSingletonMapTemplate} to efficiently create
+ * {@link SharedSingletonMap} instances.
+ *
+ * @param <K> the type of keys maintained by this map
+ * @param <V> the type of mapped values
+ */
+@Beta
+public abstract class SharedSingletonMap<K, V> implements Serializable, UnmodifiableMapPhase<K, V> {
+    static final class Ordered<K, V> extends SharedSingletonMap<K, V> {
+        private static final long serialVersionUID = 1L;
+
+        Ordered(final K key, final V value) {
+            super(key, value);
+        }
+
+        Ordered(final SingletonSet<K> keySet, final V value) {
+            super(keySet, value);
+        }
+
+        @Override
+        public @NonNull ModifiableMapPhase<K, V> toModifiableMap() {
+            return MutableOffsetMap.orderedCopyOf(this);
+        }
+    }
+
+    static final class Unordered<K, V> extends SharedSingletonMap<K, V> {
+        private static final long serialVersionUID = 1L;
+
+        Unordered(final K key, final V value) {
+            super(key, value);
+        }
+
+        Unordered(final SingletonSet<K> keySet, final V value) {
+            super(keySet, value);
+        }
+
+        @Override
+        public @NonNull ModifiableMapPhase<K, V> toModifiableMap() {
+            return MutableOffsetMap.unorderedCopyOf(this);
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+    private static final LoadingCache<Object, SingletonSet<Object>> CACHE = CacheBuilder.newBuilder().weakValues()
+            .build(new CacheLoader<Object, SingletonSet<Object>>() {
+                @Override
+                public SingletonSet<Object> load(final Object key) {
+                    return SingletonSet.of(key);
+                }
+            });
+
+    private final @NonNull SingletonSet<K> keySet;
+    private final @NonNull V value;
+    private int hashCode;
+
+    SharedSingletonMap(final SingletonSet<K> keySet, final V value) {
+        this.keySet = requireNonNull(keySet);
+        this.value = requireNonNull(value);
+    }
+
+    SharedSingletonMap(final K key, final V value) {
+        this(cachedSet(key), value);
+    }
+
+    /**
+     * Create a {@link SharedSingletonMap} of specified {@code key} and {@code value}, which retains insertion order
+     * when transformed via {@link #toModifiableMap()}.
+     *
+     * @param key key
+     * @param value value
+     * @return A SharedSingletonMap
+     * @throws NullPointerException if any of the arguments is null
+     */
+    public static <K, V> @NonNull SharedSingletonMap<K, V> orderedOf(final K key, final V value) {
+        return new Ordered<>(key, value);
+    }
+
+    /**
+     * Create a {@link SharedSingletonMap} of specified {@code key} and {@code value}, which does not retain insertion
+     * order when transformed via {@link #toModifiableMap()}.
+     *
+     * @param key key
+     * @param value value
+     * @return A SharedSingletonMap
+     * @throws NullPointerException if any of the arguments is null
+     */
+    public static <K, V> @NonNull SharedSingletonMap<K, V> unorderedOf(final K key, final V value) {
+        return new Unordered<>(key, value);
+    }
+
+    /**
+     * Create a {@link SharedSingletonMap} of specified {@code key} and {@code value}, which retains insertion order
+     * when transformed via {@link #toModifiableMap()}.
+     *
+     * @param map input map
+     * @return A SharedSingletonMap
+     * @throws NullPointerException if {@code map} is null
+     * @throws IllegalArgumentException if {@code map} does not have exactly one entry
+     */
+    public static <K, V> @NonNull SharedSingletonMap<K, V> orderedCopyOf(final Map<K, V> map) {
+        final Entry<K, V> e = singleEntry(map);
+        return new Ordered<>(e.getKey(), e.getValue());
+    }
+
+    /**
+     * Create a {@link SharedSingletonMap} from specified single-element map, which does not retain insertion order when
+     * transformed via {@link #toModifiableMap()}.
+     *
+     * @param map input map
+     * @return A SharedSingletonMap
+     * @throws NullPointerException if {@code map} is null
+     * @throws IllegalArgumentException if {@code map} does not have exactly one entry
+     */
+    public static <K, V> @NonNull SharedSingletonMap<K, V> unorderedCopyOf(final Map<K, V> map) {
+        final Entry<K, V> e = singleEntry(map);
+        return new Unordered<>(e.getKey(), e.getValue());
+    }
+
+    public final Entry<K, V> getEntry() {
+        return new SimpleImmutableEntry<>(keySet.getElement(), value);
+    }
+
+    @Override
+    public final @NonNull SingletonSet<Entry<K, V>> entrySet() {
+        return SingletonSet.of(getEntry());
+    }
+
+    @Override
+    public final @NonNull SingletonSet<K> keySet() {
+        return keySet;
+    }
+
+    @Override
+    public final @NonNull SingletonSet<V> values() {
+        return SingletonSet.of(value);
+    }
+
+    @Override
+    public final boolean containsKey(final Object key) {
+        return keySet.contains(key);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:hiddenField")
+    public final boolean containsValue(final Object value) {
+        return this.value.equals(value);
+    }
+
+    @Override
+    public final V get(final Object key) {
+        return keySet.contains(key) ? value : null;
+    }
+
+    @Override
+    public final int size() {
+        return 1;
+    }
+
+    @Override
+    public final boolean isEmpty() {
+        return false;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:hiddenField")
+    public final V put(final K key, final V value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final V remove(final Object key) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final void putAll(final Map<? extends K, ? extends V> m) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final int hashCode() {
+        if (hashCode == 0) {
+            hashCode = keySet.getElement().hashCode() ^ value.hashCode();
+        }
+        return hashCode;
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof Map)) {
+            return false;
+        }
+
+        final Map<?, ?> m = (Map<?, ?>)obj;
+        return m.size() == 1 && value.equals(m.get(keySet.getElement()));
+    }
+
+    @Override
+    public final String toString() {
+        return "{" + keySet.getElement() + '=' + value + '}';
+    }
+
+    @SuppressWarnings("unchecked")
+    static <K> @NonNull SingletonSet<K> cachedSet(final K key) {
+        return (SingletonSet<K>) CACHE.getUnchecked(key);
+    }
+
+    private static <K, V> Entry<K, V> singleEntry(final Map<K, V> map) {
+        final Iterator<Entry<K, V>> it = map.entrySet().iterator();
+        checkArgument(it.hasNext(), "Input map is empty");
+        final Entry<K, V> ret = it.next();
+        checkArgument(!it.hasNext(), "Input map has more than one entry");
+        return ret;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/SharedSingletonMapTemplate.java b/common/util/src/main/java/org/opendaylight/yangtools/util/SharedSingletonMapTemplate.java
new file mode 100644 (file)
index 0000000..97f3115
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.checkArgument;
+
+import com.google.common.base.MoreObjects;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.BiFunction;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Template for instantiating {@link SharedSingletonMap} instances with a fixed key. The template can then be
+ * used as a factory for instances via using {@link #instantiateTransformed(Map, BiFunction)} or, more efficiently,
+ * using {@link #instantiateWithValue(Object)}.
+ *
+ * @param <K> the type of keys maintained by this template
+ */
+public abstract class SharedSingletonMapTemplate<K> extends ImmutableMapTemplate<K> {
+    private static final class Ordered<K> extends SharedSingletonMapTemplate<K> {
+        Ordered(final K key) {
+            super(key);
+        }
+
+        @Override
+        public <V> @NonNull SharedSingletonMap<K, V> instantiateWithValue(final V value) {
+            return new SharedSingletonMap.Ordered<>(keySet(), value);
+        }
+    }
+
+    private static final class Unordered<K> extends SharedSingletonMapTemplate<K> {
+        Unordered(final K key) {
+            super(key);
+        }
+
+        @Override
+        public <V> @NonNull SharedSingletonMap<K, V> instantiateWithValue(final V value) {
+            return new SharedSingletonMap.Unordered<>(keySet(), value);
+        }
+    }
+
+    private final @NonNull SingletonSet<K> keySet;
+
+    SharedSingletonMapTemplate(final K key) {
+        this.keySet = SharedSingletonMap.cachedSet(key);
+    }
+
+    /**
+     * Create a template which produces Maps with specified key. The resulting map will retain insertion order through
+     * {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
+     *
+     * @param key Single key in resulting map
+     * @param <K> the type of keys maintained by resulting template
+     * @return A template object.
+     * @throws NullPointerException if {@code key} is null
+     */
+    public static <K> @NonNull SharedSingletonMapTemplate<K> ordered(final K key) {
+        return new Ordered<>(key);
+    }
+
+    /**
+     * Create a template which produces Maps with specified key. The resulting map will NOT retain ordering through
+     * {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
+     *
+     * @param key Single key in resulting map
+     * @param <K> the type of keys maintained by resulting template
+     * @return A template object.
+     * @throws NullPointerException if {@code key} is null
+     */
+    public static <K> @NonNull SharedSingletonMapTemplate<K> unordered(final K key) {
+        return new Unordered<>(key);
+    }
+
+    @Override
+    public final SingletonSet<K> keySet() {
+        return keySet;
+    }
+
+    @Override
+    public final <T, V> @NonNull SharedSingletonMap<K, V> instantiateTransformed(final Map<K, T> fromMap,
+            final BiFunction<K, T, V> valueTransformer) {
+        final Iterator<Entry<K, T>> it = fromMap.entrySet().iterator();
+        checkArgument(it.hasNext(), "Input is empty while expecting 1 item");
+
+        final Entry<K, T> entry = it.next();
+        final K expected = keySet.getElement();
+        final K actual = entry.getKey();
+        checkArgument(expected.equals(actual), "Unexpected key %s, expecting %s", actual, expected);
+
+        final V value = transformValue(actual, entry.getValue(), valueTransformer);
+        checkArgument(!it.hasNext(), "Input has more than one item");
+        return instantiateWithValue(value);
+    }
+
+    @Override
+    @SafeVarargs
+    public final <V> @NonNull SharedSingletonMap<K, V> instantiateWithValues(final V... values) {
+        checkArgument(values.length == 1);
+        return instantiateWithValue(values[0]);
+    }
+
+    /**
+     * Instantiate an immutable map with the value supplied.
+     *
+     * @param value Value to use
+     * @param <V> the type of mapped values
+     * @return An immutable map
+     * @throws NullPointerException if {@code value} is null
+     */
+    public abstract <V> @NonNull SharedSingletonMap<K, V> instantiateWithValue(V value);
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(this).add("keySet", keySet).toString();
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/SingletonSet.java b/common/util/src/main/java/org/opendaylight/yangtools/util/SingletonSet.java
new file mode 100644 (file)
index 0000000..914268a
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2015 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 java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Iterators;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Spliterator;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * A {@link Set} containing a single value. For some reason neither Java nor Guava provide direct access to the retained
+ * element -- which is desirable in some situations, as is the case in {@link SharedSingletonMap#entrySet()}.
+ */
+@Beta
+public abstract class SingletonSet<E> implements Set<E>, Immutable, Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private static final SingletonSet<?> NULL_SINGLETON = new SingletonSet<Object>() {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        @SuppressWarnings("checkstyle:parameterName")
+        public boolean contains(final Object o) {
+            return o == null;
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:equalsHashCode")
+        public int hashCode() {
+            return 0;
+        }
+
+        @Override
+        public @Nullable Object getElement() {
+            return null;
+        }
+
+        @Override
+        public @NonNull Spliterator<Object> spliterator() {
+            return SingletonSpliterators.immutableOfNull();
+        }
+
+        @Override
+        public @NonNull String toString() {
+            return "[null]";
+        }
+
+        private Object readResolve() {
+            return NULL_SINGLETON;
+        }
+    };
+
+    @SuppressWarnings("unchecked")
+    public static <E> @NonNull SingletonSet<E> of(final @Nullable E element) {
+        return element == null ? (SingletonSet<E>) NULL_SINGLETON : new RegularSingletonSet<>(element);
+    }
+
+    public abstract @Nullable E getElement();
+
+    @Override
+    public final int size() {
+        return 1;
+    }
+
+    @Override
+    public final boolean isEmpty() {
+        return false;
+    }
+
+    @Override
+    public final @NonNull Iterator<E> iterator() {
+        return Iterators.singletonIterator(getElement());
+    }
+
+    @Override
+    public abstract @NonNull Spliterator<E> spliterator();
+
+    @Override
+    public final @NonNull Object[] toArray() {
+        return new Object[] { getElement() };
+    }
+
+    @SuppressWarnings({ "unchecked", "checkstyle:parameterName" })
+    @Override
+    public final <T> @NonNull T[] toArray(final T[] a) {
+        if (a.length > 0) {
+            a[0] = (T)getElement();
+            return a;
+        }
+
+        return (T[]) new Object[] {getElement()};
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final boolean add(final E e) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final boolean remove(final Object o) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final boolean containsAll(final Collection<?> c) {
+        return c.isEmpty() || c.size() == 1 && otherContains(c);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final boolean addAll(final Collection<? extends E> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final boolean retainAll(final Collection<?> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final boolean removeAll(final Collection<?> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    @SuppressWarnings("checkstyle:equalsHashCode")
+    public final boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof Set)) {
+            return false;
+        }
+
+        final Set<?> s = (Set<?>)obj;
+        return s.size() == 1 && otherContains(s);
+    }
+
+    private boolean otherContains(final @NonNull Collection<?> other) {
+        try {
+            return other.contains(getElement());
+        } catch (ClassCastException | NullPointerException e) {
+            return false;
+        }
+    }
+
+    @NonNullByDefault
+    private static final class RegularSingletonSet<E> extends SingletonSet<E> {
+        private static final long serialVersionUID = 1L;
+
+        private final E element;
+
+        RegularSingletonSet(final E element) {
+            this.element = requireNonNull(element);
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:parameterName")
+        public boolean contains(final @Nullable Object o) {
+            return element.equals(o);
+        }
+
+        @Override
+        public E getElement() {
+            return element;
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:equalsHashCode")
+        public int hashCode() {
+            return getElement().hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return "[" + element + ']';
+        }
+
+        @Override
+        public Spliterator<E> spliterator() {
+            return SingletonSpliterators.immutableOf(element);
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/SingletonSpliterators.java b/common/util/src/main/java/org/opendaylight/yangtools/util/SingletonSpliterators.java
new file mode 100644 (file)
index 0000000..d67626c
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Mutable;
+
+/**
+ * Utility methods for instantiating {@link Spliterator}s containing a single element.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public final class SingletonSpliterators {
+    private SingletonSpliterators() {
+
+    }
+
+    /**
+     * Create a new {@link Spliterator} reporting specified element. It has the following characteristics:
+     * <ul>
+     *   <li>{@link Spliterator#DISTINCT}</li>
+     *   <li>{@link Spliterator#IMMUTABLE}</li>
+     *   <li>{@link Spliterator#NONNULL}</li>
+     *   <li>{@link Spliterator#ORDERED}</li>
+     *   <li>{@link Spliterator#SIZED}</li>
+     *   <li>{@link Spliterator#SUBSIZED}</li>
+     * </ul>
+     *
+     * @param element Single element to report
+     * @param <T> the type of elements returned by this Spliterator
+     * @return A new spliterator
+     * @throws NullPointerException if element is null
+     */
+    public static <T> Spliterator<T> immutableOf(final T element) {
+        return new ImmutableNonNull<>(element);
+    }
+
+    /**
+     * Create a new {@link Spliterator} reporting a {@code null} element. It has the following characteristics:
+     * <ul>
+     *   <li>{@link Spliterator#DISTINCT}</li>
+     *   <li>{@link Spliterator#IMMUTABLE}</li>
+     *   <li>{@link Spliterator#ORDERED}</li>
+     *   <li>{@link Spliterator#SIZED}</li>
+     *   <li>{@link Spliterator#SUBSIZED}</li>
+     * </ul>
+     *
+     * @return A new spliterator
+     */
+    public static <@Nullable T> Spliterator<T> immutableOfNull() {
+        return new ImmutableNull<>();
+    }
+
+    private static final class ImmutableNonNull<T> implements Mutable, Spliterator<T> {
+        private final T element;
+
+        private boolean consumed;
+
+        private ImmutableNonNull(final T element) {
+            this.element = requireNonNull(element);
+        }
+
+        @Override
+        public boolean tryAdvance(final @Nullable Consumer<? super T> action) {
+            requireNonNull(action);
+            if (consumed) {
+                return false;
+            }
+
+            action.accept(element);
+            consumed = true;
+            return true;
+        }
+
+        @Override
+        public @Nullable Spliterator<T> trySplit() {
+            return null;
+        }
+
+        @Override
+        public long estimateSize() {
+            return consumed ? 0 : 1;
+        }
+
+        @Override
+        public int characteristics() {
+            return Spliterator.NONNULL | Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.IMMUTABLE
+                    | Spliterator.DISTINCT | Spliterator.ORDERED;
+        }
+    }
+
+    private static final class ImmutableNull<@Nullable E> implements Mutable, Spliterator<E> {
+        private boolean consumed;
+
+        @Override
+        public boolean tryAdvance(final @Nullable Consumer<? super E> action) {
+            requireNonNull(action);
+            if (consumed) {
+                return false;
+            }
+
+            action.accept(null);
+            consumed = true;
+            return true;
+        }
+
+        @Override
+        public @Nullable Spliterator<E> trySplit() {
+            return null;
+        }
+
+        @Override
+        public long estimateSize() {
+            return consumed ? 0 : 1;
+        }
+
+        @Override
+        public int characteristics() {
+            return Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.IMMUTABLE | Spliterator.DISTINCT
+                    | Spliterator.ORDERED;
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/SynchronizedDurationStatsTracker.java b/common/util/src/main/java/org/opendaylight/yangtools/util/SynchronizedDurationStatsTracker.java
new file mode 100644 (file)
index 0000000..b3e9007
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import com.google.common.primitives.UnsignedLong;
+
+/**
+ * Non-concurrent implementation, useful for non-contended cases.
+ */
+final class SynchronizedDurationStatsTracker extends DurationStatisticsTracker {
+    private static final long NOT_SET = -1;
+
+    // Hot fields in the order in which they are accessed
+    private long durationSum = 0;
+    private long durationCount = 0;
+    private long shortestDuration = NOT_SET;
+    private long longestDuration = NOT_SET;
+
+    // Cold fields, longest has a higher chance of being accessed
+    private long longestTimestamp;
+    private long shortestTimestamp;
+
+    SynchronizedDurationStatsTracker() {
+        // Hidden on purpose
+    }
+
+    @Override
+    public synchronized void addDuration(final long duration) {
+        durationSum += duration;
+        durationCount++;
+
+        if (duration < shortestDuration || shortestDuration == NOT_SET) {
+            shortestDuration = duration;
+            shortestTimestamp = System.currentTimeMillis();
+        }
+        if (duration > longestDuration) {
+            longestDuration = duration;
+            longestTimestamp = System.currentTimeMillis();
+        }
+    }
+
+    @Override
+    public synchronized double getAverageDuration() {
+        return durationCount == 0 ? 0 : UnsignedLong.fromLongBits(durationSum).doubleValue() / durationCount;
+    }
+
+    @Override
+    public synchronized long getTotalDurations() {
+        return durationCount;
+    }
+
+    @Override
+    public synchronized void reset() {
+        durationSum = 0;
+        durationCount = 0;
+        longestDuration = NOT_SET;
+        shortestDuration = NOT_SET;
+    }
+
+    @Override
+    protected synchronized DurationWithTime getShortest() {
+        return shortestDuration == NOT_SET ? null : new DurationWithTime(shortestDuration, shortestTimestamp);
+    }
+
+    @Override
+    protected synchronized DurationWithTime getLongest() {
+        return longestDuration == NOT_SET ? null : new DurationWithTime(longestDuration, longestTimestamp);
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/TopologicalSort.java b/common/util/src/main/java/org/opendaylight/yangtools/util/TopologicalSort.java
new file mode 100644 (file)
index 0000000..300e5c9
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * 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 static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.annotations.Beta;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Utility class that provides topological sort.
+ *
+ * <p>
+ * Note this class is non-public to allow for API transition.
+ */
+@Beta
+public final class TopologicalSort {
+
+    /**
+     * It isn't desirable to create instance of this class.
+     */
+    private TopologicalSort() {
+    }
+
+    /**
+     * Topological sort of dependent nodes in acyclic graphs.
+     *
+     * @param nodes graph nodes
+     * @return Sorted {@link List} of {@link Node}s. Order: Nodes with no dependencies starting.
+     * @throws IllegalStateException when cycle is present in the graph
+     */
+    public static List<Node> sort(final Set<Node> nodes) {
+        List<Node> sortedNodes = new ArrayList<>(nodes.size());
+
+        Set<Node> dependentNodes = getDependentNodes(nodes);
+
+        while (!dependentNodes.isEmpty()) {
+            Node node = dependentNodes.iterator().next();
+            dependentNodes.remove(node);
+
+            sortedNodes.add(node);
+
+            for (Edge edge : node.getInEdges()) {
+                Node referent = edge.getFrom();
+                referent.getOutEdges().remove(edge);
+
+                if (referent.getOutEdges().isEmpty()) {
+                    dependentNodes.add(referent);
+                }
+            }
+        }
+
+        detectCycles(nodes);
+
+        return sortedNodes;
+    }
+
+    private static Set<Node> getDependentNodes(final Set<Node> nodes) {
+        Set<Node> dependentNodes = new HashSet<>();
+        for (Node node : nodes) {
+            if (node.getOutEdges().isEmpty()) {
+                dependentNodes.add(node);
+            }
+        }
+        return dependentNodes;
+    }
+
+    private static void detectCycles(final Set<Node> nodes) {
+        // Detect cycles
+        boolean cycle = false;
+        Node cycledNode = null;
+
+        for (Node node : nodes) {
+            if (!node.getOutEdges().isEmpty()) {
+                cycle = true;
+                cycledNode = node;
+                break;
+            }
+        }
+        checkState(!cycle, "Cycle detected in graph around node: " + cycledNode);
+    }
+
+    /**
+     * Interface for nodes in graph that can be sorted topologically.
+     */
+    @Beta
+    public interface Node {
+        Set<Edge> getInEdges();
+
+        Set<Edge> getOutEdges();
+    }
+
+    /**
+     * Interface for edges in graph that can be sorted topologically.
+     */
+    @Beta
+    public interface Edge {
+        Node getFrom();
+
+        Node getTo();
+    }
+
+    /**
+     * Basic Node implementation.
+     */
+    @Beta
+    public static class NodeImpl implements Node {
+        private final Set<Edge> inEdges = new HashSet<>();
+        private final Set<Edge> outEdges = new HashSet<>();
+
+        @Override
+        public Set<Edge> getInEdges() {
+            return inEdges;
+        }
+
+        @Override
+        public Set<Edge> getOutEdges() {
+            return outEdges;
+        }
+
+        public void addEdge(final Node to) {
+            Edge edge = new EdgeImpl(this, to);
+            outEdges.add(edge);
+            to.getInEdges().add(edge);
+        }
+    }
+
+    /**
+     * Basic Edge implementation.
+     */
+    @Beta
+    public static class EdgeImpl implements Edge {
+        private final Node from;
+        private final Node to;
+
+        public EdgeImpl(final Node from, final Node to) {
+            this.from = from;
+            this.to = to;
+        }
+
+        @Override
+        public Node getFrom() {
+            return from;
+        }
+
+        @Override
+        public Node getTo() {
+            return to;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + Objects.hashCode(from);
+            result = prime * result + Objects.hashCode(to);
+            return result;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            EdgeImpl other = (EdgeImpl) obj;
+            if (from == null) {
+                if (other.from != null) {
+                    return false;
+                }
+            } else if (!from.equals(other.from)) {
+                return false;
+            }
+            if (to == null) {
+                if (other.to != null) {
+                    return false;
+                }
+            } else if (!to.equals(other.to)) {
+                return false;
+            }
+            return true;
+        }
+    }
+
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/UnmodifiableCollection.java b/common/util/src/main/java/org/opendaylight/yangtools/util/UnmodifiableCollection.java
new file mode 100644 (file)
index 0000000..4e0314e
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * 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 static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.collect.Iterators;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import org.eclipse.jdt.annotation.NonNull;
+import org.gaul.modernizer_maven_annotations.SuppressModernizer;
+
+/**
+ * An unmodifiable view over a {@link Collection}. Unlike the view returned via
+ * {@link Collections#unmodifiableCollection(Collection)}, this class checks its
+ * argument to ensure multiple encapsulation does not occur.
+ *
+ * <p>This class checks
+ * the argument so it prevents multiple encapsulation. Subclasses of
+ * {@link ImmutableCollection} are also recognized and not encapsulated.
+ *
+ * @param <E> the type of elements in this collection
+ */
+public final class UnmodifiableCollection<E> implements Collection<E>, Serializable {
+    private static final long serialVersionUID = 1L;
+    private static final @NonNull Class<?> UNMODIFIABLE_COLLECTION_CLASS;
+    private static final @NonNull Collection<Class<?>> SINGLETON_CLASSES;
+
+    static {
+        UNMODIFIABLE_COLLECTION_CLASS = Collections.unmodifiableCollection(new ArrayList<>()).getClass();
+
+        final Builder<Class<?>> b = ImmutableSet.builder();
+        b.add(Collections.singleton(null).getClass());
+        b.add(Collections.singletonList(null).getClass());
+        SINGLETON_CLASSES = b.build();
+    }
+
+    private final @NonNull Collection<E> delegate;
+
+    private UnmodifiableCollection(final @NonNull Collection<E> delegate) {
+        this.delegate = requireNonNull(delegate);
+    }
+
+    /**
+     * Create an unmodifiable view of the target collection. If the instance is known
+     * to be unmodifiable, that instance is returned.
+     *
+     * @param collection Target collection
+     * @return An unmodifiable view of the collection
+     * @throws NullPointerException if {@code collection} is null
+     */
+    @SuppressModernizer
+    public static <T> @NonNull Collection<T> create(final @NonNull Collection<T> collection) {
+        if (collection instanceof UnmodifiableCollection || collection instanceof ImmutableCollection
+                || Collections.EMPTY_LIST == collection || Collections.EMPTY_SET == collection
+                || UNMODIFIABLE_COLLECTION_CLASS.isInstance(collection)
+                || SINGLETON_CLASSES.contains(collection.getClass())) {
+            return collection;
+        }
+
+        return new UnmodifiableCollection<>(collection);
+    }
+
+    @Override
+    public @NonNull Iterator<E> iterator() {
+        return Iterators.unmodifiableIterator(delegate.iterator());
+    }
+
+    @Override
+    public int size() {
+        return delegate.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return delegate.isEmpty();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean contains(final Object o) {
+        return delegate.contains(o);
+    }
+
+    @Override
+    public Object[] toArray() {
+        return delegate.toArray();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public <T> T[] toArray(final T[] a) {
+        return delegate.toArray(a);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean containsAll(final Collection<?> c) {
+        return delegate.containsAll(c);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean add(final E e) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean addAll(final Collection<? extends E> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean remove(final Object o) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean removeAll(final Collection<?> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean retainAll(final Collection<?> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public @NonNull String toString() {
+        return "UnmodifiableCollection{" + delegate + "}";
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/UnmodifiableMapPhase.java b/common/util/src/main/java/org/opendaylight/yangtools/util/UnmodifiableMapPhase.java
new file mode 100644 (file)
index 0000000..98d38fa
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import com.google.common.annotations.Beta;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * A {@link Map} which cannot be modified and supports efficient conversion to a {@link ModifiableMapPhase}.
+ *
+ * @param <K> the type of keys maintained by this map
+ * @param <V> the type of mapped values
+ */
+@Beta
+public interface UnmodifiableMapPhase<K, V> extends Map<K, V>, Immutable {
+    /**
+     * Return an isolated modifiable version of this map. Its mappings must match the mappings present in this map. Any
+     * modification of the returned map must not be affect the contents of this map.
+     *
+     * @return An modifiable version of this map.
+     */
+    @NonNull ModifiableMapPhase<K, V> toModifiableMap();
+}
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..e79409e
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * 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 java.util.Objects.requireNonNull;
+
+import com.google.common.util.concurrent.ExecutionList;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableFutureTask;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 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.
+ *
+ * <p>Also note that the use of this task may attach some (small) amount of state to the threads
+ * interacting with it. That state will not be detached automatically, but you can use
+ *  {@link #cleanStateForCurrentThread()} to clean it up.
+ *
+ * @author Thomas Pantelis
+ * @author Robert Varga
+ *
+ * @param <V> the Future result value type
+ */
+public class AsyncNotifyingListenableFutureTask<V> extends FutureTask<V> implements ListenableFuture<V> {
+
+    private static final class DelegatingAsyncNotifyingListenableFutureTask<V>
+            extends AsyncNotifyingListenableFutureTask<V> {
+
+        /**
+         * The executor used to run listener callbacks.
+         */
+        private final @NonNull Executor listenerExecutor;
+
+        private DelegatingAsyncNotifyingListenableFutureTask(final Callable<V> callable,
+                final @NonNull Executor listenerExecutor) {
+            super(callable);
+            this.listenerExecutor = requireNonNull(listenerExecutor);
+        }
+
+        private DelegatingAsyncNotifyingListenableFutureTask(final Runnable runnable, @Nullable final V result,
+                final @NonNull Executor listenerExecutor) {
+            super(runnable, result);
+            this.listenerExecutor = requireNonNull(listenerExecutor);
+        }
+
+        @Override
+        public void addListener(final Runnable listener,  final Executor executor) {
+            // Wrap the listener Runnable in a DelegatingRunnable. If the specified executor is one that
+            // runs tasks in the same thread as the caller submitting the task
+            // (e.g. {@link com.google.common.util.concurrent.MoreExecutors#sameThreadExecutor}) and the
+            // listener is executed from the #done method, then the DelegatingRunnable will detect this
+            // via the ThreadLocal and submit the listener Runnable to the listenerExecutor.
+            //
+            // On the other hand, if this task is already complete, the call to ExecutionList#add in
+            // superclass will execute the listener Runnable immediately and, since the ThreadLocal won't be set,
+            // the DelegatingRunnable will run the listener Runnable inline.
+            super.addListener(new DelegatingRunnable(listener, listenerExecutor), executor);
+        }
+    }
+
+    private static final class DelegatingRunnable implements Runnable {
+        private final Runnable delegate;
+        private final Executor executor;
+
+        DelegatingRunnable(final Runnable delegate, final Executor executor) {
+            this.delegate = requireNonNull(delegate);
+            this.executor = requireNonNull(executor);
+        }
+
+        @Override
+        public void run() {
+            if (ON_TASK_COMPLETION_THREAD_TL.get().isSet()) {
+                // We're running on the task completion thread so off-load to the executor.
+                LOG.trace("Submitting ListenenableFuture Runnable from thread {} to executor {}",
+                        Thread.currentThread().getName(), executor);
+                executor.execute(delegate);
+            } else {
+                // We're not running on the task completion thread so run the delegate inline.
+                LOG.trace("Executing ListenenableFuture Runnable on this thread: {}",
+                        Thread.currentThread().getName());
+                delegate.run();
+            }
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(AsyncNotifyingListenableFutureTask.class);
+
+    /**
+     * ThreadLocal used to detect if the task completion thread is running the listeners.
+     */
+    private static final SettableBooleanThreadLocal ON_TASK_COMPLETION_THREAD_TL = new SettableBooleanThreadLocal();
+
+    /**
+     *  The execution list to hold our listeners.
+     */
+    private final ExecutionList executionList = new ExecutionList();
+
+    private AsyncNotifyingListenableFutureTask(final @NonNull Callable<V> callable) {
+        super(callable);
+    }
+
+    private AsyncNotifyingListenableFutureTask(final @NonNull Runnable runnable, final @Nullable V result) {
+        super(runnable, result);
+    }
+
+    /**
+     * 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> @NonNull AsyncNotifyingListenableFutureTask<V> create(final @NonNull Callable<V> callable,
+            @Nullable final Executor listenerExecutor) {
+        if (listenerExecutor == null) {
+            return new AsyncNotifyingListenableFutureTask<>(callable);
+        }
+        return new DelegatingAsyncNotifyingListenableFutureTask<>(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> @NonNull AsyncNotifyingListenableFutureTask<V> create(final @NonNull Runnable runnable,
+            @Nullable final V result, @Nullable final Executor listenerExecutor) {
+        if (listenerExecutor == null) {
+            return new AsyncNotifyingListenableFutureTask<>(runnable, result);
+        }
+        return new DelegatingAsyncNotifyingListenableFutureTask<>(runnable, result, listenerExecutor);
+    }
+
+    @Override
+    public void addListener(final Runnable listener, final Executor executor) {
+        executionList.add(listener, executor);
+    }
+
+    /**
+     * Remove the state which may have attached to the calling thread. If no state
+     * was attached this method does nothing.
+     */
+    public static void cleanStateForCurrentThread() {
+        ON_TASK_COMPLETION_THREAD_TL.remove();
+    }
+
+    /**
+     * Called by the base class when the future result is set. We invoke our listeners.
+     */
+    @Override
+    protected void done() {
+        final SettableBoolean b = ON_TASK_COMPLETION_THREAD_TL.get();
+        b.set();
+
+        try {
+            executionList.execute();
+        } finally {
+            b.reset();
+        }
+    }
+}
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..1be7500
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * 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 java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.util.concurrent.AbstractListeningExecutorService;
+import com.google.common.util.concurrent.ListenableFuture;
+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 org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * An {@link com.google.common.util.concurrent.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 com.google.common.util.concurrent.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 (i.e. {@code MoreExecutors#sameThreadExecutor}) to {@link ListenableFuture#addListener}.
+ *
+ * <p>
+ * Most commonly, this class would be used in lieu of {@code MoreExecutors#listeningDecorator} 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 @NonNull ExecutorService delegate;
+    private final @Nullable 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(final @NonNull ExecutorService delegate,
+            @Nullable final Executor listenableFutureExecutor) {
+        this.delegate = requireNonNull(delegate);
+        this.listenableFutureExecutor = listenableFutureExecutor;
+    }
+
+    /**
+     * Creates an {@link AsyncNotifyingListenableFutureTask} instance with the listener Executor.
+     *
+     * @param task the Runnable to execute
+     */
+    private <T> @NonNull AsyncNotifyingListenableFutureTask<T> newFutureTask(final @NonNull Runnable task,
+            final T result) {
+        return AsyncNotifyingListenableFutureTask.create(task, result, listenableFutureExecutor);
+    }
+
+    /**
+     * Returns the delegate ExecutorService.
+     */
+    protected @NonNull ExecutorService getDelegate() {
+        return delegate;
+    }
+
+    @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();
+    }
+
+    @Override
+    public void shutdown() {
+        delegate.shutdown();
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+        return delegate.shutdownNow();
+    }
+
+    @Override
+    public void execute(final Runnable command) {
+        delegate.execute(command);
+    }
+
+    @Override
+    public <T> ListenableFuture<T> submit(final Callable<T> task) {
+        final AsyncNotifyingListenableFutureTask<T> futureTask = AsyncNotifyingListenableFutureTask.create(
+            requireNonNull(task), listenableFutureExecutor);
+        delegate.execute(futureTask);
+        return futureTask;
+    }
+
+    @Override
+    public ListenableFuture<?> submit(final Runnable task) {
+        final AsyncNotifyingListenableFutureTask<Void> futureTask = newFutureTask(requireNonNull(task), null);
+        delegate.execute(futureTask);
+        return futureTask;
+    }
+
+    @Override
+    public <T> ListenableFuture<T> submit(final Runnable task, final T result) {
+        final AsyncNotifyingListenableFutureTask<T> futureTask = newFutureTask(requireNonNull(task), result);
+        delegate.execute(futureTask);
+        return futureTask;
+    }
+
+    protected @NonNull ToStringHelper addToStringAttributes(final @NonNull ToStringHelper toStringHelper) {
+        return toStringHelper;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.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..ce8cb03
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * 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 java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+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 org.slf4j.LoggerFactory;
+
+/**
+ * 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 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.
+     * @param loggerIdentity
+     *               the class to use as logger name for logging uncaught exceptions from the threads.
+     */
+    // due to loggerIdentity argument usage
+    @SuppressWarnings("checkstyle:LoggerFactoryClassParameter")
+    public CachedThreadPoolExecutor(final int maximumPoolSize, final int maximumQueueSize, final String threadPrefix,
+            final Class<?> loggerIdentity) {
+        // 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 = requireNonNull(threadPrefix);
+        this.maximumQueueSize = maximumQueueSize;
+
+        setThreadFactory(ThreadFactoryProvider.builder().namePrefix(threadPrefix)
+                .logger(LoggerFactory.getLogger(loggerIdentity)).build().get());
+
+        executorQueue = (ExecutorQueue)super.getQueue();
+
+        rejectedTaskHandler = new RejectedTaskHandler(
+                executorQueue.getBackingQueue(), CountingRejectedExecutionHandler.newAbortPolicy());
+        super.setRejectedExecutionHandler(rejectedTaskHandler);
+    }
+
+    @Override
+    public void setRejectedExecutionHandler(final RejectedExecutionHandler handler) {
+        rejectedTaskHandler.setDelegateRejectedExecutionHandler(requireNonNull(handler));
+    }
+
+    @Override
+    public RejectedExecutionHandler getRejectedExecutionHandler() {
+        return rejectedTaskHandler.getDelegateRejectedExecutionHandler();
+    }
+
+    @Override
+    public BlockingQueue<Runnable> getQueue() {
+        return executorQueue.getBackingQueue();
+    }
+
+    public long getLargestQueueSize() {
+        return executorQueue.getBackingQueue().getLargestQueueSize();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.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;
+
+        @SuppressFBWarnings("SE_BAD_FIELD")
+        // Runnable is not Serializable
+        private final TrackingLinkedBlockingQueue<Runnable> backingQueue;
+
+        ExecutorQueue(final int maxBackingQueueSize) {
+            backingQueue = new TrackingLinkedBlockingQueue<>(maxBackingQueueSize);
+        }
+
+        TrackingLinkedBlockingQueue<Runnable> getBackingQueue() {
+            return backingQueue;
+        }
+
+        @Override
+        public Runnable poll(final long timeout, final 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 volatile RejectedExecutionHandler delegateRejectedExecutionHandler;
+
+        RejectedTaskHandler(final LinkedBlockingQueue<Runnable> backingQueue,
+                             final RejectedExecutionHandler delegateRejectedExecutionHandler) {
+            this.backingQueue = backingQueue;
+            this.delegateRejectedExecutionHandler = delegateRejectedExecutionHandler;
+        }
+
+        void setDelegateRejectedExecutionHandler(
+                final RejectedExecutionHandler delegateRejectedExecutionHandler) {
+            this.delegateRejectedExecutionHandler = delegateRejectedExecutionHandler;
+        }
+
+        RejectedExecutionHandler getDelegateRejectedExecutionHandler() {
+            return delegateRejectedExecutionHandler;
+        }
+
+        @Override
+        public void rejectedExecution(final Runnable task, final ThreadPoolExecutor executor) {
+            if (executor.isShutdown()) {
+                throw new RejectedExecutionException("Executor has been shutdown.");
+            }
+
+            if (!backingQueue.offer(task)) {
+                delegateRejectedExecutionHandler.rejectedExecution(task, executor);
+            }
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CountingRejectedExecutionHandler.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CountingRejectedExecutionHandler.java
new file mode 100644 (file)
index 0000000..61adf52
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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 java.util.Objects.requireNonNull;
+
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.LongAdder;
+import org.opendaylight.yangtools.util.ExecutorServiceUtil;
+
+/**
+ * A RejectedExecutionHandler that delegates to a backing RejectedExecutionHandler and counts the
+ * number of rejected tasks.
+ *
+ * @author Thomas Pantelis
+ */
+public class CountingRejectedExecutionHandler implements RejectedExecutionHandler {
+    private final LongAdder rejectedTaskCounter = new LongAdder();
+    private final RejectedExecutionHandler delegate;
+
+    /**
+     * Constructor.
+     *
+     * @param delegate the backing RejectedExecutionHandler.
+     */
+    public CountingRejectedExecutionHandler(final RejectedExecutionHandler delegate) {
+        this.delegate = requireNonNull(delegate);
+    }
+
+    @Override
+    public void rejectedExecution(final Runnable task, final ThreadPoolExecutor executor) {
+        rejectedTaskCounter.increment();
+        delegate.rejectedExecution(task, executor);
+    }
+
+    /**
+     * Returns the rejected task count.
+     */
+    public long getRejectedTaskCount() {
+        return rejectedTaskCounter.sum();
+    }
+
+    /**
+     * Returns a counting handler for rejected tasks that runs the rejected task directly in the
+     * calling thread of the execute method, unless the executor has been shut down, in which case
+     * the task is discarded.
+     */
+    public static CountingRejectedExecutionHandler newCallerRunsPolicy() {
+        return new CountingRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+    }
+
+    /**
+     * Returns a counting handler for rejected tasks that throws a RejectedExecutionException.
+     */
+    public static CountingRejectedExecutionHandler newAbortPolicy() {
+        return new CountingRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
+    }
+
+    /**
+     * Returns a counting handler for rejected tasks that blocks on the
+     * {@link ThreadPoolExecutor}'s backing queue until it can add the task to the queue.
+     */
+    public static CountingRejectedExecutionHandler newCallerWaitsPolicy() {
+        return new CountingRejectedExecutionHandler(ExecutorServiceUtil.waitInQueueExecutionHandler());
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorService.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorService.java
new file mode 100644 (file)
index 0000000..de4a91e
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+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 java.util.function.Supplier;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * An implementation of ListeningExecutorService that attempts to detect deadlock scenarios that could occur if clients
+ * invoke the returned Future's <code>get</code> methods synchronously.
+ *
+ * <p>Deadlock scenarios are most apt to occur with a backing single-threaded executor where setting of the Future's
+ * result is executed on the single thread. Here's a scenario:
+ * <ul>
+ * <li>Client code is currently executing in an executor's single thread.</li>
+ * <li>The client submits another task to the same executor.</li>
+ * <li>The client calls <code>get()</code> synchronously on the returned Future</li>
+ * </ul>
+ * The second submitted task will never execute since the single thread is currently executing the client code which
+ * is blocked waiting for the submitted task to complete. Thus, deadlock has occurred.
+ *
+ * <p>This class prevents this scenario via the use of a ThreadLocal variable. When a task is invoked, the ThreadLocal
+ * is set and, when a task completes, the ThreadLocal is cleared. Futures returned from this class override
+ * the {@code get} methods to check if the ThreadLocal is set. If it is, an ExecutionException is thrown with a custom
+ * cause.
+ *
+ * <p>Note that the ThreadLocal is not removed automatically, so some state may be left hanging off of threads which
+ * have encountered this class. If you need to clean that state up, use {@link #cleanStateForCurrentThread()}.
+ *
+ * @author Thomas Pantelis
+ * @author Robert Varga
+ */
+public class DeadlockDetectingListeningExecutorService extends AsyncNotifyingListeningExecutorService {
+    /*
+     * We cannot use a static field simply because our API contract allows nesting, which means some
+     * tasks may be submitted to underlay and some to overlay service -- and the two cases need to
+     * be discerned reliably.
+     */
+    private final @NonNull SettableBooleanThreadLocal deadlockDetector = new SettableBooleanThreadLocal();
+    private final @NonNull Supplier<Exception> deadlockExceptionFunction;
+
+    /**
+     * Constructor.
+     *
+     * @param delegate the backing ExecutorService.
+     * @param deadlockExceptionSupplier Supplier that returns an Exception instance to set as the
+     *             cause of the ExecutionException when a deadlock is detected.
+     */
+    public DeadlockDetectingListeningExecutorService(final @NonNull ExecutorService delegate,
+            final @NonNull Supplier<Exception> deadlockExceptionSupplier) {
+        this(delegate, deadlockExceptionSupplier, null);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param delegate the backing ExecutorService.
+     * @param deadlockExceptionSupplier Supplier that returns an Exception instance to set as the
+     *             cause of the ExecutionException when a deadlock is detected.
+     * @param listenableFutureExecutor the executor used to run listener callbacks asynchronously.
+     *             If null, no executor is used.
+     */
+    public DeadlockDetectingListeningExecutorService(final @NonNull ExecutorService delegate,
+            @NonNull final Supplier<Exception> deadlockExceptionSupplier,
+            @Nullable final Executor listenableFutureExecutor) {
+        super(delegate, listenableFutureExecutor);
+        this.deadlockExceptionFunction = requireNonNull(deadlockExceptionSupplier);
+    }
+
+    @Override
+    public void execute(final Runnable command) {
+        getDelegate().execute(wrapRunnable(command));
+    }
+
+    @Override
+    public <T> ListenableFuture<T> submit(final Callable<T> task) {
+        return wrapListenableFuture(super.submit(wrapCallable(task)));
+    }
+
+    @Override
+    public ListenableFuture<?> submit(final Runnable task) {
+        return wrapListenableFuture(super.submit(wrapRunnable(task)));
+    }
+
+    @Override
+    public <T> ListenableFuture<T> submit(final Runnable task, final T result) {
+        return wrapListenableFuture(super.submit(wrapRunnable(task), result));
+    }
+
+    /**
+     * Remove the state this instance may have attached to the calling thread. If no state
+     * was attached this method does nothing.
+     */
+    public void cleanStateForCurrentThread() {
+        deadlockDetector.remove();
+    }
+
+    private @NonNull SettableBoolean primeDetector() {
+        final SettableBoolean b = deadlockDetector.get();
+        checkState(!b.isSet(), "Detector for {} has already been primed", this);
+        b.set();
+        return b;
+    }
+
+    private @NonNull Runnable wrapRunnable(final @Nullable Runnable task) {
+        requireNonNull(task);
+        return () -> {
+            final SettableBoolean b = primeDetector();
+            try {
+                task.run();
+            } finally {
+                b.reset();
+            }
+        };
+    }
+
+    private <T> @NonNull Callable<T> wrapCallable(final @NonNull Callable<T> task) {
+        requireNonNull(task);
+        return () -> {
+            final SettableBoolean b = primeDetector();
+            try {
+                return task.call();
+            } finally {
+                b.reset();
+            }
+        };
+    }
+
+    private <T> @NonNull ListenableFuture<T> wrapListenableFuture(final @NonNull 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.
+         */
+        return new SimpleForwardingListenableFuture<T>(delegate) {
+            @Override
+            public T get() throws InterruptedException, ExecutionException {
+                checkDeadLockDetectorTL();
+                return super.get();
+            }
+
+            @Override
+            public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException,
+                    TimeoutException {
+                checkDeadLockDetectorTL();
+                return super.get(timeout, unit);
+            }
+
+            void checkDeadLockDetectorTL() throws ExecutionException {
+                if (deadlockDetector.get().isSet()) {
+                    throw new ExecutionException("A potential deadlock was detected.",
+                            deadlockExceptionFunction.get());
+                }
+            }
+        };
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ExceptionMapper.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ExceptionMapper.java
new file mode 100644 (file)
index 0000000..1bca9e6
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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 java.util.Objects.requireNonNull;
+
+import com.google.common.base.Function;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import org.gaul.modernizer_maven_annotations.SuppressModernizer;
+
+/**
+ * Utility exception mapper which translates an Exception to a specified type of
+ * Exception.
+ *
+ * <p>
+ * This mapper is intended to be used with
+ * {@link com.google.common.util.concurrent.Futures#makeChecked(
+ * com.google.common.util.concurrent.ListenableFuture, Function)}
+ * <ul>
+ * <li>if exception is the specified type or one of its subclasses, it returns
+ * original exception.
+ * <li>if exception is {@link ExecutionException} and the cause is of the
+ * specified type, it returns the cause
+ * <li>otherwise returns an instance of the specified exception type with
+ * original exception as the cause.
+ * </ul>
+ *
+ * @param <X> the exception type
+ * @author Thomas Pantelis
+ */
+@SuppressModernizer
+public abstract class ExceptionMapper<X extends Exception> implements Function<Exception, X> {
+
+    private final Class<X> exceptionType;
+    private final String opName;
+
+    /**
+     * Constructor.
+     *
+     * @param opName the String prefix for exception messages.
+     * @param exceptionType the exception type to which to translate.
+     */
+    public ExceptionMapper(final String opName, final Class<X> exceptionType) {
+        this.exceptionType = requireNonNull(exceptionType);
+        this.opName = requireNonNull(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.
+     *
+     * @param message the message for the new exception.
+     * @param cause the cause for the new exception.
+     *
+     * @return an instance of the exception type.
+     */
+    protected abstract X newWithCause(String message, Throwable cause);
+
+    @Override
+    @SuppressWarnings("unchecked")
+    @SuppressFBWarnings({"BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", "NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE"})
+    public X apply(final Exception input) {
+
+        // If exception is of the specified type,return it.
+        if (exceptionType.isAssignableFrom(input.getClass())) {
+            return (X) input;
+        }
+
+        // If exception is ExecutionException whose cause is of the specified
+        // type, return the cause.
+        if (input instanceof ExecutionException && input.getCause() != null) {
+            if (exceptionType.isAssignableFrom(input.getCause().getClass())) {
+                return (X) input.getCause();
+            }
+            return newWithCause(opName + " execution failed", input.getCause());
+        }
+
+        // Otherwise return an instance of the specified type with the original
+        // cause.
+
+        if (input instanceof InterruptedException) {
+            return newWithCause(opName + " was interupted.", input);
+        }
+
+        if (input instanceof CancellationException) {
+            return newWithCause(opName + " was cancelled.", input);
+        }
+
+        // We really shouldn't get here but need to cover it anyway for completeness.
+        return newWithCause(opName + " encountered an unexpected failure", input);
+    }
+}
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..128fbcc
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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 com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A ThreadPoolExecutor with a specified bounded queue capacity that favors creating new threads
+ * over queuing, as the former is faster.
+ *
+ * <p>See {@link SpecialExecutors#newBoundedFastThreadPool} 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.
+     * @param loggerIdentity
+     *               the class to use as logger name for logging uncaught exceptions from the threads.
+     */
+    public FastThreadPoolExecutor(final int maximumPoolSize, final int maximumQueueSize, final String threadPrefix,
+            final Class<?> loggerIdentity) {
+        this(maximumPoolSize, maximumQueueSize, DEFAULT_IDLE_TIMEOUT_IN_SEC, TimeUnit.SECONDS,
+              threadPrefix, loggerIdentity);
+    }
+
+    /**
+     * 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.
+     * @param loggerIdentity
+     *               the class to use as logger name for logging uncaught exceptions from the threads.
+     */
+    // due to loggerIdentity argument usage
+    @SuppressWarnings("checkstyle:LoggerFactoryClassParameter")
+    public FastThreadPoolExecutor(final int maximumPoolSize, final int maximumQueueSize, final long keepAliveTime,
+            final TimeUnit unit, final String threadPrefix, final Class<?> loggerIdentity) {
+        // 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 TrackingLinkedBlockingQueue<>(maximumQueueSize));
+
+        this.threadPrefix = threadPrefix;
+        this.maximumQueueSize = maximumQueueSize;
+
+        setThreadFactory(ThreadFactoryProvider.builder().namePrefix(threadPrefix)
+                .logger(LoggerFactory.getLogger(loggerIdentity)).build().get());
+
+        if (keepAliveTime > 0) {
+            // Need to specifically configure core threads to timeout.
+            allowCoreThreadTimeOut(true);
+        }
+
+        setRejectedExecutionHandler(CountingRejectedExecutionHandler.newAbortPolicy());
+    }
+
+    public long getLargestQueueSize() {
+        return ((TrackingLinkedBlockingQueue<?>)getQueue()).getLargestQueueSize();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.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("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();
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/FluentFutures.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/FluentFutures.java
new file mode 100644 (file)
index 0000000..c94a74f
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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 java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.Futures;
+import java.util.concurrent.Future;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Utility methods for working with {@link FluentFuture}s. This class provides methods which should really live in
+ * Guava's, for example in {@link Futures}, as the implementations provided by {@link Futures#immediateFuture(Object)}
+ * and others already implement {@link FluentFuture} and so getting a FluentFuture is a matter of pure boiler-plate.
+ *
+ * <p>
+ * {@link #immediateBooleanFluentFuture(boolean)}, {@link #immediateBooleanFluentFuture(Boolean)},
+ * {@link #immediateFalseFluentFuture()}, {@link #immediateTrueFluentFuture()} and {@link #immediateNullFluentFuture()}
+ * provide low-cardinality constants, which are generally useful to reduce allocations.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+@SuppressWarnings("null")
+public final class FluentFutures {
+    private static final FluentFuture<?> CANCELLED_FUTURE = FluentFuture.from(Futures.immediateCancelledFuture());
+    private static final FluentFuture<?> NULL_FUTURE = FluentFuture.from(Futures.immediateFuture(null));
+    private static final FluentFuture<Boolean> FALSE_FUTURE = FluentFuture.from(Futures.immediateFuture(Boolean.FALSE));
+    private static final FluentFuture<Boolean> TRUE_FUTURE = FluentFuture.from(Futures.immediateFuture(Boolean.TRUE));
+
+    private FluentFutures() {
+
+    }
+
+    /**
+     * Return a {@link FluentFuture} which is immediately {@link Future#cancel(boolean)}led.
+     *
+     * @return An immediately-cancelled FluentFuture.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> FluentFuture<T> immediateCancelledFluentFuture() {
+        return (FluentFuture<T>) CANCELLED_FUTURE;
+    }
+
+    /**
+     * Return a {@link FluentFuture} which is immediately failed, reporting specified failure {@code cause}.
+     *
+     * @param cause failure cause
+     * @return An immediately-failed FluentFuture.
+     * @throws NullPointerException if {@code cause} is null
+     */
+    public static <T> FluentFuture<T> immediateFailedFluentFuture(final Throwable cause) {
+        return FluentFuture.from(Futures.immediateFailedFuture(requireNonNull(cause)));
+    }
+
+    /**
+     * Return a {@link FluentFuture} which is immediately completed, reporting specified {@code result}.
+     *
+     * @param result result of the future
+     * @return An immediately-completed FluentFuture.
+     * @throws NullPointerException if {@code result} is null
+     */
+    public static <T> FluentFuture<T> immediateFluentFuture(final T result) {
+        return FluentFuture.from(Futures.immediateFuture(requireNonNull(result)));
+    }
+
+    /**
+     * Return a {@link FluentFuture} which is immediately completed with a {@code null} result.
+     *
+     * @return An immediately-completed FluentFuture.
+     */
+    @SuppressWarnings("unchecked")
+    public static <@Nullable T> FluentFuture<T> immediateNullFluentFuture() {
+        return (FluentFuture<T>) NULL_FUTURE;
+    }
+
+    /**
+     * Return a {@link FluentFuture} which is immediately completed, reporting specified {@code result}.
+     *
+     * @param result boolean result
+     * @return An immediately-completed FluentFuture reporting specified {@code result}
+     * @throws NullPointerException if {@code result} is null
+     */
+    public static FluentFuture<Boolean> immediateBooleanFluentFuture(final Boolean result) {
+        return immediateBooleanFluentFuture(result.booleanValue());
+    }
+
+    /**
+     * Return a {@link FluentFuture} which is immediately completed, reporting specified {@code result}.
+     *
+     * @param result boolean result
+     * @return An immediately-completed FluentFuture reporting specified {@code result}
+     */
+    public static FluentFuture<Boolean> immediateBooleanFluentFuture(final boolean result) {
+        return result ? TRUE_FUTURE : FALSE_FUTURE;
+    }
+
+    /**
+     * Return a {@link FluentFuture} which is immediately completed, reporting specified {@link Boolean#TRUE}.
+     *
+     * @return An immediately-completed FluentFuture reporting {@link Boolean#TRUE}
+     */
+    public static FluentFuture<Boolean> immediateTrueFluentFuture() {
+        return TRUE_FUTURE;
+    }
+
+    /**
+     * Return a {@link FluentFuture} which is immediately completed, reporting specified {@link Boolean#TRUE}.
+     *
+     * @return An immediately-completed FluentFuture reporting {@link Boolean#TRUE}
+     */
+    public static FluentFuture<Boolean> immediateFalseFluentFuture() {
+        return FALSE_FUTURE;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ListenerNotificationQueueStats.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ListenerNotificationQueueStats.java
new file mode 100644 (file)
index 0000000..8ccd079
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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 java.util.Objects.requireNonNull;
+
+import javax.management.ConstructorParameters;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Class used by the {@link QueuedNotificationManager} that contains a snapshot of notification
+ * queue statistics for a listener.
+ *
+ * @author Thomas Pantelis
+ * @see QueuedNotificationManager
+ */
+@NonNullByDefault
+public class ListenerNotificationQueueStats {
+    private final String listenerClassName;
+    private final int currentQueueSize;
+
+    @ConstructorParameters({ "listenerClassName", "currentQueueSize" })
+    public ListenerNotificationQueueStats(final String listenerClassName, final int currentQueueSize) {
+        this.listenerClassName = requireNonNull(listenerClassName);
+        this.currentQueueSize = currentQueueSize;
+    }
+
+    /**
+     * Returns the name of the listener class.
+     */
+    public String getListenerClassName() {
+        return listenerClassName;
+    }
+
+    /**
+     * Returns the current notification queue size.
+     */
+    public int getCurrentQueueSize() {
+        return currentQueueSize;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/LoggingThreadUncaughtExceptionHandler.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/LoggingThreadUncaughtExceptionHandler.java
new file mode 100644 (file)
index 0000000..eec88fa
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017 Red Hat, 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.lang.Thread.UncaughtExceptionHandler;
+import java.util.Objects;
+import org.slf4j.Logger;
+
+/**
+ * Thread's UncaughtExceptionHandler which logs to slf4j.
+ *
+ * @author Michael Vorburger.ch
+ */
+@SuppressWarnings({"checkstyle:LoggerVariableName", "LoggerVariableModifiers"})
+public final class LoggingThreadUncaughtExceptionHandler implements UncaughtExceptionHandler {
+
+    // This class is also available in infrautils (but yangtools cannot depend on infrautils)
+    // as org.opendaylight.infrautils.utils.concurrent.LoggingThreadUncaughtExceptionHandler
+
+    /**
+     * Factory method to obtain an instance of this bound to the passed slf4j Logger.
+     */
+    public static UncaughtExceptionHandler toLogger(Logger logger) {
+        return new LoggingThreadUncaughtExceptionHandler(logger);
+    }
+
+    private final Logger logger;
+
+    private LoggingThreadUncaughtExceptionHandler(Logger logger) {
+        this.logger = Objects.requireNonNull(logger, "logger");
+    }
+
+    @Override
+    public void uncaughtException(Thread thread, Throwable throwable) {
+        logger.error("Thread terminated due to uncaught exception: {}", thread.getName(), throwable);
+    }
+}
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..531adfc
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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);
+
+    /**
+     * 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);
+
+}
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..dd0bcb7
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
+import org.checkerframework.checker.lock.qual.GuardedBy;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.ForwardingIdentityObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 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 final class QueuedNotificationManager<L, N> implements NotificationManager<L, N> {
+    @FunctionalInterface
+    public interface BatchedInvoker<L, N> {
+        /**
+         * Called to invoke a listener with a notification.
+         *
+         * @param listener the listener to invoke
+         * @param notifications notifications to send
+         */
+        void invokeListener(@NonNull L listener, @NonNull ImmutableList<N> notifications);
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(QueuedNotificationManager.class);
+
+    /**
+     * Caps the maximum number of attempts to offer notification to a particular listener.  Each
+     * attempt window is 1 minute, so an offer times out after roughly 10 minutes.
+     */
+    private static final int MAX_NOTIFICATION_OFFER_MINUTES = 10;
+    private static final long GIVE_UP_NANOS = TimeUnit.MINUTES.toNanos(MAX_NOTIFICATION_OFFER_MINUTES);
+    private static final long TASK_WAIT_NANOS = TimeUnit.MILLISECONDS.toNanos(10);
+
+    /**
+     * 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 final ConcurrentMap<ForwardingIdentityObject<L>, NotificationTask> listenerCache =
+            new ConcurrentHashMap<>();
+    private final @NonNull QueuedNotificationManagerMXBean mxBean = new QueuedNotificationManagerMXBeanImpl(this);
+    private final @NonNull BatchedInvoker<L, N> listenerInvoker;
+    private final @NonNull Executor executor;
+    private final @NonNull String name;
+    private final int maxQueueCapacity;
+
+    private QueuedNotificationManager(final @NonNull Executor executor,
+            final @NonNull BatchedInvoker<L, N> listenerInvoker, final int maxQueueCapacity,
+            final @NonNull String name) {
+        checkArgument(maxQueueCapacity > 0, "Invalid maxQueueCapacity %s must be > 0", maxQueueCapacity);
+        this.executor = requireNonNull(executor);
+        this.listenerInvoker = requireNonNull(listenerInvoker);
+        this.maxQueueCapacity = maxQueueCapacity;
+        this.name = requireNonNull(name);
+    }
+
+    /**
+     * Create a new notification manager.
+     *
+     * @param executor the {@link Executor} to use for notification tasks
+     * @param listenerInvoker the {@link BatchedInvoker} to use for invoking listeners
+     * @param maxQueueCapacity the capacity of each listener queue
+     * @param name the name of this instance for logging info
+     */
+    public static <L, N> QueuedNotificationManager<L, N> create(final @NonNull Executor executor,
+            final@NonNull  BatchedInvoker<L, N> listenerInvoker, final int maxQueueCapacity,
+            final @NonNull String name) {
+        return new QueuedNotificationManager<>(executor, listenerInvoker, maxQueueCapacity, name);
+    }
+
+    /**
+     * Returns the maximum listener queue capacity.
+     */
+    public int getMaxQueueCapacity() {
+        return maxQueueCapacity;
+    }
+
+    /**
+     * Return an {@link QueuedNotificationManagerMXBean} tied to this instance.
+     *
+     * @return An QueuedNotificationManagerMXBean object.
+     */
+    public @NonNull QueuedNotificationManagerMXBean getMXBean() {
+        return mxBean;
+    }
+
+    /**
+     * Returns the {@link Executor} to used for notification tasks.
+     */
+    public @NonNull Executor getExecutor() {
+        return executor;
+    }
+
+    /* (non-Javadoc)
+     * @see org.opendaylight.yangtools.util.concurrent.NotificationManager#addNotification(L, N)
+     */
+    @Override
+    public void submitNotification(final L listener, final N notification) {
+        if (notification != null) {
+            submitNotifications(listener, Collections.singletonList(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) {
+
+        if (notifications == null || listener == null) {
+            return;
+        }
+
+        LOG.trace("{}: submitNotifications for listener {}: {}", name, listener, notifications);
+
+        final ForwardingIdentityObject<L> key = ForwardingIdentityObject.of(listener);
+
+        // 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 {
+            Iterator<N> it = notifications.iterator();
+
+            while (true) {
+                NotificationTask task = listenerCache.get(key);
+                if (task == null) {
+                    // No task found, try to insert a new one
+                    final NotificationTask newTask = new NotificationTask(key, it);
+                    task = listenerCache.putIfAbsent(key, newTask);
+                    if (task == null) {
+                        // We were able to put our new task - now submit it to the executor and
+                        // we're done. If it throws a RejectedExecutionException, let that propagate
+                        // to the caller.
+                        runTask(listener, newTask);
+                        break;
+                    }
+
+                    // We have a racing task, hence we can continue, but we need to refresh our iterator from
+                    // the task.
+                    it = newTask.recoverItems();
+                }
+
+                final boolean completed = task.submitNotifications(it);
+                if (!completed) {
+                    // Task is indicating it is exiting before it has consumed all the items and is exiting. Rather
+                    // than spinning on removal, we try to replace it.
+                    final NotificationTask newTask = new NotificationTask(key, it);
+                    if (listenerCache.replace(key, task, newTask)) {
+                        runTask(listener, newTask);
+                        break;
+                    }
+
+                    // We failed to replace the task, hence we need retry. Note we have to recover the items to be
+                    // published from the new task.
+                    it = newTask.recoverItems();
+                    LOG.debug("{}: retrying task queueing for {}", name, listener);
+                    continue;
+                }
+
+                // All notifications have either been delivered or we have timed out and warned about the ones we
+                // have failed to deliver. In any case we are done here.
+                break;
+            }
+        } catch (InterruptedException e) {
+            // We were interrupted trying to offer to the listener's queue. Somebody's probably
+            // telling us to quit.
+            LOG.warn("{}: Interrupted trying to add to {} listener's queue", name, listener);
+        }
+
+        LOG.trace("{}: submitNotifications done for listener {}", name, listener);
+    }
+
+    /**
+     * Returns {@link ListenerNotificationQueueStats} instances for each current listener
+     * notification task in progress.
+     */
+    public List<ListenerNotificationQueueStats> getListenerNotificationQueueStats() {
+        return listenerCache.values().stream().map(t -> new ListenerNotificationQueueStats(t.listenerKey.toString(),
+            t.size())).collect(Collectors.toList());
+    }
+
+    private void runTask(final L listener, final NotificationTask task) {
+        LOG.debug("{}: Submitting NotificationTask for listener {}", name, listener);
+        executor.execute(task);
+    }
+
+    /**
+     * Executor task for a single listener that queues notifications and sends them serially to the
+     * listener.
+     */
+    private class NotificationTask implements Runnable {
+        private final Lock lock = new ReentrantLock();
+        private final Condition notEmpty = lock.newCondition();
+        private final Condition notFull = lock.newCondition();
+        private final @NonNull ForwardingIdentityObject<L> listenerKey;
+
+        @GuardedBy("lock")
+        private final Queue<N> queue = new ArrayDeque<>();
+        @GuardedBy("lock")
+        private boolean exiting;
+
+        NotificationTask(final @NonNull ForwardingIdentityObject<L> listenerKey,
+                final @NonNull Iterator<N> notifications) {
+            this.listenerKey = requireNonNull(listenerKey);
+            while (notifications.hasNext()) {
+                queue.add(notifications.next());
+            }
+        }
+
+        @NonNull Iterator<N> recoverItems() {
+            // This violates @GuardedBy annotation, but is invoked only when the task is not started and will never
+            // get started, hence this is safe.
+            return queue.iterator();
+        }
+
+        int size() {
+            lock.lock();
+            try {
+                return queue.size();
+            } finally {
+                lock.unlock();
+            }
+        }
+
+        boolean submitNotifications(final @NonNull Iterator<N> notifications) throws InterruptedException {
+            final long start = System.nanoTime();
+            final long deadline = start + GIVE_UP_NANOS;
+
+            lock.lock();
+            try {
+                // Lock may have blocked for some time, we need to take that into account. We may have exceedded
+                // the deadline, but that is unlikely and even in that case we can make some progress without further
+                // blocking.
+                long canWait = deadline - System.nanoTime();
+
+                while (true) {
+                    // Check the exiting 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 (exiting) {
+                        return false;
+                    }
+
+                    final int avail = maxQueueCapacity - queue.size();
+                    if (avail <= 0) {
+                        if (canWait <= 0) {
+                            LOG.warn("{}: Failed to offer notifications {} to the queue for listener {}. Exceeded"
+                                + "maximum allowable time of {} minutes; the listener is likely in an unrecoverable"
+                                + "state (deadlock or endless loop). ", name, ImmutableList.copyOf(notifications),
+                                listenerKey, MAX_NOTIFICATION_OFFER_MINUTES);
+                            return true;
+                        }
+
+                        canWait = notFull.awaitNanos(canWait);
+                        continue;
+                    }
+
+                    for (int i = 0; i < avail; ++i) {
+                        if (!notifications.hasNext()) {
+                            notEmpty.signal();
+                            return true;
+                        }
+
+                        queue.add(notifications.next());
+                    }
+                }
+            } finally {
+                lock.unlock();
+            }
+        }
+
+        @GuardedBy("lock")
+        private boolean waitForQueue() {
+            long timeout = TASK_WAIT_NANOS;
+
+            while (queue.isEmpty()) {
+                if (timeout <= 0) {
+                    return false;
+                }
+
+                try {
+                    timeout = notEmpty.awaitNanos(timeout);
+                } 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);
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        @Override
+        public void run() {
+            try {
+                // Loop until we've dispatched all the notifications in the queue.
+                while (true) {
+                    final @NonNull ImmutableList<N> notifications;
+
+                    lock.lock();
+                    try {
+                        if (!waitForQueue()) {
+                            exiting = true;
+                            break;
+                        }
+
+                        // Splice the entire queue
+                        notifications = ImmutableList.copyOf(queue);
+                        queue.clear();
+
+                        notFull.signalAll();
+                    } finally {
+                        lock.unlock();
+                    }
+
+                    invokeListener(notifications);
+                }
+            } finally {
+                // We're exiting, gracefully or not - either way make sure we always remove
+                // ourselves from the cache.
+                listenerCache.remove(listenerKey, this);
+            }
+        }
+
+        @SuppressWarnings("checkstyle:illegalCatch")
+        private void invokeListener(final @NonNull ImmutableList<N> notifications) {
+            LOG.debug("{}: Invoking listener {} with notification: {}", name, listenerKey, notifications);
+            try {
+                listenerInvoker.invokeListener(listenerKey.getDelegate(), notifications);
+            } catch (Exception e) {
+                // We'll let a RuntimeException from the listener slide and keep sending any remaining notifications.
+                LOG.error("{}: Error notifying listener {} with {}", name, listenerKey, notifications, e);
+            }
+        }
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManagerMXBean.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManagerMXBean.java
new file mode 100644 (file)
index 0000000..7f9259e
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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 javax.management.MXBean;
+
+/**
+ * MXBean interface for {@link QueuedNotificationManager} statistic metrics.
+ *
+ * @author Thomas Pantelis
+ */
+@MXBean
+public interface QueuedNotificationManagerMXBean {
+    /**
+     * Returns a list of stat instances for each current listener notification task in progress.
+     */
+    List<ListenerNotificationQueueStats> getCurrentListenerQueueStats();
+
+    /**
+     * Returns the configured maximum listener queue size.
+     */
+    int getMaxListenerQueueSize();
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManagerMXBeanImpl.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManagerMXBeanImpl.java
new file mode 100644 (file)
index 0000000..c9a2c2d
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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 java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+final class QueuedNotificationManagerMXBeanImpl implements QueuedNotificationManagerMXBean {
+    private final QueuedNotificationManager<?, ?> manager;
+
+    QueuedNotificationManagerMXBeanImpl(final QueuedNotificationManager<?, ?> manager) {
+        this.manager = requireNonNull(manager);
+    }
+
+    /**
+     * Returns a list of stat instances for each current listener notification task in progress.
+     */
+    @Override
+    public List<ListenerNotificationQueueStats> getCurrentListenerQueueStats() {
+        return manager.getListenerNotificationQueueStats();
+    }
+
+    /**
+     * Returns the configured maximum listener queue size.
+     */
+    @Override
+    public int getMaxListenerQueueSize() {
+        return manager.getMaxQueueCapacity();
+    }
+}
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..ae043f1
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014 Robert Varga. 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.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) {
+        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/SettableBoolean.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SettableBoolean.java
new file mode 100644 (file)
index 0000000..0d584af
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util.concurrent;
+
+/**
+ * Simple container encapsulating a boolean flag, which can be toggled. It starts
+ * off in the reset state.
+ */
+final class SettableBoolean {
+    private boolean value = false;
+
+    /**
+     * Set the flag to its initial (false) state.
+     */
+    public void reset() {
+        value = false;
+    }
+
+    /**
+     * Set the flag.
+     */
+    public void set() {
+        value = true;
+    }
+
+    /**
+     * Query the flag.
+     *
+     * @return True if the flag has been set since instantiation or last {@link #reset()}.
+     */
+    public boolean isSet() {
+        return value;
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SettableBooleanThreadLocal.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SettableBooleanThreadLocal.java
new file mode 100644 (file)
index 0000000..8826f99
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util.concurrent;
+
+/**
+ * A reusable {@link ThreadLocal} which returns a {@link SettableBoolean}.
+ */
+final class SettableBooleanThreadLocal extends ThreadLocal<SettableBoolean> {
+    @Override
+    protected SettableBoolean initialValue() {
+        return new SettableBoolean();
+    }
+
+    @Override
+    public void set(final SettableBoolean value) {
+        throw new UnsupportedOperationException("Resetting the value is not supported");
+    }
+}
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..d00dab1
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * 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.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.
+     * @param loggerIdentity
+     *               the class to use as logger name for logging uncaught exceptions from the threads.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBoundedFastThreadPool(int maximumPoolSize,
+            int maximumQueueSize, String threadPrefix, Class<?> loggerIdentity) {
+        return new FastThreadPoolExecutor(maximumPoolSize, maximumQueueSize, threadPrefix, loggerIdentity);
+    }
+
+    /**
+     * 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.
+     * @param loggerIdentity
+     *               the class to use as logger name for logging uncaught exceptions from the threads.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBlockingBoundedFastThreadPool(int maximumPoolSize,
+            int maximumQueueSize, String threadPrefix, Class<?> loggerIdentity) {
+
+        FastThreadPoolExecutor executor = new FastThreadPoolExecutor(maximumPoolSize, maximumQueueSize, threadPrefix,
+                loggerIdentity);
+        executor.setRejectedExecutionHandler(CountingRejectedExecutionHandler.newCallerRunsPolicy());
+        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, Class<?> loggerIdentity) {
+        return new CachedThreadPoolExecutor(maximumPoolSize, maximumQueueSize, threadPrefix, loggerIdentity);
+    }
+
+    /**
+     * 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, Class<?> loggerIdentity) {
+
+        CachedThreadPoolExecutor executor = new CachedThreadPoolExecutor(maximumPoolSize, maximumQueueSize,
+                threadPrefix, loggerIdentity);
+        executor.setRejectedExecutionHandler(CountingRejectedExecutionHandler.newCallerRunsPolicy());
+        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.
+     * @param loggerIdentity
+     *               the class to use as logger name for logging uncaught exceptions from the threads.
+     * @return a new ExecutorService with the specified configuration.
+     */
+    public static ExecutorService newBoundedSingleThreadExecutor(int maximumQueueSize,
+            String threadPrefix, Class<?> loggerIdentity) {
+        return new FastThreadPoolExecutor(1, maximumQueueSize, Long.MAX_VALUE, TimeUnit.SECONDS,
+                threadPrefix, loggerIdentity);
+    }
+
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ThreadFactoryProvider.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ThreadFactoryProvider.java
new file mode 100644 (file)
index 0000000..077e827
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017 Red Hat, 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 com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.Optional;
+import java.util.concurrent.ThreadFactory;
+import org.immutables.value.Value;
+import org.slf4j.Logger;
+
+/**
+ * Builder for {@link ThreadFactory}. Easier to use than the
+ * {@link ThreadFactoryBuilder}, because it enforces setting all required
+ * properties through a staged builder.
+ *
+ * @author Michael Vorburger.ch
+ */
+@Value.Immutable
+@Value.Style(stagedBuilder = true)
+public abstract class ThreadFactoryProvider {
+
+    // This class is also available in infrautils (but yangtools cannot depend on infrautils)
+    // as org.opendaylight.infrautils.utils.concurrent.ThreadFactoryProvider
+
+    public static ImmutableThreadFactoryProvider.NamePrefixBuildStage builder() {
+        return ImmutableThreadFactoryProvider.builder();
+    }
+
+    /**
+     * Prefix for threads from this factory. For example, "rpc-pool", to create
+     * "rpc-pool-1/2/3" named threads. Note that this is a prefix, not a format,
+     * so you pass just "rpc-pool" instead of e.g. "rpc-pool-%d".
+     */
+    @Value.Parameter public abstract String namePrefix();
+
+    /**
+     * Logger used to log uncaught exceptions from new threads created via this factory.
+     */
+    @Value.Parameter public abstract Logger logger();
+
+    /**
+     * Priority for new threads from this factory.
+     */
+    @Value.Parameter public abstract Optional<Integer> priority();
+
+    /**
+     * Daemon or not for new threads created via this factory.
+     * <b>NB: Defaults to true.</b>
+     */
+    @Value.Default public boolean daemon() {
+        return true;
+    }
+
+    public ThreadFactory get() {
+        ThreadFactoryBuilder guavaBuilder = new ThreadFactoryBuilder();
+        guavaBuilder.setNameFormat(namePrefix() + "-%d");
+        guavaBuilder.setUncaughtExceptionHandler(LoggingThreadUncaughtExceptionHandler.toLogger(logger()));
+        guavaBuilder.setDaemon(daemon());
+        priority().ifPresent(guavaBuilder::setPriority);
+        logger().info("ThreadFactory created: {}", namePrefix());
+        return guavaBuilder.build();
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueue.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueue.java
new file mode 100644 (file)
index 0000000..966dae7
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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 com.google.common.annotations.Beta;
+import java.util.Collection;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * A {@link LinkedBlockingQueue} that tracks the largest queue size for debugging.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <E> the element t.ype
+ */
+public class TrackingLinkedBlockingQueue<E> extends LinkedBlockingQueue<E> {
+    private static final long serialVersionUID = 1L;
+
+    @SuppressWarnings("rawtypes")
+    private static final AtomicIntegerFieldUpdater<TrackingLinkedBlockingQueue> LARGEST_QUEUE_SIZE_UPDATER
+        = AtomicIntegerFieldUpdater.newUpdater(TrackingLinkedBlockingQueue.class, "largestQueueSize");
+
+    /**
+     * Holds largestQueueSize, this long field should be only accessed
+     * using {@link #LARGEST_QUEUE_SIZE_UPDATER}.
+     */
+    private volatile int largestQueueSize = 0;
+
+    /**
+     * See {@link LinkedBlockingQueue#LinkedBlockingQueue()}.
+     */
+    public TrackingLinkedBlockingQueue() {
+    }
+
+    /**
+     * See {@link LinkedBlockingQueue#LinkedBlockingQueue(Collection)}.
+     */
+    @SuppressWarnings("checkstyle:parameterName")
+    public TrackingLinkedBlockingQueue(final @NonNull Collection<? extends E> c) {
+        super(c);
+    }
+
+    /**
+     * See {@link LinkedBlockingQueue#LinkedBlockingQueue(int)}.
+     */
+    public TrackingLinkedBlockingQueue(final int capacity) {
+        super(capacity);
+    }
+
+    /**
+     * Returns the largest queue size.
+     *
+     * <p>FIXME: the this return will be changed to int in a future release.
+     */
+    @Beta
+    public long getLargestQueueSize() {
+        return largestQueueSize;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean offer(final E e, final long timeout, final TimeUnit unit) throws InterruptedException {
+        if (super.offer(e, timeout, unit)) {
+            updateLargestQueueSize();
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean offer(final E e) {
+        if (super.offer(e)) {
+            updateLargestQueueSize();
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public void put(final E e) throws InterruptedException {
+        super.put(e);
+        updateLargestQueueSize();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean add(final E e) {
+        boolean result = super.add(e);
+        updateLargestQueueSize();
+        return result;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean addAll(final Collection<? extends E> c) {
+        try {
+            return super.addAll(c);
+        } finally {
+            updateLargestQueueSize();
+        }
+    }
+
+    private void updateLargestQueueSize() {
+        final int size = size();
+
+        int largest;
+        do {
+            largest = largestQueueSize;
+        } while (size > largest && !LARGEST_QUEUE_SIZE_UPDATER.weakCompareAndSet(this, largest, size));
+    }
+}
diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/xml/UntrustedXML.java b/common/util/src/main/java/org/opendaylight/yangtools/util/xml/UntrustedXML.java
new file mode 100644 (file)
index 0000000..1eea944
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2016 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.xml;
+
+import com.google.common.annotations.Beta;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.function.Supplier;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.ClassLoaderUtils;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+
+/**
+ * Set of utility methods for instantiating parser that deal with untrusted XML sources.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class UntrustedXML {
+    private static final @NonNull DocumentBuilderFactory DBF;
+
+    static {
+        final DocumentBuilderFactory f = getLimited(DocumentBuilderFactory::newInstance);
+        f.setCoalescing(true);
+        f.setExpandEntityReferences(false);
+        f.setIgnoringElementContentWhitespace(true);
+        f.setIgnoringComments(true);
+        f.setNamespaceAware(true);
+        f.setXIncludeAware(false);
+        try {
+            f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+            f.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+            f.setFeature("http://xml.org/sax/features/external-general-entities", false);
+            f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+        } catch (final ParserConfigurationException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+        DBF = f;
+    }
+
+    private static final SAXParserFactory SPF;
+
+    static {
+        final SAXParserFactory f = getLimited(SAXParserFactory::newInstance);
+        f.setNamespaceAware(true);
+        f.setXIncludeAware(false);
+        try {
+            f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+            f.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+            f.setFeature("http://xml.org/sax/features/external-general-entities", false);
+            f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+        } catch (final SAXNotRecognizedException | SAXNotSupportedException | ParserConfigurationException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+
+        SPF = f;
+    }
+
+    private static final XMLInputFactory XIF;
+
+    static {
+        final XMLInputFactory f = getLimited(XMLInputFactory::newInstance);
+        f.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
+        f.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.TRUE);
+        f.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
+        f.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
+
+        XIF = f;
+    }
+
+    private UntrustedXML() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Create a new {@link DocumentBuilder} for dealing with untrusted XML data. This method is equivalent to
+     * {@link DocumentBuilderFactory#newDocumentBuilder()}, except it does not throw a checked exception.
+     *
+     * @return A new DocumentBuilder
+     * @throws UnsupportedOperationException if the runtime fails to instantiate a good enough builder
+     */
+    public static @NonNull DocumentBuilder newDocumentBuilder() {
+        try {
+            return DBF.newDocumentBuilder();
+        } catch (ParserConfigurationException e) {
+            throw new UnsupportedOperationException("Failed to instantiate a DocumentBuilder", e);
+        }
+    }
+
+    /**
+     * Create a new {@link SAXParser} for dealing with untrusted XML data. This method is equivalent to
+     * {@link SAXParserFactory#newSAXParser()}, except it does not throw a checked exception.
+     *
+     * @return A new SAXParser
+     * @throws UnsupportedOperationException if the runtime fails to instantiate a good enough builder
+     */
+    public static @NonNull SAXParser newSAXParser() {
+        try {
+            return SPF.newSAXParser();
+        } catch (ParserConfigurationException | SAXException e) {
+            throw new UnsupportedOperationException("Failed to instantiate a SAXParser", e);
+        }
+    }
+
+    /**
+     * Create a new {@link XMLStreamReader} for dealing with untrusted XML data. This method is equivalent to
+     * {@link XMLInputFactory#createXMLStreamReader(InputStream)}.
+     *
+     * @return A new XMLStreamReader
+     * @throws XMLStreamException when the underlying factory throws it
+     */
+    public static @NonNull XMLStreamReader createXMLStreamReader(final InputStream stream) throws XMLStreamException {
+        return XIF.createXMLStreamReader(stream);
+    }
+
+    /**
+     * Create a new {@link XMLStreamReader} for dealing with untrusted XML data. This method is equivalent to
+     * {@link XMLInputFactory#createXMLStreamReader(InputStream, String)}, except it takes an explict charset argument.
+     *
+     * @return A new XMLStreamReader
+     * @throws XMLStreamException when the underlying factory throws it
+     */
+    public static @NonNull XMLStreamReader createXMLStreamReader(final InputStream stream, final Charset charset)
+            throws XMLStreamException {
+        return XIF.createXMLStreamReader(stream, charset.name());
+    }
+
+    /**
+     * Create a new {@link XMLStreamReader} for dealing with untrusted XML data. This method is equivalent to
+     * {@link XMLInputFactory#createXMLStreamReader(Reader)}.
+     *
+     * @return A new XMLStreamReader
+     * @throws XMLStreamException when the underlying factory throws it
+     */
+    public static @NonNull XMLStreamReader createXMLStreamReader(final Reader reader) throws XMLStreamException {
+        return XIF.createXMLStreamReader(reader);
+    }
+
+    private static <T> T getLimited(final @NonNull Supplier<T> supplier) {
+        final ClassLoader loader = UntrustedXML.class.getClassLoader();
+        return loader == null ? supplier.get() : ClassLoaderUtils.getWithClassLoader(loader, supplier);
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/ConstantArrayCollectionTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/ConstantArrayCollectionTest.java
new file mode 100644 (file)
index 0000000..c33c9cb
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Test;
+
+public class ConstantArrayCollectionTest {
+    private static final String[] ARRAY = new String[] { "a", "bb", "ccc" };
+    private static final Collection<String> REF = ImmutableList.copyOf(ARRAY);
+
+    private static Collection<String> create() {
+        return new ConstantArrayCollection<>(ARRAY.clone());
+    }
+
+    @Test
+    public void testToString() {
+        // Empty
+        assertEquals(Collections.emptySet().toString(), new ConstantArrayCollection<>(new Object[0]).toString());
+
+        // Normal
+        assertEquals(REF.toString(), create().toString());
+    }
+
+    @Test
+    public void testEquals() {
+        final Collection<?> c = create();
+
+        assertTrue(c.containsAll(REF));
+        assertTrue(REF.containsAll(c));
+        assertTrue(Iterables.elementsEqual(REF, c));
+    }
+
+    @Test
+    public void testSimpleOperations() {
+        final Collection<?> c = create();
+
+        assertEquals(ARRAY.length, c.size());
+        assertFalse(c.isEmpty());
+        assertTrue(c.contains("ccc"));
+        assertFalse(c.contains(""));
+        assertFalse(c.contains(1));
+
+        assertTrue(c.containsAll(Collections.emptyList()));
+        assertFalse(c.containsAll(Collections.singleton("")));
+        assertFalse(c.containsAll(Collections.singleton(1)));
+    }
+
+    @Test
+    public void testProtection() {
+        final Collection<?> c = create();
+
+        try {
+            c.add(null);
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            c.remove(null);
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            c.addAll(null);
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            c.removeAll(null);
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            c.retainAll(null);
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            c.clear();
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/DurationStatisticsTrackerTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/DurationStatisticsTrackerTest.java
new file mode 100644 (file)
index 0000000..0cc3eeb
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for DurationStatsTracker.
+ *
+ * @author Thomas Pantelis
+ */
+public class DurationStatisticsTrackerTest {
+
+    @Test
+    public void test() {
+
+        DurationStatisticsTracker tracker = DurationStatisticsTracker.createConcurrent();
+
+        tracker.addDuration(10000);
+        assertEquals("getTotalDurations", 1, tracker.getTotalDurations());
+        assertEquals("getAverageDuration", 10000.0, tracker.getAverageDuration(), 0.1);
+        assertEquals("getLongestDuration", 10000, tracker.getLongestDuration());
+        assertEquals("getShortestDuration", 10000, tracker.getShortestDuration());
+
+        tracker.addDuration(30000);
+        assertEquals("getTotalDurations", 2, tracker.getTotalDurations());
+        assertEquals("getAverageDuration", 20000.0, tracker.getAverageDuration(), 0.1);
+        assertEquals("getLongestDuration", 30000, tracker.getLongestDuration());
+        assertEquals("getShortestDuration", 10000, tracker.getShortestDuration());
+
+        verifyDisplayableString("getDisplayableAverageDuration",
+                tracker.getDisplayableAverageDuration(), "20.0");
+        verifyDisplayableString("getDisplayableLongestDuration",
+                tracker.getDisplayableLongestDuration(), "30.0");
+        verifyDisplayableString("getDisplayableShortestDuration",
+                tracker.getDisplayableShortestDuration(), "10.0");
+
+        tracker.addDuration(10000);
+        assertEquals("getTotalDurations", 3, tracker.getTotalDurations());
+        assertEquals("getAverageDuration", 16666.0, tracker.getAverageDuration(), 1.0);
+        assertEquals("getLongestDuration", 30000, tracker.getLongestDuration());
+        assertEquals("getShortestDuration", 10000, tracker.getShortestDuration());
+
+        tracker.addDuration(5000);
+        assertEquals("getTotalDurations", 4, tracker.getTotalDurations());
+        assertEquals("getAverageDuration", 13750.0, tracker.getAverageDuration(), 1.0);
+        assertEquals("getLongestDuration", 30000, tracker.getLongestDuration());
+        assertEquals("getShortestDuration", 5000, tracker.getShortestDuration());
+
+        tracker.reset();
+        assertEquals("getTotalDurations", 0, tracker.getTotalDurations());
+        assertEquals("getAverageDuration", 0.0, tracker.getAverageDuration(), 0.1);
+        assertEquals("getLongestDuration", 0, tracker.getLongestDuration());
+        assertEquals("getShortestDuration", 0, tracker.getShortestDuration());
+
+        tracker.addDuration(10000);
+        assertEquals("getTotalDurations", 1, tracker.getTotalDurations());
+        assertEquals("getAverageDuration", 10000.0, tracker.getAverageDuration(), 0.1);
+        assertEquals("getLongestDuration", 10000, tracker.getLongestDuration());
+        assertEquals("getShortestDuration", 10000, tracker.getShortestDuration());
+    }
+
+    private static void verifyDisplayableString(final String name, final String actual, final String expPrefix) {
+        assertTrue(name + " starts with " + expPrefix + ". Actual: " + actual,
+                actual.startsWith(expPrefix));
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/EmptyDequeTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/EmptyDequeTest.java
new file mode 100644 (file)
index 0000000..fafe79d
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+import java.util.NoSuchElementException;
+import org.junit.Test;
+
+public class EmptyDequeTest {
+
+    @Test
+    public void testEmptyDeque() {
+        final EmptyDeque<?> deque = EmptyDeque.instance();
+        assertFalse(deque.offer(null));
+        assertFalse(deque.offerFirst(null));
+        assertFalse(deque.offerLast(null));
+        assertNull(deque.peek());
+        assertNull(deque.peekFirst());
+        assertNull(deque.peekLast());
+        assertNull(deque.poll());
+        assertNull(deque.pollFirst());
+        assertNull(deque.pollLast());
+
+        assertEquals(0, deque.size());
+        assertFalse(deque.iterator().hasNext());
+        assertFalse(deque.descendingIterator().hasNext());
+        assertEquals(0L, deque.spliterator().estimateSize());
+
+        final Object[] a = deque.toArray();
+        assertEquals(0, a.length);
+        assertSame(a, deque.toArray());
+        assertSame(a, deque.toArray(a));
+
+        assertFalse(deque.removeFirstOccurrence(null));
+        assertFalse(deque.removeLastOccurrence(null));
+
+        try {
+            deque.push(null);
+            fail();
+        } catch (IllegalStateException e) {
+            // expeced
+        }
+        try {
+            deque.addFirst(null);
+            fail();
+        } catch (IllegalStateException e) {
+            // expeced
+        }
+        try {
+            deque.addLast(null);
+            fail();
+        } catch (IllegalStateException e) {
+            // expeced
+        }
+
+        try {
+            deque.getFirst();
+            fail();
+        } catch (NoSuchElementException e) {
+            // expeced
+        }
+        try {
+            deque.getLast();
+            fail();
+        } catch (NoSuchElementException e) {
+            // expeced
+        }
+        try {
+            deque.pop();
+            fail();
+        } catch (NoSuchElementException e) {
+            // expeced
+        }
+        try {
+            deque.remove();
+            fail();
+        } catch (NoSuchElementException e) {
+            // expeced
+        }
+        try {
+            deque.removeFirst();
+            fail();
+        } catch (NoSuchElementException e) {
+            // expeced
+        }
+        try {
+            deque.removeLast();
+            fail();
+        } catch (NoSuchElementException e) {
+            // expeced
+        }
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/EvenMoreObjectsTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/EvenMoreObjectsTest.java
new file mode 100644 (file)
index 0000000..022e784
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 Red Hat, 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 org.junit.Assert.assertTrue;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.testing.EqualsTester;
+import java.util.Objects;
+import org.junit.Test;
+
+public class EvenMoreObjectsTest {
+
+    @Test
+    public void thingPassesEqualsTester() {
+        new EqualsTester()
+                .addEqualityGroup(new Thing("hello", 123), new Thing("hello", 123))
+                .addEqualityGroup(new Thing("hoi", 123), new Thing("hoi", 123))
+                .addEqualityGroup(new Thing("hoi", null))
+                .addEqualityGroup(new Thing(null, null))
+                .testEquals();
+    }
+
+    @Test
+    public void nullEqualsNull() {
+        assertTrue(EvenMoreObjects.equalsHelper(null, null, (one, another) -> Boolean.TRUE));
+    }
+
+    private static class Thing {
+        String name;
+        Integer age;
+
+        @Override
+        public boolean equals(final Object obj) {
+            return EvenMoreObjects.equalsHelper(this, obj,
+                (one, another) -> Objects.equals(one.name, another.name) && Objects.equals(one.age, another.age));
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(name, age);
+        }
+
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this).add("name", name).add("age", age).toString();
+        }
+
+        Thing(final String name, final Integer age) {
+            this.name = name;
+            this.age = age;
+        }
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/HashCodeBuilderTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/HashCodeBuilderTest.java
new file mode 100644 (file)
index 0000000..4863357
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.util;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class HashCodeBuilderTest {
+
+    @Test
+    public void testAllMethodsOfHashCodeBuilder() {
+        final HashCodeBuilder<String> builder = new HashCodeBuilder<>();
+        assertEquals("Default hash code should be '1'.", 1, builder.build().intValue());
+
+        int nextHashCode = HashCodeBuilder.nextHashCode(1, "test");
+        assertEquals("Next hash code should be '3556529'.", 3556529, nextHashCode);
+
+        builder.addArgument("another test");
+        assertEquals("Updated internal hash code should be '700442706'.", -700442706, builder.build().intValue());
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/ImmutableMapTemplateTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/ImmutableMapTemplateTest.java
new file mode 100644 (file)
index 0000000..97caa8f
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Test;
+
+public class ImmutableMapTemplateTest {
+    private static final String FOO = "foo";
+    private static final String BAR = "bar";
+    private static final String BAZ = "baz";
+    private static final Set<String> ONE_KEYSET = ImmutableSet.of(FOO);
+    private static final Set<String> TWO_KEYSET = ImmutableSet.of(FOO, BAR);
+
+    @Test
+    public void testEmpty() {
+        ImmutableMapTemplate<?> template;
+        try {
+            template = ImmutableMapTemplate.ordered(ImmutableList.of());
+            fail("Returned template " + template);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            template = ImmutableMapTemplate.unordered(ImmutableList.of());
+            fail("Returned template " + template);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testOneKeyTemplate() {
+        assertOne(ImmutableMapTemplate.ordered(ONE_KEYSET), SharedSingletonMap.Ordered.class);
+        assertOne(ImmutableMapTemplate.unordered(ONE_KEYSET), SharedSingletonMap.Unordered.class);
+    }
+
+    @Test
+    public void testTwoKeyTemplate() {
+        assertTwo(ImmutableMapTemplate.ordered(TWO_KEYSET), ImmutableOffsetMap.Ordered.class);
+        assertTwo(ImmutableMapTemplate.unordered(TWO_KEYSET), ImmutableOffsetMap.Unordered.class);
+    }
+
+    private static void assertOne(final ImmutableMapTemplate<String> template, final Class<?> mapClass) {
+        assertEquals(ONE_KEYSET, template.keySet());
+        assertEquals(mapClass.getSimpleName() + "{keySet=[foo]}", template.toString());
+
+        // Successful instantiation
+        Map<String, String> map = template.instantiateWithValues(BAR);
+        assertTrue(mapClass.isInstance(map));
+        assertEquals(ImmutableMap.of(FOO, BAR), map);
+        assertEquals("{foo=bar}", map.toString());
+
+        map = template.instantiateTransformed(ImmutableMap.of(FOO, BAR), (key, value) -> key);
+        assertTrue(mapClass.isInstance(map));
+        assertEquals(ImmutableMap.of(FOO, FOO), map);
+        assertEquals("{foo=foo}", map.toString());
+
+        // Null transformation
+        try {
+            map = template.instantiateTransformed(ImmutableMap.of(FOO, BAR), (key, value) -> null);
+            fail("Returned map " + map);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        // Empty input
+        try {
+            map = template.instantiateWithValues();
+            fail("Returned map " + map);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            map = template.instantiateTransformed(ImmutableMap.of(), (key, value) -> key);
+            fail("Returned map " + map);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        // Two-item input
+        try {
+            map = template.instantiateWithValues(FOO, BAR);
+            fail("Returned map " + map);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            map = template.instantiateTransformed(ImmutableMap.of(FOO, FOO, BAR, BAR), (key, value) -> key);
+            fail("Returned map " + map);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        // Mismatched input
+        try {
+            map = template.instantiateTransformed(ImmutableMap.of(BAR, FOO), (key, value) -> key);
+            fail("Returned map " + map);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    private static void assertTwo(final ImmutableMapTemplate<String> template, final Class<?> mapClass) {
+        assertEquals(TWO_KEYSET, template.keySet());
+        assertEquals(mapClass.getSimpleName() + "{offsets={foo=0, bar=1}}", template.toString());
+
+        // Successful instantiation
+        Map<String, String> map = template.instantiateWithValues(BAR, FOO);
+        assertTrue(mapClass.isInstance(map));
+        assertEquals(ImmutableMap.of(FOO, BAR, BAR, FOO), map);
+        assertEquals("{foo=bar, bar=foo}", map.toString());
+
+        map = template.instantiateTransformed(ImmutableMap.of(FOO, BAR, BAR, FOO), (key, value) -> key);
+        assertTrue(mapClass.isInstance(map));
+        assertEquals(ImmutableMap.of(FOO, FOO, BAR, BAR), map);
+        assertEquals("{foo=foo, bar=bar}", map.toString());
+
+        // Empty input
+        try {
+            map = template.instantiateWithValues();
+            fail("Returned map " + map);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            map = template.instantiateTransformed(ImmutableMap.of(), (key, value) -> key);
+            fail("Returned map " + map);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        // One-item input
+        try {
+            map = template.instantiateWithValues(FOO);
+            fail("Returned map " + map);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        try {
+            map = template.instantiateTransformed(ImmutableMap.of(FOO, BAR), (key, value) -> key);
+            fail("Returned map " + map);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        // Mismatched input
+        try {
+            map = template.instantiateTransformed(ImmutableMap.of(FOO, BAR, BAZ, FOO), (key, value) -> key);
+            fail("Returned map " + map);
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/LazyCollectionsTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/LazyCollectionsTest.java
new file mode 100644 (file)
index 0000000..66af2db
--- /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.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+
+public class LazyCollectionsTest {
+
+    @Test
+    public void testLazyAddMethod() {
+        final List<Integer> list = new ArrayList<>();
+        List<Integer> anotherList = LazyCollections.lazyAdd(list, 5);
+        assertEquals(1, anotherList.size());
+
+        anotherList = LazyCollections.lazyAdd(anotherList, 4);
+        assertEquals(2, anotherList.size());
+
+        anotherList = LazyCollections.lazyAdd(anotherList, 3);
+        assertEquals(3, anotherList.size());
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/ListenerRegistryTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/ListenerRegistryTest.java
new file mode 100644 (file)
index 0000000..a5996bb
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.EventListener;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+public class ListenerRegistryTest {
+
+    private TestEventListener testEventListener;
+    private ExtendedTestEventListener extendedTestEventListener;
+    private ListenerRegistry<TestEventListener> registry;
+
+    @Rule
+    public ExpectedException expException = ExpectedException.none();
+
+    @Before
+    public void init() {
+        testEventListener = new TestEventListener() {};
+        extendedTestEventListener = new ExtendedTestEventListener() {};
+        registry = ListenerRegistry.create();
+    }
+
+    @Test
+    public void testCreateNewInstance() {
+        assertNotNull("Intance of listener registry should not be null.", registry);
+    }
+
+    @Test
+    public void tetGetListenersMethod() {
+        assertEquals("Listener registry should have any listeners.", ImmutableSet.of(), registry.getRegistrations());
+    }
+
+    @Test
+    public void testRegisterMethod() {
+        final ListenerRegistration<ExtendedTestEventListener> listenerRegistration = registry.register(
+            extendedTestEventListener);
+        assertEquals("Listeners should be the same.", extendedTestEventListener, listenerRegistration.getInstance());
+    }
+
+    interface TestEventListener extends EventListener {
+
+    }
+
+    interface ExtendedTestEventListener extends TestEventListener {
+
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/MapAdaptorTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/MapAdaptorTest.java
new file mode 100644 (file)
index 0000000..3b0cbb7
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MapAdaptorTest {
+    private MapAdaptor adaptor;
+
+    @Before
+    public void setUp() {
+        adaptor = MapAdaptor.getInstance(true, 10, 5);
+    }
+
+    @Test
+    public void testTreeToEmpty() {
+        final Map<String, String> input = new TreeMap<>();
+
+        // Converts the input into a hashmap;
+        final Map<?, ?> snap = adaptor.takeSnapshot(input);
+        assertNotSame(input, snap);
+        assertTrue(snap instanceof HashMap);
+
+        final Map<?, ?> opt1 = adaptor.optimize(input);
+        assertSame(ImmutableMap.of(), opt1);
+
+        final Map<?, ?> opt2 = adaptor.optimize(snap);
+        assertSame(ImmutableMap.of(), opt2);
+    }
+
+    @Test
+    public void testTreeToSingleton() {
+        final Map<String, String> input = new TreeMap<>();
+        input.put("a", "b");
+
+        final Map<?, ?> snap = adaptor.takeSnapshot(input);
+        assertNotSame(input, snap);
+        assertTrue(snap instanceof HashMap);
+        assertEquals(input, snap);
+
+        final Map<?, ?> opt1 = adaptor.optimize(input);
+        assertNotSame(input, opt1);
+        assertEquals(input, opt1);
+        assertEquals(Collections.singletonMap(null, null).getClass(), opt1.getClass());
+        final Map<?, ?> snap1 = adaptor.takeSnapshot(opt1);
+        assertTrue(snap1 instanceof HashMap);
+        assertEquals(input, snap1);
+
+        final Map<?, ?> opt2 = adaptor.optimize(snap);
+        assertNotSame(snap, opt2);
+        assertEquals(input, opt2);
+        assertEquals(Collections.singletonMap(null, null).getClass(), opt2.getClass());
+
+        final Map<?, ?> snap2 = adaptor.takeSnapshot(opt2);
+        assertNotSame(opt2, snap2);
+        assertTrue(snap2 instanceof HashMap);
+        assertEquals(input, snap2);
+    }
+
+    @Test
+    public void testTreeToTrie() {
+        final Map<String, String> input = new TreeMap<>();
+        for (char c = 'a'; c <= 'z'; ++c) {
+            final String s = String.valueOf(c);
+            input.put(s, s);
+        }
+
+        final Map<String, String> snap = adaptor.takeSnapshot(input);
+        assertTrue(snap instanceof HashMap);
+        assertEquals(input, snap);
+
+        final Map<String, String> opt1 = adaptor.optimize(input);
+        assertEquals(input, opt1);
+        assertEquals(ReadOnlyTrieMap.class, opt1.getClass());
+
+        final Map<String, String> snap2 = adaptor.takeSnapshot(opt1);
+        assertTrue(snap2 instanceof ReadWriteTrieMap);
+        assertEquals(opt1, snap2);
+        assertEquals(26, snap2.size());
+
+        // snap2 and snap3 are independent
+        final Map<String, String> snap3 = adaptor.takeSnapshot(opt1);
+
+        snap2.remove("a");
+        assertEquals(25, snap2.size());
+        assertEquals(26, snap3.size());
+
+        snap3.remove("b");
+        snap3.remove("c");
+        assertEquals(25, snap2.size());
+        assertEquals(24, snap3.size());
+
+        snap2.put("foo", "foo");
+        snap2.put("bar", "baz");
+        snap3.put("bar", "baz");
+        assertEquals(27, snap2.size());
+        assertEquals(25, snap3.size());
+    }
+
+    @Test
+    public void testTrieToHash() {
+        final Map<String, String> input = new TreeMap<>();
+        for (char c = 'a'; c <= 'k'; ++c) {
+            final String s = String.valueOf(c);
+            input.put(s, s);
+        }
+
+        // Translated to read-only
+        final Map<String, String> opt1 = adaptor.optimize(input);
+        assertEquals(input, opt1);
+        assertEquals(ReadOnlyTrieMap.class, opt1.getClass());
+        assertEquals(11, opt1.size());
+
+        // 11 elements -- should retain TrieMap
+        final Map<String, String> snap1 = adaptor.takeSnapshot(opt1);
+        assertEquals(ReadWriteTrieMap.class, snap1.getClass());
+        assertEquals(11, snap1.size());
+
+        for (char c = 'e'; c <= 'k'; ++c) {
+            final String s = String.valueOf(c);
+            snap1.remove(s);
+        }
+
+        // 4 elements: should revert to HashMap
+        assertEquals(4, snap1.size());
+
+        final Map<String, String> opt2 = adaptor.optimize(snap1);
+        assertEquals(snap1, opt2);
+        assertEquals(HashMap.class, opt2.getClass());
+        assertEquals(4, opt2.size());
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/OffsetMapTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/OffsetMapTest.java
new file mode 100644 (file)
index 0000000..bc93d2c
--- /dev/null
@@ -0,0 +1,768 @@
+/*
+ * Copyright (c) 2015 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+
+public class OffsetMapTest {
+    private final Map<String, String> twoEntryMap = ImmutableMap.of("k1", "v1", "k2", "v2");
+    private final Map<String, String> threeEntryMap = ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3");
+
+    private ImmutableOffsetMap<String, String> createMap() {
+        return (ImmutableOffsetMap<String, String>) ImmutableOffsetMap.orderedCopyOf(twoEntryMap);
+    }
+
+    private ImmutableOffsetMap<String, String> unorderedMap() {
+        return (ImmutableOffsetMap<String, String>) ImmutableOffsetMap.unorderedCopyOf(twoEntryMap);
+    }
+
+    @Before
+    public void setup() {
+        OffsetMapCache.invalidateCache();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWrongImmutableConstruction() {
+        new ImmutableOffsetMap.Ordered<>(ImmutableMap.of(), new String[1]);
+    }
+
+    @Test
+    public void testCopyEmptyMap() {
+        final Map<String, String> source = Collections.emptyMap();
+        final Map<String, String> result = ImmutableOffsetMap.orderedCopyOf(source);
+
+        assertEquals(source, result);
+        assertTrue(result instanceof ImmutableMap);
+    }
+
+    @Test
+    public void testCopySingletonMap() {
+        final Map<String, String> source = Collections.singletonMap("a", "b");
+        final Map<String, String> result = ImmutableOffsetMap.orderedCopyOf(source);
+
+        assertEquals(source, result);
+        assertTrue(result instanceof SharedSingletonMap);
+    }
+
+    @Test
+    public void testCopyMap() {
+        final ImmutableOffsetMap<String, String> map = createMap();
+
+        // Equality in both directions
+        assertEquals(twoEntryMap, map);
+        assertEquals(map, twoEntryMap);
+
+        // hashcode has to match
+        assertEquals(twoEntryMap.hashCode(), map.hashCode());
+
+        // Iterator order needs to be preserved
+        assertTrue(Iterators.elementsEqual(twoEntryMap.entrySet().iterator(), map.entrySet().iterator()));
+
+        // Should result in the same object
+        assertSame(map, ImmutableOffsetMap.orderedCopyOf(map));
+
+        final Map<String, String> mutable = map.toModifiableMap();
+        final Map<String, String> copy = ImmutableOffsetMap.orderedCopyOf(mutable);
+
+        assertEquals(mutable, copy);
+        assertEquals(map, copy);
+        assertNotSame(mutable, copy);
+        assertNotSame(map, copy);
+    }
+
+    @Test
+    public void testImmutableSimpleEquals() {
+        final Map<String, String> map = createMap();
+
+        assertTrue(map.equals(map));
+        assertFalse(map.equals(null));
+        assertFalse(map.equals("string"));
+    }
+
+    @Test
+    public void testImmutableGet() {
+        final Map<String, String> map = createMap();
+
+        assertEquals("v1", map.get("k1"));
+        assertEquals("v2", map.get("k2"));
+        assertNull(map.get("non-existent"));
+        assertNull(map.get(null));
+    }
+
+    @Test
+    public void testImmutableGuards() {
+        final Map<String, String> map = createMap();
+
+        try {
+            map.values().add("v1");
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            map.values().remove("v1");
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            map.values().clear();
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            final Iterator<String> it = map.values().iterator();
+            it.next();
+            it.remove();
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            map.keySet().add("k1");
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            map.keySet().clear();
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            map.keySet().remove("k1");
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            final Iterator<String> it = map.keySet().iterator();
+            it.next();
+            it.remove();
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            map.entrySet().clear();
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            map.entrySet().add(new SimpleEntry<>("k1", "v1"));
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            map.entrySet().remove(new SimpleEntry<>("k1", "v1"));
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            final Iterator<Entry<String, String>> it = map.entrySet().iterator();
+            it.next();
+            it.remove();
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            map.clear();
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            map.put("k1", "fail");
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            map.putAll(ImmutableMap.of("k1", "fail"));
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+
+        try {
+            map.remove("k1");
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // OK
+        }
+    }
+
+    @Test
+    public void testMutableGet() {
+        final Map<String, String> map = createMap().toModifiableMap();
+
+        map.put("k3", "v3");
+        assertEquals("v1", map.get("k1"));
+        assertEquals("v2", map.get("k2"));
+        assertEquals("v3", map.get("k3"));
+        assertNull(map.get("non-existent"));
+        assertNull(map.get(null));
+    }
+
+    @Test
+    public void testImmutableSize() {
+        final Map<String, String> map = createMap();
+        assertEquals(2, map.size());
+    }
+
+    @Test
+    public void testImmutableIsEmpty() {
+        final Map<String, String> map = createMap();
+        assertFalse(map.isEmpty());
+    }
+
+    @Test
+    public void testImmutableContains() {
+        final Map<String, String> map = createMap();
+        assertTrue(map.containsKey("k1"));
+        assertTrue(map.containsKey("k2"));
+        assertFalse(map.containsKey("non-existent"));
+        assertFalse(map.containsKey(null));
+        assertTrue(map.containsValue("v1"));
+        assertFalse(map.containsValue("non-existent"));
+    }
+
+    @Test
+    public void testImmutableEquals() {
+        final Map<String, String> map = createMap();
+
+        assertFalse(map.equals(threeEntryMap));
+        assertFalse(map.equals(ImmutableMap.of("k1", "v1", "k3", "v3")));
+        assertFalse(map.equals(ImmutableMap.of("k1", "v1", "k2", "different-value")));
+    }
+
+    @Test
+    public void testMutableContains() {
+        final Map<String, String> map = createMap().toModifiableMap();
+        map.put("k3", "v3");
+        assertTrue(map.containsKey("k1"));
+        assertTrue(map.containsKey("k2"));
+        assertTrue(map.containsKey("k3"));
+        assertFalse(map.containsKey("non-existent"));
+        assertFalse(map.containsKey(null));
+    }
+
+    @Test
+    public void testtoModifiableMap() {
+        final ImmutableOffsetMap<String, String> source = createMap();
+        final Map<String, String> result = source.toModifiableMap();
+
+        // The two maps should be equal, but isolated
+        assertTrue(result instanceof MutableOffsetMap);
+        assertEquals(source, result);
+        assertEquals(result, source);
+
+        // Quick test for clearing MutableOffsetMap
+        result.clear();
+        assertEquals(0, result.size());
+        assertEquals(Collections.emptyMap(), result);
+
+        // The two maps should differ now
+        assertFalse(source.equals(result));
+        assertFalse(result.equals(source));
+
+        // The source map should still equal the template
+        assertEquals(twoEntryMap, source);
+        assertEquals(source, twoEntryMap);
+    }
+
+    @Test
+    public void testReusedFields() {
+        final ImmutableOffsetMap<String, String> source = createMap();
+        final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
+
+        // Should not affect the result
+        mutable.remove("non-existent");
+
+        // Resulting map should be equal, but not the same object
+        final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable
+                .toUnmodifiableMap();
+        assertNotSame(source, result);
+        assertEquals(source, result);
+
+        // Internal fields should be reused
+        assertSame(source.offsets(), result.offsets());
+        assertSame(source.objects(), result.objects());
+    }
+
+    @Test
+    public void testReusedOffsets() {
+        final ImmutableOffsetMap<String, String> source = createMap();
+        final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
+
+        mutable.remove("k1");
+        mutable.put("k1", "v1");
+
+        final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable
+                .toUnmodifiableMap();
+        assertTrue(source.equals(result));
+        assertTrue(result.equals(source));
+
+        // Iterator order must not be preserved
+        assertFalse(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
+    }
+
+    @Test
+    public void testReusedOffsetsUnordered() {
+        final ImmutableOffsetMap<String, String> source = unorderedMap();
+        final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
+
+        mutable.remove("k1");
+        mutable.put("k1", "v1");
+
+        final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable
+                .toUnmodifiableMap();
+        assertEquals(source, result);
+
+        // Only offsets should be shared
+        assertSame(source.offsets(), result.offsets());
+        assertNotSame(source.objects(), result.objects());
+
+        // Iterator order needs to be preserved
+        assertTrue(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
+    }
+
+    @Test
+    public void testEmptyMutable() throws CloneNotSupportedException {
+        final MutableOffsetMap<String, String> map = MutableOffsetMap.ordered();
+        assertTrue(map.isEmpty());
+
+        final Map<String, String> other = map.clone();
+        assertEquals(other, map);
+        assertNotSame(other, map);
+    }
+
+    @Test
+    public void testMutableToEmpty() {
+        final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
+
+        mutable.remove("k1");
+        mutable.remove("k2");
+
+        assertTrue(mutable.isEmpty());
+        assertSame(ImmutableMap.of(), mutable.toUnmodifiableMap());
+    }
+
+    @Test
+    public void testMutableToSingleton() {
+        final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
+
+        mutable.remove("k1");
+
+        final Map<String, String> result = mutable.toUnmodifiableMap();
+
+        // Should devolve to a singleton
+        assertTrue(result instanceof SharedSingletonMap);
+        assertEquals(ImmutableMap.of("k2", "v2"), result);
+    }
+
+    @Test
+    public void testMutableToNewSingleton() {
+        final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
+
+        mutable.remove("k1");
+        mutable.put("k3", "v3");
+
+        final Map<String, String> result = mutable.toUnmodifiableMap();
+
+        assertTrue(result instanceof ImmutableOffsetMap);
+        assertEquals(ImmutableMap.of("k2", "v2", "k3", "v3"), result);
+    }
+
+    @Test
+    public void testMutableSize() {
+        final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
+        assertEquals(2, mutable.size());
+
+        mutable.put("k3", "v3");
+        assertEquals(3, mutable.size());
+        mutable.remove("k2");
+        assertEquals(2, mutable.size());
+        mutable.put("k1", "new-v1");
+        assertEquals(2, mutable.size());
+    }
+
+    @Test
+    public void testExpansionWithOrder() {
+        final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
+
+        mutable.remove("k1");
+        mutable.put("k3", "v3");
+        mutable.put("k1", "v1");
+
+        assertEquals(ImmutableMap.of("k1", "v1", "k3", "v3"), mutable.newKeys());
+
+        final Map<String, String> result = mutable.toUnmodifiableMap();
+
+        assertTrue(result instanceof ImmutableOffsetMap);
+        assertEquals(threeEntryMap, result);
+        assertEquals(result, threeEntryMap);
+        assertFalse(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
+    }
+
+    @Test
+    public void testExpansionWithoutOrder() {
+        final MutableOffsetMap<String, String> mutable = unorderedMap().toModifiableMap();
+
+        mutable.remove("k1");
+        mutable.put("k3", "v3");
+        mutable.put("k1", "v1");
+
+        assertEquals(ImmutableMap.of("k3", "v3"), mutable.newKeys());
+
+        final Map<String, String> result = mutable.toUnmodifiableMap();
+
+        assertTrue(result instanceof ImmutableOffsetMap);
+        assertEquals(threeEntryMap, result);
+        assertEquals(result, threeEntryMap);
+        assertTrue(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
+    }
+
+    @Test
+    public void testReplacedValue() {
+        final ImmutableOffsetMap<String, String> source = createMap();
+        final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
+
+        mutable.put("k1", "replaced");
+
+        final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable
+                .toUnmodifiableMap();
+        final Map<String, String> reference = ImmutableMap.of("k1", "replaced", "k2", "v2");
+
+        assertEquals(reference, result);
+        assertEquals(result, reference);
+        assertSame(source.offsets(), result.offsets());
+        assertNotSame(source.objects(), result.objects());
+    }
+
+    @Test
+    public void testCloneableFlipping() throws CloneNotSupportedException {
+        final MutableOffsetMap<String, String> source = createMap().toModifiableMap();
+
+        // Must clone before mutation
+        assertTrue(source.needClone());
+
+        // Non-existent entry, should not clone
+        source.remove("non-existent");
+        assertTrue(source.needClone());
+
+        // Changes the array, should clone
+        source.remove("k1");
+        assertFalse(source.needClone());
+
+        // Create a clone of the map, which shares the array
+        final MutableOffsetMap<String, String> result = source.clone();
+        assertFalse(source.needClone());
+        assertTrue(result.needClone());
+        assertSame(source.array(), result.array());
+
+        // Changes the array, should clone
+        source.put("k1", "v2");
+        assertFalse(source.needClone());
+        assertTrue(result.needClone());
+
+        // Forced copy, no cloning needed, but maps are equal
+        final ImmutableOffsetMap<String, String> immutable = (ImmutableOffsetMap<String, String>) source
+                .toUnmodifiableMap();
+        assertFalse(source.needClone());
+        assertTrue(source.equals(immutable));
+        assertTrue(immutable.equals(source));
+        assertTrue(Iterables.elementsEqual(source.entrySet(), immutable.entrySet()));
+    }
+
+    @Test
+    public void testCloneableFlippingUnordered() throws CloneNotSupportedException {
+        final MutableOffsetMap<String, String> source = unorderedMap().toModifiableMap();
+
+        // Must clone before mutation
+        assertTrue(source.needClone());
+
+        // Non-existent entry, should not clone
+        source.remove("non-existent");
+        assertTrue(source.needClone());
+
+        // Changes the array, should clone
+        source.remove("k1");
+        assertFalse(source.needClone());
+
+        // Create a clone of the map, which shares the array
+        final MutableOffsetMap<String, String> result = source.clone();
+        assertFalse(source.needClone());
+        assertTrue(result.needClone());
+        assertSame(source.array(), result.array());
+
+        // Changes the array, should clone
+        source.put("k1", "v2");
+        assertFalse(source.needClone());
+        assertTrue(result.needClone());
+
+        // Creates a immutable view, which shares the array
+        final ImmutableOffsetMap<String, String> immutable = (ImmutableOffsetMap<String, String>) source
+                .toUnmodifiableMap();
+        assertTrue(source.needClone());
+        assertSame(source.array(), immutable.objects());
+    }
+
+    @Test
+    public void testMutableEntrySet() {
+        final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
+
+        assertTrue(map.entrySet().add(new SimpleEntry<>("k3", "v3")));
+        assertTrue(map.containsKey("k3"));
+        assertEquals("v3", map.get("k3"));
+
+        // null is not an Entry: ignore
+        assertFalse(map.entrySet().remove(null));
+
+        // non-matching value: ignore
+        assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", "other")));
+        assertTrue(map.containsKey("k1"));
+
+        // ignore null values
+        assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", null)));
+        assertTrue(map.containsKey("k1"));
+
+        assertTrue(map.entrySet().remove(new SimpleEntry<>("k1", "v1")));
+        assertFalse(map.containsKey("k1"));
+    }
+
+    private static void assertIteratorBroken(final Iterator<?> it) {
+        try {
+            it.hasNext();
+            fail();
+        } catch (ConcurrentModificationException e) {
+            // OK
+        }
+        try {
+            it.next();
+            fail();
+        } catch (ConcurrentModificationException e) {
+            // OK
+        }
+        try {
+            it.remove();
+            fail();
+        } catch (ConcurrentModificationException e) {
+            // OK
+        }
+    }
+
+    @Test
+    public void testMutableSimpleEquals() {
+        final ImmutableOffsetMap<String, String> source = createMap();
+        final Map<String, String> map = source.toModifiableMap();
+
+        assertTrue(map.equals(map));
+        assertFalse(map.equals(null));
+        assertFalse(map.equals("string"));
+        assertTrue(map.equals(source));
+    }
+
+    @Test
+    public void testMutableSimpleHashCode() {
+        final Map<String, String> map = createMap().toModifiableMap();
+
+        assertEquals(twoEntryMap.hashCode(), map.hashCode());
+    }
+
+    @Test
+    public void testMutableIteratorBasics() {
+        final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
+        final Iterator<Entry<String, String>> it = map.entrySet().iterator();
+
+        // Not advanced, remove should fail
+        try {
+            it.remove();
+            fail();
+        } catch (IllegalStateException e) {
+            // OK
+        }
+
+        assertTrue(it.hasNext());
+        assertEquals("k1", it.next().getKey());
+        assertTrue(it.hasNext());
+        assertEquals("k2", it.next().getKey());
+        assertFalse(it.hasNext());
+
+        // Check end-of-iteration throw
+        try {
+            it.next();
+            fail();
+        } catch (NoSuchElementException e) {
+            // OK
+        }
+    }
+
+    @Test
+    public void testMutableIteratorWithRemove() {
+        final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
+        final Iterator<Entry<String, String>> it = map.entrySet().iterator();
+
+        // Advance one element
+        assertTrue(it.hasNext());
+        assertEquals("k1", it.next().getKey());
+
+        // Remove k1
+        it.remove();
+        assertEquals(1, map.size());
+        assertFalse(map.containsKey("k1"));
+
+        // Iterator should still work
+        assertTrue(it.hasNext());
+        assertEquals("k2", it.next().getKey());
+        assertFalse(it.hasNext());
+    }
+
+    @Test
+    public void testMutableIteratorOffsetReplaceWorks() {
+        final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
+        final Iterator<Entry<String, String>> it = map.entrySet().iterator();
+        it.next();
+
+        map.put("k1", "new-v1");
+        assertTrue(it.hasNext());
+    }
+
+    @Test
+    public void testMutableIteratorNewReplaceWorks() {
+        final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
+        map.put("k3", "v3");
+        final Iterator<Entry<String, String>> it = map.entrySet().iterator();
+        it.next();
+
+        map.put("k3", "new-v3");
+        assertTrue(it.hasNext());
+    }
+
+    @Test
+    public void testMutableIteratorOffsetAddBreaks() {
+        final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
+        map.put("k3", "v3");
+        map.remove("k1");
+
+        final Iterator<Entry<String, String>> it = map.entrySet().iterator();
+        it.next();
+
+        map.put("k1", "new-v1");
+        assertIteratorBroken(it);
+    }
+
+    @Test
+    public void testMutableIteratorNewAddBreaks() {
+        final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
+        final Iterator<Entry<String, String>> it = map.entrySet().iterator();
+        it.next();
+
+        map.put("k3", "v3");
+        assertIteratorBroken(it);
+    }
+
+    @Test
+    public void testMutableIteratorOffsetRemoveBreaks() {
+        final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
+        final Iterator<Entry<String, String>> it = map.entrySet().iterator();
+        it.next();
+
+        map.remove("k1");
+        assertIteratorBroken(it);
+    }
+
+    @Test
+    public void testMutableIteratorNewRemoveBreaks() {
+        final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
+        map.put("k3", "v3");
+        final Iterator<Entry<String, String>> it = map.entrySet().iterator();
+        it.next();
+
+        map.remove("k3");
+        assertIteratorBroken(it);
+    }
+
+    @Test
+    public void testMutableCrossIteratorRemove() {
+        final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
+        final Set<Entry<String, String>> es = map.entrySet();
+        final Iterator<Entry<String, String>> it1 = es.iterator();
+        final Iterator<Entry<String, String>> it2 = es.iterator();
+
+        // Remove k1 via it1
+        it1.next();
+        it2.next();
+        it1.remove();
+        assertEquals(1, map.size());
+
+        // Check it2 was broken
+        assertIteratorBroken(it2);
+    }
+
+    @Test
+    public void testImmutableSerialization() throws IOException, ClassNotFoundException {
+        final Map<String, String> source = createMap();
+
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+            oos.writeObject(source);
+        }
+
+        final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
+        @SuppressWarnings("unchecked")
+        final Map<String, String> result = (Map<String, String>) ois.readObject();
+
+        assertEquals(source, result);
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/PropertyUtilsTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/PropertyUtilsTest.java
new file mode 100644 (file)
index 0000000..8166113
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class PropertyUtilsTest {
+
+    @Test
+    public void testGetIntSystemProperty() {
+        final int testValue = PropertyUtils.getIntSystemProperty("file.separator", 1);
+        assertEquals("Property value should be '1'.", 1, testValue);
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/ReadWriteTrieMapTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/ReadWriteTrieMapTest.java
new file mode 100644 (file)
index 0000000..c78b1d7
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.junit.Test;
+import tech.pantheon.triemap.MutableTrieMap;
+import tech.pantheon.triemap.TrieMap;
+
+public class ReadWriteTrieMapTest {
+
+    @Test
+    public void testMethodsOfReadWriteTrieMap() {
+        final MutableTrieMap<String, String> trieMap = TrieMap.create();
+        trieMap.put("0", "zero");
+        trieMap.put("1", "one");
+
+        final ReadWriteTrieMap<String, String> readWriteTrieMap = new ReadWriteTrieMap<>(trieMap, 5);
+
+        assertNotNull("Object readOnlyTrieMap shouldn't be 'null'.", readWriteTrieMap);
+
+        assertEquals("Size of readOnlyTrieMap should be '5'.", 5, readWriteTrieMap.size());
+        assertFalse("Object readOnlyTrieMap shouldn't be empty.", readWriteTrieMap.isEmpty());
+
+        assertTrue("Object readOnlyTrieMap should have key '0'.", readWriteTrieMap.containsKey("0"));
+        assertTrue("Object readOnlyTrieMap should have value 'zero'.", readWriteTrieMap.containsValue("zero"));
+        assertEquals("Object readOnlyTrieMap should have value 'zero'.", "zero", readWriteTrieMap.get("0"));
+
+        final Map<String, String> rwMap = readWriteTrieMap;
+        rwMap.put("2", "two");
+        rwMap.put("3", "three");
+
+        assertEquals("Removed value from readOnlyTrieMap should be 'one'.", "one", rwMap.remove("1"));
+
+        final Set<String> trieMapKeySet = readWriteTrieMap.keySet();
+        assertEquals("Size of keySet should be '3'.", 3, trieMapKeySet.size());
+
+        final Collection<String> trieMapValues = readWriteTrieMap.values();
+        assertEquals("Size of values should be '3'.", 3, trieMapValues.size());
+
+        assertEquals(convertSetEntryToMap(readWriteTrieMap.entrySet()), trieMap);
+
+        trieMap.put("2", "two");
+        final ReadWriteTrieMap<String, String> readWriteTrieMap2 = new ReadWriteTrieMap<>(trieMap, 4);
+
+        assertNotEquals(readWriteTrieMap, readWriteTrieMap2);
+        assertEquals(readWriteTrieMap.hashCode(), readWriteTrieMap2.hashCode());
+
+        final Map<String, String> readOnlyTrieMap = readWriteTrieMap.toReadOnly();
+        readWriteTrieMap.clear();
+        assertEquals(0, readWriteTrieMap.size());
+        assertEquals(6, readOnlyTrieMap.size());
+    }
+
+    private static Map<String, String> convertSetEntryToMap(final Set<Entry<String, String>> input) {
+        Map<String, String> resultMap = new HashMap<>();
+        for (Entry<String, String> entry : input) {
+            resultMap.put(entry.getKey(), entry.getValue());
+        }
+        return resultMap;
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/SharedSingletonMapTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/SharedSingletonMapTest.java
new file mode 100644 (file)
index 0000000..f47e859
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Test;
+
+public class SharedSingletonMapTest {
+    private static UnmodifiableMapPhase<String, String> create() {
+        return SharedSingletonMap.orderedOf("k1", "v1");
+    }
+
+    @Test
+    public void testSimpleOperations() {
+        final Map<String, String> m = create();
+
+        assertFalse(m.isEmpty());
+        assertEquals(1, m.size());
+
+        assertTrue(m.containsKey("k1"));
+        assertFalse(m.containsKey(null));
+        assertFalse(m.containsKey("v1"));
+
+        assertTrue(m.containsValue("v1"));
+        assertFalse(m.containsValue(null));
+        assertFalse(m.containsValue("k1"));
+
+        assertEquals("v1", m.get("k1"));
+        assertNull(m.get(null));
+        assertNull(m.get("v1"));
+
+        assertFalse(m.equals(null));
+        assertTrue(m.equals(m));
+        assertFalse(m.equals(""));
+
+        final Map<String, String> same = Collections.singletonMap("k1", "v1");
+        assertEquals(same.toString(), m.toString());
+        assertTrue(same.equals(m));
+        assertTrue(m.equals(same));
+        assertEquals(same.entrySet(), m.entrySet());
+        assertEquals(same.values(), m.values());
+
+        // Perform twice to exercise the cache
+        assertEquals(same.hashCode(), m.hashCode());
+        assertEquals(same.hashCode(), m.hashCode());
+
+        assertFalse(m.equals(Collections.singletonMap(null, null)));
+        assertFalse(m.equals(Collections.singletonMap("k1", null)));
+        assertFalse(m.equals(Collections.singletonMap(null, "v1")));
+        assertFalse(m.equals(Collections.singletonMap("k1", "v2")));
+        assertFalse(m.equals(ImmutableMap.of("k1", "v1", "k2", "v2")));
+
+        final Set<String> set = m.keySet();
+        assertTrue(set instanceof SingletonSet);
+        assertTrue(set.contains("k1"));
+    }
+
+    @Test
+    public void testOrderedCopyOf() {
+        final Map<String, String> t = Collections.singletonMap("k1", "v1");
+        final Map<String, String> m = SharedSingletonMap.orderedCopyOf(t);
+        assertTrue(t.equals(m));
+        assertTrue(m.equals(t));
+    }
+
+    @Test
+    public void testUnorderedCopyOf() {
+        final Map<String, String> t = Collections.singletonMap("k1", "v1");
+        final Map<String, String> m = SharedSingletonMap.unorderedCopyOf(t);
+        assertTrue(t.equals(m));
+        assertTrue(m.equals(t));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testEmptyOrderedCopyOf() {
+        SharedSingletonMap.orderedCopyOf(ImmutableMap.of());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testEmptyUnorderedCopyOf() {
+        SharedSingletonMap.unorderedCopyOf(ImmutableMap.of());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testClear() {
+        create().clear();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testPut() {
+        create().put(null, null);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testPutAll() {
+        create().putAll(Collections.singletonMap("", ""));
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemove() {
+        create().remove(null);
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/SingletonSetTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/SingletonSetTest.java
new file mode 100644 (file)
index 0000000..8438554
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Iterator;
+import org.junit.Test;
+
+public class SingletonSetTest {
+    private static final String ELEMENT = "element";
+
+    private static SingletonSet<?> nullSet() {
+        return SingletonSet.of(null);
+    }
+
+    @Test
+    public void testNullSingleton() {
+        final SingletonSet<?> s = nullSet();
+
+        assertFalse(s.isEmpty());
+        assertEquals(1, s.size());
+        assertFalse(s.contains(""));
+        assertTrue(s.contains(null));
+        assertNull(s.getElement());
+        assertEquals(0, s.hashCode());
+        assertTrue(s.equals(Collections.singleton(null)));
+        assertFalse(s.equals(Collections.singleton("")));
+        assertFalse(s.equals(""));
+        assertTrue(s.equals(s));
+        assertFalse(s.equals(null));
+        assertEquals(Collections.singleton(null).toString(), s.toString());
+    }
+
+    @Test
+    public void testRegularSingleton() {
+        final SingletonSet<?> s = SingletonSet.of(ELEMENT);
+
+        assertFalse(s.isEmpty());
+        assertEquals(1, s.size());
+        assertFalse(s.contains(""));
+        assertFalse(s.contains(null));
+        assertTrue(s.contains(ELEMENT));
+
+        assertSame(ELEMENT, s.getElement());
+        assertEquals(ELEMENT.hashCode(), s.hashCode());
+        assertTrue(s.equals(Collections.singleton(ELEMENT)));
+        assertFalse(s.equals(Collections.singleton("")));
+        assertFalse(s.equals(Collections.singleton(null)));
+        assertFalse(s.equals(""));
+        assertTrue(s.equals(s));
+        assertFalse(s.equals(null));
+        assertEquals(Collections.singleton(ELEMENT).toString(), s.toString());
+    }
+
+    @Test
+    public void testIterator() {
+        final SingletonSet<?> s = SingletonSet.of(ELEMENT);
+        final Iterator<?> it = s.iterator();
+
+        assertTrue(it.hasNext());
+        assertSame(ELEMENT, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRejectedAdd() {
+        final SingletonSet<?> s = nullSet();
+        s.add(null);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRejectedAddAll() {
+        final SingletonSet<?> s = nullSet();
+        s.addAll(null);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRejectedClear() {
+        final SingletonSet<?> s = nullSet();
+        s.clear();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRejectedRemove() {
+        final SingletonSet<?> s = nullSet();
+        s.remove(null);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRejectedRemoveAll() {
+        final SingletonSet<?> s = nullSet();
+        s.removeAll(null);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRejectedRetainAll() {
+        final SingletonSet<?> s = nullSet();
+        s.retainAll(null);
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/SynchronizedDurationStatsTrackerTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/SynchronizedDurationStatsTrackerTest.java
new file mode 100644 (file)
index 0000000..050c573
--- /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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+
+public class SynchronizedDurationStatsTrackerTest {
+
+    @Test
+    public void testAllMethodsOfSynchronizedDurationStatsTracker() {
+        final SynchronizedDurationStatsTracker statsTracker = new SynchronizedDurationStatsTracker();
+        statsTracker.addDuration(1000);
+        statsTracker.addDuration(2000);
+        statsTracker.addDuration(3000);
+
+        assertEquals("Shortest recorded duration should be '1000'.", 1000, statsTracker.getShortest().getDuration());
+        assertEquals("Average recorded duration should be '2000'.", 2000, statsTracker.getAverageDuration(), 0.0001);
+        assertEquals("Longest recorded duration should be '3000'.", 3000, statsTracker.getLongest().getDuration());
+        assertEquals("Total recorded duration count should be '3'.", 3, statsTracker.getTotalDurations());
+
+        statsTracker.reset();
+
+        assertNull("Shortest recorded duration should be 'null'.", statsTracker.getShortest());
+        assertEquals("Average recorded duration should be '0'.", 0, statsTracker.getAverageDuration(), 0.0001);
+        assertNull("Longest recorded duration should be '0'.", statsTracker.getLongest());
+        assertEquals("Total recorded duration should be '0'.", 0, statsTracker.getTotalDurations());
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/TopologicalSortTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/TopologicalSortTest.java
new file mode 100644 (file)
index 0000000..37043a1
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.TopologicalSort.Node;
+import org.opendaylight.yangtools.util.TopologicalSort.NodeImpl;
+
+public class TopologicalSortTest {
+
+    @Test(expected = IllegalStateException.class)
+    public void test() {
+        Set<Node> nodes = new HashSet<>();
+
+        NodeImpl node1 = new NodeImpl();
+        nodes.add(node1);
+        NodeImpl node2 = new NodeImpl();
+        nodes.add(node2);
+        NodeImpl node3 = new NodeImpl();
+        nodes.add(node3);
+
+        node1.addEdge(node2);
+        node2.addEdge(node3);
+        node3.addEdge(node1);
+
+        // We expect an IllegalStateException here
+        TopologicalSort.sort(nodes);
+    }
+
+    @Test
+    public void testValidSimple() throws Exception {
+        Set<Node> nodes = new HashSet<>();
+
+        NodeImpl node1 = new NodeImpl();
+        nodes.add(node1);
+        NodeImpl node2 = new NodeImpl();
+        nodes.add(node2);
+        NodeImpl node3 = new NodeImpl();
+        nodes.add(node3);
+        Node node4 = new NodeImpl();
+        nodes.add(node4);
+
+        node1.addEdge(node2);
+        node1.addEdge(node3);
+        node2.addEdge(node4);
+        node3.addEdge(node2);
+
+        List<Node> sorted = TopologicalSort.sort(nodes);
+
+        assertEquals(node4, sorted.get(0));
+        assertEquals(node2, sorted.get(1));
+        assertEquals(node3, sorted.get(2));
+        assertEquals(node1, sorted.get(3));
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/UnmodifiableCollectionTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/UnmodifiableCollectionTest.java
new file mode 100644 (file)
index 0000000..b363701
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.UnmodifiableIterator;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import org.junit.Test;
+
+public class UnmodifiableCollectionTest {
+
+    @Test
+    public void testUnmodifiableCollection() {
+        final List<Integer> immutableTestList = ImmutableList.<Integer>builder()
+                .add(1)
+                .add(2)
+                .add(3)
+                .add(4)
+                .add(5).build();
+
+        final Collection<Integer> testUnmodifiableCollection = UnmodifiableCollection.create(immutableTestList);
+        assertNotNull(testUnmodifiableCollection);
+
+        // Note: this cannot be ImmutableList, because UnmodifiableCollection does recognize it and returns it as is,
+        //       without converting it to an UnmodifiableCollection -- which is not what we want.
+        final List<Integer> testList = Arrays.asList(1, 2, 3, 4, 5);
+        final Collection<Integer> testUnmodifiableCollection2 = UnmodifiableCollection.create(testList);
+
+        final Iterator<Integer> iterator = testUnmodifiableCollection2.iterator();
+        assertNotNull(iterator);
+        assertTrue(iterator instanceof UnmodifiableIterator);
+
+        assertEquals(5, testUnmodifiableCollection2.size());
+
+        assertFalse(testUnmodifiableCollection2.isEmpty());
+
+        assertTrue(testUnmodifiableCollection2.contains(1));
+
+        final Object[] objectArray = testUnmodifiableCollection2.toArray();
+        assertNotNull(objectArray);
+        assertEquals(5, objectArray.length);
+
+        final Integer[] integerArray = testUnmodifiableCollection2.toArray(
+                new Integer[testUnmodifiableCollection2.size()]);
+        assertNotNull(integerArray);
+        assertEquals(5, integerArray.length);
+
+        assertTrue(testUnmodifiableCollection2.containsAll(testUnmodifiableCollection));
+
+        assertEquals("UnmodifiableCollection{" + testList + "}", testUnmodifiableCollection2.toString());
+    }
+}
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..d973cc9
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * 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.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+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;
+
+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.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.ArrayList;
+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 org.opendaylight.yangtools.util.concurrent.CommonTestUtils.Invoker;
+
+/**
+ * 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(final AsyncNotifyingListeningExecutorService executor,
+            final 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(final ListenableFuture<?> future, final CountDownLatch futureNotifiedLatch,
+            final String expListenerThreadPrefix, final AtomicReference<AssertionError> assertError) {
+
+        Futures.addCallback(future, new FutureCallback<Object>() {
+            @Override
+            public void onSuccess(final 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
+            @SuppressWarnings("checkstyle:parameterName")
+            public void onFailure(final Throwable t) {
+                // Shouldn't happen
+                fail("Unexpected failure " + t);
+            }
+        }, MoreExecutors.directExecutor());
+    }
+
+    @Test
+    public void testDelegatedMethods() throws InterruptedException {
+
+        Runnable task = () -> { };
+
+        List<Runnable> taskList = new ArrayList<>();
+
+        ExecutorService mockDelegate = mock(ExecutorService.class);
+        doNothing().when(mockDelegate).execute(task);
+        doNothing().when(mockDelegate).shutdown();
+        doReturn(taskList).when(mockDelegate).shutdownNow();
+        doReturn(Boolean.TRUE).when(mockDelegate).awaitTermination(3, TimeUnit.SECONDS);
+        doReturn(Boolean.TRUE).when(mockDelegate).isShutdown();
+        doReturn(Boolean.TRUE).when(mockDelegate).isTerminated();
+
+        AsyncNotifyingListeningExecutorService executor = new AsyncNotifyingListeningExecutorService(
+                                                                   mockDelegate, null);
+
+        executor.execute(task);
+        executor.shutdown();
+        assertTrue("awaitTermination", executor.awaitTermination(3, TimeUnit.SECONDS));
+        assertSame("shutdownNow", taskList, executor.shutdownNow());
+        assertTrue("isShutdown", executor.isShutdown());
+        assertTrue("isTerminated", 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..b0c5c10
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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 com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.Uninterruptibles;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Some common test utilities.
+ *
+ * @author Thomas Pantelis
+ */
+public final class CommonTestUtils {
+    private CommonTestUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    @FunctionalInterface
+    public interface Invoker {
+        ListenableFuture<?> invokeExecutor(ListeningExecutorService executor, CountDownLatch blockingLatch);
+    }
+
+    public static final Invoker SUBMIT_CALLABLE = (executor, blockingLatch) -> executor.submit(() -> {
+        if (blockingLatch != null) {
+            Uninterruptibles.awaitUninterruptibly(blockingLatch);
+        }
+        return null;
+    });
+
+    public static final Invoker SUBMIT_RUNNABLE = (executor, blockingLatch) -> executor.submit(() -> {
+        if (blockingLatch != null) {
+            Uninterruptibles.awaitUninterruptibly(blockingLatch);
+        }
+    });
+
+    public static final Invoker SUBMIT_RUNNABLE_WITH_RESULT = (executor, blockingLatch) -> executor.submit(() -> {
+        if (blockingLatch != null) {
+            Uninterruptibles.awaitUninterruptibly(blockingLatch);
+        }
+    }, "foo");
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/CountingRejectedExecutionHandlerTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/CountingRejectedExecutionHandlerTest.java
new file mode 100644 (file)
index 0000000..e6dd9f3
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import org.junit.After;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.ExecutorServiceUtil;
+import org.opendaylight.yangtools.util.concurrent.ThreadPoolExecutorTest.Task;
+
+/**
+ * Unit tests for CountingRejectedExecutionHandler.
+ *
+ * @author Thomas Pantelis
+ */
+public class CountingRejectedExecutionHandlerTest {
+
+    private ThreadPoolExecutor executor;
+
+    @After
+    public void tearDown() {
+        if (executor != null) {
+            executor.shutdownNow();
+        }
+    }
+
+    @Test
+    public void testCallerRunsPolicyHandler() throws InterruptedException {
+
+        CountDownLatch tasksRunLatch = new CountDownLatch(1);
+        CountDownLatch blockLatch = new CountDownLatch(1);
+
+        executor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
+                ExecutorServiceUtil.offerFailingBlockingQueue(new LinkedBlockingQueue<>()));
+
+        CountingRejectedExecutionHandler countingHandler = CountingRejectedExecutionHandler.newCallerRunsPolicy();
+        executor.setRejectedExecutionHandler(countingHandler);
+
+        executor.execute(new Task(tasksRunLatch, blockLatch));
+
+        int tasks = 5;
+        for (int i = 0; i < tasks - 1; i++) {
+            executor.execute(new Task(null, null, null, null, 0));
+        }
+
+        assertEquals("getRejectedTaskCount", tasks - 1, countingHandler.getRejectedTaskCount());
+
+        blockLatch.countDown();
+
+        assertTrue("Tasks complete", tasksRunLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testAbortPolicyHandler() throws InterruptedException {
+
+        CountDownLatch tasksRunLatch = new CountDownLatch(1);
+        CountDownLatch blockLatch = new CountDownLatch(1);
+
+        executor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
+                ExecutorServiceUtil.offerFailingBlockingQueue(new LinkedBlockingQueue<>()));
+
+        CountingRejectedExecutionHandler countingHandler = CountingRejectedExecutionHandler.newAbortPolicy();
+        executor.setRejectedExecutionHandler(countingHandler);
+
+        executor.execute(new Task(tasksRunLatch, blockLatch));
+
+        int tasks = 5;
+        for (int i = 0; i < tasks - 1; i++) {
+            try {
+                executor.execute(new Task(null, null, null, null, 0));
+                fail("Expected RejectedExecutionException");
+            } catch (RejectedExecutionException e) {
+                // Expected
+            }
+        }
+
+        assertEquals("getRejectedTaskCount", tasks - 1, countingHandler.getRejectedTaskCount());
+
+        blockLatch.countDown();
+
+        assertTrue("Tasks complete", tasksRunLatch.await(5, TimeUnit.SECONDS));
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorServiceTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/DeadlockDetectingListeningExecutorServiceTest.java
new file mode 100644 (file)
index 0000000..e9962bf
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * 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.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.util.concurrent.AsyncNotifyingListeningExecutorServiceTest.testListenerCallback;
+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;
+
+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.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.concurrent.CommonTestUtils.Invoker;
+
+/**
+ * Unit tests for DeadlockDetectingListeningExecutorService.
+ *
+ * @author Thomas Pantelis
+ */
+public class DeadlockDetectingListeningExecutorServiceTest {
+
+    interface InitialInvoker {
+        void invokeExecutor(ListeningExecutorService executor, Runnable task);
+    }
+
+    static final InitialInvoker SUBMIT = ListeningExecutorService::submit;
+
+    static final InitialInvoker EXECUTE = Executor::execute;
+
+    public static class TestDeadlockException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+    }
+
+    private static final Supplier<Exception> DEADLOCK_EXECUTOR_SUPPLIER = TestDeadlockException::new;
+
+    DeadlockDetectingListeningExecutorService executor;
+
+    @Before
+    public void setup() {
+    }
+
+    @After
+    public void tearDown() {
+        if (executor != null) {
+            executor.shutdownNow();
+        }
+    }
+
+    DeadlockDetectingListeningExecutorService newExecutor() {
+        return new DeadlockDetectingListeningExecutorService(Executors.newSingleThreadExecutor(),
+                DEADLOCK_EXECUTOR_SUPPLIER);
+    }
+
+    @Test
+    public void testBlockingSubmitOffExecutor() throws Exception {
+
+        executor = newExecutor();
+
+        // Test submit with Callable.
+
+        ListenableFuture<String> future = executor.submit(() -> "foo");
+
+        assertEquals("Future result", "foo", future.get(5, TimeUnit.SECONDS));
+
+        // Test submit with Runnable.
+
+        executor.submit(() -> { }).get();
+
+        // Test submit with Runnable and value.
+
+        future = executor.submit(() -> { }, "foo");
+
+        assertEquals("Future result", "foo", future.get(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    @SuppressWarnings("checkstyle:illegalThrows")
+    public void testNonBlockingSubmitOnExecutorThread() throws Throwable {
+
+        executor = newExecutor();
+
+        testNonBlockingSubmitOnExecutorThread(SUBMIT, SUBMIT_CALLABLE);
+        testNonBlockingSubmitOnExecutorThread(SUBMIT, SUBMIT_RUNNABLE);
+        testNonBlockingSubmitOnExecutorThread(SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT);
+
+        testNonBlockingSubmitOnExecutorThread(EXECUTE, SUBMIT_CALLABLE);
+    }
+
+    @SuppressWarnings("checkstyle:illegalThrows")
+    void testNonBlockingSubmitOnExecutorThread(final InitialInvoker initialInvoker, final Invoker invoker)
+            throws Throwable {
+
+        final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
+        final CountDownLatch futureCompletedLatch = new CountDownLatch(1);
+
+        Runnable task = () -> Futures.addCallback(invoker.invokeExecutor(executor, null), new FutureCallback<Object>() {
+            @Override
+            public void onSuccess(final Object result) {
+                futureCompletedLatch.countDown();
+            }
+
+            @Override
+            @SuppressWarnings("checkstyle:parameterName")
+            public void onFailure(final Throwable t) {
+                caughtEx.set(t);
+                futureCompletedLatch.countDown();
+            }
+        }, MoreExecutors.directExecutor());
+
+        initialInvoker.invokeExecutor(executor, task);
+
+        assertTrue("Task did not complete - executor likely deadlocked",
+                futureCompletedLatch.await(5, TimeUnit.SECONDS));
+
+        if (caughtEx.get() != null) {
+            throw caughtEx.get();
+        }
+    }
+
+    @Test
+    public void testBlockingSubmitOnExecutorThread() throws InterruptedException {
+
+        executor = newExecutor();
+
+        testBlockingSubmitOnExecutorThread(SUBMIT, SUBMIT_CALLABLE);
+        testBlockingSubmitOnExecutorThread(SUBMIT, SUBMIT_RUNNABLE);
+        testBlockingSubmitOnExecutorThread(SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT);
+
+        testBlockingSubmitOnExecutorThread(EXECUTE, SUBMIT_CALLABLE);
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    void testBlockingSubmitOnExecutorThread(final InitialInvoker initialInvoker, final Invoker invoker)
+            throws InterruptedException {
+
+        final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        Runnable task = () -> {
+
+            try {
+                invoker.invokeExecutor(executor, null).get();
+            } catch (ExecutionException e) {
+                caughtEx.set(e.getCause());
+            } catch (Throwable e) {
+                caughtEx.set(e);
+            } finally {
+                latch.countDown();
+            }
+        };
+
+        initialInvoker.invokeExecutor(executor, task);
+
+        assertTrue("Task did not complete - executor likely deadlocked", latch.await(5, TimeUnit.SECONDS));
+        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_SUPPLIER, 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..0d5cd77
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * 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.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.Uninterruptibles;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+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 org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager.BatchedInvoker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 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(final int expCount, final int id) {
+            name = "TestListener " + id;
+            actual = Collections.synchronizedList(new ArrayList<>(expCount));
+            reset(expCount);
+        }
+
+        void reset(final int newExpCount) {
+            this.expCount = newExpCount;
+            latch = new CountDownLatch(newExpCount);
+            actual.clear();
+        }
+
+        void onNotification(final Collection<? extends N> data) {
+
+            try {
+                if (sleepTime > 0) {
+                    Uninterruptibles.sleepUninterruptibly(sleepTime, TimeUnit.MILLISECONDS);
+                }
+
+                if (cacheNotifications) {
+                    actual.addAll(data);
+                }
+
+                RuntimeException localRuntimeEx = runtimeEx;
+                if (localRuntimeEx != null) {
+                    runtimeEx = null;
+                    throw localRuntimeEx;
+                }
+
+                Error localJvmError = jvmError;
+                if (localJvmError != null) {
+                    jvmError = null;
+                    throw localJvmError;
+                }
+
+            } finally {
+                data.forEach(action -> latch.countDown());
+            }
+        }
+
+        void verifyNotifications() {
+            boolean done = Uninterruptibles.awaitUninterruptibly(latch, 5, TimeUnit.SECONDS);
+            if (!done) {
+                long actualCount = latch.getCount();
+                fail(name + ": Received " + (expCount - actualCount) + " notifications. Expected " + expCount);
+            }
+        }
+
+        void verifyNotifications(final List<N> expected) {
+            verifyNotifications();
+            assertEquals(name + ": Notifications", 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(final Object obj) {
+            TestListener<?> other = (TestListener<?>) obj;
+            return other != null;
+        }
+    }
+
+    static class TestListener2<N> extends TestListener<N> {
+        TestListener2(final int expCount, final int id) {
+            super(expCount, id);
+        }
+    }
+
+    static class TestListener3<N> extends TestListener<N> {
+        TestListener3(final int expCount, final int id) {
+            super(expCount, id);
+        }
+    }
+
+    static class TestNotifier<N> implements BatchedInvoker<TestListener<N>, N> {
+        @Override
+        public void invokeListener(final TestListener<N> listener, final ImmutableList<N> notifications) {
+            listener.onNotification(notifications);
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(QueuedNotificationManagerTest.class);
+    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 = QueuedNotificationManager.create(queueExecutor,
+                new TestNotifier<>(), 10, "TestMgr");
+
+        int count = 100;
+
+        TestListener<Integer> listener = new TestListener<>(count, 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.emptyList());
+        manager.submitNotifications(listener, null);
+        manager.submitNotification(listener, null);
+
+        Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
+
+        listener.sleepTime = 0;
+
+        List<Integer> expNotifications = new ArrayList<>(count);
+        expNotifications.addAll(Arrays.asList(1, 2, 3, 4, 5, 6));
+        int initialCount = 6;
+        for (int i = 1; i <= count - initialCount; i++) {
+            Integer val = Integer.valueOf(initialCount + i);
+            expNotifications.add(val);
+            manager.submitNotification(listener, val);
+        }
+
+        listener.verifyNotifications(expNotifications);
+    }
+
+    @Test
+    public void testNotificationsWithMultipleListeners() throws InterruptedException {
+
+        int count = 10;
+        queueExecutor = Executors.newFixedThreadPool(count);
+        final ExecutorService stagingExecutor = Executors.newFixedThreadPool(count);
+        final NotificationManager<TestListener<Integer>, Integer> manager = QueuedNotificationManager.create(
+                queueExecutor, new TestNotifier<>(), 5000, "TestMgr");
+
+        final int nNotifications = 100000;
+
+        LOG.info("Testing {} listeners with {} notifications each...", count, nNotifications);
+
+        final Integer[] notifications = new Integer[nNotifications];
+        for (int i = 1; i <= nNotifications; i++) {
+            notifications[i - 1] = Integer.valueOf(i);
+        }
+
+        Stopwatch stopWatch = Stopwatch.createStarted();
+
+        List<TestListener<Integer>> listeners = new ArrayList<>();
+        List<Thread> threads = new ArrayList<>();
+        for (int i = 1; i <= count; i++) {
+            final TestListener<Integer> listener =
+                    i == 2 ? new TestListener2<>(nNotifications, i) :
+                    i == 3 ? new TestListener3<>(nNotifications, i) :
+                            new TestListener<>(nNotifications, i);
+            listeners.add(listener);
+
+            final Thread t = new Thread(() -> {
+                for (int j = 1; j <= nNotifications; j++) {
+                    final Integer n = notifications[j - 1];
+                    stagingExecutor.execute(() -> manager.submitNotification(listener, n));
+                }
+            });
+
+            t.start();
+            threads.add(t);
+
+        }
+
+        try {
+            for (TestListener<Integer> listener: listeners) {
+                listener.verifyNotifications();
+                LOG.info("{} succeeded", listener.name);
+            }
+        } finally {
+            stagingExecutor.shutdownNow();
+        }
+
+        stopWatch.stop();
+
+        LOG.info("Elapsed time: {}", stopWatch);
+        LOG.info("Executor: {}", queueExecutor);
+
+        for (Thread t : threads) {
+            t.join();
+        }
+    }
+
+    @Test(timeout = 10000)
+    public void testNotificationsWithListenerRuntimeEx() {
+
+        queueExecutor = Executors.newFixedThreadPool(1);
+        NotificationManager<TestListener<Integer>, Integer> manager = QueuedNotificationManager.create(queueExecutor,
+            new TestNotifier<>(), 10, "TestMgr");
+
+        TestListener<Integer> listener = new TestListener<>(2, 1);
+        final RuntimeException mockedRuntimeException = new RuntimeException("mock");
+        listener.runtimeEx = mockedRuntimeException;
+
+        manager.submitNotification(listener, 1);
+        manager.submitNotification(listener, 2);
+
+        listener.verifyNotifications();
+        List<Runnable> tasks = queueExecutor.shutdownNow();
+        assertTrue(tasks.isEmpty());
+    }
+
+    @Test(timeout = 10000)
+    public void testNotificationsWithListenerJVMError() {
+
+        final CountDownLatch errorCaughtLatch = new CountDownLatch(1);
+        queueExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>()) {
+            @Override
+            @SuppressWarnings("checkstyle:illegalCatch")
+            public void execute(final Runnable command) {
+                super.execute(() -> {
+                    try {
+                        command.run();
+                    } catch (Error e) {
+                        errorCaughtLatch.countDown();
+                    }
+                });
+            }
+        };
+
+        NotificationManager<TestListener<Integer>, Integer> manager = QueuedNotificationManager.create(queueExecutor,
+                new TestNotifier<>(), 10, "TestMgr");
+
+        TestListener<Integer> listener = new TestListener<>(2, 1);
+        listener.jvmError = mock(Error.class);
+
+        manager.submitNotification(listener, 1);
+
+        assertTrue("JVM Error caught", Uninterruptibles.awaitUninterruptibly(errorCaughtLatch, 5, TimeUnit.SECONDS));
+
+        manager.submitNotification(listener, 2);
+
+        listener.verifyNotifications();
+        List<Runnable> tasks = queueExecutor.shutdownNow();
+        assertTrue(tasks.isEmpty());
+    }
+}
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..2e1aa18
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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 class ReflectiveExceptionMapperTest {
+    static final class NoArgumentCtorException extends Exception {
+        private static final long serialVersionUID = 1L;
+
+        NoArgumentCtorException() {
+        }
+    }
+
+    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;
+
+        FailingCtorException(final String message, final Throwable cause) {
+            throw new IllegalArgumentException("just for test");
+        }
+    }
+
+    public 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..ff760dc
--- /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.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Stopwatch;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tests various ThreadPoolExecutor implementations.
+ *
+ * @author Thomas Pantelis
+ */
+public class ThreadPoolExecutorTest {
+    private static final Logger LOG = LoggerFactory.getLogger(ThreadPoolExecutorTest.class);
+
+    private ExecutorService executor;
+
+    @After
+    public void tearDown() {
+        if (executor != null) {
+            executor.shutdownNow();
+        }
+    }
+
+    @Test
+    public void testFastThreadPoolExecution() throws InterruptedException {
+        testThreadPoolExecution(
+                SpecialExecutors.newBoundedFastThreadPool(50, 100000, "TestPool", getClass()), 100000, "TestPool", 0);
+    }
+
+    @Test(expected = RejectedExecutionException.class)
+    public void testFastThreadPoolRejectingTask() throws InterruptedException {
+        executor = SpecialExecutors.newBoundedFastThreadPool(1, 1, "TestPool", getClass());
+
+        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 InterruptedException {
+        // With a queue capacity of 1, it should block at some point.
+        testThreadPoolExecution(
+                SpecialExecutors.newBlockingBoundedFastThreadPool(2, 1, "TestPool", getClass()), 1000, null, 10);
+    }
+
+    @Test
+    public void testCachedThreadPoolExecution() throws InterruptedException {
+        testThreadPoolExecution(SpecialExecutors.newBoundedCachedThreadPool(10, 100000, "TestPool", getClass()),
+                100000, "TestPool", 0);
+    }
+
+    @Test(expected = RejectedExecutionException.class)
+    public void testCachedThreadRejectingTask() throws InterruptedException {
+        ExecutorService localExecutor = SpecialExecutors.newBoundedCachedThreadPool(1, 1, "TestPool", getClass());
+
+        for (int i = 0; i < 5; i++) {
+            localExecutor.execute(new Task(null, null, null, null, TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS)));
+        }
+    }
+
+    @Test
+    public void testBlockingCachedThreadPoolExecution() throws InterruptedException {
+        testThreadPoolExecution(
+                SpecialExecutors.newBlockingBoundedCachedThreadPool(2, 1, "TestPool", getClass()), 1000, null, 10);
+    }
+
+    void testThreadPoolExecution(final ExecutorService executorToTest, final int numTasksToRun,
+            final String expThreadPrefix, final long taskDelay) throws InterruptedException {
+
+        this.executor = executorToTest;
+
+        LOG.debug("Testing {} with {} tasks.", executorToTest.getClass().getSimpleName(), numTasksToRun);
+
+        final CountDownLatch tasksRunLatch = new CountDownLatch(numTasksToRun);
+        final ConcurrentMap<Thread, AtomicLong> taskCountPerThread = new ConcurrentHashMap<>();
+        final AtomicReference<AssertionError> threadError = new AtomicReference<>();
+
+        Stopwatch stopWatch = Stopwatch.createStarted();
+
+        new Thread() {
+            @Override
+            public void run() {
+                for (int i = 0; i < numTasksToRun; i++) {
+//                    if (i%100 == 0) {
+//                        Uninterruptibles.sleepUninterruptibly(20, TimeUnit.MICROSECONDS);
+//                    }
+
+                    executorToTest.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();
+        }
+
+        LOG.debug("{} threads used:", taskCountPerThread.size());
+        for (Map.Entry<Thread, AtomicLong> e : taskCountPerThread.entrySet()) {
+            LOG.debug("  {} - {} tasks", e.getKey().getName(), e.getValue());
+        }
+
+        LOG.debug("{}", executorToTest);
+        LOG.debug("Elapsed time: {}", stopWatch);
+    }
+
+    static class Task implements Runnable {
+        final CountDownLatch tasksRunLatch;
+        final CountDownLatch blockLatch;
+        final ConcurrentMap<Thread, AtomicLong> taskCountPerThread;
+        final AtomicReference<AssertionError> threadError;
+        final String expThreadPrefix;
+        final long delay;
+
+        Task(final CountDownLatch tasksRunLatch, final ConcurrentMap<Thread, AtomicLong> taskCountPerThread,
+                final AtomicReference<AssertionError> threadError, final String expThreadPrefix, final long delay) {
+            this.tasksRunLatch = tasksRunLatch;
+            this.taskCountPerThread = taskCountPerThread;
+            this.threadError = threadError;
+            this.expThreadPrefix = expThreadPrefix;
+            this.delay = delay;
+            blockLatch = null;
+        }
+
+        Task(final CountDownLatch tasksRunLatch, final CountDownLatch blockLatch) {
+            this.tasksRunLatch = tasksRunLatch;
+            this.blockLatch = blockLatch;
+            this.taskCountPerThread = null;
+            this.threadError = null;
+            this.expThreadPrefix = null;
+            this.delay = 0;
+        }
+
+        @Override
+        public void run() {
+            try {
+                try {
+                    if (delay > 0) {
+                        TimeUnit.MICROSECONDS.sleep(delay);
+                    } else if (blockLatch != null) {
+                        blockLatch.await();
+                    }
+                } catch (InterruptedException e) {
+                    // Ignored
+                }
+
+                if (expThreadPrefix != null) {
+                    assertTrue("Thread name starts with " + expThreadPrefix,
+                            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();
+                }
+            }
+        }
+    }
+}
diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueueTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueueTest.java
new file mode 100644 (file)
index 0000000..17c66d7
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+
+/**
+ * Unit tests for TrackingLinkedBlockingQueue.
+ *
+ * @author Thomas Pantelis
+ */
+public class TrackingLinkedBlockingQueueTest {
+
+    @Test
+    public void testOffer() throws InterruptedException {
+        TrackingLinkedBlockingQueue<String> queue = new TrackingLinkedBlockingQueue<>(2);
+
+        assertTrue("offer", queue.offer("1"));
+        assertEquals("getLargestQueueSize", 1, queue.getLargestQueueSize());
+        assertEquals("size", 1, queue.size());
+
+        assertTrue("offer", queue.offer("2", 1, TimeUnit.MILLISECONDS));
+        assertEquals("getLargestQueueSize", 2, queue.getLargestQueueSize());
+        assertEquals("size", 2, queue.size());
+
+        assertFalse("offer", queue.offer("3"));
+        assertEquals("getLargestQueueSize", 2, queue.getLargestQueueSize());
+        assertEquals("size", 2, queue.size());
+
+        assertFalse("offer", queue.offer("4", 1, TimeUnit.MILLISECONDS));
+        assertEquals("getLargestQueueSize", 2, queue.getLargestQueueSize());
+        assertEquals("size", 2, queue.size());
+    }
+
+    @Test
+    public void testPut() throws InterruptedException {
+        TrackingLinkedBlockingQueue<String> queue = new TrackingLinkedBlockingQueue<>();
+
+        queue.put("1");
+        assertEquals("getLargestQueueSize", 1, queue.getLargestQueueSize());
+        assertEquals("size", 1, queue.size());
+
+        queue.put("2");
+        assertEquals("getLargestQueueSize", 2, queue.getLargestQueueSize());
+        assertEquals("size", 2, queue.size());
+    }
+
+    @Test
+    public void testAdd() {
+        TrackingLinkedBlockingQueue<String> queue = new TrackingLinkedBlockingQueue<>(2);
+
+        assertTrue("add", queue.add("1"));
+        assertEquals("getLargestQueueSize", 1, queue.getLargestQueueSize());
+        assertEquals("size", 1, queue.size());
+
+        assertTrue("add", queue.add("2"));
+        assertEquals("getLargestQueueSize", 2, queue.getLargestQueueSize());
+        assertEquals("size", 2, queue.size());
+
+        try {
+            queue.add("3");
+            fail("Expected IllegalStateException");
+        } catch (IllegalStateException e) {
+            // Expected
+            assertEquals("getLargestQueueSize", 2, queue.getLargestQueueSize());
+            assertEquals("size", 2, queue.size());
+        }
+    }
+
+    @Test
+    public void testAddAll() {
+        TrackingLinkedBlockingQueue<String> queue = new TrackingLinkedBlockingQueue<>(3);
+
+        queue.addAll(Arrays.asList("1", "2"));
+        assertEquals("getLargestQueueSize", 2, queue.getLargestQueueSize());
+        assertEquals("size", 2, queue.size());
+
+        try {
+            queue.addAll(Arrays.asList("3", "4"));
+            fail("Expected IllegalStateException");
+        } catch (IllegalStateException e) {
+            // Expected
+            assertEquals("getLargestQueueSize", 3, queue.getLargestQueueSize());
+            assertEquals("size", 3, queue.size());
+        }
+    }
+}
diff --git a/distribution-karaf/pom.xml b/distribution-karaf/pom.xml
new file mode 100644 (file)
index 0000000..84c3e1f
--- /dev/null
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (c) 2016 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>karaf4-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>distribution-karaf</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <!-- DO NOT install or deploy the karaf artifact -->
+    <properties>
+        <maven.deploy.skip>true</maven.deploy.skip>
+        <maven.install.skip>true</maven.install.skip>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <!-- scope is compile so all features (there is only one) are installed
+                 into startup.properties and the feature repo itself is not installed -->
+             <groupId>org.apache.karaf.features</groupId>
+             <artifactId>framework</artifactId>
+             <type>kar</type>
+         </dependency>
+
+         <!-- Project local feautures -->
+         <dependency>
+             <groupId>org.opendaylight.yangtools</groupId>
+             <artifactId>features-yangtools</artifactId>
+             <classifier>features</classifier>
+             <type>xml</type>
+             <scope>runtime</scope>
+         </dependency>
+         <dependency>
+             <groupId>org.opendaylight.yangtools</groupId>
+             <artifactId>features-yangtools-experimental</artifactId>
+             <classifier>features</classifier>
+             <type>xml</type>
+             <scope>runtime</scope>
+         </dependency>
+     </dependencies>
+
+     <scm>
+         <connection>scm:git:ssh://git.opendaylight.org:29418/yangtools.git</connection>
+         <developerConnection>scm:git:ssh://git.opendaylight.org:29418/yangtools.git</developerConnection>
+         <tag>HEAD</tag>
+         <url>https://git.opendaylight.org/gerrit/gitweb?p=yangtools.git;a=summary</url>
+     </scm>
+</project>
diff --git a/docs/assembly/guides.xml b/docs/assembly/guides.xml
new file mode 100644 (file)
index 0000000..1db9dbd
--- /dev/null
@@ -0,0 +1,15 @@
+<assembly
+    xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+    <id>guides</id>
+    <formats>
+        <format>zip</format>
+    </formats>
+    <fileSets>
+        <fileSet>
+            <directory>${project.build.directory}/generated-docs</directory>
+            <outputDirectory>/</outputDirectory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/docs/pom.xml b/docs/pom.xml
new file mode 100644 (file)
index 0000000..2140edc
--- /dev/null
@@ -0,0 +1,357 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yangtools-docs</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description>YANG Tools documentation</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-transform</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-jaxen</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-codec-gson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-codec-xml</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-export</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-reactor</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-rfc7950</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-test-util</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-maven-plugin-spi</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odlext-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odlext-parser-support</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>openconfig-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>openconfig-parser-support</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc6536-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc6536-parser-support</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-parser-support</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8040-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8040-parser-support</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-data-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-parser-support</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-xpath-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-xpath-impl</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>util</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>testutils</artifactId>
+        </dependency>
+
+        <!-- Also add dependencies we are using -->
+        <dependency>
+            <groupId>org.kohsuke.metainf-services</groupId>
+            <artifactId>metainf-services</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.immutables</groupId>
+            <artifactId>value</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.sonatype.plexus</groupId>
+            <artifactId>plexus-build-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.asciidoctor</groupId>
+                <artifactId>asciidoctor-maven-plugin</artifactId>
+                <version>1.5.7.1</version>
+                <executions>
+                    <execution>
+                        <id>output-html</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>process-asciidoc</goal>
+                        </goals>
+                        <configuration>
+                            <sourceHighlighter>coderay</sourceHighlighter>
+                            <backend>html</backend>
+                            <attributes>
+                                <toc/>
+                                <linkcss>false</linkcss>
+                            </attributes>
+                        </configuration>
+                    </execution>
+                </executions>
+                <configuration>
+                    <attributes>
+                        <revnumber>${project.version}</revnumber>
+                        <revdate>${maven.build.timestamp}</revdate>
+                        <organization>${project.organization.name}</organization>
+                    </attributes>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>unpack-sources</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>unpack-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <silent>true</silent>
+                            <classifier>sources</classifier>
+                            <includes>org/opendaylight/**</includes>
+                            <includeGroupIds>org.opendaylight.yangtools</includeGroupIds>
+                            <outputDirectory>${project.build.directory}/src</outputDirectory>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>add-source</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>${project.build.directory}/src</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <artifactId>maven-source-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <!-- prepare-package so we build the source package before javadoc -->
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>jar-no-fork</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>attach-javadocs</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <!-- FIXME: remove this section once we can activate javadoc-links profile -->
+                <configuration combine.children="append">
+                    <links>
+                        <link>https://junit.org/junit4/javadoc/4.11/</link>
+                        <link>http://hamcrest.org/JavaHamcrest/javadoc/1.3/</link>
+                        <link>http://google.github.io/truth/api/0.42/</link>
+                        <link>http://www.slf4j.org/apidocs/</link>
+                        <link>https://google.github.io/guava/releases/25.1-jre/api/docs/</link>
+                        <link>https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/</link>
+                        <link>https://commons.apache.org/proper/commons-lang/javadocs/api-3.8.1/</link>
+                        <link>https://commons.apache.org/proper/commons-codec/apidocs/</link>
+                    </links>
+                    <groups>
+                        <group>
+                            <title>Common concepts</title>
+                            <packages>org.opendaylight.yangtools.concepts*</packages>
+                        </group>
+                        <group>
+                            <title>Common utilities</title>
+                            <packages>org.opendaylight.yangtools.util*</packages>
+                        </group>
+                        <group>
+                            <title>Common YANG constructs</title>
+                            <packages>org.opendaylight.yangtools.yang.common*</packages>
+                        </group>
+                        <group>
+                            <title>YANG schema interfaces</title>
+                            <packages>org.opendaylight.yangtools.yang.model.api*:org.opendaylight.yangtools.yang.model.util*</packages>
+                        </group>
+                        <group>
+                            <title>YANG data tree interfaces</title>
+                            <packages>org.opendaylight.yangtools.yang.data.api*:org.opendaylight.yangtools.yang.data.util*</packages>
+                        </group>
+                        <group>
+                            <title>yang-maven-plugin codegen interface</title>
+                            <packages>org.opendaylight.yangtools.yang2sources.spi*</packages>
+                        </group>
+                        <group>
+                            <title>Common YANG parser</title>
+                            <packages>org.opendaylight.yangtools.yang.parser*</packages>
+                        </group>
+                    </groups>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/docs/src/main/asciidoc/contributing.adoc b/docs/src/main/asciidoc/contributing.adoc
new file mode 100644 (file)
index 0000000..6378e84
--- /dev/null
@@ -0,0 +1,13 @@
+= Contributor Guide
+
+== Architecture
+
+== Design
+
+=== Design considerations
+
+=== Design decisions
+
+== Common concepts
+
+== Recomendations
diff --git a/docs/src/main/asciidoc/developing.adoc b/docs/src/main/asciidoc/developing.adoc
new file mode 100644 (file)
index 0000000..904204e
--- /dev/null
@@ -0,0 +1,467 @@
+= Developer Guide
+:rfc6020: https://tools.ietf.org/html/rfc6020
+:lhotka-yang-json: https://tools.ietf.org/html/draft-lhotka-netmod-yang-json-01
+
+== Overview
+YANG Tools is set of libraries and tooling providing support for use {rfc6020}[YANG] for Java (or other JVM-based language) projects and applications.
+
+YANG Tools provides following features in OpenDaylight:
+
+- parsing of YANG sources and
+semantic inference of relationship across YANG models as defined in
+{rfc6020}[RFC6020]
+- representation of YANG-modeled data in Java
+** *Normalized Node* representation - DOM-like tree model, which uses conceptual
+  meta-model more tailored to YANG and OpenDaylight use-cases than a standard XML
+  DOM model allows for.
+- serialization / deserialization of YANG-modeled data driven by YANG
+models
+** XML - as defined in {rfc6020}[RFC6020]
+** JSON - as defined in {rfc6020}[draft-lhotka-netmod-yang-json-01]
+** support for third-party generators processing YANG models.
+
+=== Architecture
+YANG Tools project consists of following logical subsystems:
+
+- *Commons* - Set of general purpose code, which is not specific to YANG, but
+  is also useful outside YANG Tools implementation.
+- *YANG Model and Parser* - YANG semantic model and lexical and semantic parser
+  of YANG models, which creates in-memory cross-referenced represenation of
+  YANG models, which is used by other components to determine their behaviour
+  based on the model.
+- *YANG Data* - Definition of Normalized Node APIs and Data Tree APIs, reference
+  implementation of these APIs and implementation of XML and JSON codecs for
+  Normalized Nodes.
+- *YANG Maven Plugin* - Maven plugin which integrates YANG parser into Maven
+  build lifecycle and provides code-generation framework for components, which
+  wants to generate code or other artefacts based on YANG model.
+
+=== Concepts
+Project defines base concepts and helper classes which are project-agnostic and could be used outside of YANG Tools project scope.
+
+=== Components
+
+- yang-common
+- yang-data-api
+- yang-data-codec-gson
+- yang-data-codec-xml
+- yang-data-impl
+- yang-data-jaxen
+- yang-data-transform
+- yang-data-util
+- yang-maven-plugin
+- yang-maven-plugin-it
+- yang-maven-plugin-spi
+- yang-model-api
+- yang-model-export
+- yang-model-util
+- yang-parser-api
+- yang-parser-impl
+
+==== YANG Model API
+Class diagram of yang model API
+
+image:models/yang-model-api.png[]
+
+==== YANG Parser
+
+Yang Statement Parser works on the idea of statement concepts as defined in RFC6020, section 6.3. We come up here with basic ModelStatement and StatementDefinition, following RFC6020 idea of having sequence of statements, where
+every statement contains keyword and zero or one argument. ModelStatement is extended by DeclaredStatement (as it comes from source, e.g. YANG source)
+and EffectiveStatement, which contains other substatements and tends to represent result of semantic processing of other statements (uses, augment for YANG).
+IdentifierNamespace represents common superclass for YANG model namespaces.
+
+Input of the Yang Statement Parser is a collection of StatementStreamSource objects.
+StatementStreamSource interface is used for inference of effective model
+and is required to emit its statements using supplied StatementWriter.
+Each source (e.g. YANG source) has to be processed in three steps
+in order to emit different statements for each step.
+This package provides support for various namespaces used across statement parser
+in order to map relations during declaration phase process.
+
+Currently, there are two implementations of StatementStreamSource in Yangtools:
+
+ - YangStatementSourceImpl - intended for yang sources
+ - YinStatementSourceImpl - intended for yin sources
+
+==== YANG Data API
+Class diagram of yang data API
+
+image:models/yang-data-api.png[]
+
+==== YANG Data Codecs
+Codecs which enable serialization of NormalizedNodes into YANG-modeled data in XML or JSON format and deserialization of YANG-modeled data in XML or JSON format into NormalizedNodes.
+
+==== YANG Maven Plugin
+Maven plugin which integrates YANG parser into Maven
+  build lifecycle and provides code-generation framework for components, which
+  wants to generate code or other artefacts based on YANG model.
+
+== How to / Tutorials
+
+=== Working with YANG Model
+First thing you need to do if you want to work with YANG models is to instantiate a SchemaContext object. This object type describes one or more parsed YANG modules.
+
+In order to create it you need to utilize YANG statement parser which takes one or more StatementStreamSource objects as input and then produces the SchemaContext object.
+
+StatementStreamSource object contains the source file information. It has two implementations, one for YANG sources - YangStatementSourceImpl, and one for YIN sources - YinStatementSourceImpl.
+
+Here is an example of creating StatementStreamSource objects for YANG files, providing them to the YANG statement parser and building the SchemaContext:
+
+[source,java]
+----
+StatementStreamSource yangModuleSource = new YangStatementSourceImpl("/example.yang", false);
+StatementStreamSource yangModuleSource2 = new YangStatementSourceImpl("/example2.yang", false);
+
+CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+reactor.addSources(yangModuleSource, yangModuleSource2);
+
+SchemaContext schemaContext = reactor.buildEffective();
+----
+
+First, StatementStreamSource objects with two constructor arguments should be instantiated: path to the yang source file (which is a regular String object) and a boolean which determines if the path is absolute or relative.
+
+Next comes the initiation of new yang parsing cycle - which is represented by CrossSourceStatementReactor.BuildAction object. You can get it by calling method newBuild() on CrossSourceStatementReactor object (RFC6020_REACTOR) in YangInferencePipeline class.
+
+Then you should feed yang sources to it by calling method addSources() that takes one or more StatementStreamSource objects as arguments.
+
+Finally you call the method buildEffective() on the reactor object which returns EffectiveSchemaContext (that is a concrete implementation of SchemaContext). Now you are ready to work with contents of the added yang sources.
+
+Let us explain how to work with models contained in the newly created SchemaContext. If you want to get all the modules in the schemaContext, you have to call method getModules() which returns a Set of modules. If you want to get all the data definitions in schemaContext, you need to call method getDataDefinitions, etc.
+
+[source, java]
+Set<Module> modules = schemaContext.getModules();
+Set<DataSchemaNodes> dataSchemaNodes = schemaContext.getDataDefinitions();
+
+Usually you want to access specific modules. Getting a concrete module from SchemaContext is a matter of calling one of these methods:
+
+* findModuleByName(),
+* findModuleByNamespace(),
+* findModuleByNamespaceAndRevision().
+
+In the first case, you need to provide module name as it is defined in the yang source file and module revision date if it specified in the yang source file (if it is not defined, you can just pass a null value). In order to provide the revision date in proper format, you can use a utility class named SimpleDateFormatUtil.
+
+[source, java]
+Module exampleModule = schemaContext.findModuleByName("example-module", null);
+// or
+Date revisionDate = SimpleDateFormatUtil.getRevisionFormat().parse("2015-09-02");
+Module exampleModule = schemaContext.findModuleByName("example-module", revisionDate);
+
+In the second case, you have to provide module namespace in form of an URI object.
+[source, java]
+Module exampleModule = schema.findModuleByNamespace(new URI("opendaylight.org/example-module"));
+
+In the third case, you provide both module namespace and revision date as arguments.
+
+Once you have a Module object, you can access its contents as they are defined in YANG Model API.
+One way to do this is to use method like getIdentities() or getRpcs() which will give you a Set of objects. Otherwise you can access a DataSchemaNode directly via the method getDataChildByName() which takes a QName object as its only argument. Here are a few examples.
+
+[source, java]
+----
+Set<AugmentationSchema> augmentationSchemas = exampleModule.getAugmentations();
+Set<ModuleImport> moduleImports = exampleModule.getImports();
+
+ChoiceSchemaNode choiceSchemaNode = (ChoiceSchemaNode) exampleModule.getDataChildByName(QName.create(exampleModule.getQNameModule(), "example-choice"));
+
+ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) exampleModule.getDataChildByName(QName.create(exampleModule.getQNameModule(), "example-container"));
+----
+
+The YANG statement parser can work in three modes:
+
+* default mode
+* mode with active resolution of if-feature statements
+* mode with active semantic version processing
+
+The default mode is active when you initialize the parsing cycle as usual by calling the method newBuild() without passing any arguments to it. The second and third mode can be activated by invoking the newBuild() with a special argument. You can either activate just one of them or both by passing proper arguments. Let us explain how these modes work.
+
+Mode with active resolution of if-features makes yang statements containing an if-feature statement conditional based on the supported features. These features are provided in the form of a QName-based java.util.Set object. In the example below, only two features are supported: example-feature-1 and example-feature-2. The Set is passed to the method newBuild() and the mode is activated.
+
+[source, java]
+----
+Set<QName> supportedFeatures = ImmutableSet.of(
+    QName.create("example-namespace", "2016-08-31", "example-feature-1"),
+    QName.create("example-namespace", "2016-08-31", "example-feature-2"));
+
+CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild(supportedFeatures);
+----
+
+In case when no features should be supported, you should provide an empty Set<QName> object.
+
+[source, java]
+----
+Set<QName> supportedFeatures = ImmutableSet.of();
+
+CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild(supportedFeatures);
+----
+
+When this mode is not activated, all features in the processed YANG sources are supported.
+
+Mode with active semantic version processing changes the way how YANG import statements work - each module import is processed based on the specified semantic version statement and the revision-date statement is ignored. In order to activate this mode, you have to provide StatementParserMode.SEMVER_MODE enum constant as argument to the method newBuild().
+
+[source, java]
+----
+CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild(StatementParserMode.SEMVER_MODE);
+----
+
+Before you use a semantic version statement in a YANG module, you need to define an extension for it so that the YANG statement parser can recognize it.
+
+[source, yang]
+----
+module semantic-version {
+    namespace "urn:opendaylight:yang:extension:semantic-version";
+    prefix sv;
+    yang-version 1;
+
+    revision 2016-02-02 {
+        description "Initial version";
+    }
+    sv:semantic-version "0.0.1";
+
+    extension semantic-version {
+        argument "semantic-version" {
+            yin-element false;
+        }
+    }
+}
+----
+
+In the example above, you see a YANG module which defines semantic version as an extension. This extension can be imported to other modules in which we want to utilize the semantic versioning concept.
+
+Below is a simple example of the semantic versioning usage. With semantic version processing mode being active, the foo module imports the bar module based on its semantic version. Notice how both modules import the module with the semantic-version extension.
+
+[source, yang]
+----
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1;
+
+    import semantic-version { prefix sv; revision-date 2016-02-02; sv:semantic-version "0.0.1"; }
+    import bar { prefix bar; sv:semantic-version "0.1.2";}
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    sv:semantic-version "0.1.1";
+
+    ...
+}
+----
+
+[source, yang]
+----
+module bar {
+    namespace bar;
+    prefix bar;
+    yang-version 1;
+
+    import semantic-version { prefix sv; revision-date 2016-02-02; sv:semantic-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    sv:semantic-version "0.1.2";
+
+    ...
+}
+----
+
+Every semantic version must have the following form: x.y.z. The x corresponds to a major version, the y corresponds to a minor version and the z corresponds to a patch version. If no semantic version is specified in a module or an import statement, then the default one is used - 0.0.0.
+
+A major version number of 0 indicates that the model is still in development and is subject to change.
+
+Following a release of major version 1, all modules will increment major version number when backwards incompatible changes to the model are made.
+
+The minor version is changed when features are added to the model that do not impact current clients use of the model.
+
+The patch version is incremented when non-feature changes (such as bugfixes or clarifications of human-readable descriptions that do not impact model functionality) are made that maintain backwards compatibility.
+
+When importing a module with activated semantic version processing mode, only the module with the newest (highest) compatible semantic version is imported. Two semantic versions are compatible when all of the following conditions are met:
+
+* the major version in the import statement and major version in the imported module are equal. For instance, 1.5.3 is compatible with 1.5.3, 1.5.4, 1.7.2, etc., but it is not compatible with 0.5.2 or 2.4.8, etc.
+
+* the combination of minor version and patch version in the import statement is not higher than the one in the imported module. For instance, 1.5.2 is compatible with 1.5.2, 1.5.4, 1.6.8 etc. In fact, 1.5.2 is also compatible with versions like 1.5.1, 1.4.9 or 1.3.7 as they have equal major version. However, they will not be imported because their minor and patch version are lower (older).
+
+If the import statement does not specify a semantic version, then the default one is chosen - 0.0.0. Thus, the module is imported only if it has a semantic version compatible with the default one, for example 0.0.0, 0.1.3, 0.3.5 and so on.
+
+=== Working with YANG Data
+If you want to work with YANG Data you are going to need NormalizedNode objects that are specified in the YANG Data API. NormalizedNode is an interface at the top of the YANG Data hierarchy. It is extended through sub-interfaces which define the behaviour of specific NormalizedNode types like AnyXmlNode, ChoiceNode, LeafNode, ContainerNode, etc. Concrete implemenations of these interfaces are defined in yang-data-impl module. Once you have one or more NormalizedNode instances, you can perform CRUD operations on YANG data tree which is an in-memory database designed to store normalized nodes in a tree-like structure.
+
+In some cases it is clear which NormalizedNode type belongs to which yang statement (e.g. AnyXmlNode, ChoiceNode, LeafNode). However, there are some normalized nodes which are named differently from their yang counterparts. They are listed below:
+
+* LeafSetNode - leaf-list
+* OrderedLeafSetNode - leaf-list that is ordered-by user
+* LeafSetEntryNode - concrete entry in a leaf-list
+* MapNode - keyed list
+* OrderedMapNode - keyed list that is ordered-by user
+* MapEntryNode - concrete entry in a keyed list
+* UnkeyedListNode - unkeyed list
+* UnkeyedListEntryNode - concrete entry in an unkeyed list
+
+In order to create a concrete NormalizedNode object you can use the utility class Builders or ImmutableNodes. These classes can be found in yang-data-impl module and they provide methods for building each type of normalized node. Here is a simple example of building a normalized node:
+
+[source, java]
+----
+\\ example 1
+ContainerNode containerNode = Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(moduleQName, "example-container")).build();
+
+\\ example 2
+ContainerNode containerNode2 = Builders.containerBuilder(containerSchemaNode).build();
+----
+Both examples produce the same result. NodeIdentifier is one of the four types of YangInstanceIdentifier (these types are described in the javadoc of YangInstanceIdentifier). The purpose of YangInstanceIdentifier is to uniquely identify a particular node in the data tree. In the first example, you have to add NodeIdentifier before building the resulting node. In the second example it is also added using the provided ContainerSchemaNode object.
+
+ImmutableNodes class offers similar builder methods and also adds an overloaded method called fromInstanceId() which allows you to create a NormalizedNode object based on YangInstanceIdentifier and SchemaContext. Below is an example which shows the use of this method.
+
+[source, java]
+----
+YangInstanceIdentifier.NodeIdentifier contId = new YangInstanceIdentifier.NodeIdentifier(QName.create(moduleQName, "example-container");
+
+NormalizedNode<?, ?> contNode = ImmutableNodes.fromInstanceId(schemaContext, YangInstanceIdentifier.create(contId));
+----
+
+Let us show a more complex example of creating a NormalizedNode. First, consider the following YANG module:
+
+[source, yang]
+----
+module example-module {
+    namespace "opendaylight.org/example-module";
+    prefix "example";
+
+    container parent-container {
+        container child-container {
+            list parent-ordered-list {
+                ordered-by user;
+
+                key "parent-key-leaf";
+
+                leaf parent-key-leaf {
+                    type string;
+                }
+
+                leaf parent-ordinary-leaf {
+                    type string;
+                }
+
+                list child-ordered-list {
+                    ordered-by user;
+
+                    key "child-key-leaf";
+
+                    leaf child-key-leaf {
+                        type string;
+                    }
+
+                    leaf child-ordinary-leaf {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+}
+----
+
+In the following example, two normalized nodes based on the module above are written to and read from the data tree.
+
+[source, java]
+----
+TipProducingDataTree inMemoryDataTree =     InMemoryDataTreeFactory.getInstance().create(TreeType.OPERATIONAL);
+inMemoryDataTree.setSchemaContext(schemaContext);
+
+// first data tree modification
+MapEntryNode parentOrderedListEntryNode = Builders.mapEntryBuilder().withNodeIdentifier(
+new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+parentOrderedListQName, parentKeyLeafQName, "pkval1"))
+.withChild(Builders.leafBuilder().withNodeIdentifier(
+new YangInstanceIdentifier.NodeIdentifier(parentOrdinaryLeafQName))
+.withValue("plfval1").build()).build();
+
+OrderedMapNode parentOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+new YangInstanceIdentifier.NodeIdentifier(parentOrderedListQName))
+.withChild(parentOrderedListEntryNode).build();
+
+ContainerNode parentContainerNode = Builders.containerBuilder().withNodeIdentifier(
+new YangInstanceIdentifier.NodeIdentifier(parentContainerQName))
+.withChild(Builders.containerBuilder().withNodeIdentifier(
+new NodeIdentifier(childContainerQName)).withChild(parentOrderedListNode).build()).build();
+
+YangInstanceIdentifier path1 = YangInstanceIdentifier.of(parentContainerQName);
+
+DataTreeModification treeModification = inMemoryDataTree.takeSnapshot().newModification();
+treeModification.write(path1, parentContainerNode);
+
+// second data tree modification
+MapEntryNode childOrderedListEntryNode = Builders.mapEntryBuilder().withNodeIdentifier(
+new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+childOrderedListQName, childKeyLeafQName, "chkval1"))
+.withChild(Builders.leafBuilder().withNodeIdentifier(
+new YangInstanceIdentifier.NodeIdentifier(childOrdinaryLeafQName))
+.withValue("chlfval1").build()).build();
+
+OrderedMapNode childOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+new YangInstanceIdentifier.NodeIdentifier(childOrderedListQName))
+.withChild(childOrderedListEntryNode).build();
+
+ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+ImmutableMap<QName, Object> keys = builder.put(parentKeyLeafQName, "pkval1").build();
+
+YangInstanceIdentifier path2 = YangInstanceIdentifier.of(parentContainerQName).node(childContainerQName)
+.node(parentOrderedListQName).node(new NodeIdentifierWithPredicates(parentOrderedListQName, keys)).node(childOrderedListQName);
+
+treeModification.write(path2, childOrderedListNode);
+treeModification.ready();
+inMemoryDataTree.validate(treeModification);
+inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification));
+
+DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path1);
+Optional<NormalizedNode<?, ?>> readNode2 = snapshotAfterCommits.readNode(path2);
+----
+First comes the creation of in-memory data tree instance. The schema context (containing the model mentioned above) of this tree is set. After that, two normalized nodes are built. The first one consists of a parent container, a child container and a parent ordered list which contains a key leaf and an ordinary leaf. The second normalized node is a child ordered list that also contains a key leaf and an ordinary leaf.
+
+In order to add a child node to a node, method withChild() is used. It takes a NormalizedNode as argument. When creating a list entry, YangInstanceIdentifier.NodeIdentifierWithPredicates should be used as its identifier. Its arguments are the QName of the list, QName of the list key and the value of the key. Method withValue() specifies a value for the ordinary leaf in the list.
+
+Before writing a node to the data tree, a path (YangInstanceIdentifier) which determines its place in the data tree needs to be defined. The path of the first normalized node starts at the parent container. The path of the second normalized node points to the child ordered list contained in the parent ordered list entry specified by the key value "pkval1".
+
+Write operation is performed with both normalized nodes mentioned earlier. It consist of several steps. The first step is to instantiate a DataTreeModification object based on a DataTreeSnapshot. DataTreeSnapshot gives you the current state of the data tree. Then comes the write operation which writes a normalized node at the provided path in the data tree. After doing both write operations, method ready() has to be called, marking the modification as ready for application to the data tree. No further operations within the modification are allowed. The modification is then validated - checked whether it can be applied to the data tree. Finally we commit it to the data tree.
+
+Now you can access the written nodes. In order to do this, you have to create a new DataTreeSnapshot instance and call the method readNode() with path argument pointing to a particular node in the tree.
+
+=== Serialization / deserialization of YANG Data
+If you want to deserialize YANG-modeled data which have the form of an XML document, you can use the XML parser found in the module yang-data-codec-xml. The parser walks through the XML document containing YANG-modeled data based on the provided SchemaContext and emits node events into a NormalizedNodeStreamWriter. The parser disallows multiple instances of the same element except for leaf-list and list entries. The parser also expects that the YANG-modeled data in the XML source are wrapped in a root element. Otherwise it will not work correctly.
+
+Here is an example of using the XML parser.
+[source, java]
+----
+InputStream resourceAsStream = ExampleClass.class.getResourceAsStream("/example-module.yang");
+
+XMLInputFactory factory = XMLInputFactory.newInstance();
+XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream);
+
+NormalizedNodeResult result = new NormalizedNodeResult();
+NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext);
+xmlParser.parse(reader);
+
+NormalizedNode<?, ?> transformedInput = result.getResult();
+----
+The XML parser utilizes the javax.xml.stream.XMLStreamReader for parsing an XML document. First, you should create an instance of this reader using XMLInputFactory and then load an XML document (in the form of InputStream object) into it.
+
+In order to emit node events while parsing the data you need to instantiate a NormalizedNodeStreamWriter. This writer is actually an interface and therefore you need to use a concrete implementation of it. In this example it is the ImmutableNormalizedNodeStreamWriter, which constructs immutable instances of NormalizedNodes.
+
+There are two ways how to create an instance of this writer using the static overloaded method from(). One version of this method takes a NormalizedNodeResult as argument. This object type is a result holder in which the resulting NormalizedNode will be stored. The other version takes a
+NormalizedNodeContainerBuilder as argument. All created nodes will be written to this builder.
+
+Next step is to create an instance of the XML parser. The parser itself is represented by a class named XmlParserStream. You can use one of two versions of the static overloaded method create() to construct this object. One version accepts a NormalizedNodeStreamWriter and a SchemaContext as arguments, the other version takes the same arguments plus a SchemaNode. Node events are emitted to the writer. The SchemaContext is used to check if the YANG data in the XML source comply with the provided YANG model(s). The last argument, a SchemaNode object, describes the node that is the parent of nodes defined in the XML data. If you do not provide this argument, the parser sets the SchemaContext as the parent node.
+
+The parser is now ready to walk through the XML. Parsing is initiated by calling the method parse() on the XmlParserStream object with XMLStreamReader as its argument.
+
+Finally you can access the result of parsing - a tree of NormalizedNodes containg the data as they are defined in the parsed XML document - by calling the method getResult() on the NormalizedNodeResult object.
+
+=== Introducing schema source repositories
+
+=== Writing YANG driven generators
+
+=== Introducing specific extension support for YANG parser
+
+=== Diagnostics
diff --git a/docs/src/main/asciidoc/images/models/yang-data-api.png b/docs/src/main/asciidoc/images/models/yang-data-api.png
new file mode 100644 (file)
index 0000000..b8fe9f8
Binary files /dev/null and b/docs/src/main/asciidoc/images/models/yang-data-api.png differ
diff --git a/docs/src/main/asciidoc/images/models/yang-model-api.png b/docs/src/main/asciidoc/images/models/yang-model-api.png
new file mode 100644 (file)
index 0000000..80e0e1a
Binary files /dev/null and b/docs/src/main/asciidoc/images/models/yang-model-api.png differ
diff --git a/docs/src/main/asciidoc/yang-1-1.adoc b/docs/src/main/asciidoc/yang-1-1.adoc
new file mode 100644 (file)
index 0000000..41b1231
--- /dev/null
@@ -0,0 +1,51 @@
+= YANG Tools support for YANG 1.1
+Tony Tkacik <ttkacik@cisco.com>
+:toc:
+:rfc6020: https://tools.ietf.org/html/rfc6020
+:rfc6020bis: https://tools.ietf.org/html/draft-ietf-netmod-rfc6020bis
+
+== Introduction
+
+=== Significant changes
+
+.Summary of Changes from draft
+-  Changed the YANG version from "1" to "1.1".
+-  Made the `yang-version` statement mandatory.
+-  Made noncharacters illegal in the built-in type `string`.
+-  Defined the legal characters in YANG modules.
+-  Changed the rules for the interpretation of escaped characters in
+  double quoted strings.  This is an backwards incompatible change
+  from YANG version 1.  A module that uses a character sequence that
+  is now illegal must change the string to match the new rules.
+-  An unquoted string cannot contain any single or double quote
+  characters.  This is an backwards incompatible change from YANG
+  version 1.
+-  Extended the `if-feature` syntax to be a boolean expression over
+  feature names.
+-  Allow `if-feature` in `bit`, `enum`, and `identity`.
+-  Allow `if-feature` in `refine`.
+-  Made `when` and `if-feature` illegal on list keys.
+-  Allow `choice` as a shorthand case statement.
+-  Added a new substatement `modifier` to pattern.
+-  Allow `must` in `input`, `output`, and `notification`.
+-  Allow `require-instance` in leafref.
+-  Allow `augment` to add conditionally mandatory nodes.
+-  Added a set of new XPath functions.
+-  Clarified the XPath context's tree.
+-  Defined the string value of an identityref in XPath expressions.
+-  Clarified what unprefixed names mean in leafrefs in typedefs.
+-  Allow identities to be derived from multiple base identities.
+-  Allow enumerations and bits to be subtyped.
+-  Allow leaf-lists to have default values.
+-  Allow non-unique values in non-configuration leaf-lists.
+-  Use [RFC7405] syntax for case-sensitive strings in the grammar.
+-  Changed the module advertisement mechanism.
+-  Changed the scoping rules for definitions in submodules.  A
+   submodule can now reference all definitions in all submodules that
+   belong to the same module, without using the `include` statement.
+-  Added a new statement `action` that is used to define operations
+   tied to data nodes.
+-  Allow notifications to be tied to data nodes.
+-  Added a new data definition statement `anydata`.
+-  Allow types `empty` and `leafref` in unions.
+-  Allow type `empty` in a `key`.
diff --git a/features/features-yangtools-experimental/pom.xml b/features/features-yangtools-experimental/pom.xml
new file mode 100644 (file)
index 0000000..f454d97
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>feature-repo-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>features-yangtools-experimental</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: Experimental Features</name>
+    <description>YANG Tools Experimental Features</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>features-yangtools</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-exp-data-xpath-api</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-exp-data-xpath</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/features-yangtools/pom.xml b/features/features-yangtools/pom.xml
new file mode 100644 (file)
index 0000000..427e240
--- /dev/null
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>feature-repo-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>features-yangtools</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: Stable Features</name>
+    <description>YANG Tools Stable Features</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-codec</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-common</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-data-api</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-data</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-export</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-parser-api</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-parser</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-util</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-xpath-api</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-xpath</artifactId>
+            <classifier>features</classifier>
+            <type>xml</type>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/odl-yangtools-codec/pom.xml b/features/odl-yangtools-codec/pom.xml
new file mode 100644 (file)
index 0000000..2671031
--- /dev/null
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>single-feature-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>odl-yangtools-codec</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: Data Codecs</name>
+    <description>Serialization/deserialization support for various formats</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.odlparent</groupId>
+            <artifactId>odl-gson</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.odlparent</groupId>
+            <artifactId>odl-stax2-api</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-data</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-codec-gson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-codec-xml</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/odl-yangtools-codec/src/main/feature/feature.xml b/features/odl-yangtools-codec/src/main/feature/feature.xml
new file mode 100644 (file)
index 0000000..c4bf354
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.4.0" name="odl-yangtools-codec">
+    <feature name="odl-yangtools-codec">
+        <feature version="[6,7)">odl-gson</feature>
+        <feature version="[6,7)">odl-stax2-api</feature>
+    </feature>
+</features>
diff --git a/features/odl-yangtools-common/pom.xml b/features/odl-yangtools-common/pom.xml
new file mode 100644 (file)
index 0000000..9580774
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>single-feature-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>odl-yangtools-common</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: Common definitions</name>
+    <description>Common YANG-related definitions</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-util</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/odl-yangtools-data-api/pom.xml b/features/odl-yangtools-data-api/pom.xml
new file mode 100644 (file)
index 0000000..322d0e7
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>single-feature-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>odl-yangtools-data-api</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: Data API</name>
+    <description>YANG modeled data APIs</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-parser-api</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-data-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-data-util</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/odl-yangtools-data/pom.xml b/features/odl-yangtools-data/pom.xml
new file mode 100644 (file)
index 0000000..3bae47e
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>single-feature-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>odl-yangtools-data</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: Data Reference Implementation</name>
+    <description>YANG modeled data implementation</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.odlparent</groupId>
+            <artifactId>odl-antlr4</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-data-api</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-transform</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/odl-yangtools-data/src/main/feature/feature.xml b/features/odl-yangtools-data/src/main/feature/feature.xml
new file mode 100644 (file)
index 0000000..dd3f693
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.4.0" name="odl-yangtools-data">
+    <feature name="odl-yangtools-data">
+        <feature version="[6,7)">odl-antlr4</feature>
+    </feature>
+</features>
diff --git a/features/odl-yangtools-exp-data-xpath-api/pom.xml b/features/odl-yangtools-exp-data-xpath-api/pom.xml
new file mode 100644 (file)
index 0000000..2864269
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>single-feature-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>odl-yangtools-exp-data-xpath-api</artifactId>
+    <version>0.2.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: Experimental :: Data XPath API</name>
+    <description>YANG-modeled data XPath APIs</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-data-api</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-xpath-api</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-xpath-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/odl-yangtools-exp-data-xpath/pom.xml b/features/odl-yangtools-exp-data-xpath/pom.xml
new file mode 100644 (file)
index 0000000..4b5b8ea
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>single-feature-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>odl-yangtools-exp-data-xpath</artifactId>
+    <version>0.2.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: Experimental :: Data XPath support</name>
+    <description>XPath support for YANG-modeled data</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-xpath</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-exp-data-xpath-api</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-jaxen</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/odl-yangtools-export/pom.xml b/features/odl-yangtools-export/pom.xml
new file mode 100644 (file)
index 0000000..3aa28de
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>single-feature-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>odl-yangtools-export</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: Export</name>
+    <description>YANG model export</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-parser-api</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-export</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/odl-yangtools-parser-api/pom.xml b/features/odl-yangtools-parser-api/pom.xml
new file mode 100644 (file)
index 0000000..2256dd0
--- /dev/null
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>single-feature-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>odl-yangtools-parser-api</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: Parser API</name>
+    <description>YANG metamodel and parser APIs</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-common</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-xpath-api</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odlext-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>openconfig-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc6241-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc6536-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8040-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/odl-yangtools-parser/pom.xml b/features/odl-yangtools-parser/pom.xml
new file mode 100644 (file)
index 0000000..c6bdeb6
--- /dev/null
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>single-feature-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>odl-yangtools-parser</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: Parser </name>
+    <description>YANG parser reference implementation</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.odlparent</groupId>
+            <artifactId>odl-antlr4</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-parser-api</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-xpath</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odlext-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>openconfig-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc6241-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc6536-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8040-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-reactor</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-rfc7950</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-spi</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/odl-yangtools-parser/src/main/feature/feature.xml b/features/odl-yangtools-parser/src/main/feature/feature.xml
new file mode 100644 (file)
index 0000000..760cd87
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.4.0" name="odl-yangtools-parser">
+    <feature name="odl-yangtools-parser">
+        <feature version="[6,7)">odl-antlr4</feature>
+    </feature>
+</features>
diff --git a/features/odl-yangtools-util/pom.xml b/features/odl-yangtools-util/pom.xml
new file mode 100644 (file)
index 0000000..3566306
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>single-feature-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>odl-yangtools-util</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: Utilities</name>
+    <description>YANG Tools common concepts and utilities</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>tech.pantheon.triemap</groupId>
+            <artifactId>pt-triemap</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.odlparent</groupId>
+            <artifactId>odl-guava</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>util</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/odl-yangtools-util/src/main/feature/feature.xml b/features/odl-yangtools-util/src/main/feature/feature.xml
new file mode 100644 (file)
index 0000000..5b38540
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.4.0" name="odl-yangtools-util">
+    <feature name="odl-yangtools-util">
+        <feature version="[6,7)">odl-guava</feature>
+        <feature version="[1,2)">pt-triemap</feature>
+    </feature>
+</features>
diff --git a/features/odl-yangtools-xpath-api/pom.xml b/features/odl-yangtools-xpath-api/pom.xml
new file mode 100644 (file)
index 0000000..18c1c22
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>single-feature-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>odl-yangtools-xpath-api</artifactId>
+    <version>2.0.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: XPath API</name>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-common</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-xpath-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/odl-yangtools-xpath/pom.xml b/features/odl-yangtools-xpath/pom.xml
new file mode 100644 (file)
index 0000000..32bfc53
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright Â© 2016 Red Hat, 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
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>single-feature-parent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>odl-yangtools-xpath</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>feature</packaging>
+    <name>OpenDaylight :: Yangtools :: XPath support</name>
+    <description>YANG XPath support</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>${project.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.odlparent</groupId>
+            <artifactId>odl-antlr4</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-xpath-api</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-xpath-impl</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/features/pom.xml b/features/pom.xml
new file mode 100644 (file)
index 0000000..3fff4a6
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright Â© 2014, 2016 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>odlparent-lite</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>features</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <modules>
+        <!-- Stable features -->
+        <module>odl-yangtools-codec</module>
+        <module>odl-yangtools-common</module>
+        <module>odl-yangtools-data-api</module>
+        <module>odl-yangtools-data</module>
+        <module>odl-yangtools-export</module>
+        <module>odl-yangtools-parser-api</module>
+        <module>odl-yangtools-parser</module>
+        <module>odl-yangtools-util</module>
+        <module>odl-yangtools-xpath-api</module>
+        <module>odl-yangtools-xpath</module>
+
+        <!-- Stable feature repository -->
+        <module>features-yangtools</module>
+
+        <!-- Experimental features -->
+        <module>odl-yangtools-exp-data-xpath-api</module>
+        <module>odl-yangtools-exp-data-xpath</module>
+
+        <!-- Experimental feature repostitory -->
+        <module>features-yangtools-experimental</module>
+    </modules>
+
+</project>
diff --git a/integration-test/yang-runtime-tests/src/test/java/org/opendaylight/yangtools/it/yang/runtime/stmt/parser/retest/MultipleRevisionsSupportTest.java b/integration-test/yang-runtime-tests/src/test/java/org/opendaylight/yangtools/it/yang/runtime/stmt/parser/retest/MultipleRevisionsSupportTest.java
new file mode 100644 (file)
index 0000000..f5870eb
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * 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.it.yang.runtime.stmt.parser.retest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableSet;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleContext;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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;
+
+public class MultipleRevisionsSupportTest {
+
+    public static final YangModuleInfo TOPOLOGY_OLD_MODULE = org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev130712.$YangModuleInfoImpl
+            .getInstance();
+    public static final YangModuleInfo TOPOLOGY_NEW_MODULE = org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.$YangModuleInfoImpl
+            .getInstance();
+
+    public static final Set<YangModuleInfo> DEPENDENCIES = ImmutableSet.<YangModuleInfo> builder() //
+            .addAll(TOPOLOGY_OLD_MODULE.getImportedModules()) //
+            .addAll(TOPOLOGY_NEW_MODULE.getImportedModules()).build();
+
+    @Test
+    public void dependenciesOlderNewer() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void dependenciesNewerOlder() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void newerOlderDependencies() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void newerDependenciesOlder() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void OlderNewerDependencies() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void olderDependenciesNewer() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    private SchemaContext contextVerified(final List<InputStream> streams) throws SourceException, ReactorException {
+        CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR
+                .newBuild();
+        return reactor.buildEffective(streams);
+    }
+
+    private void verifyBindingDifference(final SchemaContext schemaContext, final YangModuleInfo oldModule, final YangModuleInfo newModule) {
+        generatedTypesVerified(schemaContext, oldModule, newModule);
+    }
+
+    private Map<Module, ModuleContext> generatedTypesVerified(final SchemaContext schemaContext, final YangModuleInfo oldModule,
+            final YangModuleInfo newModule) {
+        BindingGeneratorImpl generator = new BindingGeneratorImpl();
+        generator.generateTypes(schemaContext);
+        return generator.getModuleContexts();
+    }
+
+    private void verifySchemaDifference(final SchemaContext context, final YangModuleInfo topologyOldModule,
+            final YangModuleInfo topologyNewModule) {
+        Module oldModel = context.findModuleByNamespaceAndRevision(//
+                URI.create(TOPOLOGY_OLD_MODULE.getNamespace()), QName.parseRevision(TOPOLOGY_OLD_MODULE.getRevision()));
+
+        Module newModel = context.findModuleByNamespaceAndRevision(//
+                URI.create(TOPOLOGY_NEW_MODULE.getNamespace()), QName.parseRevision(TOPOLOGY_NEW_MODULE.getRevision()));
+
+        SchemaNode oldNode = findSchemaNode(oldModel, "network-topology", "topology", "link");
+        SchemaNode newNode = findSchemaNode(newModel, "network-topology", "topology", "link");
+
+        assertNotNull(oldNode);
+        assertNotNull(newNode);
+
+        assertDeepRevision(TOPOLOGY_OLD_MODULE.getRevision(), oldNode);
+        assertDeepRevision(TOPOLOGY_NEW_MODULE.getRevision(), newNode);
+    }
+
+    private static void assertDeepRevision(final String revision, final SchemaNode node) {
+        assertEquals("Wrong revision: " + node.getPath(), revision, node.getQName().getFormattedRevision());
+        if (node instanceof DataNodeContainer) {
+            for (DataSchemaNode child : ((DataNodeContainer) node).getChildNodes()) {
+                assertDeepRevision(revision, child);
+            }
+        } else if (node instanceof ChoiceSchemaNode) {
+            for (DataSchemaNode child : ((ChoiceSchemaNode) node).getCases()) {
+                assertDeepRevision(revision, child);
+            }
+        }
+    }
+
+    private static final SchemaNode findSchemaNode(final DataNodeContainer container, final String... pathArgs) {
+        DataNodeContainer previous = container;
+
+        SchemaNode result = (container instanceof SchemaNode) ? (SchemaNode) container : null;
+        for (String arg : pathArgs) {
+            if (previous == null) {
+                return null;
+            }
+            for (DataSchemaNode child : previous.getChildNodes()) {
+                if (child.getQName().getLocalName().equals(arg)) {
+                    if (child instanceof DataNodeContainer) {
+                        previous = (DataNodeContainer) child;
+                    } else {
+                        previous = null;
+                    }
+                    result = child;
+                    break;
+                }
+            }
+        }
+        return result;
+    }
+
+    private static final Iterable<? extends InputStream> toInputStreams(final Set<YangModuleInfo> moduleInfos)
+            throws Exception {
+        Builder<InputStream> streams = ImmutableList.<InputStream> builder();
+        for (YangModuleInfo yangModuleInfo : moduleInfos) {
+            streams.add(yangModuleInfo.getModuleSourceStream());
+        }
+        return streams.build();
+    }
+
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..a81add9
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+      <groupId>org.opendaylight.odlparent</groupId>
+      <artifactId>odlparent-lite</artifactId>
+      <version>6.0.1</version>
+      <relativePath/>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>yangtools-aggregator</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <name>yangtools</name> <!-- Used by Sonar to set project name -->
+    <packaging>pom</packaging>
+
+    <scm>
+        <connection>scm:git:ssh://git.opendaylight.org:29418/yangtools.git</connection>
+        <developerConnection>scm:git:ssh://git.opendaylight.org:29418/yangtools.git</developerConnection>
+        <url>https://wiki.opendaylight.org/view/YANG_Tools:Main</url>
+        <tag>HEAD</tag>
+    </scm>
+
+    <modules>
+        <module>artifacts</module>
+        <module>benchmarks</module>
+        <module>bundle-parent</module>
+        <module>common</module>
+        <module>docs</module>
+        <module>features</module>
+        <module>yang</module>
+        <module>yang-validation-tool</module>
+    </modules>
+
+    <properties>
+        <maven.deploy.skip>true</maven.deploy.skip>
+        <maven.install.skip>true</maven.install.skip>
+    </properties>
+
+    <profiles>
+        <profile>
+            <id>karaf</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+
+            <modules>
+                <module>distribution-karaf</module>
+            </modules>
+        </profile>
+        <profile>
+            <id>sonar-jacoco-aggregate</id>
+            <activation>
+                <property>
+                    <name>odl.jacoco.aggregateFile</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.jacoco</groupId>
+                        <artifactId>jacoco-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>merge</id>
+                                <goals>
+                                    <goal>merge</goal>
+                                </goals>
+                                <phase>generate-resources</phase>
+                                <configuration>
+                                    <destFile>${odl.jacoco.aggregateFile}</destFile>
+                                    <fileSets>
+                                        <fileSet>
+                                            <directory>${project.basedir}</directory>
+                                            <includes>
+                                                <include>**/target/code-coverage/*.exec</include>
+                                            </includes>
+                                        </fileSet>
+                                    </fileSets>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/yang-validation-tool/pom.xml b/yang-validation-tool/pom.xml
new file mode 100644 (file)
index 0000000..49a396f
--- /dev/null
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2015 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>odlparent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>yang-validation-tool</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+
+    <properties>
+        <!-- FIXME: we have some violations here -->
+        <odlparent.spotbugs.enforce>false</odlparent.spotbugs.enforce>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-test-util</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.sourceforge.argparse4j</groupId>
+            <artifactId>argparse4j</artifactId>
+            <version>0.6.0</version>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <configuration/>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <!-- TODO investigate why jar fails without this
+                                filter -->
+                            <filters>
+                                <filter>
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>META-INF/*.SF</exclude>
+                                        <exclude>META-INF/*.DSA</exclude>
+                                        <exclude>META-INF/*.RSA</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                            <transformers>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>org.opendaylight.yangtools.yang.validation.tool.Main</mainClass>
+                                </transformer>
+                            </transformers>
+                            <shadedArtifactAttached>true</shadedArtifactAttached>
+                            <shadedClassifierName>executable</shadedClassifierName>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/Main.java b/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/Main.java
new file mode 100644 (file)
index 0000000..7c69a71
--- /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.validation.tool;
+
+import java.io.File;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class Main {
+    private static final Logger LOG = LoggerFactory.getLogger(Main.class);
+
+    private Main() {
+
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    public static void main(final String[] args) {
+        final Params params = ParamsUtil.parseArgs(args, Params.getParser());
+
+        if (params.isValid()) {
+            final File[] yangModels = params.getYangSourceDir().listFiles();
+
+            try {
+                YangParserTestUtils.parseYangFiles(yangModels);
+            } catch (Exception e) {
+                LOG.error("Yang files could not be parsed.", e);
+            }
+        }
+    }
+}
diff --git a/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/Params.java b/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/Params.java
new file mode 100644 (file)
index 0000000..82b92cf
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.validation.tool;
+
+import java.io.File;
+import net.sourceforge.argparse4j.ArgumentParsers;
+import net.sourceforge.argparse4j.annotation.Arg;
+import net.sourceforge.argparse4j.inf.ArgumentParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class Params {
+
+    private static final Logger LOG = LoggerFactory.getLogger(Params.class);
+    @Arg(dest = "yang-source-dir")
+    private File yangSourceDir;
+
+    static ArgumentParser getParser() {
+        final ArgumentParser parser = ArgumentParsers.newArgumentParser("jar_file_name");
+        parser.description("Validation Tool for Yang Models")
+            .formatUsage();
+
+        parser.addArgumentGroup("Required arguments")
+            .addArgument("--yang-source-dir")
+            .type(File.class)
+            .required(true)
+            .help("directory containing yang models which will be parsed")
+            .dest("yang-source-dir")
+            .metavar("");
+
+        return parser;
+    }
+
+    public boolean isValid() {
+        if (yangSourceDir == null) {
+            return false;
+        }
+        if (!yangSourceDir.exists()) {
+            LOG.error("Yang source directory has to exist");
+            return false;
+        }
+        if (!yangSourceDir.canRead()) {
+            LOG.error("Yang source directory has to be readable");
+            return false;
+        }
+        if (yangSourceDir.list().length == 0) {
+            LOG.error("Yang source directory {} doesn't contain any model", yangSourceDir.getPath());
+            return false;
+        }
+
+        return true;
+    }
+
+    public File getYangSourceDir() {
+        return yangSourceDir;
+    }
+}
diff --git a/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/ParamsUtil.java b/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/ParamsUtil.java
new file mode 100644 (file)
index 0000000..bfdfc3c
--- /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.validation.tool;
+
+import net.sourceforge.argparse4j.inf.ArgumentParser;
+import net.sourceforge.argparse4j.inf.ArgumentParserException;
+
+final class ParamsUtil {
+    private ParamsUtil() {
+
+    }
+
+    public static Params parseArgs(final String[] args, final ArgumentParser parser) {
+        final Params params = new Params();
+        try {
+            parser.parseArgs(args, params);
+            return params;
+        } catch (final ArgumentParserException e) {
+            parser.handleError(e);
+        }
+        System.exit(1);
+        return null;
+    }
+}
diff --git a/yang/odlext-model-api/pom.xml b/yang/odlext-model-api/pom.xml
new file mode 100644 (file)
index 0000000..1c52d17
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>odlext-model-api</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>OpenDaylight yang-ext metamodel</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.odlext.model.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/AnyxmlSchemaLocationEffectiveStatement.java b/yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/AnyxmlSchemaLocationEffectiveStatement.java
new file mode 100644 (file)
index 0000000..5ec1649
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.odlext.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+
+@Beta
+public interface AnyxmlSchemaLocationEffectiveStatement
+    extends EffectiveStatement<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement> {
+
+}
diff --git a/yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/AnyxmlSchemaLocationStatement.java b/yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/AnyxmlSchemaLocationStatement.java
new file mode 100644 (file)
index 0000000..62f9c1c
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.odlext.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+
+@Beta
+public interface AnyxmlSchemaLocationStatement extends UnknownStatement<SchemaNodeIdentifier> {
+
+}
diff --git a/yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/OpenDaylightExtensionsConstants.java b/yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/OpenDaylightExtensionsConstants.java
new file mode 100644 (file)
index 0000000..ce366cd
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.odlext.model.api;
+
+import com.google.common.collect.ImmutableList;
+import java.net.URI;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Constants associated with OpenDaylight extension in yang-ext.yang.
+ *
+ * @author Robert Varga
+ */
+public final class OpenDaylightExtensionsConstants {
+    private static final String MODULE_NAME = "yang-ext";
+    private static final URI MODULE_NAMESPACE = URI.create("urn:opendaylight:yang:extension:yang-ext");
+    private static final Revision ORIGINAL_REVISION = Revision.of("2013-07-09");
+
+    /**
+     * Runtime baseline identity.
+     */
+    public static final QNameModule ORIGINAL_MODULE = QNameModule.create(MODULE_NAMESPACE, ORIGINAL_REVISION).intern();
+
+    /**
+     * Baseline model source name.
+     */
+    public static final SourceIdentifier ORIGINAL_SOURCE = RevisionSourceIdentifier.create(MODULE_NAME,
+        ORIGINAL_REVISION);
+
+    private OpenDaylightExtensionsConstants() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Return identifiers of all sources known to define the metadata extension.
+     *
+     * @return Collection of identifiers.
+     */
+    public static Collection<SourceIdentifier> knownModelSources() {
+        return ImmutableList.of(ORIGINAL_SOURCE);
+    }
+}
diff --git a/yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/OpenDaylightExtensionsStatements.java b/yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/OpenDaylightExtensionsStatements.java
new file mode 100644 (file)
index 0000000..7ec3fe7
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.odlext.model.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+@Beta
+@NonNullByDefault
+public enum OpenDaylightExtensionsStatements implements StatementDefinition {
+    // FIXME: this extension is not present in yang-ext.yang as published by mdsal
+    ANYXML_SCHEMA_LOCATION(QName.create(OpenDaylightExtensionsConstants.ORIGINAL_MODULE, "anyxml-schema-location"),
+        "target-node", AnyxmlSchemaLocationStatement.class, AnyxmlSchemaLocationEffectiveStatement.class);
+    // FIXME: add augment-identifier
+    // FIXME: add rpc-context-instance
+    // FIXME: add context-reference
+    // FIXME: add context-instance
+    // FIXME: add instance-target
+
+    private final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation;
+    private final Class<? extends DeclaredStatement<?>> declaredRepresentation;
+    private final QName statementName;
+    private final ArgumentDefinition argumentDef;
+
+    OpenDaylightExtensionsStatements(final QName statementName, final String argumentName,
+            final Class<? extends DeclaredStatement<?>> declaredRepresentation,
+            final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation) {
+        this.statementName = statementName.intern();
+        this.argumentDef = ArgumentDefinition.of(QName.create(statementName, argumentName).intern(), false);
+        this.declaredRepresentation = requireNonNull(declaredRepresentation);
+        this.effectiveRepresentation = requireNonNull(effectiveRepresentation);
+    }
+
+    @Override
+    public QName getStatementName() {
+        return statementName;
+    }
+
+    @Override
+    public Optional<ArgumentDefinition> getArgumentDefinition() {
+        return Optional.of(argumentDef);
+    }
+
+    @Override
+    public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+        return declaredRepresentation;
+    }
+
+    @Override
+    public Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass() {
+        return effectiveRepresentation;
+    }
+}
diff --git a/yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/YangModeledAnyxmlSchemaNode.java b/yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/YangModeledAnyxmlSchemaNode.java
new file mode 100644 (file)
index 0000000..bcfa211
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015 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.odlext.model.api;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+/**
+ * The "YangModeledAnyXml" interface defines an interior node in the schema tree. It takes
+ * one argument, which is an identifier represented by QName inherited from
+ * {@link SchemaNode}, followed by a block of substatements that holds detailed
+ * anyxml information. The substatements are defined in {@link DataSchemaNode}.
+ * The "YangModeledAnyXml" in contrast to the "AnyXml" interface can also provide schema
+ * of contained XML information. <br>
+ * <br>
+ * This interface was modeled according to definition in <a
+ * href="https://tools.ietf.org/html/rfc6020#section-7.10">[RFC-6020] The anyxml
+ * Statement</a>
+ */
+@Beta
+public interface YangModeledAnyxmlSchemaNode extends AnyxmlSchemaNode {
+
+    /**
+     * Returns the root schema node of the data in this anyxml node.
+     *
+     * @return DataSchemaNode - schema of contained XML data
+     */
+    @NonNull ContainerSchemaNode getSchemaOfAnyXmlData();
+}
diff --git a/yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/package-info.java b/yang/odlext-model-api/src/main/java/org/opendaylight/yangtools/odlext/model/api/package-info.java
new file mode 100644 (file)
index 0000000..fc54ac2
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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
+ */
+/**
+ * YANG metamodel extensions to support OpenDaylight extensions as defined in yang-ext.yang.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.odlext.model.api;
diff --git a/yang/odlext-parser-support/pom.xml b/yang/odlext-parser-support/pom.xml
new file mode 100644 (file)
index 0000000..c226663
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>odlext-parser-support</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>OpenDaylight yang-ext parser support</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odlext-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-rfc7950</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.odlext.parser.support</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlSchemaLocationEffectiveStatementImpl.java b/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlSchemaLocationEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..8b8dc70
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015 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.odlext.parser;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.odlext.model.api.AnyxmlSchemaLocationEffectiveStatement;
+import org.opendaylight.yangtools.odlext.model.api.AnyxmlSchemaLocationStatement;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.UnknownEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+@VisibleForTesting
+public final class AnyxmlSchemaLocationEffectiveStatementImpl
+        extends UnknownEffectiveStatementBase<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement>
+        implements AnyxmlSchemaLocationEffectiveStatement {
+
+    private final @NonNull SchemaPath path;
+
+    AnyxmlSchemaLocationEffectiveStatementImpl(
+            final StmtContext<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement, ?> ctx) {
+        super(ctx);
+        path = ctx.coerceParentContext().getSchemaPath().get().createChild(getNodeType());
+    }
+
+    @Override
+    public @NonNull QName getQName() {
+        return getNodeType();
+    }
+
+    @Override
+    public @NonNull SchemaPath getPath() {
+        return path;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(path);
+        result = prime * result + Objects.hashCode(getNodeType());
+        result = prime * result + Objects.hashCode(getNodeParameter());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        AnyxmlSchemaLocationEffectiveStatementImpl other = (AnyxmlSchemaLocationEffectiveStatementImpl) obj;
+        return Objects.equals(path, other.path) && Objects.equals(getNodeType(), other.getNodeType())
+                && Objects.equals(getNodeParameter(), other.getNodeParameter());
+    }
+}
diff --git a/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlSchemaLocationNamespace.java b/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlSchemaLocationNamespace.java
new file mode 100644 (file)
index 0000000..6c98636
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015 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.odlext.parser;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.odlext.model.api.AnyxmlSchemaLocationStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+
+/**
+ * Source-specific mapping of prefixes to namespaces.
+ */
+public interface AnyxmlSchemaLocationNamespace extends IdentifierNamespace<StatementDefinition,
+    Mutable<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement,
+        EffectiveStatement<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement>>> {
+    NamespaceBehaviour<StatementDefinition, Mutable<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement,
+        EffectiveStatement<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement>>,
+        @NonNull AnyxmlSchemaLocationNamespace> BEHAVIOUR =
+            NamespaceBehaviour.treeScoped(AnyxmlSchemaLocationNamespace.class);
+}
diff --git a/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlSchemaLocationStatementImpl.java b/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlSchemaLocationStatementImpl.java
new file mode 100644 (file)
index 0000000..ec7d4f8
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 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.odlext.parser;
+
+import org.opendaylight.yangtools.odlext.model.api.AnyxmlSchemaLocationStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class AnyxmlSchemaLocationStatementImpl extends AbstractDeclaredStatement<SchemaNodeIdentifier>
+        implements AnyxmlSchemaLocationStatement {
+    AnyxmlSchemaLocationStatementImpl(
+            final StmtContext<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement, ?> context) {
+        super(context);
+    }
+
+    @Override
+    public SchemaNodeIdentifier getArgument() {
+        return argument();
+    }
+}
diff --git a/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlSchemaLocationStatementSupport.java b/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlSchemaLocationStatementSupport.java
new file mode 100644 (file)
index 0000000..9e9b13b
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.odlext.parser;
+
+import static org.opendaylight.yangtools.odlext.model.api.OpenDaylightExtensionsStatements.ANYXML_SCHEMA_LOCATION;
+
+import org.opendaylight.yangtools.odlext.model.api.AnyxmlSchemaLocationStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class AnyxmlSchemaLocationStatementSupport
+        extends AbstractStatementSupport<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement,
+            EffectiveStatement<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement>> {
+    private static final AnyxmlSchemaLocationStatementSupport INSTANCE =
+            new AnyxmlSchemaLocationStatementSupport(ANYXML_SCHEMA_LOCATION);
+
+    private final SubstatementValidator validator;
+
+    private AnyxmlSchemaLocationStatementSupport(final StatementDefinition definition) {
+        super(definition);
+        validator = SubstatementValidator.builder(definition).build();
+    }
+
+    public static AnyxmlSchemaLocationStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public SchemaNodeIdentifier parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return ArgumentUtils.nodeIdentifierFromPath(ctx, value);
+    }
+
+    @Override
+    public void onFullDefinitionDeclared(final Mutable<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement,
+            EffectiveStatement<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement>> stmt) {
+        super.onFullDefinitionDeclared(stmt);
+        stmt.coerceParentContext().addToNs(AnyxmlSchemaLocationNamespace.class, ANYXML_SCHEMA_LOCATION, stmt);
+    }
+
+    @Override
+    public AnyxmlSchemaLocationStatement createDeclared(
+            final StmtContext<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement, ?> ctx) {
+        return new AnyxmlSchemaLocationStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement> createEffective(
+            final StmtContext<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement,
+            EffectiveStatement<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement>> ctx) {
+        return new AnyxmlSchemaLocationEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return validator;
+    }
+}
\ No newline at end of file
diff --git a/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlStatementSupportOverride.java b/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/AnyxmlStatementSupportOverride.java
new file mode 100644 (file)
index 0000000..86c8873
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.odlext.parser;
+
+import java.util.Map;
+import java.util.Optional;
+import org.opendaylight.yangtools.odlext.model.api.AnyxmlSchemaLocationStatement;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.anyxml.AnyxmlStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ForwardingStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+
+public final class AnyxmlStatementSupportOverride
+        extends ForwardingStatementSupport<QName, AnyxmlStatement, AnyxmlEffectiveStatement> {
+    private static final AnyxmlStatementSupportOverride INSTANCE = new AnyxmlStatementSupportOverride();
+
+    private AnyxmlStatementSupportOverride() {
+
+    }
+
+    public static AnyxmlStatementSupportOverride getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected StatementSupport<QName, AnyxmlStatement, AnyxmlEffectiveStatement> delegate() {
+        return AnyxmlStatementSupport.getInstance();
+    }
+
+    @Override
+    public Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass() {
+        // FIXME: this is not entirely accurate?
+        return delegate().getEffectiveRepresentationClass();
+    }
+
+    @Override
+    public AnyxmlEffectiveStatement createEffective(
+            final StmtContext<QName, AnyxmlStatement, AnyxmlEffectiveStatement> ctx) {
+        final AnyxmlEffectiveStatement delegateStatement = delegate().createEffective(ctx);
+        final Map<StatementDefinition, Mutable<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement,
+            EffectiveStatement<SchemaNodeIdentifier, AnyxmlSchemaLocationStatement>>> schemaLocations =
+                ctx.getAllFromCurrentStmtCtxNamespace(AnyxmlSchemaLocationNamespace.class);
+        if (schemaLocations != null && !schemaLocations.isEmpty()) {
+            final SchemaNodeIdentifier anyXmlSchemaNodeIdentifier = schemaLocations.values().iterator().next()
+                    .getStatementArgument();
+            final Optional<ContainerSchemaNode> anyXmlSchema = getAnyXmlSchema(ctx, anyXmlSchemaNodeIdentifier);
+            if (anyXmlSchema.isPresent()) {
+                return new YangModeledAnyxmlEffectiveStatementImpl(delegateStatement, anyXmlSchema.get());
+            }
+        }
+
+        return delegateStatement;
+    }
+
+    private static Optional<ContainerSchemaNode> getAnyXmlSchema(
+            final StmtContext<QName, AnyxmlStatement, AnyxmlEffectiveStatement> ctx,
+            final SchemaNodeIdentifier contentSchemaPath) {
+        return ChildSchemaNodeNamespace.findNode(ctx.getRoot(), contentSchemaPath)
+                .map(StmtContext::buildEffective)
+                .filter(ContainerSchemaNode.class::isInstance).map(ContainerSchemaNode.class::cast);
+    }
+}
diff --git a/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/YangModeledAnyxmlEffectiveStatementImpl.java b/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/YangModeledAnyxmlEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..fbb678c
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2015 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.odlext.parser;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.ForwardingEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlStatement;
+
+final class YangModeledAnyxmlEffectiveStatementImpl
+        extends ForwardingEffectiveStatement<QName, AnyxmlStatement, AnyxmlEffectiveStatement>
+        implements YangModeledAnyxmlSchemaNode, AnyxmlEffectiveStatement {
+    private final @NonNull AnyxmlEffectiveStatement delegate;
+    private final @NonNull ContainerSchemaNode contentSchema;
+
+    YangModeledAnyxmlEffectiveStatementImpl(final AnyxmlEffectiveStatement delegate,
+        final ContainerSchemaNode contentSchema) {
+        this.delegate = requireNonNull(delegate);
+        this.contentSchema = requireNonNull(contentSchema);
+    }
+
+    @Override
+    protected @NonNull AnyxmlEffectiveStatement delegate() {
+        return delegate;
+    }
+
+    @Override
+    public @NonNull ContainerSchemaNode getSchemaOfAnyXmlData() {
+        return contentSchema;
+    }
+
+    private AnyxmlSchemaNode delegateSchemaNode() {
+        return (AnyxmlSchemaNode) delegate;
+    }
+
+    @Override
+    public boolean isConfiguration() {
+        return delegateSchemaNode().isConfiguration();
+    }
+
+    @Override
+    public QName getQName() {
+        return delegateSchemaNode().getQName();
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return delegateSchemaNode().getPath();
+    }
+
+    @Override
+    public Status getStatus() {
+        return delegateSchemaNode().getStatus();
+    }
+
+    @Override
+    public Optional<String> getDescription() {
+        return delegateSchemaNode().getDescription();
+    }
+
+    @Override
+    public Optional<String> getReference() {
+        return delegateSchemaNode().getReference();
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAugmenting() {
+        return delegateSchemaNode().isAugmenting();
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAddedByUses() {
+        return delegateSchemaNode().isAddedByUses();
+    }
+
+    @Override
+    public Optional<RevisionAwareXPath> getWhenCondition() {
+        return delegateSchemaNode().getWhenCondition();
+    }
+
+    @Override
+    public boolean isMandatory() {
+        return delegateSchemaNode().isConfiguration();
+    }
+
+    @Override
+    public Collection<MustDefinition> getMustConstraints() {
+        return delegateSchemaNode().getMustConstraints();
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return delegateSchemaNode().getUnknownSchemaNodes();
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getQName());
+        result = prime * result + Objects.hashCode(getPath());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        YangModeledAnyxmlEffectiveStatementImpl other = (YangModeledAnyxmlEffectiveStatementImpl) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return YangModeledAnyxmlEffectiveStatementImpl.class.getSimpleName() + "["
+               + "qname=" + getQName()
+               + ", path=" + getPath()
+               + "]";
+    }
+}
diff --git a/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/package-info.java b/yang/odlext-parser-support/src/main/java/org/opendaylight/yangtools/odlext/parser/package-info.java
new file mode 100644 (file)
index 0000000..bd3cedc
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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
+ */
+/**
+ * YANG parser support for OpenDaylight extensions as defined in yang-ext.yang.
+ *
+ * <p>
+ * To access this extension, add {@link org.opendaylight.yangtools.odlext.parser.AnyxmlSchemaLocationStatementSupport},
+ * {@link org.opendaylight.yangtools.odlext.parser.AnyxmlStatementSupportOverride} and
+ * {@link org.opendaylight.yangtools.odlext.parser.AnyxmlSchemaLocationNamespace#BEHAVIOUR} to the reactor.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.odlext.parser;
diff --git a/yang/odlext-parser-support/src/test/java/org/opendaylight/yangtools/odlext/parser/Bug3874ExtensionTest.java b/yang/odlext-parser-support/src/test/java/org/opendaylight/yangtools/odlext/parser/Bug3874ExtensionTest.java
new file mode 100644 (file)
index 0000000..f0b309d
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016 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.odlext.parser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.List;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.odlext.model.api.OpenDaylightExtensionsStatements;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+
+public class Bug3874ExtensionTest {
+    private static CrossSourceStatementReactor reactor;
+
+    @BeforeClass
+    public static void createReactor() {
+        reactor = RFC7950Reactors.vanillaReactorBuilder()
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
+                    AnyxmlSchemaLocationStatementSupport.getInstance())
+                .addNamespaceSupport(ModelProcessingPhase.FULL_DECLARATION, AnyxmlSchemaLocationNamespace.BEHAVIOUR)
+                .overrideStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
+                    AnyxmlStatementSupportOverride.getInstance())
+                .build();
+    }
+
+    @AfterClass
+    public static void freeReactor() {
+        reactor = null;
+    }
+
+    @Test
+    public void test() throws Exception {
+        SchemaContext context = reactor.newBuild()
+                .addSource(YangStatementStreamSource.create(YangTextSchemaSource.forResource("/bugs/bug3874/foo.yang")))
+                .addSource(YangStatementStreamSource.create(
+                    YangTextSchemaSource.forResource("/bugs/bug3874/yang-ext.yang")))
+                .buildEffective();
+
+        QNameModule foo = QNameModule.create(URI.create("foo"));
+        QName myContainer2QName = QName.create(foo, "my-container-2");
+        QName myAnyXmlDataQName = QName.create(foo, "my-anyxml-data");
+
+        DataSchemaNode dataChildByName = context.findDataChildByName(myAnyXmlDataQName).get();
+        assertTrue(dataChildByName instanceof YangModeledAnyxmlSchemaNode);
+        YangModeledAnyxmlSchemaNode yangModeledAnyXml = (YangModeledAnyxmlSchemaNode) dataChildByName;
+
+        SchemaNode myContainer2 = SchemaContextUtil.findDataSchemaNode(context,
+            SchemaPath.create(true, myContainer2QName));
+        assertTrue(myContainer2 instanceof ContainerSchemaNode);
+        assertEquals(myContainer2, yangModeledAnyXml.getSchemaOfAnyXmlData());
+
+        List<UnknownSchemaNode> unknownSchemaNodes = yangModeledAnyXml.getUnknownSchemaNodes();
+        assertEquals(1, unknownSchemaNodes.size());
+
+        UnknownSchemaNode next = unknownSchemaNodes.iterator().next();
+        assertTrue(next instanceof AnyxmlSchemaLocationEffectiveStatementImpl);
+        AnyxmlSchemaLocationEffectiveStatementImpl anyxmlSchemaLocationUnknownNode =
+                (AnyxmlSchemaLocationEffectiveStatementImpl) next;
+        assertEquals(OpenDaylightExtensionsStatements.ANYXML_SCHEMA_LOCATION.getStatementName(),
+            anyxmlSchemaLocationUnknownNode.getNodeType());
+        assertEquals(OpenDaylightExtensionsStatements.ANYXML_SCHEMA_LOCATION.getStatementName(),
+            anyxmlSchemaLocationUnknownNode.getQName());
+    }
+}
diff --git a/yang/odlext-parser-support/src/test/resources/bugs/bug3874/foo.yang b/yang/odlext-parser-support/src/test/resources/bugs/bug3874/foo.yang
new file mode 100644 (file)
index 0000000..a36ba24
--- /dev/null
@@ -0,0 +1,35 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    import yang-ext { prefix ext; revision-date 2013-07-09; }
+
+    container my-container-1 {
+        leaf my-leaf-1 {
+            type string;
+        }
+    }
+
+    container my-container-2 {
+        container inner-container {
+            leaf my-leaf-2 {
+                type string;
+            }
+        }
+        leaf my-leaf-3 {
+            type string;
+        }
+    }
+
+    anyxml my-anyxml-data {
+        ext:anyxml-schema-location "/my-container-2";
+    }
+
+    foo:my-extension "arg-value" {
+        foo:my-extension "arg-value-2";
+    }
+
+    extension my-extension {
+        argument arg-name;
+    }
+}
diff --git a/yang/odlext-parser-support/src/test/resources/bugs/bug3874/yang-ext.yang b/yang/odlext-parser-support/src/test/resources/bugs/bug3874/yang-ext.yang
new file mode 100644 (file)
index 0000000..2cdc211
--- /dev/null
@@ -0,0 +1,75 @@
+module yang-ext {
+    yang-version 1;
+    namespace "urn:opendaylight:yang:extension:yang-ext";
+    prefix "ext";
+
+    contact "Anton Tkacik <ttkacik@cisco.com>";
+
+    description
+        "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";
+
+    revision "2013-07-09" {
+        description "";
+    }
+
+    // Augmentation name
+
+    extension "augment-identifier" {
+        description
+                "YANG language extension which assigns an identifier to augmentation.
+                Augment identifier is used to identify specific augment statement
+                by name. The identifier syntax is defined formally defined by
+                the rule 'identifier' in Section 12 of RFC 6020. All augment identifiers
+                defined in a namespace MUST be unique. The namespace of augment
+                identifiers is shared by module and its submodules.";
+
+        /*
+                Discussion:
+                This extension allows for ease of development / debug
+                of YANG modules and it is suitable for code generation,
+                where each augment statement is nicely identified by
+                unique name instead of combination of augment target
+                and when condition. 
+            */
+        argument "identifier";
+    }
+
+    // Context-aware RPCs
+
+    grouping rpc-context-ref {
+        description
+                "A reference to RPC context.";
+        leaf context-instance {
+            type instance-identifier;
+            description "Pointer to the context. ";
+        }
+    }
+
+    extension "rpc-context-instance" {
+        description
+                "YANG language extension which defines enclosing (parent) schema
+                node as referencable context for RPCs. The argument is identity
+                which is used to identify RPC context type.";
+
+        argument "context-type";
+    }
+
+    extension "context-reference" {
+        argument "context-type";
+    }
+
+    extension "context-instance" {
+        argument "context-type";
+    }
+
+    extension "instance-target" {
+        argument "path";
+    }
+
+    extension "anyxml-schema-location" {
+        argument "target-node";
+    }
+}
diff --git a/yang/openconfig-model-api/pom.xml b/yang/openconfig-model-api/pom.xml
new file mode 100644 (file)
index 0000000..6382c63
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>openconfig-model-api</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>OpenConfig extensions metamodel</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.openconfig.model.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigConstants.java b/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigConstants.java
new file mode 100644 (file)
index 0000000..f3efaa1
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.openconfig.model.api;
+
+import com.google.common.collect.ImmutableList;
+import java.net.URI;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Constants associated with OpenDaylight extension in yang-ext.yang.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault
+public final class OpenConfigConstants {
+    private static final String MODULE_NAME = "yang-ext";
+
+    // Package-visible, because openconfig-version applies across all known revisions and needs to bind to all of them
+    static final URI MODULE_NAMESPACE = URI.create("http://openconfig.net/yang/openconfig-ext");
+
+    // Initial revision, defining semantic-version
+    private static final Revision SEMVER_REVISION = Revision.of("2015-10-09");
+
+    // Revised extension, adds openconfig-encrypted-value
+    private static final Revision ENCRYPTED_VALUE_REVISION = Revision.of("2017-01-29");
+
+    // Revised extension, renames openconfig-encrypted-value to openconfig-hashed-value
+    private static final Revision HASHED_VALUE_REVISION = Revision.of("2017-04-11");
+
+    /**
+     * Runtime identity of initial model.
+     */
+    public static final QNameModule SEMVER_MODULE = QNameModule.create(MODULE_NAMESPACE, SEMVER_REVISION).intern();
+
+    /**
+     * Runtime identity of model which exposed encrypted-value.
+     */
+    public static final QNameModule ENCRYPTED_VALUE_MODULE = QNameModule.create(MODULE_NAMESPACE,
+        ENCRYPTED_VALUE_REVISION).intern();
+
+    /**
+     * Runtime identity of model which exposed encrypted-value.
+     */
+    public static final QNameModule HASHED_VALUE_MODULE = QNameModule.create(MODULE_NAMESPACE, HASHED_VALUE_REVISION)
+            .intern();
+
+    /**
+     * Original model source name.
+     */
+    public static final SourceIdentifier SEMVER_SOURCE = RevisionSourceIdentifier.create(MODULE_NAME,
+        SEMVER_REVISION);
+
+    /**
+     * Original model source name.
+     */
+    public static final SourceIdentifier ENCRYPTED_VALUE_SOURCE = RevisionSourceIdentifier.create(MODULE_NAME,
+        ENCRYPTED_VALUE_REVISION);
+
+    /**
+     * Original model source name.
+     */
+    public static final SourceIdentifier HASHED_VALUE_SOURCE = RevisionSourceIdentifier.create(MODULE_NAME,
+        HASHED_VALUE_REVISION);
+
+    /**
+     * Normative prefix to use when importing {@link #SEMVER_SOURCE} and later.
+     */
+    public static final String MODULE_PREFIX = "oc-ext";
+
+    private OpenConfigConstants() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Return identifiers of all sources known to define the metadata extension.
+     *
+     * @return Collection of identifiers.
+     */
+    public static Collection<SourceIdentifier> knownModelSources() {
+        return ImmutableList.of(HASHED_VALUE_SOURCE, ENCRYPTED_VALUE_SOURCE, SEMVER_SOURCE);
+    }
+}
diff --git a/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigHashedValueEffectiveStatement.java b/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigHashedValueEffectiveStatement.java
new file mode 100644 (file)
index 0000000..5ee1197
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.openconfig.model.api;
+
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Effective statement corresponding to config's "openconfig-hashed-value" (new name) or "openconfig-encrypted-value"
+ * (old name).
+ *
+ * @author Robert Varga
+ */
+public interface OpenConfigHashedValueEffectiveStatement
+    extends EffectiveStatement<Void, OpenConfigHashedValueStatement> {
+
+}
diff --git a/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigHashedValueSchemaNode.java b/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigHashedValueSchemaNode.java
new file mode 100644 (file)
index 0000000..494997d
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.openconfig.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+/**
+ * SchemaNode presence in SchemaNode API space.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface OpenConfigHashedValueSchemaNode extends UnknownSchemaNode {
+    /**
+     * Determine if specified SchemaNode is marked to report its value in hashed form.
+     *
+     * @param schemaNode schema node to examine
+     * @return True if specified node is marked, false otherwise
+     * @throws NullPointerException if schemaNode is null
+     */
+    static boolean isPresentIn(final SchemaNode schemaNode) {
+        return schemaNode.getUnknownSchemaNodes().stream().anyMatch(OpenConfigHashedValueSchemaNode.class::isInstance);
+    }
+}
diff --git a/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigHashedValueStatement.java b/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigHashedValueStatement.java
new file mode 100644 (file)
index 0000000..f96439a
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.openconfig.model.api;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+
+/**
+ * Declared statement corresponding to config's "openconfig-hashed-value" (new name) or "openconfig-encrypted-value"
+ * (old name).
+ *
+ * @author Robert Varga
+ */
+public interface OpenConfigHashedValueStatement extends UnknownStatement<Void> {
+
+}
diff --git a/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigStatements.java b/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigStatements.java
new file mode 100644 (file)
index 0000000..9fcc47c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.openconfig.model.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+@Beta
+@NonNullByDefault
+public enum OpenConfigStatements implements StatementDefinition {
+    OPENCONFIG_ENCRYPTED_VALUE(QName.create(OpenConfigConstants.ENCRYPTED_VALUE_MODULE, "openconfig-encrypted-value"),
+        null, OpenConfigHashedValueStatement.class, OpenConfigHashedValueEffectiveStatement.class),
+    OPENCONFIG_HASHED_VALUE(QName.create(OpenConfigConstants.HASHED_VALUE_MODULE, "openconfig-hashed-value"), null,
+        OpenConfigHashedValueStatement.class, OpenConfigHashedValueEffectiveStatement.class),
+    OPENCONFIG_VERSION(QName.create(OpenConfigConstants.MODULE_NAMESPACE, "openconfig-version"), "semver",
+        OpenConfigVersionStatement.class, OpenConfigVersionEffectiveStatement.class);
+
+    private final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation;
+    private final Class<? extends DeclaredStatement<?>> declaredRepresentation;
+    private final QName statementName;
+    private final @Nullable QName argumentName;
+
+    @SuppressFBWarnings("NP_STORE_INTO_NONNULL_FIELD")
+    OpenConfigStatements(final QName statementName, @Nullable final String argumentName,
+            final Class<? extends DeclaredStatement<?>> declaredRepresentation,
+            final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation) {
+        this.statementName = statementName.intern();
+        this.argumentName = argumentName != null ? QName.create(statementName, argumentName) : null;
+        this.declaredRepresentation = requireNonNull(declaredRepresentation);
+        this.effectiveRepresentation = requireNonNull(effectiveRepresentation);
+    }
+
+    @Override
+    public QName getStatementName() {
+        return statementName;
+    }
+
+    @Override
+    public Optional<ArgumentDefinition> getArgumentDefinition() {
+        return ArgumentDefinition.ofNullable(argumentName, false);
+    }
+
+    @Override
+    public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+        return declaredRepresentation;
+    }
+
+    @Override
+    public Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass() {
+        return effectiveRepresentation;
+    }
+}
diff --git a/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigVersionEffectiveStatement.java b/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigVersionEffectiveStatement.java
new file mode 100644 (file)
index 0000000..44e7778
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.openconfig.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface OpenConfigVersionEffectiveStatement extends EffectiveStatement<SemVer, OpenConfigVersionStatement> {
+
+}
diff --git a/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigVersionStatement.java b/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/OpenConfigVersionStatement.java
new file mode 100644 (file)
index 0000000..2db4f4e
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.openconfig.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+
+@Beta
+public interface OpenConfigVersionStatement extends UnknownStatement<SemVer> {
+
+}
diff --git a/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/package-info.java b/yang/openconfig-model-api/src/main/java/org/opendaylight/yangtools/openconfig/model/api/package-info.java
new file mode 100644 (file)
index 0000000..434cecf
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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
+ */
+/**
+ * YANG parser support for metamodel extensions defined in
+ * <a href="https://github.com/openconfig/public/blob/master/release/models/openconfig-extensions.yang">OpenConfig</a>.
+ *
+ * <p>
+ * The openconfig-version extension is integrated in the parser, as it supports the semantic version import resolution,
+ * for other extensions use
+ * {@link org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements}. SchemaNode world
+ * primary entry point is defined in
+ * {@link org.opendaylight.yangtools.openconfig.model.api.OpenConfigHashedValueSchemaNode#isPresentIn(
+ * org.opendaylight.yangtools.yang.model.api.SchemaNode)}.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.openconfig.model.api;
diff --git a/yang/openconfig-parser-support/pom.xml b/yang/openconfig-parser-support/pom.xml
new file mode 100644 (file)
index 0000000..0ad72d9
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>openconfig-parser-support</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>OpenConfig extensions parser support</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>openconfig-model-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-rfc7950</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.openconfig.parser.support</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/openconfig-parser-support/src/main/java/org/opendaylight/yangtools/openconfig/parser/AbstractHashedValueStatementSupport.java b/yang/openconfig-parser-support/src/main/java/org/opendaylight/yangtools/openconfig/parser/AbstractHashedValueStatementSupport.java
new file mode 100644 (file)
index 0000000..d51e00d
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.openconfig.parser;
+
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigHashedValueEffectiveStatement;
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigHashedValueStatement;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.UnknownEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractVoidStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+abstract class AbstractHashedValueStatementSupport
+        extends AbstractVoidStatementSupport<OpenConfigHashedValueStatement,
+            OpenConfigHashedValueEffectiveStatement> {
+
+    private static final class Declared extends AbstractDeclaredStatement<Void>
+        implements OpenConfigHashedValueStatement {
+        Declared(final StmtContext<Void, ?, ?> context) {
+            super(context);
+        }
+
+        @Override
+        public Void getArgument() {
+            return null;
+        }
+    }
+
+    private static final class Effective
+            extends UnknownEffectiveStatementBase<Void, OpenConfigHashedValueStatement>
+            implements OpenConfigHashedValueEffectiveStatement {
+
+        private final SchemaPath path;
+
+        Effective(final StmtContext<Void, OpenConfigHashedValueStatement, ?> ctx) {
+            super(ctx);
+            path = ctx.coerceParentContext().getSchemaPath().get().createChild(
+                ctx.getPublicDefinition().getStatementName());
+        }
+
+        @Override
+        public QName getQName() {
+            return path.getLastComponent();
+        }
+
+        @Override
+        public SchemaPath getPath() {
+            return path;
+        }
+    }
+
+    private final SubstatementValidator validator;
+
+    AbstractHashedValueStatementSupport(final StatementDefinition definition) {
+        super(definition);
+        this.validator = SubstatementValidator.builder(definition).build();
+    }
+
+    @Override
+    public final OpenConfigHashedValueStatement createDeclared(
+            final StmtContext<Void, OpenConfigHashedValueStatement, ?> ctx) {
+        return new Declared(ctx);
+    }
+
+    @Override
+    public final OpenConfigHashedValueEffectiveStatement createEffective(
+            final StmtContext<Void, OpenConfigHashedValueStatement, OpenConfigHashedValueEffectiveStatement> ctx) {
+        return new Effective(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return validator;
+    }
+}
diff --git a/yang/openconfig-parser-support/src/main/java/org/opendaylight/yangtools/openconfig/parser/EncryptedValueStatementSupport.java b/yang/openconfig-parser-support/src/main/java/org/opendaylight/yangtools/openconfig/parser/EncryptedValueStatementSupport.java
new file mode 100644 (file)
index 0000000..22cf305
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.openconfig.parser;
+
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
+
+public final class EncryptedValueStatementSupport extends AbstractHashedValueStatementSupport {
+    private static final EncryptedValueStatementSupport INSTANCE = new EncryptedValueStatementSupport();
+
+    private EncryptedValueStatementSupport() {
+        super(OpenConfigStatements.OPENCONFIG_ENCRYPTED_VALUE);
+    }
+
+    public static EncryptedValueStatementSupport getInstance() {
+        return INSTANCE;
+    }
+}
diff --git a/yang/openconfig-parser-support/src/main/java/org/opendaylight/yangtools/openconfig/parser/HashedValueStatementSupport.java b/yang/openconfig-parser-support/src/main/java/org/opendaylight/yangtools/openconfig/parser/HashedValueStatementSupport.java
new file mode 100644 (file)
index 0000000..5ed33d8
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.openconfig.parser;
+
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
+
+public final class HashedValueStatementSupport extends AbstractHashedValueStatementSupport {
+    private static final HashedValueStatementSupport INSTANCE = new HashedValueStatementSupport();
+
+    private HashedValueStatementSupport() {
+        super(OpenConfigStatements.OPENCONFIG_HASHED_VALUE);
+    }
+
+    public static HashedValueStatementSupport getInstance() {
+        return INSTANCE;
+    }
+}
diff --git a/yang/openconfig-parser-support/src/main/java/org/opendaylight/yangtools/openconfig/parser/package-info.java b/yang/openconfig-parser-support/src/main/java/org/opendaylight/yangtools/openconfig/parser/package-info.java
new file mode 100644 (file)
index 0000000..98fa75e
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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
+ */
+/**
+ * YANG parser support for metamodel extensions defined in
+ * <a href="https://github.com/openconfig/public/blob/master/release/models/openconfig-extensions.yang">OpenConfig</a>.
+ *
+ * <p>
+ * The openconfig-version extension is integrated in the parser, as it supports the semantic version import resolution,
+ * for other extensions use {@link org.opendaylight.yangtools.openconfig.parser.EncryptedValueStatementSupport}
+ * and {@link org.opendaylight.yangtools.openconfig.parser.HashedValueStatementSupport}.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.openconfig.parser;
diff --git a/yang/pom.xml b/yang/pom.xml
new file mode 100644 (file)
index 0000000..3e01fb2
--- /dev/null
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <groupId>org.opendaylight.odlparent</groupId>
+        <artifactId>odlparent-lite</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>yang-aggregator</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>yang-common</module>
+        <module>yang-data-api</module>
+        <module>yang-data-xpath-api</module>
+        <module>yang-data-jaxen</module>
+        <module>yang-data-util</module>
+        <module>yang-data-impl</module>
+        <module>yang-data-transform</module>
+        <module>yang-data-codec-gson</module>
+        <module>yang-data-codec-xml</module>
+        <module>yang-maven-plugin</module>
+        <module>yang-maven-plugin-it</module>
+        <module>yang-maven-plugin-spi</module>
+        <module>yang-model-api</module>
+        <module>yang-model-export</module>
+        <module>yang-model-util</module>
+
+        <!-- YANG XPath API and implementation -->
+        <module>yang-xpath-api</module>
+        <module>yang-xpath-impl</module>
+
+        <!-- End-user utility for validating YANG models -->
+        <module>yang-model-validator</module>
+
+        <!-- YANG parser end-user API -->
+        <module>yang-parser-api</module>
+        <module>yang-test-util</module>
+
+        <!-- YANG parser reference implementation -->
+        <module>yang-parser-impl</module>
+
+        <!-- Metamodel statement reactor -->
+        <module>yang-parser-spi</module>
+        <module>yang-parser-reactor</module>
+
+        <!-- Combined RFC6020/RFC7960 metamodel implementation -->
+        <module>yang-parser-rfc7950</module>
+
+        <!-- OpenConfig (semantic version, hashed value) metamodel support -->
+        <module>openconfig-model-api</module>
+        <!-- OpenConfig hashed value parser support. Semantic version support
+             is integrated in yang-parser-rfc7950. -->
+        <module>openconfig-parser-support</module>
+
+        <!-- OpenDaylight messy extensions -->
+        <module>odlext-model-api</module>
+        <module>odlext-parser-support</module>
+
+        <!-- NETCONF messy extensions -->
+        <module>rfc6241-model-api</module>
+        <module>rfc6241-parser-support</module>
+
+        <!-- NACM metamodel support -->
+        <module>rfc6536-model-api</module>
+        <module>rfc6536-parser-support</module>
+
+        <!-- Metadata (annotation) metamodel support -->
+        <module>rfc7952-data-api</module>
+        <module>rfc7952-data-util</module>
+        <module>rfc7952-model-api</module>
+        <module>rfc7952-parser-support</module>
+
+        <!-- RFC8040 (yang-data) metamodel support -->
+        <module>rfc8040-model-api</module>
+        <module>rfc8040-parser-support</module>
+
+        <!-- RFC8528 (mount-point) metamodel support -->
+        <module>rfc8528-data-api</module>
+        <module>rfc8528-data-util</module>
+        <module>rfc8528-model-api</module>
+        <module>rfc8528-parser-support</module>
+    </modules>
+
+</project>
diff --git a/yang/rfc6241-model-api/pom.xml b/yang/rfc6241-model-api/pom.xml
new file mode 100644 (file)
index 0000000..5117108
--- /dev/null
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc6241-model-api</artifactId>
+    <version>2.0.3-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC6241 extensions for unbound metadata</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc6241.model.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/GetFilterElementAttributesEffectiveStatement.java b/yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/GetFilterElementAttributesEffectiveStatement.java
new file mode 100644 (file)
index 0000000..fa499a4
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc6241.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Effective statement representation of 'get-filter-element-attributes' extension defined in
+ * <a href="https://tools.ietf.org/html/rfc6241">RFC6241</a>.
+ */
+@Beta
+public interface GetFilterElementAttributesEffectiveStatement
+        extends EffectiveStatement<Void, GetFilterElementAttributesStatement> {
+
+}
diff --git a/yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/GetFilterElementAttributesSchemaNode.java b/yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/GetFilterElementAttributesSchemaNode.java
new file mode 100644 (file)
index 0000000..5394df1
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc6241.model.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+/**
+ * Represents the effect of 'get-filter-element-attributes' extension, as defined in
+ * <a href="https://tools.ietf.org/html/rfc6241">RFC6241</a>.
+ */
+@Beta
+public interface GetFilterElementAttributesSchemaNode extends UnknownSchemaNode {
+    /**
+     * Attempt to find a {@link GetFilterElementAttributesSchemaNode} in a parent {@link AnyxmlSchemaNode}.
+     *
+     * @param parent Parent to search
+     * @return {@link GetFilterElementAttributesSchemaNode} child, if present.
+     */
+    static Optional<GetFilterElementAttributesSchemaNode> findIn(final AnyxmlSchemaNode parent) {
+        return parent.getUnknownSchemaNodes().stream().filter(GetFilterElementAttributesSchemaNode.class::isInstance)
+                .findAny().map(GetFilterElementAttributesSchemaNode.class::cast);
+    }
+}
diff --git a/yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/GetFilterElementAttributesStatement.java b/yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/GetFilterElementAttributesStatement.java
new file mode 100644 (file)
index 0000000..6d3a6a3
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2019 PATHEON.tech, s.r.o. 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.rfc6241.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+
+/**
+ * Declared statement representation of 'get-filter-element-attributes' extension defined in
+ * <a href="https://tools.ietf.org/html/rfc6241">RFC6241</a>.
+ */
+@Beta
+public interface GetFilterElementAttributesStatement extends UnknownStatement<Void> {
+
+}
diff --git a/yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/NetconfConstants.java b/yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/NetconfConstants.java
new file mode 100644 (file)
index 0000000..4c16ad8
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc6241.model.api;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import java.net.URI;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Constants associated with RFC6241.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public final class NetconfConstants {
+    private static final String MODULE_NAME = "ietf-netconf";
+    private static final URI MODULE_NAMESPACE = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
+    private static final Revision RFC6241_REVISION = Revision.of("2011-06-01");
+
+    /**
+     * Runtime RFC6241 identity.
+     */
+    public static final QNameModule RFC6241_MODULE = QNameModule.create(MODULE_NAMESPACE, RFC6241_REVISION).intern();
+
+    /**
+     * RFC6241 model source name.
+     */
+    public static final SourceIdentifier RFC6241_SOURCE = RevisionSourceIdentifier.create(MODULE_NAME,
+        RFC6241_REVISION);
+
+    /**
+     * Normative prefix to use when importing {@link #RFC6241_SOURCE}.
+     */
+    public static final String MODULE_PREFIX = "nc";
+
+    private NetconfConstants() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Return identifiers of all sources known to define NACM extension.
+     *
+     * @return Collection of identifiers.
+     */
+    public static Collection<SourceIdentifier> knownModelSources() {
+        return ImmutableList.of(RFC6241_SOURCE);
+    }
+}
diff --git a/yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/NetconfStatements.java b/yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/NetconfStatements.java
new file mode 100644 (file)
index 0000000..c8fe9ce
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc6241.model.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+/**
+ * {@link StatementDefinition}s for statements defined by RFC6241.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public enum NetconfStatements implements StatementDefinition {
+    GET_FILTER_ELEMENT_ATTRIBUTES(QName.create(NetconfConstants.RFC6241_MODULE, "get-filter-element-attributes"),
+        GetFilterElementAttributesStatement.class, GetFilterElementAttributesEffectiveStatement.class);
+
+    private final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation;
+    private final Class<? extends DeclaredStatement<?>> declaredRepresentation;
+    private final QName statementName;
+
+    NetconfStatements(final QName statementName, final Class<? extends DeclaredStatement<?>> declaredRepresentation,
+            final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation) {
+        this.statementName = statementName.intern();
+        this.declaredRepresentation = requireNonNull(declaredRepresentation);
+        this.effectiveRepresentation = requireNonNull(effectiveRepresentation);
+    }
+
+    @Override
+    public Optional<ArgumentDefinition> getArgumentDefinition() {
+        return Optional.empty();
+    }
+
+    @Override
+    public QName getStatementName() {
+        return statementName;
+    }
+
+    @Override
+    public Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass() {
+        return effectiveRepresentation;
+    }
+
+    @Override
+    public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+        return declaredRepresentation;
+    }
+}
diff --git a/yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/package-info.java b/yang/rfc6241-model-api/src/main/java/org/opendaylight/yangtools/rfc6241/model/api/package-info.java
new file mode 100644 (file)
index 0000000..5878373
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+ */
+/**
+ * YANG metamodel extensions to support NETCONF, filters as defined in
+ * <a href="https://tools.ietf.org/html/rfc6241">RFC6241</a>.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.rfc6241.model.api;
diff --git a/yang/rfc6241-parser-support/pom.xml b/yang/rfc6241-parser-support/pom.xml
new file mode 100644 (file)
index 0000000..b01b164
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc6241-parser-support</artifactId>
+    <version>2.0.3-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC6241 parser support</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc6241-model-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-rfc7950</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc6241.parser.support</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc6241-parser-support/src/main/java/org/opendaylight/yangtools/rfc6241/parser/GetFilterElementAttributesStatementSupport.java b/yang/rfc6241-parser-support/src/main/java/org/opendaylight/yangtools/rfc6241/parser/GetFilterElementAttributesStatementSupport.java
new file mode 100644 (file)
index 0000000..ffbb4f7
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc6241.parser;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.rfc6241.model.api.GetFilterElementAttributesEffectiveStatement;
+import org.opendaylight.yangtools.rfc6241.model.api.GetFilterElementAttributesSchemaNode;
+import org.opendaylight.yangtools.rfc6241.model.api.GetFilterElementAttributesStatement;
+import org.opendaylight.yangtools.rfc6241.model.api.NetconfStatements;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.UnknownEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractVoidStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+public final class GetFilterElementAttributesStatementSupport
+    extends AbstractVoidStatementSupport<GetFilterElementAttributesStatement,
+        GetFilterElementAttributesEffectiveStatement> {
+
+    private static final class Declared extends AbstractDeclaredStatement<Void>
+            implements GetFilterElementAttributesStatement {
+        Declared(final StmtContext<Void, ?, ?> context) {
+            super(context);
+        }
+
+        @Override
+        public Void getArgument() {
+            return null;
+        }
+    }
+
+    private static final class Effective
+            extends UnknownEffectiveStatementBase<Void, GetFilterElementAttributesStatement>
+            implements GetFilterElementAttributesEffectiveStatement, GetFilterElementAttributesSchemaNode {
+
+        private final SchemaPath path;
+
+        Effective(final StmtContext<Void, GetFilterElementAttributesStatement, ?> ctx) {
+            super(ctx);
+            path = ctx.coerceParentContext().getSchemaPath().get().createChild(
+                ctx.getPublicDefinition().getStatementName());
+        }
+
+        @Override
+        public QName getQName() {
+            return path.getLastComponent();
+        }
+
+        @Override
+        public SchemaPath getPath() {
+            return path;
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(GetFilterElementAttributesStatementSupport.class);
+    private static final GetFilterElementAttributesStatementSupport INSTANCE =
+            new GetFilterElementAttributesStatementSupport(NetconfStatements.GET_FILTER_ELEMENT_ATTRIBUTES);
+
+    private final SubstatementValidator validator;
+
+    GetFilterElementAttributesStatementSupport(final StatementDefinition definition) {
+        super(definition);
+        this.validator = SubstatementValidator.builder(definition).build();
+    }
+
+    public static GetFilterElementAttributesStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public GetFilterElementAttributesStatement createDeclared(
+            final StmtContext<Void, GetFilterElementAttributesStatement, ?> ctx) {
+        return new Declared(ctx);
+    }
+
+    @Override
+    public GetFilterElementAttributesEffectiveStatement createEffective(final StmtContext<Void,
+            GetFilterElementAttributesStatement, GetFilterElementAttributesEffectiveStatement> ctx) {
+        return new Effective(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return validator;
+    }
+
+    @Override
+    public void onFullDefinitionDeclared(final Mutable<Void, GetFilterElementAttributesStatement,
+            GetFilterElementAttributesEffectiveStatement> stmt) {
+        super.onFullDefinitionDeclared(stmt);
+        stmt.setIsSupportedToBuildEffective(computeSupported(stmt));
+    }
+
+    private static boolean computeSupported(final StmtContext<?, ?, ?> stmt) {
+        final StmtContext<?, ?, ?> parent = stmt.getParentContext();
+        if (parent == null) {
+            LOG.debug("No parent, ignoring get-filter-element-attributes statement");
+            return false;
+        }
+        if (parent.getPublicDefinition() != YangStmtMapping.ANYXML) {
+            LOG.debug("Parent is not an anyxml node, ignoring get-filter-element-attributes statement");
+            return false;
+        }
+        if (!"filter".equals(parent.rawStatementArgument())) {
+            LOG.debug("Parent is not named 'filter', ignoring get-filter-element-attributes statement");
+            return false;
+        }
+
+        final StmtContext<?, ?, ?> grandParent = parent.getParentContext();
+        if (grandParent == null) {
+            LOG.debug("No grandparent, ignoring get-filter-element-attributes statement");
+            return false;
+        }
+        if (grandParent.getPublicDefinition() != YangStmtMapping.INPUT) {
+            LOG.debug("Grandparent is not an input node, ignoring get-filter-element-attributes statement");
+            return false;
+        }
+
+        final StmtContext<?, ?, ?> greatGrandParent = grandParent.getParentContext();
+        if (greatGrandParent == null) {
+            LOG.debug("No grandparent, ignoring get-filter-element-attributes statement");
+            return false;
+        }
+        if (greatGrandParent.getPublicDefinition() != YangStmtMapping.RPC) {
+            LOG.debug("Grandparent is not an RPC node, ignoring get-filter-element-attributes statement");
+            return false;
+        }
+
+        switch (greatGrandParent.rawStatementArgument()) {
+            case "get":
+            case "get-config":
+                return true;
+            default:
+                LOG.debug("Great-grandparent is not named 'get' nor 'get-config, ignoring get-filter-element-attributes"
+                    + " statement");
+                return false;
+        }
+    }
+}
diff --git a/yang/rfc6241-parser-support/src/main/java/org/opendaylight/yangtools/rfc6241/parser/package-info.java b/yang/rfc6241-parser-support/src/main/java/org/opendaylight/yangtools/rfc6241/parser/package-info.java
new file mode 100644 (file)
index 0000000..fe3b450
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+ */
+/**
+ * YANG parser support for metamodel extensions defined in <a href="https://tools.ietf.org/html/rfc6241">RFC6241</a>.
+ * Add {@link org.opendaylight.yangtools.rfc6241.parser.GetFilterElementAttributesStatementSupport} to your reactor to
+ * add support for this extension.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.rfc6241.parser;
diff --git a/yang/rfc6241-parser-support/src/test/java/org/opendaylight/yangtools/rfc6241/parser/NetconfTest.java b/yang/rfc6241-parser-support/src/test/java/org/opendaylight/yangtools/rfc6241/parser/NetconfTest.java
new file mode 100644 (file)
index 0000000..9af969f
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc6241.parser;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Optional;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.rfc6241.model.api.GetFilterElementAttributesSchemaNode;
+import org.opendaylight.yangtools.rfc6241.model.api.NetconfConstants;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+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.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+
+public class NetconfTest {
+    private static final QName FILTER = QName.create(NetconfConstants.RFC6241_MODULE, "filter");
+
+    private static CrossSourceStatementReactor reactor;
+
+    @BeforeClass
+    public static void createReactor() {
+        reactor = RFC7950Reactors.defaultReactorBuilder()
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
+                    GetFilterElementAttributesStatementSupport.getInstance())
+                .build();
+    }
+
+    @AfterClass
+    public static void freeReactor() {
+        reactor = null;
+    }
+
+    @Test
+    public void testResolution() throws ReactorException, IOException, YangSyntaxErrorException {
+        final SchemaContext context = reactor.newBuild()
+                .addLibSources(YangStatementStreamSource.create(
+                    YangTextSchemaSource.forResource("/ietf-inet-types@2013-07-15.yang")))
+                .addSource(YangStatementStreamSource.create(
+                    YangTextSchemaSource.forResource("/ietf-netconf@2011-06-01.yang")))
+                .buildEffective();
+
+        final Module module = context.findModule(NetconfConstants.RFC6241_MODULE).get();
+        final Iterator<RpcDefinition> it = module.getRpcs().iterator();
+        assertExtension(false, it.next());
+        assertExtension(false, it.next());
+        assertExtension(true, it.next());
+        assertExtension(true, it.next());
+        it.forEachRemaining(def -> assertExtension(false, def));
+    }
+
+    private static void assertExtension(final boolean expected, final RpcDefinition def) {
+        final Optional<DataSchemaNode> optFilter = def.getInput().findDataTreeChild(FILTER);
+        assertEquals(expected, optFilter.isPresent());
+        optFilter.ifPresent(filter -> {
+            assertThat(filter, is(instanceOf(AnyxmlSchemaNode.class)));
+            assertTrue(GetFilterElementAttributesSchemaNode.findIn((AnyxmlSchemaNode) filter).isPresent());
+        });
+    }
+}
diff --git a/yang/rfc6241-parser-support/src/test/resources/ietf-inet-types@2013-07-15.yang b/yang/rfc6241-parser-support/src/test/resources/ietf-inet-types@2013-07-15.yang
new file mode 100644 (file)
index 0000000..eacefb6
--- /dev/null
@@ -0,0 +1,458 @@
+module ietf-inet-types {
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
+  prefix "inet";
+
+  organization
+   "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+   "WG Web:   <http://tools.ietf.org/wg/netmod/>
+    WG List:  <mailto:netmod@ietf.org>
+
+    WG Chair: David Kessens
+              <mailto:david.kessens@nsn.com>
+
+    WG Chair: Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>
+
+    Editor:   Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>";
+
+  description
+   "This module contains a collection of generally useful derived
+    YANG data types for Internet addresses and related things.
+
+    Copyright (c) 2013 IETF Trust and the persons identified as
+    authors of the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or
+    without modification, is permitted pursuant to, and subject
+    to the license terms contained in, the Simplified BSD License
+    set forth in Section 4.c of the IETF Trust's Legal Provisions
+    Relating to IETF Documents
+    (http://trustee.ietf.org/license-info).
+
+    This version of this YANG module is part of RFC 6991; see
+    the RFC itself for full legal notices.";
+
+  revision 2013-07-15 {
+    description
+     "This revision adds the following new data types:
+      - ip-address-no-zone
+      - ipv4-address-no-zone
+      - ipv6-address-no-zone";
+    reference
+     "RFC 6991: Common YANG Data Types";
+  }
+
+  revision 2010-09-24 {
+    description
+     "Initial revision.";
+    reference
+     "RFC 6021: Common YANG Data Types";
+  }
+
+  /*** collection of types related to protocol fields ***/
+
+  typedef ip-version {
+    type enumeration {
+      enum unknown {
+        value "0";
+        description
+         "An unknown or unspecified version of the Internet
+          protocol.";
+      }
+      enum ipv4 {
+        value "1";
+        description
+         "The IPv4 protocol as defined in RFC 791.";
+      }
+      enum ipv6 {
+        value "2";
+        description
+         "The IPv6 protocol as defined in RFC 2460.";
+      }
+    }
+    description
+     "This value represents the version of the IP protocol.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetVersion textual convention of the SMIv2.";
+    reference
+     "RFC  791: Internet Protocol
+      RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+      RFC 4001: Textual Conventions for Internet Network Addresses";
+  }
+
+  typedef dscp {
+    type uint8 {
+      range "0..63";
+    }
+    description
+     "The dscp type represents a Differentiated Services Code Point
+      that may be used for marking packets in a traffic stream.
+      In the value set and its semantics, this type is equivalent
+      to the Dscp textual convention of the SMIv2.";
+    reference
+     "RFC 3289: Management Information Base for the Differentiated
+                Services Architecture
+      RFC 2474: Definition of the Differentiated Services Field
+                (DS Field) in the IPv4 and IPv6 Headers
+      RFC 2780: IANA Allocation Guidelines For Values In
+                the Internet Protocol and Related Headers";
+  }
+
+  typedef ipv6-flow-label {
+    type uint32 {
+      range "0..1048575";
+    }
+    description
+     "The ipv6-flow-label type represents the flow identifier or Flow
+      Label in an IPv6 packet header that may be used to
+      discriminate traffic flows.
+
+      In the value set and its semantics, this type is equivalent
+      to the IPv6FlowLabel textual convention of the SMIv2.";
+    reference
+     "RFC 3595: Textual Conventions for IPv6 Flow Label
+      RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
+  }
+
+  typedef port-number {
+    type uint16 {
+      range "0..65535";
+    }
+    description
+     "The port-number type represents a 16-bit port number of an
+      Internet transport-layer protocol such as UDP, TCP, DCCP, or
+      SCTP.  Port numbers are assigned by IANA.  A current list of
+      all assignments is available from <http://www.iana.org/>.
+
+      Note that the port number value zero is reserved by IANA.  In
+      situations where the value zero does not make sense, it can
+      be excluded by subtyping the port-number type.
+      In the value set and its semantics, this type is equivalent
+      to the InetPortNumber textual convention of the SMIv2.";
+    reference
+     "RFC  768: User Datagram Protocol
+      RFC  793: Transmission Control Protocol
+      RFC 4960: Stream Control Transmission Protocol
+      RFC 4340: Datagram Congestion Control Protocol (DCCP)
+      RFC 4001: Textual Conventions for Internet Network Addresses";
+  }
+
+  /*** collection of types related to autonomous systems ***/
+
+  typedef as-number {
+    type uint32;
+    description
+     "The as-number type represents autonomous system numbers
+      which identify an Autonomous System (AS).  An AS is a set
+      of routers under a single technical administration, using
+      an interior gateway protocol and common metrics to route
+      packets within the AS, and using an exterior gateway
+      protocol to route packets to other ASes.  IANA maintains
+      the AS number space and has delegated large parts to the
+      regional registries.
+
+      Autonomous system numbers were originally limited to 16
+      bits.  BGP extensions have enlarged the autonomous system
+      number space to 32 bits.  This type therefore uses an uint32
+      base type without a range restriction in order to support
+      a larger autonomous system number space.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetAutonomousSystemNumber textual convention of
+      the SMIv2.";
+    reference
+     "RFC 1930: Guidelines for creation, selection, and registration
+                of an Autonomous System (AS)
+      RFC 4271: A Border Gateway Protocol 4 (BGP-4)
+      RFC 4001: Textual Conventions for Internet Network Addresses
+      RFC 6793: BGP Support for Four-Octet Autonomous System (AS)
+                Number Space";
+  }
+
+  /*** collection of types related to IP addresses and hostnames ***/
+
+  typedef ip-address {
+    type union {
+      type inet:ipv4-address;
+      type inet:ipv6-address;
+    }
+    description
+     "The ip-address type represents an IP address and is IP
+      version neutral.  The format of the textual representation
+      implies the IP version.  This type supports scoped addresses
+      by allowing zone identifiers in the address format.";
+    reference
+     "RFC 4007: IPv6 Scoped Address Architecture";
+  }
+
+  typedef ipv4-address {
+    type string {
+      pattern
+        '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+      +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+      + '(%[\p{N}\p{L}]+)?';
+    }
+    description
+      "The ipv4-address type represents an IPv4 address in
+       dotted-quad notation.  The IPv4 address may include a zone
+       index, separated by a % sign.
+
+       The zone index is used to disambiguate identical address
+       values.  For link-local addresses, the zone index will
+       typically be the interface index number or the name of an
+       interface.  If the zone index is not present, the default
+       zone of the device will be used.
+
+       The canonical format for the zone index is the numerical
+       format";
+  }
+
+  typedef ipv6-address {
+    type string {
+      pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+            + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+            + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+            + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+            + '(%[\p{N}\p{L}]+)?';
+      pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+            + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+            + '(%.+)?';
+    }
+    description
+     "The ipv6-address type represents an IPv6 address in full,
+      mixed, shortened, and shortened-mixed notation.  The IPv6
+      address may include a zone index, separated by a % sign.
+
+      The zone index is used to disambiguate identical address
+      values.  For link-local addresses, the zone index will
+      typically be the interface index number or the name of an
+      interface.  If the zone index is not present, the default
+      zone of the device will be used.
+
+      The canonical format of IPv6 addresses uses the textual
+      representation defined in Section 4 of RFC 5952.  The
+      canonical format for the zone index is the numerical
+      format as described in Section 11.2 of RFC 4007.";
+    reference
+     "RFC 4291: IP Version 6 Addressing Architecture
+      RFC 4007: IPv6 Scoped Address Architecture
+      RFC 5952: A Recommendation for IPv6 Address Text
+                Representation";
+  }
+
+  typedef ip-address-no-zone {
+    type union {
+      type inet:ipv4-address-no-zone;
+      type inet:ipv6-address-no-zone;
+    }
+    description
+     "The ip-address-no-zone type represents an IP address and is
+      IP version neutral.  The format of the textual representation
+      implies the IP version.  This type does not support scoped
+      addresses since it does not allow zone identifiers in the
+      address format.";
+    reference
+     "RFC 4007: IPv6 Scoped Address Architecture";
+  }
+
+  typedef ipv4-address-no-zone {
+    type inet:ipv4-address {
+      pattern '[0-9\.]*';
+    }
+    description
+      "An IPv4 address without a zone index.  This type, derived from
+       ipv4-address, may be used in situations where the zone is
+       known from the context and hence no zone index is needed.";
+  }
+
+  typedef ipv6-address-no-zone {
+    type inet:ipv6-address {
+      pattern '[0-9a-fA-F:\.]*';
+    }
+    description
+      "An IPv6 address without a zone index.  This type, derived from
+       ipv6-address, may be used in situations where the zone is
+       known from the context and hence no zone index is needed.";
+    reference
+     "RFC 4291: IP Version 6 Addressing Architecture
+      RFC 4007: IPv6 Scoped Address Architecture
+      RFC 5952: A Recommendation for IPv6 Address Text
+                Representation";
+  }
+
+  typedef ip-prefix {
+    type union {
+      type inet:ipv4-prefix;
+      type inet:ipv6-prefix;
+    }
+    description
+     "The ip-prefix type represents an IP prefix and is IP
+      version neutral.  The format of the textual representations
+      implies the IP version.";
+  }
+
+  typedef ipv4-prefix {
+    type string {
+      pattern
+         '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+       +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+       + '/(([0-9])|([1-2][0-9])|(3[0-2]))';
+    }
+    description
+     "The ipv4-prefix type represents an IPv4 address prefix.
+      The prefix length is given by the number following the
+      slash character and must be less than or equal to 32.
+
+      A prefix length value of n corresponds to an IP address
+      mask that has n contiguous 1-bits from the most
+      significant bit (MSB) and all other bits set to 0.
+
+      The canonical format of an IPv4 prefix has all bits of
+      the IPv4 address set to zero that are not part of the
+      IPv4 prefix.";
+  }
+
+  typedef ipv6-prefix {
+    type string {
+      pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+            + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+            + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+            + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+            + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
+      pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+            + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+            + '(/.+)';
+    }
+
+    description
+     "The ipv6-prefix type represents an IPv6 address prefix.
+      The prefix length is given by the number following the
+      slash character and must be less than or equal to 128.
+
+      A prefix length value of n corresponds to an IP address
+      mask that has n contiguous 1-bits from the most
+      significant bit (MSB) and all other bits set to 0.
+
+      The IPv6 address should have all bits that do not belong
+      to the prefix set to zero.
+
+      The canonical format of an IPv6 prefix has all bits of
+      the IPv6 address set to zero that are not part of the
+      IPv6 prefix.  Furthermore, the IPv6 address is represented
+      as defined in Section 4 of RFC 5952.";
+    reference
+     "RFC 5952: A Recommendation for IPv6 Address Text
+                Representation";
+  }
+
+  /*** collection of domain name and URI types ***/
+
+  typedef domain-name {
+    type string {
+      pattern
+        '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+      + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+      + '|\.';
+      length "1..253";
+    }
+    description
+     "The domain-name type represents a DNS domain name.  The
+      name SHOULD be fully qualified whenever possible.
+
+      Internet domain names are only loosely specified.  Section
+      3.5 of RFC 1034 recommends a syntax (modified in Section
+      2.1 of RFC 1123).  The pattern above is intended to allow
+      for current practice in domain name use, and some possible
+      future expansion.  It is designed to hold various types of
+      domain names, including names used for A or AAAA records
+      (host names) and other records, such as SRV records.  Note
+      that Internet host names have a stricter syntax (described
+      in RFC 952) than the DNS recommendations in RFCs 1034 and
+      1123, and that systems that want to store host names in
+      schema nodes using the domain-name type are recommended to
+      adhere to this stricter standard to ensure interoperability.
+
+      The encoding of DNS names in the DNS protocol is limited
+      to 255 characters.  Since the encoding consists of labels
+      prefixed by a length bytes and there is a trailing NULL
+      byte, only 253 characters can appear in the textual dotted
+      notation.
+
+      The description clause of schema nodes using the domain-name
+      type MUST describe when and how these names are resolved to
+      IP addresses.  Note that the resolution of a domain-name value
+      may require to query multiple DNS records (e.g., A for IPv4
+      and AAAA for IPv6).  The order of the resolution process and
+      which DNS record takes precedence can either be defined
+      explicitly or may depend on the configuration of the
+      resolver.
+
+      Domain-name values use the US-ASCII encoding.  Their canonical
+      format uses lowercase US-ASCII characters.  Internationalized
+      domain names MUST be A-labels as per RFC 5890.";
+    reference
+     "RFC  952: DoD Internet Host Table Specification
+      RFC 1034: Domain Names - Concepts and Facilities
+      RFC 1123: Requirements for Internet Hosts -- Application
+                and Support
+      RFC 2782: A DNS RR for specifying the location of services
+                (DNS SRV)
+      RFC 5890: Internationalized Domain Names in Applications
+                (IDNA): Definitions and Document Framework";
+  }
+
+  typedef host {
+    type union {
+      type inet:ip-address;
+      type inet:domain-name;
+    }
+    description
+     "The host type represents either an IP address or a DNS
+      domain name.";
+  }
+
+  typedef uri {
+    type string;
+    description
+     "The uri type represents a Uniform Resource Identifier
+      (URI) as defined by STD 66.
+
+      Objects using the uri type MUST be in US-ASCII encoding,
+      and MUST be normalized as described by RFC 3986 Sections
+      6.2.1, 6.2.2.1, and 6.2.2.2.  All unnecessary
+      percent-encoding is removed, and all case-insensitive
+      characters are set to lowercase except for hexadecimal
+      digits, which are normalized to uppercase as described in
+      Section 6.2.2.1.
+
+      The purpose of this normalization is to help provide
+      unique URIs.  Note that this normalization is not
+      sufficient to provide uniqueness.  Two URIs that are
+      textually distinct after this normalization may still be
+      equivalent.
+
+      Objects using the uri type may restrict the schemes that
+      they permit.  For example, 'data:' and 'urn:' schemes
+      might not be appropriate.
+
+      A zero-length URI is not a valid URI.  This can be used to
+      express 'URI absent' where required.
+
+      In the value set and its semantics, this type is equivalent
+      to the Uri SMIv2 textual convention defined in RFC 5017.";
+    reference
+     "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
+      RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
+                Group: Uniform Resource Identifiers (URIs), URLs,
+                and Uniform Resource Names (URNs): Clarifications
+                and Recommendations
+      RFC 5017: MIB Textual Conventions for Uniform Resource
+                Identifiers (URIs)";
+  }
+
+}
diff --git a/yang/rfc6241-parser-support/src/test/resources/ietf-netconf@2011-06-01.yang b/yang/rfc6241-parser-support/src/test/resources/ietf-netconf@2011-06-01.yang
new file mode 100644 (file)
index 0000000..44c19c3
--- /dev/null
@@ -0,0 +1,927 @@
+module ietf-netconf {
+
+  // the namespace for NETCONF XML definitions is unchanged
+  // from RFC 4741, which this document replaces
+  namespace "urn:ietf:params:xml:ns:netconf:base:1.0";
+
+  prefix nc;
+
+  import ietf-inet-types {
+    prefix inet;
+  }
+
+  organization
+    "IETF NETCONF (Network Configuration) Working Group";
+
+  contact
+    "WG Web:   <http://tools.ietf.org/wg/netconf/>
+     WG List:  <netconf@ietf.org>
+
+     WG Chair: Bert Wijnen
+               <bertietf@bwijnen.net>
+
+     WG Chair: Mehmet Ersue
+               <mehmet.ersue@nsn.com>
+
+     Editor:   Martin Bjorklund
+               <mbj@tail-f.com>
+
+     Editor:   Juergen Schoenwaelder
+               <j.schoenwaelder@jacobs-university.de>
+
+     Editor:   Andy Bierman
+               <andy.bierman@brocade.com>";
+  description
+    "NETCONF Protocol Data Types and Protocol Operations.
+
+     Copyright (c) 2011 IETF Trust and the persons identified as
+     the document authors.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD License
+     set forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6241; see
+     the RFC itself for full legal notices.";
+  revision 2011-06-01 {
+    description
+      "Initial revision";
+    reference
+      "RFC 6241: Network Configuration Protocol";
+  }
+
+  extension get-filter-element-attributes {
+    description
+      "If this extension is present within an 'anyxml'
+       statement named 'filter', which must be conceptually
+       defined within the RPC input section for the <get>
+       and <get-config> protocol operations, then the
+       following unqualified XML attribute is supported
+       within the <filter> element, within a <get> or
+       <get-config> protocol operation:
+
+         type : optional attribute with allowed
+                value strings 'subtree' and 'xpath'.
+                If missing, the default value is 'subtree'.
+
+       If the 'xpath' feature is supported, then the
+       following unqualified XML attribute is
+       also supported:
+
+         select: optional attribute containing a
+                 string representing an XPath expression.
+                 The 'type' attribute must be equal to 'xpath'
+                 if this attribute is present.";
+  }
+
+  // NETCONF capabilities defined as features
+  feature writable-running {
+    description
+      "NETCONF :writable-running capability;
+       If the server advertises the :writable-running
+       capability for a session, then this feature must
+       also be enabled for that session.  Otherwise,
+       this feature must not be enabled.";
+    reference "RFC 6241, Section 8.2";
+  }
+
+  feature candidate {
+    description
+      "NETCONF :candidate capability;
+       If the server advertises the :candidate
+       capability for a session, then this feature must
+       also be enabled for that session.  Otherwise,
+       this feature must not be enabled.";
+    reference "RFC 6241, Section 8.3";
+  }
+
+  feature confirmed-commit {
+    if-feature candidate;
+    description
+      "NETCONF :confirmed-commit:1.1 capability;
+       If the server advertises the :confirmed-commit:1.1
+       capability for a session, then this feature must
+       also be enabled for that session.  Otherwise,
+       this feature must not be enabled.";
+
+    reference "RFC 6241, Section 8.4";
+  }
+
+  feature rollback-on-error {
+    description
+      "NETCONF :rollback-on-error capability;
+       If the server advertises the :rollback-on-error
+       capability for a session, then this feature must
+       also be enabled for that session.  Otherwise,
+       this feature must not be enabled.";
+    reference "RFC 6241, Section 8.5";
+  }
+
+  feature validate {
+    description
+      "NETCONF :validate:1.1 capability;
+       If the server advertises the :validate:1.1
+       capability for a session, then this feature must
+       also be enabled for that session.  Otherwise,
+       this feature must not be enabled.";
+    reference "RFC 6241, Section 8.6";
+  }
+
+  feature startup {
+    description
+      "NETCONF :startup capability;
+       If the server advertises the :startup
+       capability for a session, then this feature must
+       also be enabled for that session.  Otherwise,
+       this feature must not be enabled.";
+    reference "RFC 6241, Section 8.7";
+  }
+
+  feature url {
+    description
+      "NETCONF :url capability;
+       If the server advertises the :url
+       capability for a session, then this feature must
+       also be enabled for that session.  Otherwise,
+       this feature must not be enabled.";
+    reference "RFC 6241, Section 8.8";
+  }
+
+  feature xpath {
+    description
+      "NETCONF :xpath capability;
+       If the server advertises the :xpath
+       capability for a session, then this feature must
+       also be enabled for that session.  Otherwise,
+       this feature must not be enabled.";
+    reference "RFC 6241, Section 8.9";
+  }
+
+  // NETCONF Simple Types
+
+  typedef session-id-type {
+    type uint32 {
+      range "1..max";
+    }
+    description
+      "NETCONF Session Id";
+  }
+
+  typedef session-id-or-zero-type {
+    type uint32;
+    description
+      "NETCONF Session Id or Zero to indicate none";
+  }
+  typedef error-tag-type {
+    type enumeration {
+       enum in-use {
+         description
+           "The request requires a resource that
+            already is in use.";
+       }
+       enum invalid-value {
+         description
+           "The request specifies an unacceptable value for one
+            or more parameters.";
+       }
+       enum too-big {
+         description
+           "The request or response (that would be generated) is
+            too large for the implementation to handle.";
+       }
+       enum missing-attribute {
+         description
+           "An expected attribute is missing.";
+       }
+       enum bad-attribute {
+         description
+           "An attribute value is not correct; e.g., wrong type,
+            out of range, pattern mismatch.";
+       }
+       enum unknown-attribute {
+         description
+           "An unexpected attribute is present.";
+       }
+       enum missing-element {
+         description
+           "An expected element is missing.";
+       }
+       enum bad-element {
+         description
+           "An element value is not correct; e.g., wrong type,
+            out of range, pattern mismatch.";
+       }
+       enum unknown-element {
+         description
+           "An unexpected element is present.";
+       }
+       enum unknown-namespace {
+         description
+           "An unexpected namespace is present.";
+       }
+       enum access-denied {
+         description
+           "Access to the requested protocol operation or
+            data model is denied because authorization failed.";
+       }
+       enum lock-denied {
+         description
+           "Access to the requested lock is denied because the
+            lock is currently held by another entity.";
+       }
+       enum resource-denied {
+         description
+           "Request could not be completed because of
+            insufficient resources.";
+       }
+       enum rollback-failed {
+         description
+           "Request to roll back some configuration change (via
+            rollback-on-error or <discard-changes> operations)
+            was not completed for some reason.";
+
+       }
+       enum data-exists {
+         description
+           "Request could not be completed because the relevant
+            data model content already exists.  For example,
+            a 'create' operation was attempted on data that
+            already exists.";
+       }
+       enum data-missing {
+         description
+           "Request could not be completed because the relevant
+            data model content does not exist.  For example,
+            a 'delete' operation was attempted on
+            data that does not exist.";
+       }
+       enum operation-not-supported {
+         description
+           "Request could not be completed because the requested
+            operation is not supported by this implementation.";
+       }
+       enum operation-failed {
+         description
+           "Request could not be completed because the requested
+            operation failed for some reason not covered by
+            any other error condition.";
+       }
+       enum partial-operation {
+         description
+           "This error-tag is obsolete, and SHOULD NOT be sent
+            by servers conforming to this document.";
+       }
+       enum malformed-message {
+         description
+           "A message could not be handled because it failed to
+            be parsed correctly.  For example, the message is not
+            well-formed XML or it uses an invalid character set.";
+       }
+     }
+     description "NETCONF Error Tag";
+     reference "RFC 6241, Appendix A";
+  }
+
+  typedef error-severity-type {
+    type enumeration {
+      enum error {
+        description "Error severity";
+      }
+      enum warning {
+        description "Warning severity";
+      }
+    }
+    description "NETCONF Error Severity";
+    reference "RFC 6241, Section 4.3";
+  }
+
+  typedef edit-operation-type {
+    type enumeration {
+      enum merge {
+        description
+          "The configuration data identified by the
+           element containing this attribute is merged
+           with the configuration at the corresponding
+           level in the configuration datastore identified
+           by the target parameter.";
+      }
+      enum replace {
+        description
+          "The configuration data identified by the element
+           containing this attribute replaces any related
+           configuration in the configuration datastore
+           identified by the target parameter.  If no such
+           configuration data exists in the configuration
+           datastore, it is created.  Unlike a
+           <copy-config> operation, which replaces the
+           entire target configuration, only the configuration
+           actually present in the config parameter is affected.";
+      }
+      enum create {
+        description
+          "The configuration data identified by the element
+           containing this attribute is added to the
+           configuration if and only if the configuration
+           data does not already exist in the configuration
+           datastore.  If the configuration data exists, an
+           <rpc-error> element is returned with an
+           <error-tag> value of 'data-exists'.";
+      }
+      enum delete {
+        description
+          "The configuration data identified by the element
+           containing this attribute is deleted from the
+           configuration if and only if the configuration
+           data currently exists in the configuration
+           datastore.  If the configuration data does not
+           exist, an <rpc-error> element is returned with
+           an <error-tag> value of 'data-missing'.";
+      }
+      enum remove {
+        description
+          "The configuration data identified by the element
+           containing this attribute is deleted from the
+           configuration if the configuration
+           data currently exists in the configuration
+           datastore.  If the configuration data does not
+           exist, the 'remove' operation is silently ignored
+           by the server.";
+      }
+    }
+    default "merge";
+    description "NETCONF 'operation' attribute values";
+    reference "RFC 6241, Section 7.2";
+  }
+
+  // NETCONF Standard Protocol Operations
+
+  rpc get-config {
+    description
+      "Retrieve all or part of a specified configuration.";
+
+    reference "RFC 6241, Section 7.1";
+
+    input {
+      container source {
+        description
+          "Particular configuration to retrieve.";
+
+        choice config-source {
+          mandatory true;
+          description
+            "The configuration to retrieve.";
+          leaf candidate {
+            if-feature candidate;
+            type empty;
+            description
+              "The candidate configuration is the config source.";
+          }
+          leaf running {
+            type empty;
+            description
+              "The running configuration is the config source.";
+          }
+          leaf startup {
+            if-feature startup;
+            type empty;
+            description
+              "The startup configuration is the config source.
+               This is optional-to-implement on the server because
+               not all servers will support filtering for this
+               datastore.";
+          }
+        }
+      }
+
+      anyxml filter {
+        description
+          "Subtree or XPath filter to use.";
+        nc:get-filter-element-attributes;
+      }
+    }
+
+    output {
+      anyxml data {
+        description
+          "Copy of the source datastore subset that matched
+           the filter criteria (if any).  An empty data container
+           indicates that the request did not produce any results.";
+      }
+    }
+  }
+
+  rpc edit-config {
+    description
+      "The <edit-config> operation loads all or part of a specified
+       configuration to the specified target configuration.";
+
+    reference "RFC 6241, Section 7.2";
+
+    input {
+      container target {
+        description
+          "Particular configuration to edit.";
+
+        choice config-target {
+          mandatory true;
+          description
+            "The configuration target.";
+
+          leaf candidate {
+            if-feature candidate;
+            type empty;
+            description
+              "The candidate configuration is the config target.";
+          }
+          leaf running {
+            if-feature writable-running;
+            type empty;
+            description
+              "The running configuration is the config source.";
+          }
+        }
+      }
+
+      leaf default-operation {
+        type enumeration {
+          enum merge {
+            description
+              "The default operation is merge.";
+          }
+          enum replace {
+            description
+              "The default operation is replace.";
+          }
+          enum none {
+            description
+              "There is no default operation.";
+          }
+        }
+        default "merge";
+        description
+          "The default operation to use.";
+      }
+
+      leaf test-option {
+        if-feature validate;
+        type enumeration {
+          enum test-then-set {
+            description
+              "The server will test and then set if no errors.";
+          }
+          enum set {
+            description
+              "The server will set without a test first.";
+          }
+
+          enum test-only {
+            description
+              "The server will only test and not set, even
+               if there are no errors.";
+          }
+        }
+        default "test-then-set";
+        description
+          "The test option to use.";
+      }
+
+      leaf error-option {
+        type enumeration {
+          enum stop-on-error {
+            description
+              "The server will stop on errors.";
+          }
+          enum continue-on-error {
+            description
+              "The server may continue on errors.";
+          }
+          enum rollback-on-error {
+            description
+              "The server will roll back on errors.
+               This value can only be used if the 'rollback-on-error'
+               feature is supported.";
+          }
+        }
+        default "stop-on-error";
+        description
+          "The error option to use.";
+      }
+
+      choice edit-content {
+        mandatory true;
+        description
+          "The content for the edit operation.";
+
+        anyxml config {
+          description
+            "Inline Config content.";
+        }
+        leaf url {
+          if-feature url;
+          type inet:uri;
+          description
+            "URL-based config content.";
+        }
+      }
+    }
+  }
+
+  rpc copy-config {
+    description
+      "Create or replace an entire configuration datastore with the
+       contents of another complete configuration datastore.";
+
+    reference "RFC 6241, Section 7.3";
+
+    input {
+      container target {
+        description
+          "Particular configuration to copy to.";
+
+        choice config-target {
+          mandatory true;
+          description
+            "The configuration target of the copy operation.";
+
+          leaf candidate {
+            if-feature candidate;
+            type empty;
+            description
+              "The candidate configuration is the config target.";
+          }
+          leaf running {
+            if-feature writable-running;
+            type empty;
+            description
+              "The running configuration is the config target.
+               This is optional-to-implement on the server.";
+          }
+          leaf startup {
+            if-feature startup;
+            type empty;
+            description
+              "The startup configuration is the config target.";
+          }
+          leaf url {
+            if-feature url;
+            type inet:uri;
+            description
+              "The URL-based configuration is the config target.";
+          }
+        }
+      }
+
+      container source {
+        description
+          "Particular configuration to copy from.";
+
+        choice config-source {
+          mandatory true;
+          description
+            "The configuration source for the copy operation.";
+
+          leaf candidate {
+            if-feature candidate;
+            type empty;
+            description
+              "The candidate configuration is the config source.";
+          }
+          leaf running {
+            type empty;
+            description
+              "The running configuration is the config source.";
+          }
+          leaf startup {
+            if-feature startup;
+            type empty;
+            description
+              "The startup configuration is the config source.";
+          }
+          leaf url {
+            if-feature url;
+            type inet:uri;
+            description
+              "The URL-based configuration is the config source.";
+          }
+          anyxml config {
+            description
+              "Inline Config content: <config> element.  Represents
+               an entire configuration datastore, not
+               a subset of the running datastore.";
+          }
+        }
+      }
+    }
+  }
+
+  rpc delete-config {
+    description
+      "Delete a configuration datastore.";
+
+    reference "RFC 6241, Section 7.4";
+
+    input {
+      container target {
+        description
+          "Particular configuration to delete.";
+
+        choice config-target {
+          mandatory true;
+          description
+            "The configuration target to delete.";
+
+          leaf startup {
+            if-feature startup;
+            type empty;
+            description
+              "The startup configuration is the config target.";
+          }
+          leaf url {
+            if-feature url;
+            type inet:uri;
+            description
+              "The URL-based configuration is the config target.";
+          }
+        }
+      }
+    }
+  }
+
+  rpc lock {
+    description
+      "The lock operation allows the client to lock the configuration
+       system of a device.";
+
+    reference "RFC 6241, Section 7.5";
+
+    input {
+      container target {
+        description
+          "Particular configuration to lock.";
+
+        choice config-target {
+          mandatory true;
+          description
+            "The configuration target to lock.";
+
+          leaf candidate {
+            if-feature candidate;
+            type empty;
+            description
+              "The candidate configuration is the config target.";
+          }
+          leaf running {
+            type empty;
+            description
+              "The running configuration is the config target.";
+          }
+          leaf startup {
+            if-feature startup;
+            type empty;
+            description
+              "The startup configuration is the config target.";
+          }
+        }
+      }
+    }
+  }
+
+  rpc unlock {
+    description
+      "The unlock operation is used to release a configuration lock,
+       previously obtained with the 'lock' operation.";
+
+    reference "RFC 6241, Section 7.6";
+
+    input {
+      container target {
+        description
+          "Particular configuration to unlock.";
+
+        choice config-target {
+          mandatory true;
+          description
+            "The configuration target to unlock.";
+
+          leaf candidate {
+            if-feature candidate;
+            type empty;
+            description
+              "The candidate configuration is the config target.";
+          }
+          leaf running {
+            type empty;
+            description
+              "The running configuration is the config target.";
+          }
+          leaf startup {
+            if-feature startup;
+            type empty;
+            description
+              "The startup configuration is the config target.";
+          }
+        }
+      }
+    }
+  }
+
+  rpc get {
+    description
+      "Retrieve running configuration and device state information.";
+
+    reference "RFC 6241, Section 7.7";
+
+    input {
+      anyxml filter {
+        description
+          "This parameter specifies the portion of the system
+           configuration and state data to retrieve.";
+        nc:get-filter-element-attributes;
+      }
+    }
+
+    output {
+      anyxml data {
+        description
+          "Copy of the running datastore subset and/or state
+           data that matched the filter criteria (if any).
+           An empty data container indicates that the request did not
+           produce any results.";
+      }
+    }
+  }
+
+  rpc close-session {
+    description
+      "Request graceful termination of a NETCONF session.";
+
+    reference "RFC 6241, Section 7.8";
+  }
+
+  rpc kill-session {
+    description
+      "Force the termination of a NETCONF session.";
+
+    reference "RFC 6241, Section 7.9";
+
+    input {
+      leaf session-id {
+        type session-id-type;
+        mandatory true;
+        description
+          "Particular session to kill.";
+      }
+    }
+  }
+
+  rpc commit {
+    if-feature candidate;
+
+    description
+      "Commit the candidate configuration as the device's new
+       current configuration.";
+
+    reference "RFC 6241, Section 8.3.4.1";
+
+    input {
+      leaf confirmed {
+        if-feature confirmed-commit;
+        type empty;
+        description
+          "Requests a confirmed commit.";
+        reference "RFC 6241, Section 8.3.4.1";
+      }
+
+      leaf confirm-timeout {
+        if-feature confirmed-commit;
+        type uint32 {
+          range "1..max";
+        }
+        units "seconds";
+        default "600";   // 10 minutes
+        description
+          "The timeout interval for a confirmed commit.";
+        reference "RFC 6241, Section 8.3.4.1";
+      }
+
+      leaf persist {
+        if-feature confirmed-commit;
+        type string;
+        description
+          "This parameter is used to make a confirmed commit
+           persistent.  A persistent confirmed commit is not aborted
+           if the NETCONF session terminates.  The only way to abort
+           a persistent confirmed commit is to let the timer expire,
+           or to use the <cancel-commit> operation.
+
+           The value of this parameter is a token that must be given
+           in the 'persist-id' parameter of <commit> or
+           <cancel-commit> operations in order to confirm or cancel
+           the persistent confirmed commit.
+
+           The token should be a random string.";
+        reference "RFC 6241, Section 8.3.4.1";
+      }
+
+      leaf persist-id {
+        if-feature confirmed-commit;
+        type string;
+        description
+          "This parameter is given in order to commit a persistent
+           confirmed commit.  The value must be equal to the value
+           given in the 'persist' parameter to the <commit> operation.
+           If it does not match, the operation fails with an
+          'invalid-value' error.";
+        reference "RFC 6241, Section 8.3.4.1";
+      }
+
+    }
+  }
+
+  rpc discard-changes {
+    if-feature candidate;
+
+    description
+      "Revert the candidate configuration to the current
+       running configuration.";
+    reference "RFC 6241, Section 8.3.4.2";
+  }
+
+  rpc cancel-commit {
+    if-feature confirmed-commit;
+    description
+      "This operation is used to cancel an ongoing confirmed commit.
+       If the confirmed commit is persistent, the parameter
+       'persist-id' must be given, and it must match the value of the
+       'persist' parameter.";
+    reference "RFC 6241, Section 8.4.4.1";
+
+    input {
+      leaf persist-id {
+        type string;
+        description
+          "This parameter is given in order to cancel a persistent
+           confirmed commit.  The value must be equal to the value
+           given in the 'persist' parameter to the <commit> operation.
+           If it does not match, the operation fails with an
+          'invalid-value' error.";
+      }
+    }
+  }
+
+  rpc validate {
+    if-feature validate;
+
+    description
+      "Validates the contents of the specified configuration.";
+
+    reference "RFC 6241, Section 8.6.4.1";
+
+    input {
+      container source {
+        description
+          "Particular configuration to validate.";
+
+        choice config-source {
+          mandatory true;
+          description
+            "The configuration source to validate.";
+
+          leaf candidate {
+            if-feature candidate;
+            type empty;
+            description
+              "The candidate configuration is the config source.";
+          }
+          leaf running {
+            type empty;
+            description
+              "The running configuration is the config source.";
+          }
+          leaf startup {
+            if-feature startup;
+            type empty;
+            description
+              "The startup configuration is the config source.";
+          }
+          leaf url {
+            if-feature url;
+            type inet:uri;
+            description
+              "The URL-based configuration is the config source.";
+          }
+          anyxml config {
+            description
+              "Inline Config content: <config> element.  Represents
+               an entire configuration datastore, not
+               a subset of the running datastore.";
+          }
+        }
+      }
+    }
+  }
+
+}
diff --git a/yang/rfc6536-model-api/pom.xml b/yang/rfc6536-model-api/pom.xml
new file mode 100644 (file)
index 0000000..cc60edb
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc6536-model-api</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC6536 metamodel</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc6536.model.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyAllEffectiveStatement.java b/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyAllEffectiveStatement.java
new file mode 100644 (file)
index 0000000..4982510
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc6536.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Effective statement representation of 'default-deny-all' extension defined in
+ * <a href="https://tools.ietf.org/html/rfc6536">RFC6536</a>.
+ */
+@Beta
+public interface DefaultDenyAllEffectiveStatement extends EffectiveStatement<Void, DefaultDenyAllStatement> {
+
+}
diff --git a/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyAllSchemaNode.java b/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyAllSchemaNode.java
new file mode 100644 (file)
index 0000000..a32130c
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc6536.model.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+/**
+ * Represents the effect of 'default-deny-all' extension, as defined in
+ * <a href="https://tools.ietf.org/html/rfc6536">RFC6536</a>, being attached to a SchemaNode.
+ */
+@Beta
+public interface DefaultDenyAllSchemaNode extends UnknownSchemaNode {
+    /**
+     * Attempt to find a {@link DefaultDenyAllSchemaNode} in a parent {@link DataSchemaNode}.
+     *
+     * @param parent Parent to search
+     * @return {@link DefaultDenyAllSchemaNode} child, if present.
+     */
+    static Optional<DefaultDenyAllSchemaNode> findIn(final DataSchemaNode parent) {
+        return parent.getUnknownSchemaNodes().stream().filter(DefaultDenyAllSchemaNode.class::isInstance).findAny()
+                .map(DefaultDenyAllSchemaNode.class::cast);
+    }
+}
diff --git a/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyAllStatement.java b/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyAllStatement.java
new file mode 100644 (file)
index 0000000..7bab5ef
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc6536.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+
+/**
+ * Declared statement representation of 'default-deny-all' extension defined in
+ * <a href="https://tools.ietf.org/html/rfc6536">RFC6536</a>.
+ */
+@Beta
+public interface DefaultDenyAllStatement extends UnknownStatement<Void> {
+
+}
diff --git a/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyWriteEffectiveStatement.java b/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyWriteEffectiveStatement.java
new file mode 100644 (file)
index 0000000..bd19179
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc6536.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Effective statement representation of 'default-deny-write' extension defined in
+ * <a href="https://tools.ietf.org/html/rfc6536">RFC6536</a>.
+ */
+@Beta
+public interface DefaultDenyWriteEffectiveStatement extends EffectiveStatement<Void, DefaultDenyWriteStatement> {
+
+}
diff --git a/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyWriteSchemaNode.java b/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyWriteSchemaNode.java
new file mode 100644 (file)
index 0000000..6acbb6d
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc6536.model.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+/**
+ * Represents the effect of 'default-deny-write' extension, as defined in
+ * <a href="https://tools.ietf.org/html/rfc6536">RFC6536</a>, being attached to a SchemaNode.
+ */
+@Beta
+public interface DefaultDenyWriteSchemaNode extends UnknownSchemaNode {
+    /**
+     * Attempt to find a {@link DefaultDenyWriteSchemaNode} in a parent {@link DataSchemaNode}.
+     *
+     * @param parent Parent to search
+     * @return {@link DefaultDenyWriteSchemaNode} child, if present.
+     */
+    static Optional<DefaultDenyWriteSchemaNode> findIn(final DataSchemaNode parent) {
+        return parent.getUnknownSchemaNodes().stream().filter(DefaultDenyWriteSchemaNode.class::isInstance).findAny()
+                .map(DefaultDenyWriteSchemaNode.class::cast);
+    }
+}
diff --git a/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyWriteStatement.java b/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/DefaultDenyWriteStatement.java
new file mode 100644 (file)
index 0000000..9fd65e1
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc6536.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+
+/**
+ * Declared statement representation of 'default-deny-write' extension defined in
+ * <a href="https://tools.ietf.org/html/rfc6536">RFC6536</a>.
+ */
+@Beta
+public interface DefaultDenyWriteStatement extends UnknownStatement<Void> {
+
+}
diff --git a/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/NACMConstants.java b/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/NACMConstants.java
new file mode 100644 (file)
index 0000000..5fbedeb
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc6536.model.api;
+
+import com.google.common.collect.ImmutableList;
+import java.net.URI;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Constants associated with RFC6536.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault
+public final class NACMConstants {
+    private static final String MODULE_NAME = "ietf-netconf-acm";
+    private static final URI MODULE_NAMESPACE = URI.create("urn:ietf:params:xml:ns:yang:ietf-netconf-acm");
+    private static final Revision RFC6536_REVISION = Revision.of("2012-02-22");
+
+    /**
+     * Runtime RFC6536 identity.
+     */
+    public static final QNameModule RFC6536_MODULE = QNameModule.create(MODULE_NAMESPACE, RFC6536_REVISION).intern();
+
+    /**
+     * RFC6536 model source name.
+     */
+    public static final SourceIdentifier RFC6536_SOURCE = RevisionSourceIdentifier.create(MODULE_NAME,
+        RFC6536_REVISION);
+
+    /**
+     * Normative prefix to use when importing {@link #RFC6536_SOURCE}.
+     */
+    public static final String MODULE_PREFIX = "nacm";
+
+    private NACMConstants() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Return identifiers of all sources known to define NACM extension.
+     *
+     * @return Collection of identifiers.
+     */
+    public static Collection<SourceIdentifier> knownModelSources() {
+        return ImmutableList.of(RFC6536_SOURCE);
+    }
+}
diff --git a/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/NACMStatements.java b/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/NACMStatements.java
new file mode 100644 (file)
index 0000000..6f34f33
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc6536.model.api;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+/**
+ * {@link StatementDefinition}s for statements defined by RFC6536.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault
+public enum NACMStatements implements StatementDefinition {
+    DEFAULT_DENY_ALL(QName.create(NACMConstants.RFC6536_MODULE, "default-deny-all"), DefaultDenyAllStatement.class,
+        DefaultDenyAllEffectiveStatement.class),
+    DEFAULT_DENY_WRITE(QName.create(NACMConstants.RFC6536_MODULE, "default-deny-write"),
+        DefaultDenyWriteStatement.class, DefaultDenyWriteEffectiveStatement.class);
+
+    private final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation;
+    private final Class<? extends DeclaredStatement<?>> declaredRepresentation;
+    private final QName statementName;
+
+    NACMStatements(final QName statementName, final Class<? extends DeclaredStatement<?>> declaredRepresentation,
+            final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation) {
+        this.statementName = statementName.intern();
+        this.declaredRepresentation = requireNonNull(declaredRepresentation);
+        this.effectiveRepresentation = requireNonNull(effectiveRepresentation);
+    }
+
+    @Override
+    public Optional<ArgumentDefinition> getArgumentDefinition() {
+        return Optional.empty();
+    }
+
+    @Override
+    public QName getStatementName() {
+        return statementName;
+    }
+
+    @Override
+    public Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass() {
+        return effectiveRepresentation;
+    }
+
+    @Override
+    public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+        return declaredRepresentation;
+    }
+}
diff --git a/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/package-info.java b/yang/rfc6536-model-api/src/main/java/org/opendaylight/yangtools/rfc6536/model/api/package-info.java
new file mode 100644 (file)
index 0000000..08ec238
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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
+ */
+/**
+ * YANG metamodel extensions to support NACM, as defined in <a href="https://tools.ietf.org/html/rfc6536">RFC6536</a>.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.rfc6536.model.api;
diff --git a/yang/rfc6536-parser-support/pom.xml b/yang/rfc6536-parser-support/pom.xml
new file mode 100644 (file)
index 0000000..320cf5e
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc6536-parser-support</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC6536 parser support</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc6536-model-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-rfc7950</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc6536.parser.support</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc6536-parser-support/src/main/java/org/opendaylight/yangtools/rfc6536/parser/DefaultDenyAllStatementSupport.java b/yang/rfc6536-parser-support/src/main/java/org/opendaylight/yangtools/rfc6536/parser/DefaultDenyAllStatementSupport.java
new file mode 100644 (file)
index 0000000..4f9d096
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc6536.parser;
+
+import org.opendaylight.yangtools.rfc6536.model.api.DefaultDenyAllEffectiveStatement;
+import org.opendaylight.yangtools.rfc6536.model.api.DefaultDenyAllSchemaNode;
+import org.opendaylight.yangtools.rfc6536.model.api.DefaultDenyAllStatement;
+import org.opendaylight.yangtools.rfc6536.model.api.NACMStatements;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.UnknownEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractVoidStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class DefaultDenyAllStatementSupport
+        extends AbstractVoidStatementSupport<DefaultDenyAllStatement, DefaultDenyAllEffectiveStatement> {
+
+    private static final class Declared extends AbstractDeclaredStatement<Void> implements DefaultDenyAllStatement {
+        Declared(final StmtContext<Void, ?, ?> context) {
+            super(context);
+        }
+
+        @Override
+        public Void getArgument() {
+            return null;
+        }
+    }
+
+    private static final class Effective extends UnknownEffectiveStatementBase<Void, DefaultDenyAllStatement>
+            implements DefaultDenyAllEffectiveStatement, DefaultDenyAllSchemaNode {
+
+        private final SchemaPath path;
+
+        Effective(final StmtContext<Void, DefaultDenyAllStatement, ?> ctx) {
+            super(ctx);
+            path = ctx.coerceParentContext().getSchemaPath().get().createChild(
+                ctx.getPublicDefinition().getStatementName());
+        }
+
+        @Override
+        public QName getQName() {
+            return path.getLastComponent();
+        }
+
+        @Override
+        public SchemaPath getPath() {
+            return path;
+        }
+    }
+
+    private static final DefaultDenyAllStatementSupport INSTANCE =
+            new DefaultDenyAllStatementSupport(NACMStatements.DEFAULT_DENY_ALL);
+
+    private final SubstatementValidator validator;
+
+    private DefaultDenyAllStatementSupport(final StatementDefinition definition) {
+        super(definition);
+        this.validator = SubstatementValidator.builder(definition).build();
+    }
+
+    public static DefaultDenyAllStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public DefaultDenyAllStatement createDeclared(final StmtContext<Void, DefaultDenyAllStatement, ?> ctx) {
+        return new Declared(ctx);
+    }
+
+    @Override
+    public DefaultDenyAllEffectiveStatement createEffective(
+            final StmtContext<Void, DefaultDenyAllStatement, DefaultDenyAllEffectiveStatement> ctx) {
+        return new Effective(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return validator;
+    }
+}
diff --git a/yang/rfc6536-parser-support/src/main/java/org/opendaylight/yangtools/rfc6536/parser/DefaultDenyWriteStatementSupport.java b/yang/rfc6536-parser-support/src/main/java/org/opendaylight/yangtools/rfc6536/parser/DefaultDenyWriteStatementSupport.java
new file mode 100644 (file)
index 0000000..ba48812
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc6536.parser;
+
+import org.opendaylight.yangtools.rfc6536.model.api.DefaultDenyWriteEffectiveStatement;
+import org.opendaylight.yangtools.rfc6536.model.api.DefaultDenyWriteSchemaNode;
+import org.opendaylight.yangtools.rfc6536.model.api.DefaultDenyWriteStatement;
+import org.opendaylight.yangtools.rfc6536.model.api.NACMStatements;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.UnknownEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractVoidStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class DefaultDenyWriteStatementSupport
+    extends AbstractVoidStatementSupport<DefaultDenyWriteStatement, DefaultDenyWriteEffectiveStatement> {
+
+    private static final class Declared extends AbstractDeclaredStatement<Void> implements DefaultDenyWriteStatement {
+        Declared(final StmtContext<Void, ?, ?> context) {
+            super(context);
+        }
+
+        @Override
+        public Void getArgument() {
+            return null;
+        }
+    }
+
+    private static final class Effective extends UnknownEffectiveStatementBase<Void, DefaultDenyWriteStatement>
+            implements DefaultDenyWriteEffectiveStatement, DefaultDenyWriteSchemaNode {
+
+        private final SchemaPath path;
+
+        Effective(final StmtContext<Void, DefaultDenyWriteStatement, ?> ctx) {
+            super(ctx);
+            path = ctx.coerceParentContext().getSchemaPath().get().createChild(
+                ctx.getPublicDefinition().getStatementName());
+        }
+
+        @Override
+        public QName getQName() {
+            return path.getLastComponent();
+        }
+
+        @Override
+        public SchemaPath getPath() {
+            return path;
+        }
+    }
+
+    private static final DefaultDenyWriteStatementSupport INSTANCE =
+            new DefaultDenyWriteStatementSupport(NACMStatements.DEFAULT_DENY_WRITE);
+
+    private final SubstatementValidator validator;
+
+    DefaultDenyWriteStatementSupport(final StatementDefinition definition) {
+        super(definition);
+        this.validator = SubstatementValidator.builder(definition).build();
+    }
+
+    public static DefaultDenyWriteStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public DefaultDenyWriteStatement createDeclared(final StmtContext<Void, DefaultDenyWriteStatement, ?> ctx) {
+        return new Declared(ctx);
+    }
+
+    @Override
+    public DefaultDenyWriteEffectiveStatement createEffective(
+            final StmtContext<Void, DefaultDenyWriteStatement, DefaultDenyWriteEffectiveStatement> ctx) {
+        return new Effective(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return validator;
+    }
+}
diff --git a/yang/rfc6536-parser-support/src/main/java/org/opendaylight/yangtools/rfc6536/parser/package-info.java b/yang/rfc6536-parser-support/src/main/java/org/opendaylight/yangtools/rfc6536/parser/package-info.java
new file mode 100644 (file)
index 0000000..eddffc7
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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
+ */
+/**
+ * YANG parser support for metamodel extensions defined in <a href="https://tools.ietf.org/html/rfc6536">RFC6536</a>.
+ * Add {@link org.opendaylight.yangtools.rfc6536.parser.DefaultDenyAllStatementSupport} and
+ * {@link org.opendaylight.yangtools.rfc6536.parser.DefaultDenyWriteStatementSupport} to your reactor to add support
+ * for this extension.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.rfc6536.parser;
diff --git a/yang/rfc6536-parser-support/src/test/java/org/opendaylight/yangtools/rfc6536/parser/NACMTest.java b/yang/rfc6536-parser-support/src/test/java/org/opendaylight/yangtools/rfc6536/parser/NACMTest.java
new file mode 100644 (file)
index 0000000..b5dd4a6
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc6536.parser;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.rfc6536.model.api.DefaultDenyAllSchemaNode;
+import org.opendaylight.yangtools.rfc6536.model.api.DefaultDenyWriteSchemaNode;
+import org.opendaylight.yangtools.rfc6536.model.api.NACMConstants;
+import org.opendaylight.yangtools.yang.common.QName;
+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.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+
+public class NACMTest {
+    private static CrossSourceStatementReactor reactor;
+
+    @BeforeClass
+    public static void createReactor() {
+        reactor = RFC7950Reactors.defaultReactorBuilder()
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
+                    DefaultDenyAllStatementSupport.getInstance())
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
+                    DefaultDenyWriteStatementSupport.getInstance())
+                .build();
+    }
+
+    @AfterClass
+    public static void freeReactor() {
+        reactor = null;
+    }
+
+    @Test
+    public void testResolution() throws ReactorException, IOException, YangSyntaxErrorException {
+        final BuildAction build = reactor.newBuild();
+        build.addSource(YangStatementStreamSource.create(
+            YangTextSchemaSource.forResource("/ietf-netconf-acm@2012-02-22.yang")));
+        build.addSource(YangStatementStreamSource.create(
+            YangTextSchemaSource.forResource("/ietf-yang-types@2013-07-15.yang")));
+        final SchemaContext context = build.buildEffective();
+
+        final Module module = context.findModule(NACMConstants.RFC6536_MODULE).get();
+        final DataSchemaNode nacm = module.findDataChildByName(QName.create(NACMConstants.RFC6536_MODULE, "nacm"))
+                .get();
+        assertTrue(DefaultDenyAllSchemaNode.findIn(nacm).isPresent());
+        assertFalse(DefaultDenyWriteSchemaNode.findIn(nacm).isPresent());
+    }
+}
diff --git a/yang/rfc6536-parser-support/src/test/resources/ietf-netconf-acm@2012-02-22.yang b/yang/rfc6536-parser-support/src/test/resources/ietf-netconf-acm@2012-02-22.yang
new file mode 100644 (file)
index 0000000..93ac229
--- /dev/null
@@ -0,0 +1,449 @@
+module ietf-netconf-acm {
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
+
+  prefix "nacm";
+
+  import ietf-yang-types {
+    prefix yang;
+  }
+
+  organization
+    "IETF NETCONF (Network Configuration) Working Group";
+
+  contact
+    "WG Web:   <http://tools.ietf.org/wg/netconf/>
+     WG List:  <mailto:netconf@ietf.org>
+
+     WG Chair: Mehmet Ersue
+               <mailto:mehmet.ersue@nsn.com>
+
+     WG Chair: Bert Wijnen
+               <mailto:bertietf@bwijnen.net>
+
+     Editor:   Andy Bierman
+               <mailto:andy@yumaworks.com>
+
+     Editor:   Martin Bjorklund
+               <mailto:mbj@tail-f.com>";
+
+  description
+    "NETCONF Access Control Model.
+
+     Copyright (c) 2012 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD
+     License set forth in Section 4.c of the IETF Trust's
+     Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6536; see
+     the RFC itself for full legal notices.";
+
+  revision "2012-02-22" {
+    description
+      "Initial version";
+    reference
+      "RFC 6536: Network Configuration Protocol (NETCONF)
+                 Access Control Model";
+  }
+
+  /*
+   * Extension statements
+   */
+
+  extension default-deny-write {
+    description
+      "Used to indicate that the data model node
+       represents a sensitive security system parameter.
+
+       If present, and the NACM module is enabled (i.e.,
+       /nacm/enable-nacm object equals 'true'), the NETCONF server
+       will only allow the designated 'recovery session' to have
+       write access to the node.  An explicit access control rule is
+       required for all other users.
+
+       The 'default-deny-write' extension MAY appear within a data
+       definition statement.  It is ignored otherwise.";
+  }
+
+  extension default-deny-all {
+    description
+      "Used to indicate that the data model node
+       controls a very sensitive security system parameter.
+
+       If present, and the NACM module is enabled (i.e.,
+       /nacm/enable-nacm object equals 'true'), the NETCONF server
+       will only allow the designated 'recovery session' to have
+       read, write, or execute access to the node.  An explicit
+       access control rule is required for all other users.
+
+       The 'default-deny-all' extension MAY appear within a data
+       definition statement, 'rpc' statement, or 'notification'
+       statement.  It is ignored otherwise.";
+  }
+
+  /*
+   * Derived types
+   */
+
+  typedef user-name-type {
+    type string {
+      length "1..max";
+    }
+    description
+      "General Purpose Username string.";
+  }
+
+  typedef matchall-string-type {
+    type string {
+      pattern "\*";
+    }
+    description
+      "The string containing a single asterisk '*' is used
+       to conceptually represent all possible values
+       for the particular leaf using this data type.";
+  }
+
+  typedef access-operations-type {
+    type bits {
+      bit create {
+        description
+          "Any protocol operation that creates a
+           new data node.";
+      }
+      bit read {
+        description
+          "Any protocol operation or notification that
+           returns the value of a data node.";
+      }
+      bit update {
+        description
+          "Any protocol operation that alters an existing
+           data node.";
+      }
+      bit delete {
+        description
+          "Any protocol operation that removes a data node.";
+      }
+      bit exec {
+        description
+          "Execution access to the specified protocol operation.";
+      }
+    }
+    description
+      "NETCONF Access Operation.";
+  }
+
+  typedef group-name-type {
+    type string {
+      length "1..max";
+      pattern "[^\*].*";
+    }
+    description
+      "Name of administrative group to which
+       users can be assigned.";
+  }
+
+  typedef action-type {
+    type enumeration {
+      enum permit {
+        description
+          "Requested action is permitted.";
+      }
+      enum deny {
+        description
+          "Requested action is denied.";
+      }
+    }
+    description
+      "Action taken by the server when a particular
+       rule matches.";
+  }
+
+  typedef node-instance-identifier {
+    type yang:xpath1.0;
+    description
+      "Path expression used to represent a special
+       data node instance identifier string.
+
+       A node-instance-identifier value is an
+       unrestricted YANG instance-identifier expression.
+       All the same rules as an instance-identifier apply
+       except predicates for keys are optional.  If a key
+       predicate is missing, then the node-instance-identifier
+       represents all possible server instances for that key.
+
+       This XPath expression is evaluated in the following context:
+
+        o  The set of namespace declarations are those in scope on
+           the leaf element where this type is used.
+
+        o  The set of variable bindings contains one variable,
+           'USER', which contains the name of the user of the current
+            session.
+
+        o  The function library is the core function library, but
+           note that due to the syntax restrictions of an
+           instance-identifier, no functions are allowed.
+
+        o  The context node is the root node in the data tree.";
+  }
+
+  /*
+   * Data definition statements
+   */
+
+  container nacm {
+    nacm:default-deny-all;
+
+    description
+      "Parameters for NETCONF Access Control Model.";
+
+    leaf enable-nacm {
+      type boolean;
+      default true;
+      description
+        "Enables or disables all NETCONF access control
+         enforcement.  If 'true', then enforcement
+         is enabled.  If 'false', then enforcement
+         is disabled.";
+    }
+
+    leaf read-default {
+      type action-type;
+      default "permit";
+      description
+        "Controls whether read access is granted if
+         no appropriate rule is found for a
+         particular read request.";
+    }
+
+    leaf write-default {
+      type action-type;
+      default "deny";
+      description
+        "Controls whether create, update, or delete access
+         is granted if no appropriate rule is found for a
+         particular write request.";
+    }
+
+    leaf exec-default {
+      type action-type;
+      default "permit";
+      description
+        "Controls whether exec access is granted if no appropriate
+         rule is found for a particular protocol operation request.";
+    }
+
+    leaf enable-external-groups {
+      type boolean;
+      default true;
+      description
+        "Controls whether the server uses the groups reported by the
+         NETCONF transport layer when it assigns the user to a set of
+         NACM groups.  If this leaf has the value 'false', any group
+         names reported by the transport layer are ignored by the
+         server.";
+    }
+
+    leaf denied-operations {
+      type yang:zero-based-counter32;
+      config false;
+      mandatory true;
+      description
+        "Number of times since the server last restarted that a
+         protocol operation request was denied.";
+    }
+
+    leaf denied-data-writes {
+      type yang:zero-based-counter32;
+      config false;
+      mandatory true;
+      description
+        "Number of times since the server last restarted that a
+         protocol operation request to alter
+         a configuration datastore was denied.";
+    }
+
+    leaf denied-notifications {
+      type yang:zero-based-counter32;
+      config false;
+      mandatory true;
+      description
+        "Number of times since the server last restarted that
+         a notification was dropped for a subscription because
+         access to the event type was denied.";
+    }
+
+    container groups {
+      description
+        "NETCONF Access Control Groups.";
+
+      list group {
+        key name;
+
+        description
+          "One NACM Group Entry.  This list will only contain
+           configured entries, not any entries learned from
+           any transport protocols.";
+
+        leaf name {
+          type group-name-type;
+          description
+            "Group name associated with this entry.";
+        }
+
+        leaf-list user-name {
+          type user-name-type;
+          description
+            "Each entry identifies the username of
+             a member of the group associated with
+             this entry.";
+        }
+      }
+    }
+
+    list rule-list {
+      key "name";
+      ordered-by user;
+      description
+        "An ordered collection of access control rules.";
+
+      leaf name {
+        type string {
+          length "1..max";
+        }
+        description
+          "Arbitrary name assigned to the rule-list.";
+      }
+      leaf-list group {
+        type union {
+          type matchall-string-type;
+          type group-name-type;
+        }
+        description
+          "List of administrative groups that will be
+           assigned the associated access rights
+           defined by the 'rule' list.
+
+           The string '*' indicates that all groups apply to the
+           entry.";
+      }
+
+      list rule {
+        key "name";
+        ordered-by user;
+        description
+          "One access control rule.
+
+           Rules are processed in user-defined order until a match is
+           found.  A rule matches if 'module-name', 'rule-type', and
+           'access-operations' match the request.  If a rule
+           matches, the 'action' leaf determines if access is granted
+           or not.";
+
+        leaf name {
+          type string {
+            length "1..max";
+          }
+          description
+            "Arbitrary name assigned to the rule.";
+        }
+
+        leaf module-name {
+          type union {
+            type matchall-string-type;
+            type string;
+          }
+          default "*";
+          description
+            "Name of the module associated with this rule.
+
+             This leaf matches if it has the value '*' or if the
+             object being accessed is defined in the module with the
+             specified module name.";
+        }
+        choice rule-type {
+          description
+            "This choice matches if all leafs present in the rule
+             match the request.  If no leafs are present, the
+             choice matches all requests.";
+          case protocol-operation {
+            leaf rpc-name {
+              type union {
+                type matchall-string-type;
+                type string;
+              }
+              description
+                "This leaf matches if it has the value '*' or if
+                 its value equals the requested protocol operation
+                 name.";
+            }
+          }
+          case notification {
+            leaf notification-name {
+              type union {
+                type matchall-string-type;
+                type string;
+              }
+              description
+                "This leaf matches if it has the value '*' or if its
+                 value equals the requested notification name.";
+            }
+          }
+          case data-node {
+            leaf path {
+              type node-instance-identifier;
+              mandatory true;
+              description
+                "Data Node Instance Identifier associated with the
+                 data node controlled by this rule.
+
+                 Configuration data or state data instance
+                 identifiers start with a top-level data node.  A
+                 complete instance identifier is required for this
+                 type of path value.
+
+                 The special value '/' refers to all possible
+                 datastore contents.";
+            }
+          }
+        }
+
+        leaf access-operations {
+          type union {
+            type matchall-string-type;
+            type access-operations-type;
+          }
+          default "*";
+          description
+            "Access operations associated with this rule.
+
+             This leaf matches if it has the value '*' or if the
+             bit corresponding to the requested operation is set.";
+        }
+
+        leaf action {
+          type action-type;
+          mandatory true;
+          description
+            "The access control action associated with the
+             rule.  If a rule is determined to match a
+             particular request, then this object is used
+             to determine whether to permit or deny the
+             request.";
+        }
+
+        leaf comment {
+          type string;
+          description
+            "A textual description of the access rule.";
+        }
+      }
+    }
+  }
+}
diff --git a/yang/rfc6536-parser-support/src/test/resources/ietf-yang-types@2013-07-15.yang b/yang/rfc6536-parser-support/src/test/resources/ietf-yang-types@2013-07-15.yang
new file mode 100644 (file)
index 0000000..bdff18c
--- /dev/null
@@ -0,0 +1,467 @@
+module ietf-yang-types {
+
+     namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+     prefix "yang";
+
+     organization
+      "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+     contact
+      "WG Web:   <http://tools.ietf.org/wg/netmod/>
+       WG List:  <mailto:netmod@ietf.org>
+       WG Chair: David Kessens
+                 <mailto:david.kessens@nsn.com>
+
+       WG Chair: Juergen Schoenwaelder
+                 <mailto:j.schoenwaelder@jacobs-university.de>
+
+       Editor:   Juergen Schoenwaelder
+                 <mailto:j.schoenwaelder@jacobs-university.de>";
+
+     description
+      "This module contains a collection of generally useful derived
+       YANG data types.
+
+       Copyright (c) 2013 IETF Trust and the persons identified as
+       authors of the code.  All rights reserved.
+
+       Redistribution and use in source and binary forms, with or
+       without modification, is permitted pursuant to, and subject
+       to the license terms contained in, the Simplified BSD License
+       set forth in Section 4.c of the IETF Trust's Legal Provisions
+       Relating to IETF Documents
+       (http://trustee.ietf.org/license-info).
+
+       This version of this YANG module is part of RFC 6991; see
+       the RFC itself for full legal notices.";
+
+     revision 2013-07-15 {
+       description
+        "This revision adds the following new data types:
+         - yang-identifier
+         - hex-string
+         - uuid
+         - dotted-quad";
+       reference
+        "RFC 6991: Common YANG Data Types";
+     }
+
+     revision 2010-09-24 {
+       description
+        "Initial revision.";
+       reference
+        "RFC 6021: Common YANG Data Types";
+     }
+
+     /*** collection of counter and gauge types ***/
+
+     typedef counter32 {
+       type uint32;
+       description
+        "The counter32 type represents a non-negative integer
+         that monotonically increases until it reaches a
+         maximum value of 2^32-1 (4294967295 decimal), when it
+         wraps around and starts increasing again from zero.
+
+         Counters have no defined 'initial' value, and thus, a
+         single value of a counter has (in general) no information
+         content.  Discontinuities in the monotonically increasing
+         value normally occur at re-initialization of the
+         management system, and at other times as specified in the
+         description of a schema node using this type.  If such
+         other times can occur, for example, the creation of
+         a schema node of type counter32 at times other than
+         re-initialization, then a corresponding schema node
+         should be defined, with an appropriate type, to indicate
+         the last discontinuity.
+
+         The counter32 type should not be used for configuration
+         schema nodes.  A default statement SHOULD NOT be used in
+         combination with the type counter32.
+
+         In the value set and its semantics, this type is equivalent
+         to the Counter32 type of the SMIv2.";
+       reference
+        "RFC 2578: Structure of Management Information Version 2
+                   (SMIv2)";
+     }
+
+     typedef zero-based-counter32 {
+       type yang:counter32;
+       default "0";
+       description
+        "The zero-based-counter32 type represents a counter32
+         that has the defined 'initial' value zero.
+
+         A schema node of this type will be set to zero (0) on creation
+         and will thereafter increase monotonically until it reaches
+         a maximum value of 2^32-1 (4294967295 decimal), when it
+         wraps around and starts increasing again from zero.
+
+         Provided that an application discovers a new schema node
+         of this type within the minimum time to wrap, it can use the
+         'initial' value as a delta.  It is important for a management
+         station to be aware of this minimum time and the actual time
+         between polls, and to discard data if the actual time is too
+         long or there is no defined minimum time.
+        In the value set and its semantics, this type is equivalent
+         to the ZeroBasedCounter32 textual convention of the SMIv2.";
+       reference
+         "RFC 4502: Remote Network Monitoring Management Information
+                    Base Version 2";
+     }
+
+     typedef counter64 {
+       type uint64;
+       description
+        "The counter64 type represents a non-negative integer
+         that monotonically increases until it reaches a
+         maximum value of 2^64-1 (18446744073709551615 decimal),
+         when it wraps around and starts increasing again from zero.
+
+         Counters have no defined 'initial' value, and thus, a
+         single value of a counter has (in general) no information
+         content.  Discontinuities in the monotonically increasing
+         value normally occur at re-initialization of the
+         management system, and at other times as specified in the
+         description of a schema node using this type.  If such
+         other times can occur, for example, the creation of
+         a schema node of type counter64 at times other than
+         re-initialization, then a corresponding schema node
+         should be defined, with an appropriate type, to indicate
+         the last discontinuity.
+
+         The counter64 type should not be used for configuration
+         schema nodes.  A default statement SHOULD NOT be used in
+         combination with the type counter64.
+
+         In the value set and its semantics, this type is equivalent
+         to the Counter64 type of the SMIv2.";
+       reference
+        "RFC 2578: Structure of Management Information Version 2
+                   (SMIv2)";
+     }
+
+     typedef zero-based-counter64 {
+       type yang:counter64;
+       default "0";
+       description
+        "The zero-based-counter64 type represents a counter64 that
+         has the defined 'initial' value zero.
+         A schema node of this type will be set to zero (0) on creation
+         and will thereafter increase monotonically until it reaches
+         a maximum value of 2^64-1 (18446744073709551615 decimal),
+         when it wraps around and starts increasing again from zero.
+
+         Provided that an application discovers a new schema node
+         of this type within the minimum time to wrap, it can use the
+         'initial' value as a delta.  It is important for a management
+         station to be aware of this minimum time and the actual time
+         between polls, and to discard data if the actual time is too
+         long or there is no defined minimum time.
+
+         In the value set and its semantics, this type is equivalent
+         to the ZeroBasedCounter64 textual convention of the SMIv2.";
+       reference
+        "RFC 2856: Textual Conventions for Additional High Capacity
+                   Data Types";
+     }
+
+     typedef gauge32 {
+       type uint32;
+       description
+        "The gauge32 type represents a non-negative integer, which
+         may increase or decrease, but shall never exceed a maximum
+         value, nor fall below a minimum value.  The maximum value
+         cannot be greater than 2^32-1 (4294967295 decimal), and
+         the minimum value cannot be smaller than 0.  The value of
+         a gauge32 has its maximum value whenever the information
+         being modeled is greater than or equal to its maximum
+         value, and has its minimum value whenever the information
+         being modeled is smaller than or equal to its minimum value.
+         If the information being modeled subsequently decreases
+         below (increases above) the maximum (minimum) value, the
+         gauge32 also decreases (increases).
+
+         In the value set and its semantics, this type is equivalent
+         to the Gauge32 type of the SMIv2.";
+       reference
+        "RFC 2578: Structure of Management Information Version 2
+                   (SMIv2)";
+     }
+
+     typedef gauge64 {
+       type uint64;
+       description
+        "The gauge64 type represents a non-negative integer, which
+         may increase or decrease, but shall never exceed a maximum
+         value, nor fall below a minimum value.  The maximum value
+         cannot be greater than 2^64-1 (18446744073709551615), and
+         the minimum value cannot be smaller than 0.  The value of
+         a gauge64 has its maximum value whenever the information
+         being modeled is greater than or equal to its maximum
+         value, and has its minimum value whenever the information
+         being modeled is smaller than or equal to its minimum value.
+         If the information being modeled subsequently decreases
+         below (increases above) the maximum (minimum) value, the
+         gauge64 also decreases (increases).
+
+         In the value set and its semantics, this type is equivalent
+         to the CounterBasedGauge64 SMIv2 textual convention defined
+         in RFC 2856";
+       reference
+        "RFC 2856: Textual Conventions for Additional High Capacity
+                   Data Types";
+     }
+
+     /*** collection of identifier-related types ***/
+
+     typedef object-identifier {
+       type string {
+         pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+               + '(\.(0|([1-9]\d*)))*';
+       }
+       description
+        "The object-identifier type represents administratively
+         assigned names in a registration-hierarchical-name tree.
+
+         Values of this type are denoted as a sequence of numerical
+         non-negative sub-identifier values.  Each sub-identifier
+         value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers
+         are separated by single dots and without any intermediate
+         whitespace.
+
+         The ASN.1 standard restricts the value space of the first
+         sub-identifier to 0, 1, or 2.  Furthermore, the value space
+         of the second sub-identifier is restricted to the range
+         0 to 39 if the first sub-identifier is 0 or 1.  Finally,
+         the ASN.1 standard requires that an object identifier
+         has always at least two sub-identifiers.  The pattern
+         captures these restrictions.
+
+         Although the number of sub-identifiers is not limited,
+         module designers should realize that there may be
+         implementations that stick with the SMIv2 limit of 128
+         sub-identifiers.
+         This type is a superset of the SMIv2 OBJECT IDENTIFIER type
+         since it is not restricted to 128 sub-identifiers.  Hence,
+         this type SHOULD NOT be used to represent the SMIv2 OBJECT
+         IDENTIFIER type; the object-identifier-128 type SHOULD be
+         used instead.";
+       reference
+        "ISO9834-1: Information technology -- Open Systems
+         Interconnection -- Procedures for the operation of OSI
+         Registration Authorities: General procedures and top
+         arcs of the ASN.1 Object Identifier tree";
+     }
+
+     typedef object-identifier-128 {
+       type object-identifier {
+         pattern '\d*(\.\d*){1,127}';
+       }
+       description
+        "This type represents object-identifiers restricted to 128
+         sub-identifiers.
+
+         In the value set and its semantics, this type is equivalent
+         to the OBJECT IDENTIFIER type of the SMIv2.";
+       reference
+        "RFC 2578: Structure of Management Information Version 2
+                   (SMIv2)";
+     }
+
+     typedef yang-identifier {
+       type string {
+         length "1..max";
+         pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
+         pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*';
+       }
+       description
+         "A YANG identifier string as defined by the 'identifier'
+          rule in Section 12 of RFC 6020.  An identifier must
+          start with an alphabetic character or an underscore
+          followed by an arbitrary sequence of alphabetic or
+          numeric characters, underscores, hyphens, or dots.
+
+          A YANG identifier MUST NOT start with any possible
+          combination of the lowercase or uppercase character
+          sequence 'xml'.";
+       reference
+         "RFC 6020: YANG - A Data Modeling Language for the Network
+                    Configuration Protocol (NETCONF)";
+     }
+     /*** collection of types related to date and time***/
+
+     typedef date-and-time {
+       type string {
+         pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+               + '(Z|[\+\-]\d{2}:\d{2})';
+       }
+       description
+        "The date-and-time type is a profile of the ISO 8601
+         standard for representation of dates and times using the
+         Gregorian calendar.  The profile is defined by the
+         date-time production in Section 5.6 of RFC 3339.
+
+         The date-and-time type is compatible with the dateTime XML
+         schema type with the following notable exceptions:
+
+         (a) The date-and-time type does not allow negative years.
+
+         (b) The date-and-time time-offset -00:00 indicates an unknown
+             time zone (see RFC 3339) while -00:00 and +00:00 and Z
+             all represent the same time zone in dateTime.
+
+         (c) The canonical format (see below) of data-and-time values
+             differs from the canonical format used by the dateTime XML
+             schema type, which requires all times to be in UTC using
+             the time-offset 'Z'.
+
+         This type is not equivalent to the DateAndTime textual
+         convention of the SMIv2 since RFC 3339 uses a different
+         separator between full-date and full-time and provides
+         higher resolution of time-secfrac.
+
+         The canonical format for date-and-time values with a known time
+         zone uses a numeric time zone offset that is calculated using
+         the device's configured known offset to UTC time.  A change of
+         the device's offset to UTC time will cause date-and-time values
+         to change accordingly.  Such changes might happen periodically
+         in case a server follows automatically daylight saving time
+         (DST) time zone offset changes.  The canonical format for
+         date-and-time values with an unknown time zone (usually
+         referring to the notion of local time) uses the time-offset
+         -00:00.";
+       reference
+        "RFC 3339: Date and Time on the Internet: Timestamps
+         RFC 2579: Textual Conventions for SMIv2
+         XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
+     }
+     typedef timeticks {
+       type uint32;
+       description
+        "The timeticks type represents a non-negative integer that
+         represents the time, modulo 2^32 (4294967296 decimal), in
+         hundredths of a second between two epochs.  When a schema
+         node is defined that uses this type, the description of
+         the schema node identifies both of the reference epochs.
+
+         In the value set and its semantics, this type is equivalent
+         to the TimeTicks type of the SMIv2.";
+       reference
+        "RFC 2578: Structure of Management Information Version 2
+                   (SMIv2)";
+     }
+
+     typedef timestamp {
+       type yang:timeticks;
+       description
+        "The timestamp type represents the value of an associated
+         timeticks schema node at which a specific occurrence
+         happened.  The specific occurrence must be defined in the
+         description of any schema node defined using this type.  When
+         the specific occurrence occurred prior to the last time the
+         associated timeticks attribute was zero, then the timestamp
+         value is zero.  Note that this requires all timestamp values
+         to be reset to zero when the value of the associated timeticks
+         attribute reaches 497+ days and wraps around to zero.
+
+         The associated timeticks schema node must be specified
+         in the description of any schema node using this type.
+
+         In the value set and its semantics, this type is equivalent
+         to the TimeStamp textual convention of the SMIv2.";
+       reference
+        "RFC 2579: Textual Conventions for SMIv2";
+     }
+
+     /*** collection of generic address types ***/
+
+     typedef phys-address {
+       type string {
+         pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+       }
+       description
+        "Represents media- or physical-level addresses represented
+         as a sequence octets, each octet represented by two hexadecimal
+         numbers.  Octets are separated by colons.  The canonical
+         representation uses lowercase characters.
+
+         In the value set and its semantics, this type is equivalent
+         to the PhysAddress textual convention of the SMIv2.";
+       reference
+        "RFC 2579: Textual Conventions for SMIv2";
+     }
+
+     typedef mac-address {
+       type string {
+         pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+       }
+       description
+        "The mac-address type represents an IEEE 802 MAC address.
+         The canonical representation uses lowercase characters.
+
+         In the value set and its semantics, this type is equivalent
+         to the MacAddress textual convention of the SMIv2.";
+       reference
+        "IEEE 802: IEEE Standard for Local and Metropolitan Area
+                   Networks: Overview and Architecture
+         RFC 2579: Textual Conventions for SMIv2";
+     }
+
+     /*** collection of XML-specific types ***/
+
+     typedef xpath1.0 {
+       type string;
+       description
+        "This type represents an XPATH 1.0 expression.
+
+         When a schema node is defined that uses this type, the
+         description of the schema node MUST specify the XPath
+         context in which the XPath expression is evaluated.";
+       reference
+        "XPATH: XML Path Language (XPath) Version 1.0";
+     }
+
+     /*** collection of string types ***/
+
+     typedef hex-string {
+       type string {
+         pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+       }
+       description
+        "A hexadecimal string with octets represented as hex digits
+         separated by colons.  The canonical representation uses
+         lowercase characters.";
+     }
+
+     typedef uuid {
+       type string {
+         pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-'
+               + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
+       }
+       description
+        "A Universally Unique IDentifier in the string representation
+         defined in RFC 4122.  The canonical representation uses
+         lowercase characters.
+
+         The following is an example of a UUID in string representation:
+         f81d4fae-7dec-11d0-a765-00a0c91e6bf6
+         ";
+       reference
+        "RFC 4122: A Universally Unique IDentifier (UUID) URN
+                   Namespace";
+     }
+
+     typedef dotted-quad {
+       type string {
+         pattern
+           '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+         + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
+       }
+       description
+         "An unsigned 32-bit number expressed in the dotted-quad
+          notation, i.e., four octets written as decimal numbers
+          and separated with the '.' (full stop) character.";
+     }
+   }
\ No newline at end of file
diff --git a/yang/rfc7952-data-api/pom.xml b/yang/rfc7952-data-api/pom.xml
new file mode 100644 (file)
index 0000000..d807cec
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc7952-data-api</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC7952 data model extensions</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-model-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc7952.data.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc7952-data-api/src/main/java/org/opendaylight/yangtools/rfc7952/data/api/MetadataNormalizedAnydata.java b/yang/rfc7952-data-api/src/main/java/org/opendaylight/yangtools/rfc7952/data/api/MetadataNormalizedAnydata.java
new file mode 100644 (file)
index 0000000..8d476d9
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7952.data.api;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedAnydata;
+
+/**
+ * A piece of {@link NormalizedAnydata} with a corresponding piece of {@link NormalizedMetadata}.
+ */
+@Beta
+@NonNullByDefault
+public interface MetadataNormalizedAnydata extends NormalizedAnydata {
+
+    NormalizedMetadata getMetadata();
+}
diff --git a/yang/rfc7952-data-api/src/main/java/org/opendaylight/yangtools/rfc7952/data/api/NormalizedMetadata.java b/yang/rfc7952-data-api/src/main/java/org/opendaylight/yangtools/rfc7952/data/api/NormalizedMetadata.java
new file mode 100644 (file)
index 0000000..6edd021
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7952.data.api;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+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.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+
+/**
+ * RFC7952 metadata counterpart to a {@link NormalizedNode}. This interface is meant to be used as a companion to
+ * a NormalizedNode instance, hence it does not support iterating over its structure like it is possible with
+ * {@link NormalizedNode#getValue()}. Children may be inquired through {@link #getChildren()}.
+ *
+ * <p>
+ * This model of metadata <em>does not</em> have the RFC7952 restriction on metadata attachment to {@code list}s and
+ * {@code leaf-list}s because NormalizedNode data model has {@link LeafSetNode}, {@link MapNode} and
+ * {@link UnkeyedListNode} to which metadata can be attached.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface NormalizedMetadata extends Identifiable<PathArgument>, Immutable {
+    /**
+     * Return the set of annotations defined in this metadata node. Values are expected to be effectively-immutable
+     * scalar types, like {@link String}s, {@link Number}s and similar. The map must also be effectively-immutable.
+     *
+     * @return The set of annotations attached to the corresponding data node.
+     */
+    @NonNull Map<QName, Object> getAnnotations();
+
+    /**
+     * Returns child nodes. Default implementation returns an empty immutable map.
+     *
+     * @return Child metadata nodes.
+     */
+    default @NonNull Map<PathArgument, NormalizedMetadata> getChildren() {
+        return ImmutableMap.of();
+    }
+}
diff --git a/yang/rfc7952-data-api/src/main/java/org/opendaylight/yangtools/rfc7952/data/api/StreamWriterMetadataExtension.java b/yang/rfc7952-data-api/src/main/java/org/opendaylight/yangtools/rfc7952/data/api/StreamWriterMetadataExtension.java
new file mode 100644 (file)
index 0000000..48a495c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7952.data.api;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriterExtension;
+
+/**
+ * Extension to the NormalizedNodeStreamWriter with metadata support. Semantically this extends the event model of
+ * {@link NormalizedNodeStreamWriter} with a new event, {@link #metadata(ImmutableMap)}. This event is valid on any
+ * open node. This event may be emitted only once.
+ *
+ * <p>
+ * Note that some implementations of this interface, notably those targeting streaming XML, may require metadata to
+ * be emitted before any other events. Such requirement is communicated through {@link #requireMetadataFirst()} and
+ * users must honor it. If such requirement is not set, metadata may be emitted at any time.
+ *
+ * <p>
+ * Furthermore implementations targeting RFC7952 encoding towards external systems are required to handle metadata
+ * attached to {@code leaf-list} and {@code list} nodes by correctly extending them to each entry.
+ */
+@Beta
+public interface StreamWriterMetadataExtension extends NormalizedNodeStreamWriterExtension {
+    /**
+     * Emit a block of metadata associated with the currently-open node. The argument is a map of annotation names,
+     * as defined {@code md:annotation} extension. Values are normalized objects, which are required to be
+     * effectively-immutable.
+     *
+     * @param metadata Metadata block
+     * @throws NullPointerException if {@code metadata} is null
+     * @throws IllegalStateException when this method is invoked outside of an open node or metadata has already been
+     *                               emitted.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void metadata(ImmutableMap<QName, Object> metadata) throws IOException;
+
+    /**
+     * Indicate whether metadata is required to be emitted just after an entry is open. The default implementation
+     * returns false.
+     *
+     * @return True if metadata must occur just after the start of an entry.
+     */
+    default boolean requireMetadataFirst() {
+        return false;
+    }
+}
diff --git a/yang/rfc7952-data-util/pom.xml b/yang/rfc7952-data-util/pom.xml
new file mode 100644 (file)
index 0000000..6e92a4d
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc7952-data-util</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC7952 data model utilities</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-model-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc7952.data.util</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/ImmutableMetadataNormalizedAnydata.java b/yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/ImmutableMetadataNormalizedAnydata.java
new file mode 100644 (file)
index 0000000..b6f4aa3
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7952.data.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.rfc7952.data.api.MetadataNormalizedAnydata;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadata;
+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.util.ImmutableNormalizedAnydata;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+@Beta
+@NonNullByDefault
+public final class ImmutableMetadataNormalizedAnydata extends ImmutableNormalizedAnydata
+        implements MetadataNormalizedAnydata {
+    private final NormalizedMetadata metadata;
+
+    public ImmutableMetadataNormalizedAnydata(final SchemaContext schemaContext, final DataSchemaNode contextNode,
+            final NormalizedNode<?, ?> data, final NormalizedMetadata metadata) {
+        super(schemaContext, contextNode, data);
+        this.metadata = requireNonNull(metadata);
+    }
+
+    public static ImmutableNormalizedAnydata ofOptional(final SchemaContext schemaContext,
+            final DataSchemaNode contextNode, final NormalizedNode<?, ?> data,
+            final Optional<NormalizedMetadata> metadata) {
+        return metadata.isPresent()
+                ? new ImmutableMetadataNormalizedAnydata(schemaContext, contextNode, data, metadata.get())
+                        : new ImmutableNormalizedAnydata(schemaContext, contextNode, data);
+    }
+
+    @Override
+    public NormalizedMetadata getMetadata() {
+        return metadata;
+    }
+
+    @Override
+    public void writeTo(final NormalizedNodeStreamWriter writer, final boolean orderKeyLeaves) throws IOException {
+        NormalizedMetadataWriter.forStreamWriter(writer, orderKeyLeaves).write(getData(), getMetadata()).flush();
+    }
+}
diff --git a/yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/ImmutableNormalizedMetadata.java b/yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/ImmutableNormalizedMetadata.java
new file mode 100644 (file)
index 0000000..21801e8
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7952.data.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadata;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Immutable implementation of {@link NormalizedMetadata}.
+ */
+@Beta
+public class ImmutableNormalizedMetadata extends AbstractIdentifiable<PathArgument> implements NormalizedMetadata {
+    private static final class Container extends ImmutableNormalizedMetadata {
+        private final @NonNull ImmutableMap<PathArgument, NormalizedMetadata> children;
+
+        Container(final PathArgument identifier, final Map<QName, Object> annotations,
+                final Map<PathArgument, ImmutableNormalizedMetadata> children) {
+            super(identifier, annotations);
+            this.children = ImmutableMap.copyOf(children);
+        }
+
+        @Override
+        public ImmutableMap<PathArgument, NormalizedMetadata> getChildren() {
+            return children;
+        }
+    }
+
+    private final @NonNull ImmutableMap<QName, Object> annotations;
+
+    ImmutableNormalizedMetadata(final @NonNull PathArgument identifier, final Map<QName, Object> annotations) {
+        super(identifier);
+        this.annotations = ImmutableMap.copyOf(annotations);
+    }
+
+    /**
+     * Return a new {@link Builder}.
+     *
+     * @return A new Builder.
+     */
+    public static final @NonNull Builder builder() {
+        return new Builder();
+    }
+
+    @Override
+    public final ImmutableMap<QName, Object> getAnnotations() {
+        return annotations;
+    }
+
+    /**
+     * {@link org.opendaylight.yangtools.concepts.Builder} of {@link ImmutableNormalizedMetadata} instances.
+     */
+    public static final class Builder
+            implements org.opendaylight.yangtools.concepts.Builder<ImmutableNormalizedMetadata> {
+        private final Map<PathArgument, ImmutableNormalizedMetadata> children = new HashMap<>();
+        private final Map<QName, Object> annotations = new HashMap<>();
+        private PathArgument identifier;
+
+        Builder() {
+            // Hidden to prevent instantiation
+        }
+
+        @SuppressWarnings("checkstyle:hiddenField")
+        public Builder withIdentifier(final PathArgument identifier) {
+            this.identifier = requireNonNull(identifier);
+            return this;
+        }
+
+        public Builder withAnnotation(final QName type, final Object value) {
+            annotations.put(requireNonNull(type, "type"), requireNonNull(value, "value"));
+            return this;
+        }
+
+        @SuppressWarnings("checkstyle:hiddenField")
+        public Builder withAnnotations(final Map<QName, Object> annotations) {
+            annotations.forEach(this::withAnnotation);
+            return this;
+        }
+
+        public Builder withChild(final ImmutableNormalizedMetadata child) {
+            children.put(child.getIdentifier(), child);
+            return this;
+        }
+
+        @SuppressWarnings("checkstyle:hiddenField")
+        public Builder withChildren(final Collection<ImmutableNormalizedMetadata> children) {
+            children.forEach(this::withChild);
+            return this;
+        }
+
+        @Override
+        public ImmutableNormalizedMetadata build() {
+            final PathArgument id = identifier;
+            checkArgument(id != null, "Identifier has not been set");
+            return children.isEmpty() ? new ImmutableNormalizedMetadata(id, annotations)
+                    : new Container(id, annotations, children);
+        }
+    }
+}
diff --git a/yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/ImmutableNormalizedMetadataStreamWriter.java b/yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/ImmutableNormalizedMetadataStreamWriter.java
new file mode 100644 (file)
index 0000000..2d9c796
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7952.data.util;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Optional;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadata;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.rfc7952.data.util.ImmutableNormalizedMetadata.Builder;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * A simple {@link StreamWriterMetadataExtension} implementation, which takes care of building
+ * {@link NormalizedMetadata} based on additional nesting instructions.
+ */
+@Beta
+public final class ImmutableNormalizedMetadataStreamWriter implements StreamWriterMetadataExtension {
+    private final Deque<Builder> builders = new ArrayDeque<>();
+
+    private ImmutableNormalizedMetadata result;
+
+    public void enter(final PathArgument identifier) {
+        checkNotDone();
+        builders.push(ImmutableNormalizedMetadata.builder().withIdentifier(identifier));
+    }
+
+    public void exit() {
+        checkNotDone();
+        final ImmutableNormalizedMetadata metadata = builders.pop().build();
+        final Builder parent = builders.peek();
+        if (parent != null) {
+            if (!metadata.getAnnotations().isEmpty()) {
+                parent.withChild(metadata);
+            }
+        } else {
+            result = metadata;
+        }
+    }
+
+    public Optional<NormalizedMetadata> getResult() {
+        checkState(result != null, "Metadata tree has not been completely built");
+        return result.getAnnotations().isEmpty() ? Optional.empty() : Optional.of(result);
+    }
+
+    @Override
+    public void metadata(final ImmutableMap<QName, Object> metadata) throws IOException {
+        builders.peek().withAnnotations(metadata);
+    }
+
+    private void checkNotDone() {
+        checkState(result != null, "Metadata already completed");
+    }
+}
diff --git a/yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/NormalizedMetadataWriter.java b/yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/NormalizedMetadataWriter.java
new file mode 100644 (file)
index 0000000..d9c9673
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7952.data.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadata;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+
+/**
+ * A utility class to attach {@link NormalizedMetadata} into a NormalizedNode stream, such as the one produced by
+ * {@link NormalizedNodeWriter}, so that a target {@link NormalizedNodeStreamWriter} sees both data and metadata in
+ * the stream. A typical use would like this:
+ *
+ * <p>
+ * <code>
+ *   // Data for output
+ *   NormalizedNode&lt;?, ?&gt; data;
+ *   // Metadata for output
+ *   NormalizedMetadata metadata;
+ *
+ *   // Target output writer
+ *   NormalizedNodeStreamWriter output = ...;
+ *   // Metadata writer
+ *   NormalizedMetadataStreamWriter metaWriter = NormalizedMetadataWriter.forStreamWriter(output);
+ *
+ *   // Write a normalized node and its metadata
+ *   dataWriter.write(data, metadata);
+ * </code>
+ *
+ * <p>
+ * This class is NOT thread-safe.
+ *
+ * @author Robert Varga
+ */
+@Beta
+// FIXME: 5.0.0: consider moving this class to api to keep related stuff together
+public final class NormalizedMetadataWriter implements Closeable, Flushable {
+    private final NormalizedNodeStreamWriter writer;
+    private final boolean orderKeyLeaves;
+
+    private NormalizedMetadataWriter(final NormalizedNodeStreamWriter writer, final boolean orderKeyLeaves) {
+        this.writer = requireNonNull(writer);
+        this.orderKeyLeaves = orderKeyLeaves;
+    }
+
+    /**
+     * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. Unlike the simple
+     * {@link #forStreamWriter(NormalizedNodeStreamWriter)} method, this allows the caller to switch off RFC6020 XML
+     * compliance, providing better throughput. The reason is that the XML mapping rules in RFC6020 require
+     * the encoding to emit leaf nodes which participate in a list's key first and in the order in which they are
+     * defined in the key. For JSON, this requirement is completely relaxed and leaves can be ordered in any way we
+     * see fit. The former requires a bit of work: first a lookup for each key and then for each emitted node we need
+     * to check whether it was already emitted.
+     *
+     * @param writer Back-end writer
+     * @param orderKeyLeaves whether the returned instance should be RFC6020 XML compliant.
+     * @return A new instance.
+     */
+    public static @NonNull NormalizedMetadataWriter forStreamWriter(final NormalizedNodeStreamWriter writer,
+            final boolean orderKeyLeaves) {
+        return new NormalizedMetadataWriter(writer, orderKeyLeaves);
+    }
+
+    /**
+     * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. This is a convenience method for
+     * {@code forStreamWriter(writer, true)}.
+     *
+     * @param writer Back-end writer
+     * @return A new instance.
+     */
+    public static @NonNull NormalizedMetadataWriter forStreamWriter(final NormalizedNodeStreamWriter writer) {
+        return forStreamWriter(writer, true);
+    }
+
+    /**
+     * Iterate over the provided {@link NormalizedNode} and {@link NormalizedMetadata} and emit write events to the
+     * encapsulated {@link NormalizedNodeStreamWriter}.
+     *
+     * @param data NormalizedNode data
+     * @param metadata {@link NormalizedMetadata} metadata
+     * @return NormalizedNodeWriter this
+     * @throws NullPointerException if any argument is null
+     * @throws IllegalArgumentException if metadata does not match data
+     * @throws IOException when thrown from the backing writer.
+     */
+    public @NonNull NormalizedMetadataWriter write(final NormalizedNode<?, ?> data, final NormalizedMetadata metadata)
+            throws IOException {
+        final PathArgument dataId = data.getIdentifier();
+        final PathArgument metaId = metadata.getIdentifier();
+        checkArgument(dataId.equals(metaId), "Mismatched data %s and metadata %s", dataId, metaId);
+
+        final StreamWriterMetadataExtension metaWriter = writer.getExtensions()
+                .getInstance(StreamWriterMetadataExtension.class);
+        final NormalizedNodeStreamWriter delegate = metaWriter == null ? writer
+                : new NormalizedNodeStreamWriterMetadataDecorator(writer, metaWriter, metadata);
+
+        final NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(delegate, orderKeyLeaves);
+        nnWriter.write(data);
+        nnWriter.flush();
+        return this;
+    }
+
+    @Override
+    public void close() throws IOException {
+        try {
+            writer.flush();
+        } finally {
+            writer.close();
+        }
+    }
+
+    @Override
+    public void flush() throws IOException {
+        writer.flush();
+    }
+}
diff --git a/yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/NormalizedNodeStreamWriterMetadataDecorator.java b/yang/rfc7952-data-util/src/main/java/org/opendaylight/yangtools/rfc7952/data/util/NormalizedNodeStreamWriterMetadataDecorator.java
new file mode 100644 (file)
index 0000000..cbcd22b
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7952.data.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Map;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadata;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.common.QName;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.ForwardingNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+
+/**
+ * A simple decorator on top of a NormalizedNodeStreamWriter, which attaches NormalizedMetadata to the event stream,
+ * so that the metadata is emitted along with data.
+ */
+final class NormalizedNodeStreamWriterMetadataDecorator extends ForwardingNormalizedNodeStreamWriter {
+    private final Deque<NormalizedMetadata> stack = new ArrayDeque<>();
+    private final StreamWriterMetadataExtension metaWriter;
+    private final NormalizedNodeStreamWriter writer;
+    private final NormalizedMetadata metadata;
+
+    private int absentDepth = 0;
+
+    NormalizedNodeStreamWriterMetadataDecorator(final NormalizedNodeStreamWriter writer,
+            final StreamWriterMetadataExtension metaWriter, final NormalizedMetadata metadata) {
+        this.writer = requireNonNull(writer);
+        this.metaWriter = requireNonNull(metaWriter);
+        this.metadata = requireNonNull(metadata);
+    }
+
+    @Override
+    protected NormalizedNodeStreamWriter delegate() {
+        return writer;
+    }
+
+    @Override
+    public void startLeafNode(final NodeIdentifier name) throws IOException {
+        super.startLeafNode(name);
+        enterMetadataNode(name);
+    }
+
+    @Override
+    public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startLeafSet(name, childSizeHint);
+        enterMetadataNode(name);
+    }
+
+    @Override
+    public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startOrderedLeafSet(name, childSizeHint);
+        enterMetadataNode(name);
+    }
+
+    @Override
+    public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
+        super.startLeafSetEntryNode(name);
+        enterMetadataNode(name);
+    }
+
+    @Override
+    public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startContainerNode(name, childSizeHint);
+        enterMetadataNode(name);
+    }
+
+    @Override
+    public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startUnkeyedList(name, childSizeHint);
+        enterMetadataNode(name);
+    }
+
+    @Override
+    public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startUnkeyedListItem(name, childSizeHint);
+        enterMetadataNode(name);
+    }
+
+    @Override
+    public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startMapNode(name, childSizeHint);
+        enterMetadataNode(name);
+    }
+
+    @Override
+    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+            throws IOException {
+        super.startMapEntryNode(identifier, childSizeHint);
+        enterMetadataNode(identifier);
+    }
+
+    @Override
+    public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startOrderedMapNode(name, childSizeHint);
+        enterMetadataNode(name);
+    }
+
+    @Override
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startChoiceNode(name, childSizeHint);
+        enterMetadataNode(name);
+    }
+
+    @Override
+    public void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
+        super.startAugmentationNode(identifier);
+        enterMetadataNode(identifier);
+    }
+
+    @Override
+    public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        final boolean ret = super.startAnyxmlNode(name, objectModel);
+        if (ret) {
+            enterMetadataNode(name);
+        }
+        return ret;
+    }
+
+    @Override
+    public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startYangModeledAnyXmlNode(name, childSizeHint);
+        enterMetadataNode(name);
+    }
+
+    @Override
+    public void endNode() throws IOException {
+        super.endNode();
+
+        if (absentDepth > 0) {
+            absentDepth--;
+        } else {
+            stack.pop();
+        }
+    }
+
+    private void enterMetadataNode(final PathArgument name) throws IOException {
+        if (absentDepth > 0) {
+            absentDepth++;
+            return;
+        }
+
+        final NormalizedMetadata current = stack.peek();
+        if (current != null) {
+            final NormalizedMetadata child = current.getChildren().get(name);
+            if (child != null) {
+                enterChild(child);
+            } else {
+                absentDepth = 1;
+            }
+        } else {
+            // Empty stack: enter first entry
+            enterChild(metadata);
+        }
+    }
+
+    private void enterChild(final NormalizedMetadata child) throws IOException {
+        final Map<QName, Object> annotations = child.getAnnotations();
+        if (!annotations.isEmpty()) {
+            metaWriter.metadata(ImmutableMap.copyOf(annotations));
+        }
+        stack.push(child);
+    }
+}
diff --git a/yang/rfc7952-model-api/pom.xml b/yang/rfc7952-model-api/pom.xml
new file mode 100644 (file)
index 0000000..5e8d392
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc7952-model-api</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC7952 metamodel</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc7952.model.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationEffectiveStatement.java b/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationEffectiveStatement.java
new file mode 100644 (file)
index 0000000..86d9e4f
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.rfc7952.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeDefinitionAware;
+
+/**
+ * Effective statement representation of 'annotation' extension defined in
+ * <a href="https://tools.ietf.org/html/rfc7952">RFC7952</a>.
+ */
+@Beta
+public interface AnnotationEffectiveStatement extends EffectiveStatement<QName, AnnotationStatement>,
+        TypeDefinitionAware {
+
+}
diff --git a/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationSchemaNode.java b/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationSchemaNode.java
new file mode 100644 (file)
index 0000000..20c3b9b
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.rfc7952.model.api;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeAware;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+/**
+ * Represents the effect of 'annotation' extension, as defined in
+ * <a href="https://tools.ietf.org/html/rfc7952">RFC7952</a>, being attached to a SchemaNode.
+ */
+@Beta
+public interface AnnotationSchemaNode extends UnknownSchemaNode, TypeAware {
+    /**
+     * Find specified annotation if it is supported by the specified SchemaContext.
+     *
+     * @param context SchemaContext to search
+     * @param qname Annotation name
+     * @return {@link AnnotationSchemaNode} corresponding to specified name, or empty if it is not supported
+     *         by the SchemaContext..
+     * @throws NullPointerException if any of the arguments is null
+     */
+    static @NonNull Optional<AnnotationSchemaNode> find(final SchemaContext context, final QName qname) {
+        if (context instanceof AnnotationSchemaNodeAware) {
+            return ((AnnotationSchemaNodeAware) context).findAnnotation(qname);
+        }
+
+        return context.findModule(qname.getModule()).flatMap(module -> {
+            return module.getUnknownSchemaNodes().stream()
+                    .filter(AnnotationSchemaNode.class::isInstance)
+                    .map(AnnotationSchemaNode.class::cast)
+                    .filter(annotation -> qname.equals(annotation.getQName())).findAny();
+        });
+    }
+
+    /**
+     * Find all annotations supported by a SchemaContext.
+     *
+     * @param context SchemaContext to search
+     * @return {@link AnnotationSchemaNode}s supported by the SchemaContext..
+     * @throws NullPointerException if context is null
+     */
+    static @NonNull Map<QName, AnnotationSchemaNode> findAll(final SchemaContext context) {
+        final Builder<QName, AnnotationSchemaNode> builder = ImmutableMap.builder();
+        for (Module module : context.getModules()) {
+            for (UnknownSchemaNode node : module.getUnknownSchemaNodes()) {
+                if (node instanceof AnnotationSchemaNode) {
+                    builder.put(node.getQName(), (AnnotationSchemaNode) node);
+                }
+            }
+        }
+
+        return builder.build();
+    }
+}
diff --git a/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationSchemaNodeAware.java b/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationSchemaNodeAware.java
new file mode 100644 (file)
index 0000000..d7cee7e
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7952.model.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Interface for entities which can lookup {@link AnnotationSchemaNode}s based on their name.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface AnnotationSchemaNodeAware {
+    /**
+     * Find an annotation based on its QName.
+     *
+     * @param qname Annotation name
+     * @return AnnotationSchemaNode if found
+     * @throws NullPointerException if {@code qname} is null
+     */
+    @NonNull Optional<AnnotationSchemaNode> findAnnotation(QName qname);
+}
diff --git a/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationSchemaNodeAwareSchemaContext.java b/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationSchemaNodeAwareSchemaContext.java
new file mode 100644 (file)
index 0000000..886a3cc
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7952.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A {@link SchemaContext} which is also {@link AnnotationSchemaNodeAware}. This is a utility capture for users which
+ * need to deal with SchemaContext, but also require it to have annotation indices.
+ */
+@Beta
+public interface AnnotationSchemaNodeAwareSchemaContext extends AnnotationSchemaNodeAware, SchemaContext {
+
+}
diff --git a/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationStatement.java b/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/AnnotationStatement.java
new file mode 100644 (file)
index 0000000..303b1f7
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.rfc7952.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.DocumentedDeclaredStatement.WithStatus;
+import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureAwareDeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeAwareDeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+
+/**
+ * Declared statement representation of 'annotation' extension defined in
+ * <a href="https://tools.ietf.org/html/rfc7952">RFC7952</a>.
+ */
+@Beta
+public interface AnnotationStatement extends UnknownStatement<QName>, WithStatus<QName>,
+    IfFeatureAwareDeclaredStatement<QName>, TypeAwareDeclaredStatement {
+
+}
diff --git a/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/MetadataConstants.java b/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/MetadataConstants.java
new file mode 100644 (file)
index 0000000..d977281
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7952.model.api;
+
+import com.google.common.collect.ImmutableList;
+import java.net.URI;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Constants associated with RFC7952.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault
+public final class MetadataConstants {
+    private static final String MODULE_NAME = "ietf-yang-metadata";
+    private static final URI MODULE_NAMESPACE = URI.create("urn:ietf:params:xml:ns:yang:ietf-yang-metadata");
+    private static final Revision RFC7952_REVISION = Revision.of("2016-08-05");
+
+    /**
+     * Runtime RFC7952 identity.
+     */
+    public static final QNameModule RFC7952_MODULE = QNameModule.create(MODULE_NAMESPACE, RFC7952_REVISION).intern();
+
+    /**
+     * RFC7952 model source name.
+     */
+    public static final SourceIdentifier RFC7952_SOURCE = RevisionSourceIdentifier.create(MODULE_NAME,
+        RFC7952_REVISION);
+
+    /**
+     * Normative prefix to use when importing {@link #RFC7952_SOURCE}.
+     */
+    public static final String MODULE_PREFIX = "md";
+
+    private MetadataConstants() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Return identifiers of all sources known to define the metadata extension.
+     *
+     * @return Collection of identifiers.
+     */
+    public static Collection<SourceIdentifier> knownModelSources() {
+        return ImmutableList.of(RFC7952_SOURCE);
+    }
+}
diff --git a/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/MetadataStatements.java b/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/MetadataStatements.java
new file mode 100644 (file)
index 0000000..4266579
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7952.model.api;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+/**
+ * {@link StatementDefinition}s for statements defined by RFC7952.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault
+public enum MetadataStatements implements StatementDefinition {
+    ANNOTATION(QName.create(MetadataConstants.RFC7952_MODULE, "annotation"), "name", AnnotationStatement.class,
+        AnnotationEffectiveStatement.class);
+
+    private final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation;
+    private final Class<? extends DeclaredStatement<?>> declaredRepresentation;
+    private final QName statementName;
+    private final ArgumentDefinition argumentDef;
+
+    MetadataStatements(final QName statementName, final String argumentName,
+            final Class<? extends DeclaredStatement<?>> declaredRepresentation,
+                    final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation) {
+        this.statementName = statementName.intern();
+        this.argumentDef = ArgumentDefinition.of(QName.create(statementName, argumentName).intern(), false);
+        this.declaredRepresentation = requireNonNull(declaredRepresentation);
+        this.effectiveRepresentation = requireNonNull(effectiveRepresentation);
+    }
+
+    @Override
+    public Optional<ArgumentDefinition> getArgumentDefinition() {
+        return Optional.of(argumentDef);
+    }
+
+    @Override
+    public QName getStatementName() {
+        return statementName;
+    }
+
+    @Override
+    public Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass() {
+        return effectiveRepresentation;
+    }
+
+    @Override
+    public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+        return declaredRepresentation;
+    }
+}
diff --git a/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/package-info.java b/yang/rfc7952-model-api/src/main/java/org/opendaylight/yangtools/rfc7952/model/api/package-info.java
new file mode 100644 (file)
index 0000000..fd32f5a
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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
+ */
+/**
+ * YANG metamodel extensions to support metadata, as defined in
+ * <a href="https://tools.ietf.org/html/rfc7952">RFC7952</a>. For SchemaNode world entry space is defined via
+ * {@link org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNode}'s static methods.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.rfc7952.model.api;
diff --git a/yang/rfc7952-parser-support/pom.xml b/yang/rfc7952-parser-support/pom.xml
new file mode 100644 (file)
index 0000000..21cafba
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc7952-parser-support</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC7952 parser support</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-model-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-rfc7950</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc7952.parser.support</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc7952-parser-support/src/main/java/org/opendaylight/yangtools/rfc7952/parser/AnnotationStatementSupport.java b/yang/rfc7952-parser-support/src/main/java/org/opendaylight/yangtools/rfc7952/parser/AnnotationStatementSupport.java
new file mode 100644 (file)
index 0000000..637c079
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7952.parser;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.rfc7952.model.api.AnnotationEffectiveStatement;
+import org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNode;
+import org.opendaylight.yangtools.rfc7952.model.api.AnnotationStatement;
+import org.opendaylight.yangtools.rfc7952.model.api.MetadataStatements;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnitsStatement;
+import org.opendaylight.yangtools.yang.model.util.type.ConcreteTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.ConcreteTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.UnknownEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class AnnotationStatementSupport
+        extends AbstractStatementSupport<QName, AnnotationStatement, AnnotationEffectiveStatement> {
+
+    private static final class Declared extends AbstractDeclaredStatement<QName> implements AnnotationStatement {
+        Declared(final StmtContext<QName, ?, ?> context) {
+            super(context);
+        }
+
+        @Override
+        public QName getArgument() {
+            return argument();
+        }
+    }
+
+    private static final class Effective extends UnknownEffectiveStatementBase<QName, AnnotationStatement>
+            implements AnnotationEffectiveStatement, AnnotationSchemaNode {
+
+        private final @NonNull TypeDefinition<?> type;
+        private final @NonNull SchemaPath path;
+
+        Effective(final StmtContext<QName, AnnotationStatement, ?> ctx) {
+            super(ctx);
+            path = ctx.coerceParentContext().getSchemaPath().get().createChild(argument());
+
+            final TypeEffectiveStatement<?> typeStmt = SourceException.throwIfNull(
+                firstSubstatementOfType(TypeEffectiveStatement.class), ctx.getStatementSourceReference(),
+                "AnnotationStatementSupport %s is missing a 'type' statement", argument());
+
+            final ConcreteTypeBuilder<?> builder = ConcreteTypes.concreteTypeBuilder(typeStmt.getTypeDefinition(),
+                path);
+            final StmtContext<String, ?, ?> unitsStmt = StmtContextUtils.findFirstEffectiveSubstatement(ctx,
+                UnitsStatement.class);
+            if (unitsStmt != null) {
+                builder.setUnits(unitsStmt.getStatementArgument());
+            }
+            type = builder.build();
+        }
+
+        @Override
+        public QName getQName() {
+            return path.getLastComponent();
+        }
+
+        @Override
+        public SchemaPath getPath() {
+            return path;
+        }
+
+        @Override
+        public TypeDefinition<?> getType() {
+            return type;
+        }
+
+        @Override
+        public TypeDefinition<?> getTypeDefinition() {
+            return type;
+        }
+    }
+
+    private static final AnnotationStatementSupport INSTANCE = new AnnotationStatementSupport(
+        MetadataStatements.ANNOTATION);
+
+    private final SubstatementValidator validator;
+
+    AnnotationStatementSupport(final StatementDefinition definition) {
+        super(definition);
+        this.validator = SubstatementValidator.builder(definition)
+                .addMandatory(YangStmtMapping.TYPE)
+                .addOptional(YangStmtMapping.DESCRIPTION)
+                .addAny(YangStmtMapping.IF_FEATURE)
+                .addOptional(YangStmtMapping.REFERENCE)
+                .addOptional(YangStmtMapping.STATUS)
+                .addOptional(YangStmtMapping.UNITS)
+                .build();
+    }
+
+    public static AnnotationStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public AnnotationStatement createDeclared(final StmtContext<QName, AnnotationStatement, ?> ctx) {
+        return new Declared(ctx);
+    }
+
+    @Override
+    public AnnotationEffectiveStatement createEffective(
+            final StmtContext<QName, AnnotationStatement, AnnotationEffectiveStatement> ctx) {
+        return new Effective(ctx);
+    }
+
+    @Override
+    public QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public void onStatementAdded(final Mutable<QName, AnnotationStatement, AnnotationEffectiveStatement> stmt) {
+        final StatementDefinition parentDef = stmt.coerceParentContext().getPublicDefinition();
+        SourceException.throwIf(YangStmtMapping.MODULE != parentDef && YangStmtMapping.SUBMODULE != parentDef,
+                stmt.getStatementSourceReference(),
+                "Annotations may only be defined at root of either a module or a submodule");
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return validator;
+    }
+}
diff --git a/yang/rfc7952-parser-support/src/main/java/org/opendaylight/yangtools/rfc7952/parser/package-info.java b/yang/rfc7952-parser-support/src/main/java/org/opendaylight/yangtools/rfc7952/parser/package-info.java
new file mode 100644 (file)
index 0000000..26e9d3e
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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
+ */
+/**
+ * YANG parser support for metamodel extensions defined in <a href="https://tools.ietf.org/html/rfc7952">RFC7952</a>.
+ * Add {@link org.opendaylight.yangtools.rfc7952.parser.AnnotationStatementSupport} to your reactor to add support
+ * for this extension.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.rfc7952.parser;
diff --git a/yang/rfc7952-parser-support/src/test/java/org/opendaylight/yangtools/rfc7952/parser/AnnotationTest.java b/yang/rfc7952-parser-support/src/test/java/org/opendaylight/yangtools/rfc7952/parser/AnnotationTest.java
new file mode 100644 (file)
index 0000000..e03b04f
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7952.parser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNode;
+import org.opendaylight.yangtools.yang.common.QName;
+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.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+
+public class AnnotationTest {
+    private static final QName LAST_MODIFIED_QNAME = QName.create("http://example.org/example-last-modified",
+            "last-modified");
+    private static CrossSourceStatementReactor reactor;
+
+    @BeforeClass
+    public static void createReactor() {
+        reactor = RFC7950Reactors.vanillaReactorBuilder()
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, AnnotationStatementSupport.getInstance())
+                .build();
+    }
+
+    @AfterClass
+    public static void freeReactor() {
+        reactor = null;
+    }
+
+    @Test
+    public void testAnnotationResolution() throws ReactorException, IOException, YangSyntaxErrorException {
+        final BuildAction build = reactor.newBuild();
+        build.addSource(YangStatementStreamSource.create(
+            YangTextSchemaSource.forResource("/ietf-yang-metadata@2016-08-05.yang")));
+        build.addSource(YangStatementStreamSource.create(
+            YangTextSchemaSource.forResource("/example-last-modified.yang")));
+        final SchemaContext context = build.buildEffective();
+
+        final Map<QName, AnnotationSchemaNode> annotations = AnnotationSchemaNode.findAll(context);
+        assertEquals(1, annotations.size());
+        final AnnotationSchemaNode annotation = annotations.get(LAST_MODIFIED_QNAME);
+        assertNotNull(annotation);
+
+        final Optional<AnnotationSchemaNode> findAnnotation = AnnotationSchemaNode.find(context, LAST_MODIFIED_QNAME);
+        assertTrue(findAnnotation.isPresent());
+        assertSame(annotation, findAnnotation.get());
+
+        assertEquals(BaseTypes.stringType(), annotation.getType());
+        assertEquals(Optional.empty(), annotation.getReference());
+        assertEquals(Optional.of("This annotation contains the date and time when the\n"
+                + "annotated instance was last modified (or created)."), annotation.getDescription());
+    }
+}
diff --git a/yang/rfc7952-parser-support/src/test/resources/example-last-modified.yang b/yang/rfc7952-parser-support/src/test/resources/example-last-modified.yang
new file mode 100644 (file)
index 0000000..124fb4c
--- /dev/null
@@ -0,0 +1,13 @@
+module example-last-modified {
+    namespace "http://example.org/example-last-modified";
+    prefix "elm";
+    import ietf-yang-metadata {
+        prefix "md";
+    }
+    md:annotation last-modified {
+        type string;
+        description
+          "This annotation contains the date and time when the
+           annotated instance was last modified (or created).";
+    }
+}
diff --git a/yang/rfc7952-parser-support/src/test/resources/ietf-yang-metadata@2016-08-05.yang b/yang/rfc7952-parser-support/src/test/resources/ietf-yang-metadata@2016-08-05.yang
new file mode 100644 (file)
index 0000000..5939eeb
--- /dev/null
@@ -0,0 +1,84 @@
+module ietf-yang-metadata {
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-yang-metadata";
+
+  prefix "md";
+
+  organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+    "WG Web:   <https://datatracker.ietf.org/wg/netmod/>
+
+     WG List:  <mailto:netmod@ietf.org>
+
+     WG Chair: Lou Berger
+               <mailto:lberger@labn.net>
+
+     WG Chair: Kent Watsen
+               <mailto:kwatsen@juniper.net>
+
+     Editor:   Ladislav Lhotka
+               <mailto:lhotka@nic.cz>";
+
+  description
+    "This YANG module defines an 'extension' statement that allows
+     for defining metadata annotations.
+
+     Copyright (c) 2016 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject to
+     the license terms contained in, the Simplified BSD License set
+     forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 7952
+     (http://www.rfc-editor.org/info/rfc7952); see the RFC itself
+     for full legal notices.";
+
+  revision 2016-08-05 {
+    description
+      "Initial revision.";
+    reference
+      "RFC 7952: Defining and Using Metadata with YANG";
+  }
+
+  extension annotation {
+    argument name;
+    description
+      "This extension allows for defining metadata annotations in
+       YANG modules.  The 'md:annotation' statement can appear only
+       at the top level of a YANG module or submodule, i.e., it
+       becomes a new alternative in the ABNF production rule for
+       'body-stmts' (Section 14 in RFC 7950).
+
+       The argument of the 'md:annotation' statement defines the name
+       of the annotation.  Syntactically, it is a YANG identifier as
+       defined in Section 6.2 of RFC 7950.
+
+       An annotation defined with this 'extension' statement inherits
+       the namespace and other context from the YANG module in which
+       it is defined.
+
+       The data type of the annotation value is specified in the same
+       way as for a leaf data node using the 'type' statement.
+
+       The semantics of the annotation and other documentation can be
+       specified using the following standard YANG substatements (all
+       are optional): 'description', 'if-feature', 'reference',
+       'status', and 'units'.
+
+       A server announces support for a particular annotation by
+       including the module in which the annotation is defined among
+       the advertised YANG modules, e.g., in a NETCONF <hello>
+       message or in the YANG library (RFC 7950).  The annotation can
+       then be attached to any instance of a data node defined in any
+       YANG module that is advertised by the server.
+
+       XML encoding and JSON encoding of annotations are defined in
+       RFC 7952.";
+  }
+}
diff --git a/yang/rfc8040-model-api/pom.xml b/yang/rfc8040-model-api/pom.xml
new file mode 100644 (file)
index 0000000..9f14b4e
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc8040-model-api</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC8040 model API</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc8040.model.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataConstants.java b/yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataConstants.java
new file mode 100644 (file)
index 0000000..9a5b62b
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc8040.model.api;
+
+import com.google.common.collect.ImmutableList;
+import java.net.URI;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Constants associated with RFC8040.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault
+public final class YangDataConstants {
+    private static final String MODULE_NAME = "ietf-restconf";
+    private static final URI MODULE_NAMESPACE = URI.create("urn:ietf:params:xml:ns:yang:ietf-restconf");
+    private static final Revision RFC8040_REVISION = Revision.of("2017-01-26");
+
+    /**
+     * Runtime RFC8040 identity.
+     */
+    public static final QNameModule RFC8040_MODULE = QNameModule.create(MODULE_NAMESPACE, RFC8040_REVISION).intern();
+
+    /**
+     * RFC8040 model source name.
+     */
+    public static final SourceIdentifier RFC8040_SOURCE = RevisionSourceIdentifier.create(MODULE_NAME,
+        RFC8040_REVISION);
+
+    /**
+     * Normative prefix to use when importing {@link #RFC8040_SOURCE}.
+     */
+    public static final String MODULE_PREFIX = "rc";
+
+    private YangDataConstants() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Return identifiers of all sources known to define the metadata extension.
+     *
+     * @return Collection of identifiers.
+     */
+    public static Collection<SourceIdentifier> knownModelSources() {
+        return ImmutableList.of(RFC8040_SOURCE);
+    }
+}
diff --git a/yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataEffectiveStatement.java b/yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataEffectiveStatement.java
new file mode 100644 (file)
index 0000000..64c48ed
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc8040.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
+
+/**
+ * Effective statement representation of 'yang-data' extension defined in
+ * <a href="https://tools.ietf.org/html/rfc8040#section-8">RFC 8040</a>.
+ */
+@Beta
+public interface YangDataEffectiveStatement extends EffectiveStatement<String, YangDataStatement> {
+
+    /**
+     * Return the container statement defined in this yang-data statement instance.
+     *
+     * @return container statement
+     */
+    ContainerEffectiveStatement getContainer();
+}
diff --git a/yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataSchemaNode.java b/yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataSchemaNode.java
new file mode 100644 (file)
index 0000000..5314f48
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc8040.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+/**
+ * Represents 'yang-data' extension statement defined in
+ * <a href="https://tools.ietf.org/html/rfc8040#section-8">RFC8040</a>. This statement must appear as a top-level
+ * statement, otherwise it is ignored and does not appear in the final schema context. It must contain exactly one
+ * top-level container node (directly or indirectly via a uses statement).
+ */
+@Beta
+public interface YangDataSchemaNode extends UnknownSchemaNode {
+    /**
+     * Returns container schema node container within this yang-data definition.
+     *
+     * @return container schema node
+     */
+    ContainerSchemaNode getContainerSchemaNode();
+}
diff --git a/yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataStatement.java b/yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataStatement.java
new file mode 100644 (file)
index 0000000..a3a0a70
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc8040.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+
+/**
+ * Declared statement representation of 'yang-data' extension defined in
+ * <a href="https://tools.ietf.org/html/rfc8040#section-8">RFC 8040</a>.
+ */
+@Beta
+public interface YangDataStatement extends UnknownStatement<String> {
+
+}
diff --git a/yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataStatements.java b/yang/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataStatements.java
new file mode 100644 (file)
index 0000000..a7ac3c9
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc8040.model.api;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+/**
+ * {@link StatementDefinition}s for statements defined by RFC8040.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault
+public enum YangDataStatements implements StatementDefinition {
+    YANG_DATA(QName.create(YangDataConstants.RFC8040_MODULE, "yang-data"), "name", YangDataStatement.class,
+        YangDataEffectiveStatement.class);
+
+    private final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation;
+    private final Class<? extends DeclaredStatement<?>> declaredRepresentation;
+    private final QName statementName;
+    private final ArgumentDefinition argumentDef;
+
+    YangDataStatements(final QName statementName, final String argumentName,
+            final Class<? extends DeclaredStatement<?>> declaredRepresentation,
+                    final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation) {
+        this.statementName = statementName.intern();
+        this.argumentDef = ArgumentDefinition.of(QName.create(statementName, argumentName).intern(), false);
+        this.declaredRepresentation = requireNonNull(declaredRepresentation);
+        this.effectiveRepresentation = requireNonNull(effectiveRepresentation);
+    }
+
+    @Override
+    public Optional<ArgumentDefinition> getArgumentDefinition() {
+        return Optional.of(argumentDef);
+    }
+
+    @Override
+    public QName getStatementName() {
+        return statementName;
+    }
+
+    @Override
+    public Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass() {
+        return effectiveRepresentation;
+    }
+
+    @Override
+    public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+        return declaredRepresentation;
+    }
+}
diff --git a/yang/rfc8040-parser-support/pom.xml b/yang/rfc8040-parser-support/pom.xml
new file mode 100644 (file)
index 0000000..ce32a3b
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc8040-parser-support</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC8040 parser support</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8040-model-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-rfc7950</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc8040.parser.support</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataEffectiveStatementImpl.java b/yang/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..9d1cacb
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc8040.parser;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Verify;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataEffectiveStatement;
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataSchemaNode;
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataStatement;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.UnknownEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+@Beta
+final class YangDataEffectiveStatementImpl extends UnknownEffectiveStatementBase<String, YangDataStatement>
+        implements YangDataEffectiveStatement, YangDataSchemaNode {
+
+    private final @NonNull SchemaPath path;
+    private final @NonNull QName maybeQNameArgument;
+    private final @NonNull ContainerEffectiveStatement container;
+
+    YangDataEffectiveStatementImpl(final StmtContext<String, YangDataStatement, ?> ctx) {
+        super(ctx);
+
+        QName maybeQNameArgumentInit;
+        try {
+            maybeQNameArgumentInit = StmtContextUtils.parseIdentifier(ctx, argument());
+        } catch (IllegalArgumentException e) {
+            maybeQNameArgumentInit = getNodeType();
+        }
+        this.maybeQNameArgument = maybeQNameArgumentInit;
+
+        path = ctx.coerceParentContext().getSchemaPath().get().createChild(maybeQNameArgument);
+        container = findFirstEffectiveSubstatement(ContainerEffectiveStatement.class).get();
+
+        // TODO: this is strong binding of two API contracts. Unfortunately ContainerEffectiveStatement design is
+        //       incomplete.
+        Verify.verify(container instanceof ContainerSchemaNode);
+    }
+
+    @Override
+    public @NonNull QName getQName() {
+        return maybeQNameArgument;
+    }
+
+    @Override
+    public @NonNull SchemaPath getPath() {
+        return path;
+    }
+
+    @Override
+    public ContainerEffectiveStatement getContainer() {
+        return container;
+    }
+
+    @Override
+    public @NonNull ContainerSchemaNode getContainerSchemaNode() {
+        // Verified in the constructor
+        return (ContainerSchemaNode) container;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(maybeQNameArgument, path);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (!(obj instanceof YangDataEffectiveStatementImpl)) {
+            return false;
+        }
+
+        final YangDataEffectiveStatementImpl other = (YangDataEffectiveStatementImpl) obj;
+        return Objects.equals(maybeQNameArgument, other.maybeQNameArgument) && Objects.equals(path, other.path);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).omitNullValues()
+                .add("qname", maybeQNameArgument)
+                .add("path", path)
+                .add("container", container).toString();
+    }
+}
diff --git a/yang/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java b/yang/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java
new file mode 100644 (file)
index 0000000..729fbb8
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc8040.parser;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataStatement;
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataStatements;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+@Beta
+public final class YangDataStatementSupport extends AbstractStatementSupport<String, YangDataStatement,
+        EffectiveStatement<String, YangDataStatement>> {
+    /**
+     * Declared statement representation of 'yang-data' extension defined in
+     * <a href="https://tools.ietf.org/html/rfc8040#section-8">RFC 8040</a>.
+     */
+    private static final class Declared extends AbstractDeclaredStatement<String> implements YangDataStatement {
+        Declared(final StmtContext<String, YangDataStatement, ?> ctx) {
+            super(ctx);
+        }
+
+        @Override
+        public String getArgument() {
+            return argument();
+        }
+    }
+
+    private static final YangDataStatementSupport INSTANCE = new YangDataStatementSupport(YangDataStatements.YANG_DATA);
+
+    private final SubstatementValidator validator;
+
+    private YangDataStatementSupport(final StatementDefinition definition) {
+        super(definition);
+        validator = SubstatementValidator.builder(definition)
+                .addMandatory(YangStmtMapping.CONTAINER)
+                .addOptional(YangStmtMapping.USES)
+                .build();
+    }
+
+    public static YangDataStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return validator;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public YangDataStatement createDeclared(final StmtContext<String, YangDataStatement, ?> ctx) {
+        return new Declared(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, YangDataStatement> createEffective(final StmtContext<String,
+            YangDataStatement, EffectiveStatement<String, YangDataStatement>> ctx) {
+        // in case of yang-data node we need to perform substatement validation at the point when we have
+        // effective substatement contexts already available - if the node has only a uses statement declared in it,
+        // one top-level container node may very well be added to the yang-data as an effective statement
+        validator.validate(ctx);
+        return new YangDataEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public void onFullDefinitionDeclared(final Mutable<String, YangDataStatement,
+            EffectiveStatement<String, YangDataStatement>> ctx) {
+        // as per https://tools.ietf.org/html/rfc8040#section-8,
+        // yang-data is ignored unless it appears as a top-level statement
+        if (ctx.coerceParentContext().getParentContext() != null) {
+            ctx.setIsSupportedToBuildEffective(false);
+        }
+    }
+
+    @Override
+    public boolean isIgnoringIfFeatures() {
+        return true;
+    }
+
+    @Override
+    public boolean isIgnoringConfig() {
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/yang/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/package-info.java b/yang/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/package-info.java
new file mode 100644 (file)
index 0000000..a88edda
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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
+ */
+/**
+ * Support package for semantics defined in RFC8040, most notably yang-data statement.
+ */
+package org.opendaylight.yangtools.rfc8040.parser;
\ No newline at end of file
diff --git a/yang/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YangDataExtensionTest.java b/yang/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YangDataExtensionTest.java
new file mode 100644 (file)
index 0000000..934e442
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.rfc8040.parser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.rfc8040.model.api.YangDataSchemaNode;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InvalidSubstatementException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.MissingSubstatementException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+
+public class YangDataExtensionTest {
+
+    private static final StatementStreamSource FOO_MODULE = sourceForResource(
+            "/yang-data-extension-test/foo.yang");
+    private static final StatementStreamSource FOO_INVALID_1_MODULE = sourceForResource(
+            "/yang-data-extension-test/foo-invalid-1.yang");
+    private static final StatementStreamSource FOO_INVALID_2_MODULE = sourceForResource(
+            "/yang-data-extension-test/foo-invalid-2.yang");
+    private static final StatementStreamSource FOO_INVALID_3_MODULE = sourceForResource(
+            "/yang-data-extension-test/foo-invalid-3.yang");
+    private static final StatementStreamSource BAR_MODULE = sourceForResource(
+            "/yang-data-extension-test/bar.yang");
+    private static final StatementStreamSource BAZ_MODULE = sourceForResource(
+            "/yang-data-extension-test/baz.yang");
+    private static final StatementStreamSource FOOBAR_MODULE = sourceForResource(
+            "/yang-data-extension-test/foobar.yang");
+    private static final StatementStreamSource IETF_RESTCONF_MODULE = sourceForResource(
+            "/yang-data-extension-test/ietf-restconf.yang");
+
+    private static final Revision REVISION = Revision.of("2017-06-01");
+    private static final QNameModule FOO_QNAMEMODULE = QNameModule.create(URI.create("foo"), REVISION);
+    private static final QName MY_YANG_DATA_A = QName.create(FOO_QNAMEMODULE, "my-yang-data-a");
+    private static final QName MY_YANG_DATA_B = QName.create(FOO_QNAMEMODULE, "my-yang-data-b");
+
+    private static CrossSourceStatementReactor reactor;
+
+    @BeforeClass
+    public static void createReactor() {
+        reactor = RFC7950Reactors.vanillaReactorBuilder()
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, YangDataStatementSupport.getInstance())
+                .build();
+    }
+
+    @AfterClass
+    public static void freeReactor() {
+        reactor = null;
+    }
+
+    @Test
+    public void testYangData() throws Exception {
+        final SchemaContext schemaContext = reactor.newBuild().addSources(FOO_MODULE, IETF_RESTCONF_MODULE)
+                .buildEffective();
+        assertNotNull(schemaContext);
+
+        final Set<ExtensionDefinition> extensions = schemaContext.getExtensions();
+        assertEquals(1, extensions.size());
+
+        final Module foo = schemaContext.findModule(FOO_QNAMEMODULE).get();
+        final List<UnknownSchemaNode> unknownSchemaNodes = foo.getUnknownSchemaNodes();
+        assertEquals(2, unknownSchemaNodes.size());
+
+        YangDataSchemaNode myYangDataANode = null;
+        YangDataSchemaNode myYangDataBNode = null;
+        for (final UnknownSchemaNode unknownSchemaNode : unknownSchemaNodes) {
+            assertTrue(unknownSchemaNode instanceof YangDataSchemaNode);
+            final YangDataSchemaNode yangDataSchemaNode = (YangDataSchemaNode) unknownSchemaNode;
+            if (MY_YANG_DATA_A.equals(yangDataSchemaNode.getQName())) {
+                myYangDataANode = yangDataSchemaNode;
+            } else if (MY_YANG_DATA_B.equals(yangDataSchemaNode.getQName())) {
+                myYangDataBNode = yangDataSchemaNode;
+            }
+        }
+
+        assertNotNull(myYangDataANode);
+        assertNotNull(myYangDataBNode);
+
+        assertNotNull(myYangDataANode.getContainerSchemaNode());
+        assertNotNull(myYangDataBNode.getContainerSchemaNode());
+    }
+
+    @Test
+    public void testConfigStatementBeingIgnoredInYangDataBody() throws Exception {
+        final SchemaContext schemaContext = reactor.newBuild().addSources(BAZ_MODULE, IETF_RESTCONF_MODULE)
+                .buildEffective();
+        assertNotNull(schemaContext);
+
+        final Module baz = schemaContext.findModule("baz", REVISION).get();
+        final List<UnknownSchemaNode> unknownSchemaNodes = baz.getUnknownSchemaNodes();
+        assertEquals(1, unknownSchemaNodes.size());
+
+        final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.iterator().next();
+        assertTrue(unknownSchemaNode instanceof YangDataSchemaNode);
+        final YangDataSchemaNode myYangDataNode = (YangDataSchemaNode) unknownSchemaNode;
+        assertNotNull(myYangDataNode);
+
+        final ContainerSchemaNode contInYangData = myYangDataNode.getContainerSchemaNode();
+        assertNotNull(contInYangData);
+        assertTrue(contInYangData.isConfiguration());
+        final ContainerSchemaNode innerCont = (ContainerSchemaNode) contInYangData.findDataChildByName(
+                QName.create(baz.getQNameModule(), "inner-cont")).get();
+        assertNotNull(innerCont);
+        assertTrue(innerCont.isConfiguration());
+        final ContainerSchemaNode grpCont = (ContainerSchemaNode) contInYangData.findDataChildByName(
+                QName.create(baz.getQNameModule(), "grp-cont")).get();
+        assertNotNull(grpCont);
+        assertTrue(grpCont.isConfiguration());
+    }
+
+    @Test
+    public void testIfFeatureStatementBeingIgnoredInYangDataBody() throws Exception {
+        final SchemaContext schemaContext = reactor.newBuild().setSupportedFeatures(ImmutableSet.of())
+                .addSources(FOOBAR_MODULE, IETF_RESTCONF_MODULE).buildEffective();
+        assertNotNull(schemaContext);
+
+        final Module foobar = schemaContext.findModule("foobar", REVISION).get();
+        final List<UnknownSchemaNode> unknownSchemaNodes = foobar.getUnknownSchemaNodes();
+        assertEquals(1, unknownSchemaNodes.size());
+
+        final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.iterator().next();
+        assertTrue(unknownSchemaNode instanceof YangDataSchemaNode);
+        final YangDataSchemaNode myYangDataNode = (YangDataSchemaNode) unknownSchemaNode;
+        assertNotNull(myYangDataNode);
+
+        final ContainerSchemaNode contInYangData = myYangDataNode.getContainerSchemaNode();
+        assertNotNull(contInYangData);
+        final ContainerSchemaNode innerCont = (ContainerSchemaNode) contInYangData.findDataChildByName(
+                QName.create(foobar.getQNameModule(), "inner-cont")).get();
+        assertNotNull(innerCont);
+        final ContainerSchemaNode grpCont = (ContainerSchemaNode) contInYangData.findDataChildByName(
+                QName.create(foobar.getQNameModule(), "grp-cont")).get();
+        assertNotNull(grpCont);
+    }
+
+    @Test
+    public void testYangDataBeingIgnored() throws Exception {
+        // yang-data statement is ignored if it does not appear as a top-level statement
+        // i.e., it will not appear in the final SchemaContext
+        final SchemaContext schemaContext = reactor.newBuild().addSources(BAR_MODULE, IETF_RESTCONF_MODULE)
+                .buildEffective();
+        assertNotNull(schemaContext);
+
+        final Module bar = schemaContext.findModule("bar", REVISION).get();
+        final ContainerSchemaNode cont = (ContainerSchemaNode) bar.findDataChildByName(
+                QName.create(bar.getQNameModule(), "cont")).get();
+        assertNotNull(cont);
+
+        final Set<ExtensionDefinition> extensions = schemaContext.getExtensions();
+        assertEquals(1, extensions.size());
+
+        final List<UnknownSchemaNode> unknownSchemaNodes = cont.getUnknownSchemaNodes();
+        assertEquals(0, unknownSchemaNodes.size());
+    }
+
+    @Test
+    public void testYangDataWithMissingTopLevelContainer() {
+        try {
+            reactor.newBuild().addSources(FOO_INVALID_1_MODULE, IETF_RESTCONF_MODULE).buildEffective();
+            fail("Exception should have been thrown because of missing top-level container in yang-data statement.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof MissingSubstatementException);
+            assertTrue(cause.getMessage().startsWith("YANG_DATA is missing CONTAINER. Minimal count is 1."));
+        }
+    }
+
+    @Test
+    public void testYangDataWithTwoTopLevelContainers() {
+        try {
+            reactor.newBuild().addSources(FOO_INVALID_2_MODULE, IETF_RESTCONF_MODULE).buildEffective();
+            fail("Exception should have been thrown because of two top-level containers in yang-data statement.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidSubstatementException);
+            assertTrue(cause.getMessage().startsWith("Maximal count of CONTAINER for YANG_DATA is 1, detected 2."));
+        }
+    }
+
+    @Test
+    public void testYangDataWithInvalidToplevelNode() {
+        try {
+            reactor.newBuild().addSources(FOO_INVALID_3_MODULE, IETF_RESTCONF_MODULE).buildEffective();
+            fail("Exception should have been thrown because of invalid top-level node in yang-data statement.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidSubstatementException);
+            assertTrue(cause.getMessage().startsWith("LEAF is not valid for YANG_DATA."));
+        }
+    }
+
+    private static StatementStreamSource sourceForResource(final String resourceName) {
+        try {
+            return YangStatementStreamSource.create(YangTextSchemaSource.forResource(resourceName));
+        } catch (IOException | YangSyntaxErrorException e) {
+            throw new IllegalArgumentException("Failed to create source", e);
+        }
+    }
+}
diff --git a/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/bar.yang b/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/bar.yang
new file mode 100644 (file)
index 0000000..2fe4298
--- /dev/null
@@ -0,0 +1,20 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    import ietf-restconf {
+        prefix rc;
+        revision-date 2017-01-26;
+    }
+
+    revision 2017-06-01;
+
+    container cont {
+        rc:yang-data "my-yang-data" {
+            // only one top-level container is allowed, but in this case it does not matter
+            // as the whole yang-data node is ignored (because it does not appear as a top-level statement)
+            container top-level-cont {}
+            container another-top-level-cont {}
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/baz.yang b/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/baz.yang
new file mode 100644 (file)
index 0000000..f483584
--- /dev/null
@@ -0,0 +1,29 @@
+module baz {
+    namespace baz;
+    prefix baz;
+
+    import ietf-restconf {
+        prefix rc;
+        revision-date 2017-01-26;
+    }
+
+    revision 2017-06-01;
+
+    rc:yang-data "my-yang-data" {
+        container cont {
+            config false;
+
+            container inner-cont {
+                config false;
+            }
+
+            uses grp;
+        }
+    }
+
+    grouping grp {
+        container grp-cont {
+            config false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foo-invalid-1.yang b/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foo-invalid-1.yang
new file mode 100644 (file)
index 0000000..b56ac4d
--- /dev/null
@@ -0,0 +1,15 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import ietf-restconf {
+        prefix rc;
+        revision-date 2017-01-26;
+    }
+
+    revision 2017-06-01;
+
+    rc:yang-data "my-yang-data" {
+        // missing top level container = should throw an exception
+    }
+}
\ No newline at end of file
diff --git a/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foo-invalid-2.yang b/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foo-invalid-2.yang
new file mode 100644 (file)
index 0000000..33eb4e1
--- /dev/null
@@ -0,0 +1,25 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import ietf-restconf {
+        prefix rc;
+        revision-date 2017-01-26;
+    }
+
+    revision 2017-06-01;
+
+    rc:yang-data "my-yang-data" {
+        // two top level containers, but only one is allowed = should throw an exception
+        container cont-1 {}
+        uses grp-1;
+    }
+
+    grouping grp-1 {
+        uses grp-2;
+    }
+
+    grouping grp-2 {
+        container cont-2 {}
+    }
+}
\ No newline at end of file
diff --git a/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foo-invalid-3.yang b/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foo-invalid-3.yang
new file mode 100644 (file)
index 0000000..98ae30f
--- /dev/null
@@ -0,0 +1,18 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import ietf-restconf {
+        prefix rc;
+        revision-date 2017-01-26;
+    }
+
+    revision 2017-06-01;
+
+    rc:yang-data "my-yang-data" {
+        // invalid top level node, it must be a container = should throw an exception
+        leaf lf {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foo.yang b/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foo.yang
new file mode 100644 (file)
index 0000000..250cb3a
--- /dev/null
@@ -0,0 +1,23 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import ietf-restconf {
+        prefix rc;
+        revision-date 2017-01-26;
+    }
+
+    revision 2017-06-01;
+
+    rc:yang-data "my-yang-data-a" {
+        container cont {}
+    }
+
+    rc:yang-data "my-yang-data-b" {
+        uses grp;
+    }
+
+    grouping grp {
+        container grp-cont {}
+    }
+}
\ No newline at end of file
diff --git a/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foobar.yang b/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/foobar.yang
new file mode 100644 (file)
index 0000000..8d34f07
--- /dev/null
@@ -0,0 +1,31 @@
+module foobar {
+    namespace foobar;
+    prefix foobar;
+
+    import ietf-restconf {
+        prefix rc;
+        revision-date 2017-01-26;
+    }
+
+    revision 2017-06-01;
+
+    feature feat;
+
+    rc:yang-data "my-yang-data" {
+        container cont {
+            if-feature feat;
+
+            container inner-cont {
+                if-feature feat;
+            }
+
+            uses grp {
+                if-feature feat;
+            }
+        }
+    }
+
+    grouping grp {
+        container grp-cont {}
+    }
+}
diff --git a/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/ietf-restconf.yang b/yang/rfc8040-parser-support/src/test/resources/yang-data-extension-test/ietf-restconf.yang
new file mode 100644 (file)
index 0000000..3008664
--- /dev/null
@@ -0,0 +1,253 @@
+module ietf-restconf {
+  yang-version 1.1;
+  namespace "urn:ietf:params:xml:ns:yang:ietf-restconf";
+  prefix "rc";
+
+  organization
+    "IETF NETCONF (Network Configuration) Working Group";
+
+  contact
+    "WG Web:   <https://datatracker.ietf.org/wg/netconf/>
+     WG List:  <mailto:netconf@ietf.org>
+     Author:   Andy Bierman
+               <mailto:andy@yumaworks.com>
+     Author:   Martin Bjorklund
+               <mailto:mbj@tail-f.com>
+     Author:   Kent Watsen
+               <mailto:kwatsen@juniper.net>";
+
+  description
+    "This module contains conceptual YANG specifications
+     for basic RESTCONF media type definitions used in
+     RESTCONF protocol messages.
+     Note that the YANG definitions within this module do not
+     represent configuration data of any kind.
+     The 'restconf-media-type' YANG extension statement
+     provides a normative syntax for XML and JSON
+     message-encoding purposes.
+     Copyright (c) 2017 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD License
+     set forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+     This version of this YANG module is part of RFC 8040; see
+     the RFC itself for full legal notices.";
+
+  revision 2017-01-26 {
+    description
+      "Initial revision.";
+    reference
+      "RFC 8040: RESTCONF Protocol.";
+  }
+
+  extension yang-data {
+    argument name {
+      yin-element true;
+    }
+    description
+      "This extension is used to specify a YANG data template that
+       represents conceptual data defined in YANG.  It is
+       intended to describe hierarchical data independent of
+       protocol context or specific message-encoding format.
+       Data definition statements within a yang-data extension
+       specify the generic syntax for the specific YANG data
+       template, whose name is the argument of the 'yang-data'
+       extension statement.
+       Note that this extension does not define a media type.
+       A specification using this extension MUST specify the
+       message-encoding rules, including the content media type.
+       The mandatory 'name' parameter value identifies the YANG
+       data template that is being defined.  It contains the
+       template name.
+       This extension is ignored unless it appears as a top-level
+       statement.  It MUST contain data definition statements
+       that result in exactly one container data node definition.
+       An instance of a YANG data template can thus be translated
+       into an XML instance document, whose top-level element
+       corresponds to the top-level container.
+       The module name and namespace values for the YANG module using
+       the extension statement are assigned to instance document data
+       conforming to the data definition statements within
+       this extension.
+       The substatements of this extension MUST follow the
+       'data-def-stmt' rule in the YANG ABNF.
+       The XPath document root is the extension statement itself,
+       such that the child nodes of the document root are
+       represented by the data-def-stmt substatements within
+       this extension.  This conceptual document is the context
+       for the following YANG statements:
+         - must-stmt
+         - when-stmt
+         - path-stmt
+         - min-elements-stmt
+         - max-elements-stmt
+         - mandatory-stmt
+         - unique-stmt
+         - ordered-by
+         - instance-identifier data type
+       The following data-def-stmt substatements are constrained
+       when used within a 'yang-data' extension statement.
+         - The list-stmt is not required to have a key-stmt defined.
+         - The if-feature-stmt is ignored if present.
+         - The config-stmt is ignored if present.
+         - The available identity values for any 'identityref'
+           leaf or leaf-list nodes are limited to the module
+           containing this extension statement and the modules
+           imported into that module.
+      ";
+  }
+
+  rc:yang-data yang-errors {
+    uses errors;
+  }
+
+  rc:yang-data yang-api {
+    uses restconf;
+  }
+
+  grouping errors {
+    description
+      "A grouping that contains a YANG container
+       representing the syntax and semantics of a
+       YANG Patch error report within a response message.";
+
+    container errors {
+      description
+        "Represents an error report returned by the server if
+         a request results in an error.";
+
+      list error {
+        description
+          "An entry containing information about one
+           specific error that occurred while processing
+           a RESTCONF request.";
+        reference
+          "RFC 6241, Section 4.3.";
+
+        leaf error-type {
+          type enumeration {
+            enum transport {
+              description
+                "The transport layer.";
+            }
+            enum rpc {
+              description
+                "The rpc or notification layer.";
+            }
+            enum protocol {
+              description
+                "The protocol operation layer.";
+            }
+            enum application {
+              description
+                "The server application layer.";
+            }
+          }
+          mandatory true;
+          description
+            "The protocol layer where the error occurred.";
+        }
+
+        leaf error-tag {
+          type string;
+          mandatory true;
+          description
+            "The enumerated error-tag.";
+        }
+
+        leaf error-app-tag {
+          type string;
+          description
+            "The application-specific error-tag.";
+        }
+
+        leaf error-path {
+          type instance-identifier;
+          description
+            "The YANG instance identifier associated
+             with the error node.";
+        }
+
+        leaf error-message {
+          type string;
+          description
+            "A message describing the error.";
+        }
+
+        anydata error-info {
+           description
+             "This anydata value MUST represent a container with
+              zero or more data nodes representing additional
+              error information.";
+        }
+      }
+    }
+  }
+
+  grouping restconf {
+    description
+      "Conceptual grouping representing the RESTCONF
+       root resource.";
+
+    container restconf {
+      description
+        "Conceptual container representing the RESTCONF
+         root resource.";
+
+      container data {
+        description
+          "Container representing the datastore resource.
+           Represents the conceptual root of all state data
+           and configuration data supported by the server.
+           The child nodes of this container can be any data
+           resources that are defined as top-level data nodes
+           from the YANG modules advertised by the server in
+           the 'ietf-yang-library' module.";
+      }
+
+      container operations {
+        description
+          "Container for all operation resources.
+           Each resource is represented as an empty leaf with the
+           name of the RPC operation from the YANG 'rpc' statement.
+           For example, the 'system-restart' RPC operation defined
+           in the 'ietf-system' module would be represented as
+           an empty leaf in the 'ietf-system' namespace.  This is
+           a conceptual leaf and will not actually be found in
+           the module:
+              module ietf-system {
+                leaf system-reset {
+                  type empty;
+                }
+              }
+           To invoke the 'system-restart' RPC operation:
+              POST /restconf/operations/ietf-system:system-restart
+           To discover the RPC operations supported by the server:
+              GET /restconf/operations
+           In XML, the YANG module namespace identifies the module:
+             <system-restart
+                xmlns='urn:ietf:params:xml:ns:yang:ietf-system'/>
+           In JSON, the YANG module name identifies the module:
+             { 'ietf-system:system-restart' : [null] }
+          ";
+      }
+
+      leaf yang-library-version {
+        type string {
+          pattern '\d{4}-\d{2}-\d{2}';
+        }
+        config false;
+        mandatory true;
+        description
+          "Identifies the revision date of the 'ietf-yang-library'
+           module that is implemented by this RESTCONF server.
+           Indicates the year, month, and day in YYYY-MM-DD
+           numeric format.";
+      }
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/yang/rfc8528-data-api/pom.xml b/yang/rfc8528-data-api/pom.xml
new file mode 100644 (file)
index 0000000..94e58e9
--- /dev/null
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc8528-data-api</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC8528 data model extensions</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-model-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc8528.data.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MPIv1.java b/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MPIv1.java
new file mode 100644 (file)
index 0000000..d469b20
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.data.api;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+final class MPIv1 implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private QName qname;
+
+    @SuppressWarnings("checkstyle:redundantModifier")
+    public MPIv1() {
+        // For Externalizable
+    }
+
+    MPIv1(final @NonNull QName qname) {
+        this.qname = requireNonNull(qname);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        qname.writeTo(out);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        qname = QName.readFrom(in);
+    }
+
+    private Object readResolve() {
+        return MountPointIdentifier.create(qname);
+    }
+}
diff --git a/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointChild.java b/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointChild.java
new file mode 100644 (file)
index 0000000..04d37fb
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.data.api;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizableAnydata;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * An unresolved child within a mount point. This is similar in functionality to {@link NormalizableAnydata}, but
+ * rather than normalizing, the data is fed into a combination of a SchemaContext and NormalizedNodeStreamWriter.
+ */
+@Beta
+@NonNullByDefault
+public interface MountPointChild {
+    /**
+     * Stream this child into a writer, with the help of a SchemaContext.
+     *
+     * @param writer Writer to emit the child into
+     * @param mountCtx MountPointContext for normalization purposes
+     * @throws IOException if an underlying error occurs
+     * @throws NullPointerException if any of the arguments is null
+     */
+    void writeTo(NormalizedNodeStreamWriter writer, MountPointContext mountCtx) throws IOException;
+
+    /**
+     * Normalized this child to a particular SchemaContext.
+     *
+     * @param schemaContext SchemaContext for normalization purposes
+     * @return A NormalizedNode representation of this child
+     * @throws IOException if an underlying error occurs
+     * @throws NullPointerException if any of the arguments is null
+     */
+    NormalizedNode<?, ?> normalizeTo(SchemaContext schemaContext) throws IOException;
+}
diff --git a/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointContext.java b/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointContext.java
new file mode 100644 (file)
index 0000000..931d2b2
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.data.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+/**
+ * A context of either an explicit (RFC8528 Schema Mount instance) or implicit (system root). It encapsulates a data
+ * {@link org.opendaylight.yangtools.yang.model.api.SchemaContext} and information resident in {@code schema-mounts}
+ * within this hierarchy.
+ *
+ * <p>
+ * Note this interface should be part of yang-data-api, as it really defines how a NormalizedNode-containerized data
+ * operates w.r.t. mount points. Further evolution is expected.
+ */
+/*
+ * FIXME: 5.0.0: consider yang-data-api integration
+ *
+ * The above note does not give the subject enough attention. RFC8528 redefines the YANG data metamodel is significant
+ * ways in that it ties it with RFC8525/RFC7895. The content of 'schema-mounts' is critical to interpreting
+ * inter-mountpoint data, notably in XML/JSON parsers, which need to be able to correctly infer their mode of
+ * encapsulation (nested mount points).
+ *
+ * Integration with DataTree is questionable here, as MountPointNode has enough information for InMemoryDataTree to
+ * operate efficiently -- all it needs to is switch the resolution root.
+ *
+ * On the other hand, requiring that a YANG data world is identified by MountPointIdentifer (which is QName) adds
+ * interpretation flexibility to SchemaContext which is currently hard-coded. For example operations which require
+ * encapsulation of an entire tree have to assume that a SchemaContext has a NodeIdentifier which it uses for the name
+ * of its top-level node. MountPointIdentifier solves this, as it can easily be converted to a unique world identifier.
+ *
+ * That allows, for example NETCONF to specify the root data identifier, properly matching the data it receives from
+ * the device to the native datastore format. Rehosting root identifier means rehosting top-level nodes of
+ * a ContainerNode, which is a simple and relatively-inexpensive operation (i.e. O(N) where N is the number of
+ * top-level nodes).
+ *
+ * To support this case, MountPointContext really wants to be Identifiable<MountPointIdentifier>, where it would also
+ * provide a 'default NodeIdentifier getRootIdentifier()' method. In PathArgument contexts, MountPointIdentifier is
+ * directly usable anyway.
+ */
+@Beta
+public interface MountPointContext extends SchemaContextProvider {
+    /**
+     * Attempt to acquire a {@link MountPointContextFactory} to resolve schemas for the purposes of interpreting
+     * this mount point. An empty result indicates the mount point is not attached.
+     *
+     * @param label Mount point label, as defined via the use of {@code mount-point} statement
+     * @return An optional handler for mount point data
+     * @throws NullPointerException if label is null
+     */
+    Optional<MountPointContextFactory> findMountPoint(@NonNull MountPointIdentifier label);
+}
diff --git a/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointContextFactory.java b/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointContextFactory.java
new file mode 100644 (file)
index 0000000..c1791a8
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.data.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.rfc8528.data.api.YangLibraryConstants.ContainerName;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
+
+/**
+ * An entity able to resolve the SchemaContext for embedded mount points based on generic data provided by the current
+ * interpretation context.
+ */
+@Beta
+@NonNullByDefault
+public interface MountPointContextFactory {
+    /**
+     * Create a mount point context based on available information. Implementations are expected to attempt to interpret
+     * provided data to their best of their ability.
+     *
+     * @param libraryContainers available YANG library containers in opaque format
+     * @param schemaMounts the content of 'schema-mounts' container, if available
+     * @return A {@link MountPointContext}
+     * @throws YangParserException if the schema cannot be assembled
+     */
+    MountPointContext createContext(Map<ContainerName, MountPointChild> libraryContainers,
+            @Nullable MountPointChild schemaMounts) throws YangParserException;
+}
diff --git a/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointIdentifier.java b/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointIdentifier.java
new file mode 100644 (file)
index 0000000..750f16c
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.data.api;
+
+import com.google.common.annotations.Beta;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.WritableObject;
+import org.opendaylight.yangtools.util.AbstractIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+@Beta
+@NonNullByDefault
+public final class MountPointIdentifier extends AbstractIdentifier<QName> implements PathArgument, WritableObject {
+    private static final long serialVersionUID = 1L;
+
+    private static final LoadingCache<QName, MountPointIdentifier> CACHE = CacheBuilder.newBuilder().weakValues()
+            .build(new CacheLoader<QName, MountPointIdentifier>() {
+                @Override
+                public MountPointIdentifier load(final QName key) {
+                    return of(key);
+                }
+            });
+
+    private MountPointIdentifier(final QName qname) {
+        super(qname);
+    }
+
+    public static MountPointIdentifier of(final QName qname) {
+        return new MountPointIdentifier(qname);
+    }
+
+    public static MountPointIdentifier create(final QName qname) {
+        final MountPointIdentifier existing = CACHE.getIfPresent(qname);
+        return existing != null ? existing : CACHE.getUnchecked(qname.intern());
+    }
+
+    public static MountPointIdentifier readFrom(final DataInput in) throws IOException {
+        return create(QName.readFrom(in));
+    }
+
+    public QName getLabel() {
+        return getValue();
+    }
+
+    @Override
+    public void writeTo(final DataOutput out) throws IOException {
+        getValue().writeTo(out);
+    }
+
+    private Object writeReplace() {
+        return new MPIv1(getValue());
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public int compareTo(final @Nullable PathArgument o) {
+        if (o instanceof AugmentationIdentifier) {
+            // Augmentations are strictly more, in keeping of their compare
+            return 1;
+        }
+        if (!(o instanceof MountPointIdentifier)) {
+            // TODO: Yeah, okay, this declaration is not quite right, but we are following AugmentationIdenfier's lead
+            return -1;
+        }
+        return getValue().compareTo(((MountPointIdentifier) o).getLabel());
+    }
+
+    @Override
+    public QName getNodeType() {
+        return getLabel();
+    }
+
+    @Override
+    public String toRelativeString(final @Nullable PathArgument previous) {
+        return toString();
+    }
+}
diff --git a/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointNode.java b/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/MountPointNode.java
new file mode 100644 (file)
index 0000000..d34f767
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.data.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MixinNode;
+
+/**
+ * Common NormalizedNode representation of a YANG mount point.
+ *
+ * <p>
+ * These nodes are not meant to be stored in a DataTree and most NormalizedNode utilities will be confused when
+ * they see them. The purpose of this interface is making data interchange between mount point-aware components more
+ * seamless.
+ */
+/*
+ * FIXME: 5.0.0: The above is not quite right. DataTree instances should be able to handle mount points and correctly
+ *               handle them, provided they get enough support from MountPointContext.
+ */
+@Beta
+public interface MountPointNode extends MixinNode, DataContainerNode<MountPointIdentifier>,
+        DataContainerChild<MountPointIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> {
+    @Override
+    default QName getNodeType() {
+        return getIdentifier().getLabel();
+    }
+
+    /**
+     * Return the underlying mount point context.
+     *
+     * @return Underlying mount point context
+     */
+    @NonNull MountPointContext getMountPointContext();
+
+    /*
+     * FIXME: consider whether this interface should contain some information based on 'parent-reference':
+     *        - List<YangXPathExpression.QualifiedBound> getParentReference()
+     *        - the node-set required to maintain referential integrity in the subtree of this node
+     */
+}
diff --git a/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/StreamWriterMountPointExtension.java b/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/StreamWriterMountPointExtension.java
new file mode 100644 (file)
index 0000000..946035c
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.data.api;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriterExtension;
+
+/**
+ * A {@link NormalizedNodeStreamWriterExtension} exposed by stream writers which can handle mount point data, notably
+ * providing the facilities to resolve a mount point schema and normalize mount point contents into a normalized
+ * structure.
+ */
+@Beta
+@NonNullByDefault
+public interface StreamWriterMountPointExtension extends NormalizedNodeStreamWriterExtension {
+    /**
+     * Start a new mount point with a specific mount point context. The returned writer will be used to emit the content
+     * of the mount point, without touching the writer to which this extension is attached to. Once that is done, the
+     * returned writer will be {@link NormalizedNodeStreamWriter#close()}d, at which point the parent writer will be
+     * used again to emit the rest of the tree.
+     *
+     * @param mountId Mount point identifier
+     * @param mountCtx Mount point context
+     * @return A new NormalizedNodeStreamWriter
+     * @throws IOException if an error occurs
+     */
+    NormalizedNodeStreamWriter startMountPoint(MountPointIdentifier mountId, MountPointContext mountCtx)
+            throws IOException;
+}
diff --git a/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/YangLibraryConstants.java b/yang/rfc8528-data-api/src/main/java/org/opendaylight/yangtools/rfc8528/data/api/YangLibraryConstants.java
new file mode 100644 (file)
index 0000000..d5e82a1
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.data.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Constants related to {@code ietf-yang-library.yang}. As schema-mount works in concert with yang-library, we need
+ * these constants to interpret correctly categorize incoming data and present them to schema resolution process.
+ *
+ * <p>
+ * While RFC7895 and RFC8525 are not strictly required by YANG, RFC7950 contains a weak reference to it when dealing
+ * with capability negotiation on protocol layers. Moreover RFC8528 makes it explicit that an instance of yang-library
+ * is mounted underneath both {@code inline} and {@code shared-schema} types of mount.
+ *
+ * <p>
+ * While we could mandate use of either RFC7895 or RFC8525 across the board, this is not feasible, as mount points may
+ * be nested and point to external systems -- hence it is completely possible to encounter both old and new information
+ * in a single mount point tree.
+ */
+@Beta
+@NonNullByDefault
+public final class YangLibraryConstants {
+    /**
+     * The namespace assigned to {@code ietf-yang-library}. This constant is required for XML-like parsers, using
+     * XML namespaces to reference modules.
+     */
+    public static final URI MODULE_NAMESPACE = URI.create("urn:ietf:params:xml:ns:yang:ietf-yang-library");
+    /**
+     * The module name assigned to {@code ietf-yang-library}. This constant is required for JSON-like parsers, using
+     * module names to reference modules.
+     */
+    public static final String MODULE_NAME = "ietf-yang-library";
+
+    /**
+     * Top-level containers which hold YANG Library information, ordered by descending preference, with more modern
+     * and/or preferred entries first.
+     */
+    public enum ContainerName {
+        // Note: order this enum from most-preferred to least-preferred name
+        /**
+         * Container in RFC8525 (NMDA) YANG Library.
+         */
+        RFC8525("yang-library"),
+        /**
+         * Container in RFC7895 (pre-NMDA) YANG Library.
+         */
+        RFC7895("modules-state");
+
+        private static final ImmutableMap<String, ContainerName> NAME_TO_ENUM = Maps.uniqueIndex(
+            Arrays.asList(values()), ContainerName::getLocalName);
+
+        private final String localName;
+
+        ContainerName(final String localName) {
+            this.localName = requireNonNull(localName);
+        }
+
+        public String getLocalName() {
+            return localName;
+        }
+
+        public static Optional<ContainerName> forLocalName(final String localName) {
+            return Optional.ofNullable(NAME_TO_ENUM.get(requireNonNull(localName)));
+        }
+    }
+
+    private YangLibraryConstants() {
+        // Hidden
+    }
+}
diff --git a/yang/rfc8528-data-util/pom.xml b/yang/rfc8528-data-util/pom.xml
new file mode 100644 (file)
index 0000000..538d398
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc8528-data-util</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC8528 data model utilities</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-data-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc8528.data.util</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/AbstractDynamicMountPointContextFactory.java b/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/AbstractDynamicMountPointContextFactory.java
new file mode 100644 (file)
index 0000000..df3526a
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rcf8528.data.util;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointChild;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.rfc8528.data.api.YangLibraryConstants.ContainerName;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract base class for dynamic resolvers.
+ */
+@Beta
+// FIXME: 5.0.0: consider integrating into AbstractMountPointContextFactory
+public abstract class AbstractDynamicMountPointContextFactory extends AbstractIdentifiable<MountPointIdentifier>
+        implements MountPointContextFactory {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractDynamicMountPointContextFactory.class);
+
+    protected AbstractDynamicMountPointContextFactory(final @NonNull MountPointIdentifier mountId) {
+        super(mountId);
+    }
+
+    @Override
+    public final MountPointContext createContext(final Map<ContainerName, MountPointChild> libraryContainers,
+            final MountPointChild schemaMounts) throws YangParserException {
+
+        for (Entry<ContainerName, MountPointChild> entry : libraryContainers.entrySet()) {
+            // Context for the specific code word
+            final Optional<SchemaContext> optLibContext = findSchemaForLibrary(entry.getKey());
+            if (optLibContext.isEmpty()) {
+                LOG.debug("YANG Library context for mount point {} container {} not found", getIdentifier(),
+                    entry.getKey());
+                continue;
+            }
+
+            final NormalizedNode<?, ?> libData;
+            try {
+                libData = entry.getValue().normalizeTo(optLibContext.get());
+            } catch (IOException e) {
+                throw new YangParserException("Failed to interpret yang-library data", e);
+            }
+            if (!(libData instanceof ContainerNode)) {
+                throw new YangParserException("Invalid yang-library non-container " + libData);
+            }
+
+            final SchemaContext schemaContext = bindLibrary(entry.getKey(), (ContainerNode) libData);
+            if (schemaMounts == null) {
+                return new EmptyMountPointContext(schemaContext);
+            }
+
+            final NormalizedNode<?, ?> mountData;
+            try {
+                mountData = schemaMounts.normalizeTo(schemaContext);
+            } catch (IOException e) {
+                throw new YangParserException("Failed to interpret schema-mount data", e);
+            }
+            if (!(mountData instanceof ContainerNode)) {
+                throw new YangParserException("Invalid schema-mount non-container " + mountData);
+            }
+
+            return createMountPointContext(schemaContext, (ContainerNode) mountData);
+        }
+
+        throw new YangParserException("Failed to interpret " + libraryContainers);
+    }
+
+    protected abstract @NonNull MountPointContext createMountPointContext(@NonNull SchemaContext schemaContext,
+            @NonNull ContainerNode mountData);
+
+    /**
+     * Assemble the MountPointContext for specified normalized YANG Library top-level container.
+     *
+     * @param containerName Top-level YANG Library container
+     * @param libData Top-level YANG Library container data
+     * @return An assembled MountPointContext
+     * @throws NullPointerException if container is null
+     * @throws YangParserException if the schema context cannot be assembled
+     */
+    protected abstract @NonNull SchemaContext bindLibrary(@NonNull ContainerName containerName,
+            @NonNull ContainerNode libData) throws YangParserException;
+
+    /**
+     * Return the schema in which YANG Library container content should be interpreted.
+     *
+     * <p>
+     * Note this schema is not guaranteed to contain any augmentations, hence parsing could fail.
+     *
+     * @param containerName Top-level YANG Library container name
+     * @return The LibraryContext to use when interpreting the specified YANG Library container, or empty
+     * @throws NullPointerException if container is null
+     */
+    protected abstract Optional<SchemaContext> findSchemaForLibrary(@NonNull ContainerName containerName);
+}
diff --git a/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/AbstractMountPointContextFactory.java b/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/AbstractMountPointContextFactory.java
new file mode 100644 (file)
index 0000000..67c627c
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rcf8528.data.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableSet;
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.rfc8528.model.api.SchemaMountConstants;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract base class for MountPointContextFactory implementations, which can process RFC8525 mount point definitions.
+ */
+@Beta
+@NonNullByDefault
+public abstract class AbstractMountPointContextFactory extends AbstractDynamicMountPointContextFactory {
+    /**
+     * Definition of a MountPoint, as known to RFC8528.
+     */
+    protected static final class MountPointDefinition extends AbstractIdentifiable<MountPointIdentifier>
+            implements Immutable {
+        private final ImmutableSet<String> parentReferences;
+        private final boolean config;
+
+        MountPointDefinition(final MountPointIdentifier identifier, final boolean config,
+                final ImmutableSet<String> parentReferences) {
+            super(identifier);
+            this.config = config;
+            this.parentReferences = requireNonNull(parentReferences);
+        }
+
+        public boolean getConfig() {
+            return config;
+        }
+
+        // FIXME: 5.0.0: make this return a set of XPath expressions
+        public ImmutableSet<String> getParentReferences() {
+            return parentReferences;
+        }
+
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+            return super.addToStringAttributes(toStringHelper)
+                    .add("config", config)
+                    .add("parentReferences", parentReferences);
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractMountPointContextFactory.class);
+    private static final NodeIdentifier SCHEMA_MOUNTS = NodeIdentifier.create(
+        QName.create(SchemaMountConstants.RFC8528_MODULE, "schema-mounts").intern());
+    private static final NodeIdentifier MOUNT_POINT = NodeIdentifier.create(
+        QName.create(SchemaMountConstants.RFC8528_MODULE, "mount-point").intern());
+    private static final NodeIdentifier CONFIG = NodeIdentifier.create(
+        QName.create(SchemaMountConstants.RFC8528_MODULE, "config").intern());
+    private static final NodeIdentifier MODULE = NodeIdentifier.create(
+        QName.create(SchemaMountConstants.RFC8528_MODULE, "module").intern());
+    private static final NodeIdentifier LABEL = NodeIdentifier.create(
+        QName.create(SchemaMountConstants.RFC8528_MODULE, "label").intern());
+    private static final NodeIdentifier SCHEMA_REF = NodeIdentifier.create(
+        QName.create(SchemaMountConstants.RFC8528_MODULE, "schema-ref").intern());
+    private static final NodeIdentifier INLINE = NodeIdentifier.create(
+        QName.create(SchemaMountConstants.RFC8528_MODULE, "inline").intern());
+    private static final NodeIdentifier SHARED_SCHEMA = NodeIdentifier.create(
+        QName.create(SchemaMountConstants.RFC8528_MODULE, "shared-schema").intern());
+    private static final NodeIdentifier PARENT_REFERENCE = NodeIdentifier.create(
+        QName.create(SchemaMountConstants.RFC8528_MODULE, "parent-reference").intern());
+
+    protected AbstractMountPointContextFactory(final MountPointIdentifier mountId) {
+        super(mountId);
+    }
+
+    @Override
+    protected final MountPointContext createMountPointContext(final SchemaContext schemaContext,
+            final ContainerNode mountData) {
+        checkArgument(SCHEMA_MOUNTS.equals(mountData.getIdentifier()), "Unexpected top-level container %s", mountData);
+
+        final Optional<DataContainerChild<?, ?>> optMountPoint = mountData.getChild(MOUNT_POINT);
+        if (optMountPoint.isEmpty()) {
+            LOG.debug("mount-point list not present in {}", mountData);
+            return new EmptyMountPointContext(schemaContext);
+        }
+
+        final DataContainerChild<?, ?> mountPoint = optMountPoint.get();
+        checkArgument(mountPoint instanceof MapNode, "mount-point list %s is not a MapNode", mountPoint);
+
+        return new ImmutableMountPointContext(schemaContext, ((MapNode) mountPoint).getValue().stream().map(entry -> {
+            final String moduleName = entry.getChild(MODULE).map(mod -> {
+                checkArgument(mod instanceof LeafNode, "Unexpected module leaf %s", mod);
+                final Object value = mod.getValue();
+                checkArgument(value instanceof String, "Unexpected module leaf value %s", value);
+                return (String) value;
+            }).orElseThrow(() -> new IllegalArgumentException("Mount module missing in " + entry));
+            final Iterator<Module> it = schemaContext.findModules(moduleName).iterator();
+            checkArgument(it.hasNext(), "Failed to find a module named %s", moduleName);
+            final QNameModule module = it.next().getQNameModule();
+
+            return new MountPointDefinition(
+                MountPointIdentifier.of(QName.create(module, entry.getChild(LABEL).map(lbl -> {
+                    checkArgument(lbl instanceof LeafNode, "Unexpected label leaf %s", lbl);
+                    final Object value = lbl.getValue();
+                    checkArgument(value instanceof String, "Unexpected label leaf value %s", value);
+                    return (String) value;
+                }).orElseThrow(() -> new IllegalArgumentException("Mount module missing in " + entry)))),
+                entry.getChild(CONFIG).map(cfg -> {
+                    checkArgument(cfg instanceof LeafNode, "Unexpected config leaf %s", cfg);
+                    final Object value = cfg.getValue();
+                    checkArgument(value instanceof Boolean, "Unexpected config leaf value %s", cfg);
+                    return (Boolean) value;
+                }).orElse(Boolean.TRUE),
+                getSchema(entry.getChild(SCHEMA_REF)
+                    .orElseThrow(() -> new IllegalArgumentException("Missing schema-ref choice in " + entry))));
+        }).collect(Collectors.toList()), this::createContextFactory);
+    }
+
+    private static ImmutableSet<String> getSchema(final DataContainerChild<?, ?> child) {
+        checkArgument(child instanceof ChoiceNode, "Unexpected schema-ref choice %s", child);
+        final ChoiceNode schemaRef = (ChoiceNode) child;
+
+        return schemaRef.getChild(SHARED_SCHEMA).map(sharedSchema -> {
+            checkArgument(sharedSchema instanceof ContainerNode, "Unexpected shared-schema container %s", sharedSchema);
+            return ((ContainerNode) sharedSchema).getChild(PARENT_REFERENCE).map(parentRef -> {
+                // FIXME: decode
+                return ImmutableSet.<String>of();
+            }).orElseGet(ImmutableSet::of);
+        }).orElseGet(() -> {
+            checkArgument(schemaRef.getChild(INLINE).isPresent(), "Unhandled schema-ref type in %s", schemaRef);
+            return ImmutableSet.of();
+        });
+    }
+
+    /**
+     * Create a fresh {@link MountPointContextFactory} for a nested {@link MountPointDefinition}.
+     *
+     * @param mountPoint Mount point definition
+     * @return A new factory, dealing with mount points nested within the mount point.
+     */
+    protected abstract MountPointContextFactory createContextFactory(MountPointDefinition mountPoint);
+}
diff --git a/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/EmptyMountPointContext.java b/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/EmptyMountPointContext.java
new file mode 100644 (file)
index 0000000..6ec6ea2
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rcf8528.data.util;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.util.AbstractSchemaContextProvider;
+
+/**
+ * A simple {@link MountPointContext} which does not contain any mount points.
+ */
+@Beta
+public final class EmptyMountPointContext extends AbstractSchemaContextProvider implements MountPointContext {
+    public EmptyMountPointContext(final SchemaContext schemaContext) {
+        super(schemaContext);
+    }
+
+    @Override
+    public Optional<MountPointContextFactory> findMountPoint(final MountPointIdentifier label) {
+        return Optional.empty();
+    }
+}
diff --git a/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/ImmutableMountPointContext.java b/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/ImmutableMountPointContext.java
new file mode 100644 (file)
index 0000000..c996e5e
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rcf8528.data.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.util.Optional;
+import java.util.function.Function;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.rcf8528.data.util.AbstractMountPointContextFactory.MountPointDefinition;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.util.AbstractSchemaContextProvider;
+
+final class ImmutableMountPointContext extends AbstractSchemaContextProvider implements Immutable, MountPointContext {
+    private final ImmutableMap<MountPointIdentifier, MountPointDefinition> mountPoints;
+    private final Function<MountPointDefinition, MountPointContextFactory> createFactory;
+
+    ImmutableMountPointContext(final SchemaContext schemaContext, final Iterable<MountPointDefinition> mountPoints,
+            final Function<MountPointDefinition, MountPointContextFactory> createFactory) {
+        super(schemaContext);
+        this.mountPoints = Maps.uniqueIndex(mountPoints, MountPointDefinition::getIdentifier);
+        this.createFactory = requireNonNull(createFactory);
+    }
+
+    @Override
+    public Optional<MountPointContextFactory> findMountPoint(final MountPointIdentifier label) {
+        final MountPointDefinition def = mountPoints.get(requireNonNull(label));
+        return def == null ? Optional.empty() : Optional.of(createFactory.apply(def));
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper).add("mountPoints", mountPoints);
+    }
+}
diff --git a/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/ImmutableMountPointNode.java b/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/ImmutableMountPointNode.java
new file mode 100644 (file)
index 0000000..d974471
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rcf8528.data.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+
+@Beta
+public final class ImmutableMountPointNode extends AbstractIdentifiable<MountPointIdentifier>
+        implements MountPointNode, Immutable {
+
+    private final @NonNull MountPointContext mountCtx;
+    private final @NonNull ContainerNode delegate;
+
+    ImmutableMountPointNode(final @NonNull MountPointIdentifier identifier,
+            final @NonNull MountPointContext mountCtx, final @NonNull ContainerNode delegate) {
+        super(identifier);
+        this.mountCtx = requireNonNull(mountCtx);
+        this.delegate = requireNonNull(delegate);
+    }
+
+    public static @NonNull ImmutableMountPointNode of(final @NonNull MountPointIdentifier identifier,
+            final @NonNull MountPointContext mountCtx, final @NonNull ContainerNode delegate) {
+        return new ImmutableMountPointNode(identifier, mountCtx, delegate);
+    }
+
+    @Override
+    public MountPointContext getMountPointContext() {
+        return mountCtx;
+    }
+
+    @Override
+    public Collection<DataContainerChild<? extends PathArgument, ?>> getValue() {
+        return delegate.getValue();
+    }
+
+    @Override
+    public Optional<DataContainerChild<? extends PathArgument, ?>> getChild(final PathArgument child) {
+        return delegate.getChild(child);
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper).add("delegate", delegate);
+    }
+}
diff --git a/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/MountPointNormalizedNodeWriter.java b/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rcf8528/data/util/MountPointNormalizedNodeWriter.java
new file mode 100644 (file)
index 0000000..6decdc2
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rcf8528.data.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointNode;
+import org.opendaylight.yangtools.rfc8528.data.api.StreamWriterMountPointExtension;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+
+/**
+ * A {@link MountPointNode}-aware counterpart to {@link NormalizedNodeWriter}. Based on the backing writer's capability
+ * it either forwards or filters MountPointNodes.
+ */
+@Beta
+public abstract class MountPointNormalizedNodeWriter extends NormalizedNodeWriter {
+    private static final class Filtering extends MountPointNormalizedNodeWriter {
+        Filtering(final NormalizedNodeStreamWriter writer) {
+            super(writer);
+        }
+
+        @Override
+        void writeMountPoint(final MountPointNode node) {
+            // No-op
+        }
+    }
+
+    private static final class Forwarding extends MountPointNormalizedNodeWriter {
+        private final StreamWriterMountPointExtension mountWriter;
+
+        Forwarding(final NormalizedNodeStreamWriter writer, final StreamWriterMountPointExtension mountWriter) {
+            super(writer);
+            this.mountWriter = requireNonNull(mountWriter);
+        }
+
+        @Override
+        void writeMountPoint(final MountPointNode node) throws IOException {
+            try (MountPointNormalizedNodeWriter writer = forStreamWriter(mountWriter.startMountPoint(
+                    node.getIdentifier(), node.getMountPointContext()))) {
+                for (DataContainerChild<? extends PathArgument, ?> child : node.getValue()) {
+                    writer.write(child);
+                }
+            }
+        }
+    }
+
+    MountPointNormalizedNodeWriter(final NormalizedNodeStreamWriter writer) {
+        super(writer);
+    }
+
+    public static @NonNull MountPointNormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer) {
+        final StreamWriterMountPointExtension mountWriter = writer.getExtensions()
+            .getInstance(StreamWriterMountPointExtension.class);
+        return mountWriter == null ? new Filtering(writer) : new Forwarding(writer, mountWriter);
+    }
+
+    public static @NonNull MountPointNormalizedNodeWriter filteringFor(final NormalizedNodeStreamWriter writer) {
+        return new Filtering(writer);
+    }
+
+    public static @NonNull MountPointNormalizedNodeWriter forwardingFor(final NormalizedNodeStreamWriter writer) {
+        final StreamWriterMountPointExtension mountWriter = writer.getExtensions()
+            .getInstance(StreamWriterMountPointExtension.class);
+        checkArgument(mountWriter != null, "Writer %s does not support mount points", writer);
+        return new Forwarding(writer, mountWriter);
+    }
+
+    @Override
+    protected final boolean wasProcessedAsCompositeNode(final NormalizedNode<?, ?> node) throws IOException {
+        if (node instanceof MountPointNode) {
+            writeMountPoint((MountPointNode) node);
+            return true;
+        }
+        return super.wasProcessedAsCompositeNode(node);
+    }
+
+    abstract void writeMountPoint(MountPointNode node) throws IOException;
+}
diff --git a/yang/rfc8528-model-api/pom.xml b/yang/rfc8528-model-api/pom.xml
new file mode 100644 (file)
index 0000000..2e63f35
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc8528-model-api</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC8528 model API</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc8528.model.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/MountPointEffectiveStatement.java b/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/MountPointEffectiveStatement.java
new file mode 100644 (file)
index 0000000..e6308ac
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Effective statement representation of 'mount-point' extension defined in
+ * <a href="https://tools.ietf.org/html/rfc8528">RFC8528</a>.
+ */
+@Beta
+public interface MountPointEffectiveStatement extends EffectiveStatement<QName, MountPointStatement> {
+
+}
diff --git a/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/MountPointSchemaNode.java b/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/MountPointSchemaNode.java
new file mode 100644 (file)
index 0000000..c0ab93b
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.model.api;
+
+import com.google.common.annotations.Beta;
+import java.util.stream.Stream;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+/**
+ * Represents the effect of 'mount-point' extension, as defined in
+ * <a href="https://tools.ietf.org/html/rfc8528">RFC8528</a>, being attached to a SchemaNode.
+ */
+@Beta
+public interface MountPointSchemaNode extends UnknownSchemaNode {
+    /**
+     * Find all mount points defined in a {@link ContainerSchemaNode}.
+     *
+     * @param schema ContainerSchemaNode to search
+     * @return {@link MountPointSchemaNode}s defined the ContainerSchemaNode.
+     * @throws NullPointerException if context is null
+     */
+    static @NonNull Stream<MountPointSchemaNode> streamAll(final ContainerSchemaNode schema) {
+        return schema.getUnknownSchemaNodes().stream()
+                .filter(MountPointSchemaNode.class::isInstance)
+                .map(MountPointSchemaNode.class::cast);
+    }
+
+    /**
+     * Find all mount points defined in a {@link ListSchemaNode}.
+     *
+     * @param schema ListSchemaNode to search
+     * @return {@link MountPointSchemaNode}s defined the ListSchemaNode.
+     * @throws NullPointerException if context is null
+     */
+    static @NonNull Stream<MountPointSchemaNode> streamAll(final ListSchemaNode schema) {
+        return schema.getUnknownSchemaNodes().stream()
+                .filter(MountPointSchemaNode.class::isInstance)
+                .map(MountPointSchemaNode.class::cast);
+    }
+}
diff --git a/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/MountPointStatement.java b/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/MountPointStatement.java
new file mode 100644 (file)
index 0000000..a81087a
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.model.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatementAwareDeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DocumentedDeclaredStatement.WithStatus;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+
+/**
+ * Declared statement representation of 'mount-point' extension defined in
+ * <a href="https://tools.ietf.org/html/rfc8528">RFC8528</a>.
+ */
+@Beta
+public interface MountPointStatement extends UnknownStatement<QName>, WithStatus<QName>,
+    ConfigStatementAwareDeclaredStatement<QName> {
+
+}
diff --git a/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/SchemaMountConstants.java b/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/SchemaMountConstants.java
new file mode 100644 (file)
index 0000000..1873116
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.model.api;
+
+import com.google.common.collect.ImmutableList;
+import java.net.URI;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Constants associated with RFC8528.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault
+public final class SchemaMountConstants {
+    private static final String MODULE_NAME = "ietf-yang-schema-mount";
+    private static final URI MODULE_NAMESPACE = URI.create("urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount");
+    private static final Revision RFC8528_REVISION = Revision.of("2019-01-14");
+
+    /**
+     * Runtime RFC8528 identity.
+     */
+    public static final QNameModule RFC8528_MODULE = QNameModule.create(MODULE_NAMESPACE, RFC8528_REVISION).intern();
+
+    /**
+     * RFC8528 model source name.
+     */
+    public static final SourceIdentifier RFC8528_SOURCE = RevisionSourceIdentifier.create(MODULE_NAME,
+        RFC8528_REVISION);
+
+    /**
+     * Normative prefix to use when importing {@link #RFC8528_SOURCE}.
+     */
+    public static final String MODULE_PREFIX = "yangmnt";
+
+    private SchemaMountConstants() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Return identifiers of all sources known to define the metadata extension.
+     *
+     * @return Collection of identifiers.
+     */
+    public static Collection<SourceIdentifier> knownModelSources() {
+        return ImmutableList.of(RFC8528_SOURCE);
+    }
+}
diff --git a/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/SchemaMountStatements.java b/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/SchemaMountStatements.java
new file mode 100644 (file)
index 0000000..9b3723d
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.model.api;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+/**
+ * {@link StatementDefinition}s for statements defined by RFC7952.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault
+public enum SchemaMountStatements implements StatementDefinition {
+    MOUNT_POINT(QName.create(SchemaMountConstants.RFC8528_MODULE, "mount-point"), "label", MountPointStatement.class,
+        MountPointEffectiveStatement.class);
+
+    private final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation;
+    private final Class<? extends DeclaredStatement<?>> declaredRepresentation;
+    private final QName statementName;
+    private final ArgumentDefinition argumentDef;
+
+    SchemaMountStatements(final QName statementName, final String argumentName,
+            final Class<? extends DeclaredStatement<?>> declaredRepresentation,
+                    final Class<? extends EffectiveStatement<?, ?>> effectiveRepresentation) {
+        this.statementName = statementName.intern();
+        this.argumentDef = ArgumentDefinition.of(QName.create(statementName, argumentName), false);
+        this.declaredRepresentation = requireNonNull(declaredRepresentation);
+        this.effectiveRepresentation = requireNonNull(effectiveRepresentation);
+    }
+
+    @Override
+    public Optional<ArgumentDefinition> getArgumentDefinition() {
+        return Optional.of(argumentDef);
+    }
+
+    @Override
+    public QName getStatementName() {
+        return statementName;
+    }
+
+    @Override
+    public Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass() {
+        return effectiveRepresentation;
+    }
+
+    @Override
+    public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+        return declaredRepresentation;
+    }
+}
diff --git a/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/package-info.java b/yang/rfc8528-model-api/src/main/java/org/opendaylight/yangtools/rfc8528/model/api/package-info.java
new file mode 100644 (file)
index 0000000..1b6a0ea
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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
+ */
+/**
+ * YANG metamodel extensions to support mount-poing, as defined in
+ * <a href="https://tools.ietf.org/html/rfc8528">RFC8528</a>. For SchemaNode world entry space is defined via
+ * {@link org.opendaylight.yangtools.rfc8528.model.api.MountPointSchemaNode}'s static methods.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.rfc8528.model.api;
diff --git a/yang/rfc8528-parser-support/pom.xml b/yang/rfc8528-parser-support/pom.xml
new file mode 100644 (file)
index 0000000..e2535ca
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>rfc8528-parser-support</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC8528 parser support</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-model-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-rfc7950</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.rfc8528.parser.support</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/rfc8528-parser-support/src/main/java/org/opendaylight/yangtools/rfc8528/parser/MountPointStatementSupport.java b/yang/rfc8528-parser-support/src/main/java/org/opendaylight/yangtools/rfc8528/parser/MountPointStatementSupport.java
new file mode 100644 (file)
index 0000000..5280242
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.parser;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.rfc8528.model.api.MountPointEffectiveStatement;
+import org.opendaylight.yangtools.rfc8528.model.api.MountPointSchemaNode;
+import org.opendaylight.yangtools.rfc8528.model.api.MountPointStatement;
+import org.opendaylight.yangtools.rfc8528.model.api.SchemaMountStatements;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.UnknownEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class MountPointStatementSupport
+        extends AbstractStatementSupport<QName, MountPointStatement, MountPointEffectiveStatement> {
+
+    private static final class Declared extends AbstractDeclaredStatement<QName> implements MountPointStatement {
+        Declared(final StmtContext<QName, ?, ?> context) {
+            super(context);
+        }
+
+        @Override
+        public QName getArgument() {
+            return argument();
+        }
+    }
+
+    private static final class Effective extends UnknownEffectiveStatementBase<QName, MountPointStatement>
+            implements MountPointEffectiveStatement, MountPointSchemaNode {
+
+        private final @NonNull SchemaPath path;
+
+        Effective(final StmtContext<QName, MountPointStatement, ?> ctx) {
+            super(ctx);
+            path = ctx.coerceParentContext().getSchemaPath().get().createChild(argument());
+        }
+
+        @Override
+        public QName getQName() {
+            return path.getLastComponent();
+        }
+
+        @Override
+        public SchemaPath getPath() {
+            return path;
+        }
+    }
+
+    private static final MountPointStatementSupport INSTANCE = new MountPointStatementSupport(
+        SchemaMountStatements.MOUNT_POINT);
+
+    private final SubstatementValidator validator;
+
+    MountPointStatementSupport(final StatementDefinition definition) {
+        super(definition);
+        this.validator = SubstatementValidator.builder(definition)
+                .addOptional(YangStmtMapping.CONFIG)
+                .addOptional(YangStmtMapping.DESCRIPTION)
+                .addOptional(YangStmtMapping.REFERENCE)
+                .addOptional(YangStmtMapping.STATUS)
+                .build();
+    }
+
+    public static MountPointStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public MountPointStatement createDeclared(final StmtContext<QName, MountPointStatement, ?> ctx) {
+        return new Declared(ctx);
+    }
+
+    @Override
+    public MountPointEffectiveStatement createEffective(
+            final StmtContext<QName, MountPointStatement, MountPointEffectiveStatement> ctx) {
+        return new Effective(ctx);
+    }
+
+    @Override
+    public QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public QName adaptArgumentValue(final StmtContext<QName, MountPointStatement, MountPointEffectiveStatement> ctx,
+            final QNameModule targetModule) {
+        return ctx.getFromNamespace(QNameCacheNamespace.class, ctx.getStatementArgument().withModule(targetModule));
+    }
+
+    @Override
+    public void onStatementAdded(final Mutable<QName, MountPointStatement, MountPointEffectiveStatement> stmt) {
+        final StatementDefinition parentDef = stmt.coerceParentContext().getPublicDefinition();
+        SourceException.throwIf(YangStmtMapping.CONTAINER != parentDef && YangStmtMapping.LIST != parentDef,
+                stmt.getStatementSourceReference(), "Mount points may only be defined at either a container or a list");
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return validator;
+    }
+}
diff --git a/yang/rfc8528-parser-support/src/main/java/org/opendaylight/yangtools/rfc8528/parser/package-info.java b/yang/rfc8528-parser-support/src/main/java/org/opendaylight/yangtools/rfc8528/parser/package-info.java
new file mode 100644 (file)
index 0000000..6bc4c97
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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
+ */
+/**
+ * YANG parser support for metamodel extensions defined in <a href="https://tools.ietf.org/html/rfc8528">RFC8528</a>.
+ * Add {@link org.opendaylight.yangtools.rfc8528.parser.MountPointStatementSupport} to your reactor to add support
+ * for this extension.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.rfc8528.parser;
diff --git a/yang/rfc8528-parser-support/src/test/java/org/opendaylight/yangtools/rfc8528/parser/MountPointTest.java b/yang/rfc8528-parser-support/src/test/java/org/opendaylight/yangtools/rfc8528/parser/MountPointTest.java
new file mode 100644 (file)
index 0000000..c57f055
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.rfc8528.parser;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.rfc8528.model.api.MountPointSchemaNode;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+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.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+
+public class MountPointTest {
+    private static final QNameModule EXAMPLE_USES = QNameModule.create(URI.create("http://example.org/example-uses"));
+    private static final QName EXAMPLE_CONT = QName.create(EXAMPLE_USES, "cont");
+    private static final QName EXAMPLE_GRP = QName.create(EXAMPLE_USES, "grp");
+    private static final QName EXAMPLE_GRP_CONT = QName.create(EXAMPLE_USES, "grp-cont");
+    private static final QName EXAMPLE_LIST = QName.create(EXAMPLE_USES, "list");
+
+    private static CrossSourceStatementReactor reactor;
+
+    @BeforeClass
+    public static void createReactor() {
+        reactor = RFC7950Reactors.vanillaReactorBuilder()
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, MountPointStatementSupport.getInstance())
+                .build();
+    }
+
+    @AfterClass
+    public static void freeReactor() {
+        reactor = null;
+    }
+
+    @Test
+    public void testMountPointResolution() throws ReactorException, IOException, YangSyntaxErrorException {
+        final SchemaContext context = reactor.newBuild()
+                .addLibSources(
+                    YangStatementStreamSource.create(YangTextSchemaSource.forResource(
+                            "/ietf-inet-types@2013-07-15.yang")),
+                    YangStatementStreamSource.create(YangTextSchemaSource.forResource(
+                            "/ietf-yang-schema-mount@2019-01-14.yang")),
+                    YangStatementStreamSource.create(YangTextSchemaSource.forResource(
+                            "/ietf-yang-types@2013-07-15.yang")))
+                .addSources(
+                    YangStatementStreamSource.create(YangTextSchemaSource.forResource("/example-grp.yang")),
+                    YangStatementStreamSource.create(YangTextSchemaSource.forResource("/example-uses.yang")))
+                .buildEffective();
+
+        assertEquals(5, context.getModules().size());
+
+        DataSchemaNode child = context.findDataTreeChild(EXAMPLE_CONT).get();
+        assertThat(child, instanceOf(ContainerSchemaNode.class));
+        List<MountPointSchemaNode> mps = MountPointSchemaNode.streamAll((ContainerSchemaNode) child)
+                .collect(Collectors.toList());
+        assertEquals(2, mps.size());
+        assertEquals(EXAMPLE_CONT, mps.get(0).getQName());
+        assertEquals(EXAMPLE_CONT, mps.get(1).getQName());
+
+        child = context.findDataTreeChild(EXAMPLE_GRP_CONT).get();
+        assertThat(child, instanceOf(ContainerSchemaNode.class));
+        mps = MountPointSchemaNode.streamAll((ContainerSchemaNode) child).collect(Collectors.toList());
+        assertEquals(1, mps.size());
+        assertEquals(EXAMPLE_GRP, mps.get(0).getQName());
+
+        child = context.findDataTreeChild(EXAMPLE_LIST).get();
+        assertThat(child, instanceOf(ListSchemaNode.class));
+        mps = MountPointSchemaNode.streamAll((ListSchemaNode) child).collect(Collectors.toList());
+        assertEquals(1, mps.size());
+        assertEquals(EXAMPLE_LIST, mps.get(0).getQName());
+    }
+}
diff --git a/yang/rfc8528-parser-support/src/test/resources/example-grp.yang b/yang/rfc8528-parser-support/src/test/resources/example-grp.yang
new file mode 100644 (file)
index 0000000..ffe2630
--- /dev/null
@@ -0,0 +1,12 @@
+module example-grp {
+    namespace "http://example.org/example-grp";
+    prefix "eg";
+    import ietf-yang-schema-mount {
+        prefix "yangmnt";
+    }
+    grouping grp {
+        container grp-cont {
+            yangmnt:mount-point grp;
+        }
+    }
+}
diff --git a/yang/rfc8528-parser-support/src/test/resources/example-uses.yang b/yang/rfc8528-parser-support/src/test/resources/example-uses.yang
new file mode 100644 (file)
index 0000000..422fd48
--- /dev/null
@@ -0,0 +1,18 @@
+module example-uses {
+    namespace "http://example.org/example-uses";
+    prefix "eu";
+    import example-grp {
+        prefix "eg";
+    }
+    import ietf-yang-schema-mount {
+        prefix "yangmnt";
+    }
+    container cont {
+        yangmnt:mount-point cont;
+        yangmnt:mount-point cont;
+    }
+    list list {
+        yangmnt:mount-point list;
+    }
+    uses eg:grp;
+}
diff --git a/yang/rfc8528-parser-support/src/test/resources/ietf-inet-types@2013-07-15.yang b/yang/rfc8528-parser-support/src/test/resources/ietf-inet-types@2013-07-15.yang
new file mode 100644 (file)
index 0000000..eacefb6
--- /dev/null
@@ -0,0 +1,458 @@
+module ietf-inet-types {
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
+  prefix "inet";
+
+  organization
+   "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+   "WG Web:   <http://tools.ietf.org/wg/netmod/>
+    WG List:  <mailto:netmod@ietf.org>
+
+    WG Chair: David Kessens
+              <mailto:david.kessens@nsn.com>
+
+    WG Chair: Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>
+
+    Editor:   Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>";
+
+  description
+   "This module contains a collection of generally useful derived
+    YANG data types for Internet addresses and related things.
+
+    Copyright (c) 2013 IETF Trust and the persons identified as
+    authors of the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or
+    without modification, is permitted pursuant to, and subject
+    to the license terms contained in, the Simplified BSD License
+    set forth in Section 4.c of the IETF Trust's Legal Provisions
+    Relating to IETF Documents
+    (http://trustee.ietf.org/license-info).
+
+    This version of this YANG module is part of RFC 6991; see
+    the RFC itself for full legal notices.";
+
+  revision 2013-07-15 {
+    description
+     "This revision adds the following new data types:
+      - ip-address-no-zone
+      - ipv4-address-no-zone
+      - ipv6-address-no-zone";
+    reference
+     "RFC 6991: Common YANG Data Types";
+  }
+
+  revision 2010-09-24 {
+    description
+     "Initial revision.";
+    reference
+     "RFC 6021: Common YANG Data Types";
+  }
+
+  /*** collection of types related to protocol fields ***/
+
+  typedef ip-version {
+    type enumeration {
+      enum unknown {
+        value "0";
+        description
+         "An unknown or unspecified version of the Internet
+          protocol.";
+      }
+      enum ipv4 {
+        value "1";
+        description
+         "The IPv4 protocol as defined in RFC 791.";
+      }
+      enum ipv6 {
+        value "2";
+        description
+         "The IPv6 protocol as defined in RFC 2460.";
+      }
+    }
+    description
+     "This value represents the version of the IP protocol.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetVersion textual convention of the SMIv2.";
+    reference
+     "RFC  791: Internet Protocol
+      RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+      RFC 4001: Textual Conventions for Internet Network Addresses";
+  }
+
+  typedef dscp {
+    type uint8 {
+      range "0..63";
+    }
+    description
+     "The dscp type represents a Differentiated Services Code Point
+      that may be used for marking packets in a traffic stream.
+      In the value set and its semantics, this type is equivalent
+      to the Dscp textual convention of the SMIv2.";
+    reference
+     "RFC 3289: Management Information Base for the Differentiated
+                Services Architecture
+      RFC 2474: Definition of the Differentiated Services Field
+                (DS Field) in the IPv4 and IPv6 Headers
+      RFC 2780: IANA Allocation Guidelines For Values In
+                the Internet Protocol and Related Headers";
+  }
+
+  typedef ipv6-flow-label {
+    type uint32 {
+      range "0..1048575";
+    }
+    description
+     "The ipv6-flow-label type represents the flow identifier or Flow
+      Label in an IPv6 packet header that may be used to
+      discriminate traffic flows.
+
+      In the value set and its semantics, this type is equivalent
+      to the IPv6FlowLabel textual convention of the SMIv2.";
+    reference
+     "RFC 3595: Textual Conventions for IPv6 Flow Label
+      RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
+  }
+
+  typedef port-number {
+    type uint16 {
+      range "0..65535";
+    }
+    description
+     "The port-number type represents a 16-bit port number of an
+      Internet transport-layer protocol such as UDP, TCP, DCCP, or
+      SCTP.  Port numbers are assigned by IANA.  A current list of
+      all assignments is available from <http://www.iana.org/>.
+
+      Note that the port number value zero is reserved by IANA.  In
+      situations where the value zero does not make sense, it can
+      be excluded by subtyping the port-number type.
+      In the value set and its semantics, this type is equivalent
+      to the InetPortNumber textual convention of the SMIv2.";
+    reference
+     "RFC  768: User Datagram Protocol
+      RFC  793: Transmission Control Protocol
+      RFC 4960: Stream Control Transmission Protocol
+      RFC 4340: Datagram Congestion Control Protocol (DCCP)
+      RFC 4001: Textual Conventions for Internet Network Addresses";
+  }
+
+  /*** collection of types related to autonomous systems ***/
+
+  typedef as-number {
+    type uint32;
+    description
+     "The as-number type represents autonomous system numbers
+      which identify an Autonomous System (AS).  An AS is a set
+      of routers under a single technical administration, using
+      an interior gateway protocol and common metrics to route
+      packets within the AS, and using an exterior gateway
+      protocol to route packets to other ASes.  IANA maintains
+      the AS number space and has delegated large parts to the
+      regional registries.
+
+      Autonomous system numbers were originally limited to 16
+      bits.  BGP extensions have enlarged the autonomous system
+      number space to 32 bits.  This type therefore uses an uint32
+      base type without a range restriction in order to support
+      a larger autonomous system number space.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetAutonomousSystemNumber textual convention of
+      the SMIv2.";
+    reference
+     "RFC 1930: Guidelines for creation, selection, and registration
+                of an Autonomous System (AS)
+      RFC 4271: A Border Gateway Protocol 4 (BGP-4)
+      RFC 4001: Textual Conventions for Internet Network Addresses
+      RFC 6793: BGP Support for Four-Octet Autonomous System (AS)
+                Number Space";
+  }
+
+  /*** collection of types related to IP addresses and hostnames ***/
+
+  typedef ip-address {
+    type union {
+      type inet:ipv4-address;
+      type inet:ipv6-address;
+    }
+    description
+     "The ip-address type represents an IP address and is IP
+      version neutral.  The format of the textual representation
+      implies the IP version.  This type supports scoped addresses
+      by allowing zone identifiers in the address format.";
+    reference
+     "RFC 4007: IPv6 Scoped Address Architecture";
+  }
+
+  typedef ipv4-address {
+    type string {
+      pattern
+        '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+      +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+      + '(%[\p{N}\p{L}]+)?';
+    }
+    description
+      "The ipv4-address type represents an IPv4 address in
+       dotted-quad notation.  The IPv4 address may include a zone
+       index, separated by a % sign.
+
+       The zone index is used to disambiguate identical address
+       values.  For link-local addresses, the zone index will
+       typically be the interface index number or the name of an
+       interface.  If the zone index is not present, the default
+       zone of the device will be used.
+
+       The canonical format for the zone index is the numerical
+       format";
+  }
+
+  typedef ipv6-address {
+    type string {
+      pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+            + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+            + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+            + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+            + '(%[\p{N}\p{L}]+)?';
+      pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+            + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+            + '(%.+)?';
+    }
+    description
+     "The ipv6-address type represents an IPv6 address in full,
+      mixed, shortened, and shortened-mixed notation.  The IPv6
+      address may include a zone index, separated by a % sign.
+
+      The zone index is used to disambiguate identical address
+      values.  For link-local addresses, the zone index will
+      typically be the interface index number or the name of an
+      interface.  If the zone index is not present, the default
+      zone of the device will be used.
+
+      The canonical format of IPv6 addresses uses the textual
+      representation defined in Section 4 of RFC 5952.  The
+      canonical format for the zone index is the numerical
+      format as described in Section 11.2 of RFC 4007.";
+    reference
+     "RFC 4291: IP Version 6 Addressing Architecture
+      RFC 4007: IPv6 Scoped Address Architecture
+      RFC 5952: A Recommendation for IPv6 Address Text
+                Representation";
+  }
+
+  typedef ip-address-no-zone {
+    type union {
+      type inet:ipv4-address-no-zone;
+      type inet:ipv6-address-no-zone;
+    }
+    description
+     "The ip-address-no-zone type represents an IP address and is
+      IP version neutral.  The format of the textual representation
+      implies the IP version.  This type does not support scoped
+      addresses since it does not allow zone identifiers in the
+      address format.";
+    reference
+     "RFC 4007: IPv6 Scoped Address Architecture";
+  }
+
+  typedef ipv4-address-no-zone {
+    type inet:ipv4-address {
+      pattern '[0-9\.]*';
+    }
+    description
+      "An IPv4 address without a zone index.  This type, derived from
+       ipv4-address, may be used in situations where the zone is
+       known from the context and hence no zone index is needed.";
+  }
+
+  typedef ipv6-address-no-zone {
+    type inet:ipv6-address {
+      pattern '[0-9a-fA-F:\.]*';
+    }
+    description
+      "An IPv6 address without a zone index.  This type, derived from
+       ipv6-address, may be used in situations where the zone is
+       known from the context and hence no zone index is needed.";
+    reference
+     "RFC 4291: IP Version 6 Addressing Architecture
+      RFC 4007: IPv6 Scoped Address Architecture
+      RFC 5952: A Recommendation for IPv6 Address Text
+                Representation";
+  }
+
+  typedef ip-prefix {
+    type union {
+      type inet:ipv4-prefix;
+      type inet:ipv6-prefix;
+    }
+    description
+     "The ip-prefix type represents an IP prefix and is IP
+      version neutral.  The format of the textual representations
+      implies the IP version.";
+  }
+
+  typedef ipv4-prefix {
+    type string {
+      pattern
+         '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+       +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+       + '/(([0-9])|([1-2][0-9])|(3[0-2]))';
+    }
+    description
+     "The ipv4-prefix type represents an IPv4 address prefix.
+      The prefix length is given by the number following the
+      slash character and must be less than or equal to 32.
+
+      A prefix length value of n corresponds to an IP address
+      mask that has n contiguous 1-bits from the most
+      significant bit (MSB) and all other bits set to 0.
+
+      The canonical format of an IPv4 prefix has all bits of
+      the IPv4 address set to zero that are not part of the
+      IPv4 prefix.";
+  }
+
+  typedef ipv6-prefix {
+    type string {
+      pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+            + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+            + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+            + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+            + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
+      pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+            + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+            + '(/.+)';
+    }
+
+    description
+     "The ipv6-prefix type represents an IPv6 address prefix.
+      The prefix length is given by the number following the
+      slash character and must be less than or equal to 128.
+
+      A prefix length value of n corresponds to an IP address
+      mask that has n contiguous 1-bits from the most
+      significant bit (MSB) and all other bits set to 0.
+
+      The IPv6 address should have all bits that do not belong
+      to the prefix set to zero.
+
+      The canonical format of an IPv6 prefix has all bits of
+      the IPv6 address set to zero that are not part of the
+      IPv6 prefix.  Furthermore, the IPv6 address is represented
+      as defined in Section 4 of RFC 5952.";
+    reference
+     "RFC 5952: A Recommendation for IPv6 Address Text
+                Representation";
+  }
+
+  /*** collection of domain name and URI types ***/
+
+  typedef domain-name {
+    type string {
+      pattern
+        '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+      + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+      + '|\.';
+      length "1..253";
+    }
+    description
+     "The domain-name type represents a DNS domain name.  The
+      name SHOULD be fully qualified whenever possible.
+
+      Internet domain names are only loosely specified.  Section
+      3.5 of RFC 1034 recommends a syntax (modified in Section
+      2.1 of RFC 1123).  The pattern above is intended to allow
+      for current practice in domain name use, and some possible
+      future expansion.  It is designed to hold various types of
+      domain names, including names used for A or AAAA records
+      (host names) and other records, such as SRV records.  Note
+      that Internet host names have a stricter syntax (described
+      in RFC 952) than the DNS recommendations in RFCs 1034 and
+      1123, and that systems that want to store host names in
+      schema nodes using the domain-name type are recommended to
+      adhere to this stricter standard to ensure interoperability.
+
+      The encoding of DNS names in the DNS protocol is limited
+      to 255 characters.  Since the encoding consists of labels
+      prefixed by a length bytes and there is a trailing NULL
+      byte, only 253 characters can appear in the textual dotted
+      notation.
+
+      The description clause of schema nodes using the domain-name
+      type MUST describe when and how these names are resolved to
+      IP addresses.  Note that the resolution of a domain-name value
+      may require to query multiple DNS records (e.g., A for IPv4
+      and AAAA for IPv6).  The order of the resolution process and
+      which DNS record takes precedence can either be defined
+      explicitly or may depend on the configuration of the
+      resolver.
+
+      Domain-name values use the US-ASCII encoding.  Their canonical
+      format uses lowercase US-ASCII characters.  Internationalized
+      domain names MUST be A-labels as per RFC 5890.";
+    reference
+     "RFC  952: DoD Internet Host Table Specification
+      RFC 1034: Domain Names - Concepts and Facilities
+      RFC 1123: Requirements for Internet Hosts -- Application
+                and Support
+      RFC 2782: A DNS RR for specifying the location of services
+                (DNS SRV)
+      RFC 5890: Internationalized Domain Names in Applications
+                (IDNA): Definitions and Document Framework";
+  }
+
+  typedef host {
+    type union {
+      type inet:ip-address;
+      type inet:domain-name;
+    }
+    description
+     "The host type represents either an IP address or a DNS
+      domain name.";
+  }
+
+  typedef uri {
+    type string;
+    description
+     "The uri type represents a Uniform Resource Identifier
+      (URI) as defined by STD 66.
+
+      Objects using the uri type MUST be in US-ASCII encoding,
+      and MUST be normalized as described by RFC 3986 Sections
+      6.2.1, 6.2.2.1, and 6.2.2.2.  All unnecessary
+      percent-encoding is removed, and all case-insensitive
+      characters are set to lowercase except for hexadecimal
+      digits, which are normalized to uppercase as described in
+      Section 6.2.2.1.
+
+      The purpose of this normalization is to help provide
+      unique URIs.  Note that this normalization is not
+      sufficient to provide uniqueness.  Two URIs that are
+      textually distinct after this normalization may still be
+      equivalent.
+
+      Objects using the uri type may restrict the schemes that
+      they permit.  For example, 'data:' and 'urn:' schemes
+      might not be appropriate.
+
+      A zero-length URI is not a valid URI.  This can be used to
+      express 'URI absent' where required.
+
+      In the value set and its semantics, this type is equivalent
+      to the Uri SMIv2 textual convention defined in RFC 5017.";
+    reference
+     "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
+      RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
+                Group: Uniform Resource Identifiers (URIs), URLs,
+                and Uniform Resource Names (URNs): Clarifications
+                and Recommendations
+      RFC 5017: MIB Textual Conventions for Uniform Resource
+                Identifiers (URIs)";
+  }
+
+}
diff --git a/yang/rfc8528-parser-support/src/test/resources/ietf-yang-schema-mount@2019-01-14.yang b/yang/rfc8528-parser-support/src/test/resources/ietf-yang-schema-mount@2019-01-14.yang
new file mode 100644 (file)
index 0000000..c49458a
--- /dev/null
@@ -0,0 +1,224 @@
+module ietf-yang-schema-mount {
+  yang-version 1.1;
+  namespace "urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount";
+  prefix yangmnt;
+
+  import ietf-inet-types {
+    prefix inet;
+    reference
+      "RFC 6991: Common YANG Data Types";
+  }
+
+  import ietf-yang-types {
+    prefix yang;
+    reference
+      "RFC 6991: Common YANG Data Types";
+  }
+
+  organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+    "WG Web:   <https://datatracker.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+
+     Editor:   Martin Bjorklund
+               <mailto:mbj@tail-f.com>
+
+     Editor:   Ladislav Lhotka
+               <mailto:lhotka@nic.cz>";
+
+  description
+    "This module defines a YANG extension statement that can be used
+     to incorporate data models defined in other YANG modules in a
+     module.  It also defines operational state data that specify the
+     overall structure of the data model.
+
+     The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
+     NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED',
+     'MAY', and 'OPTIONAL' in this document are to be interpreted as
+     described in BCP 14 (RFC 2119) (RFC 8174) when, and only when,
+     they appear in all capitals, as shown here.
+
+     Copyright (c) 2019 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject to
+     the license terms contained in, the Simplified BSD License set
+     forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (https://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 8528;
+     see the RFC itself for full legal notices.";
+
+  revision 2019-01-14 {
+    description
+      "Initial revision.";
+    reference
+      "RFC 8528: YANG Schema Mount";
+  }
+
+  /*
+   * Extensions
+   */
+
+  extension mount-point {
+    argument label;
+    description
+      "The argument 'label' is a YANG identifier, i.e., it is of the
+       type 'yang:yang-identifier'.
+
+       The 'mount-point' statement MUST NOT be used in a YANG
+       version 1 module, neither explicitly nor via a 'uses'
+       statement.
+       The 'mount-point' statement MAY be present as a substatement
+       of 'container' and 'list' and MUST NOT be present elsewhere.
+       There MUST NOT be more than one 'mount-point' statement in a
+       given 'container' or 'list' statement.
+
+       If a mount point is defined within a grouping, its label is
+       bound to the module where the grouping is used.
+
+       A mount point defines a place in the node hierarchy where
+       other data models may be attached.  A server that implements a
+       module with a mount point populates the
+       '/schema-mounts/mount-point' list with detailed information on
+       which data models are mounted at each mount point.
+
+       Note that the 'mount-point' statement does not define a new
+       data node.";
+  }
+
+  /*
+   * State data nodes
+   */
+
+  container schema-mounts {
+    config false;
+    description
+      "Contains information about the structure of the overall
+       mounted data model implemented in the server.";
+    list namespace {
+      key "prefix";
+      description
+        "This list provides a mapping of namespace prefixes that are
+         used in XPath expressions of 'parent-reference' leafs to the
+         corresponding namespace URI references.";
+      leaf prefix {
+        type yang:yang-identifier;
+        description
+          "Namespace prefix.";
+      }
+      leaf uri {
+        type inet:uri;
+        description
+          "Namespace URI reference.";
+      }
+    }
+    list mount-point {
+      key "module label";
+
+      description
+        "Each entry of this list specifies a schema for a particular
+         mount point.
+
+         Each mount point MUST be defined using the 'mount-point'
+         extension in one of the modules listed in the server's
+         YANG library instance with conformance type 'implement'.";
+      leaf module {
+        type yang:yang-identifier;
+        description
+          "Name of a module containing the mount point.";
+      }
+      leaf label {
+        type yang:yang-identifier;
+        description
+          "Label of the mount point defined using the 'mount-point'
+           extension.";
+      }
+      leaf config {
+        type boolean;
+        default "true";
+        description
+          "If this leaf is set to 'false', then all data nodes in the
+           mounted schema are read-only ('config false'), regardless
+           of their 'config' property.";
+      }
+      choice schema-ref {
+        mandatory true;
+        description
+          "Alternatives for specifying the schema.";
+        container inline {
+          presence
+            "A complete self-contained schema is mounted at the
+             mount point.";
+          description
+            "This node indicates that the server has mounted at least
+             the module 'ietf-yang-library' at the mount point, and
+             its instantiation provides the information about the
+             mounted schema.
+
+             Different instances of the mount point may have
+             different schemas mounted.";
+        }
+        container shared-schema {
+          presence
+            "The mounted schema together with the 'parent-reference'
+             make up the schema for this mount point.";
+
+          description
+            "This node indicates that the server has mounted at least
+             the module 'ietf-yang-library' at the mount point, and
+             its instantiation provides the information about the
+             mounted schema.  When XPath expressions in the mounted
+             schema are evaluated, the 'parent-reference' leaf-list
+             is taken into account.
+
+             Different instances of the mount point MUST have the
+             same schema mounted.";
+          leaf-list parent-reference {
+            type yang:xpath1.0;
+            description
+              "Entries of this leaf-list are XPath 1.0 expressions
+               that are evaluated in the following context:
+
+               - The context node is the node in the parent data tree
+                 where the mount-point is defined.
+
+               - The accessible tree is the parent data tree
+                 *without* any nodes defined in modules that are
+                 mounted inside the parent schema.
+
+               - The context position and context size are both equal
+                 to 1.
+
+               - The set of variable bindings is empty.
+
+               - The function library is the core function library
+                 defined in the W3C XPath 1.0 document
+                 (http://www.w3.org/TR/1999/REC-xpath-19991116) and
+                 the functions defined in Section 10 of RFC 7950.
+
+               - The set of namespace declarations is defined by the
+                 'namespace' list under 'schema-mounts'.
+
+               Each XPath expression MUST evaluate to a node-set
+               (possibly empty).  For the purposes of evaluating
+               XPath expressions whose context nodes are defined in
+               the mounted schema, the union of all these node-sets
+               together with ancestor nodes are added to the
+               accessible data tree.
+
+               Note that in the case 'ietf-yang-schema-mount' is
+               itself mounted, a 'parent-reference' in the mounted
+               module may refer to nodes that were brought into the
+               accessible tree through a 'parent-reference' in the
+               parent schema.";
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/yang/rfc8528-parser-support/src/test/resources/ietf-yang-types@2013-07-15.yang b/yang/rfc8528-parser-support/src/test/resources/ietf-yang-types@2013-07-15.yang
new file mode 100644 (file)
index 0000000..ee58fa3
--- /dev/null
@@ -0,0 +1,474 @@
+module ietf-yang-types {
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+  prefix "yang";
+
+  organization
+   "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+   "WG Web:   <http://tools.ietf.org/wg/netmod/>
+    WG List:  <mailto:netmod@ietf.org>
+
+    WG Chair: David Kessens
+              <mailto:david.kessens@nsn.com>
+
+    WG Chair: Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>
+
+    Editor:   Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>";
+
+  description
+   "This module contains a collection of generally useful derived
+    YANG data types.
+
+    Copyright (c) 2013 IETF Trust and the persons identified as
+    authors of the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or
+    without modification, is permitted pursuant to, and subject
+    to the license terms contained in, the Simplified BSD License
+    set forth in Section 4.c of the IETF Trust's Legal Provisions
+    Relating to IETF Documents
+    (http://trustee.ietf.org/license-info).
+
+    This version of this YANG module is part of RFC 6991; see
+    the RFC itself for full legal notices.";
+
+  revision 2013-07-15 {
+    description
+     "This revision adds the following new data types:
+      - yang-identifier
+      - hex-string
+      - uuid
+      - dotted-quad";
+    reference
+     "RFC 6991: Common YANG Data Types";
+  }
+
+  revision 2010-09-24 {
+    description
+     "Initial revision.";
+    reference
+     "RFC 6021: Common YANG Data Types";
+  }
+
+  /*** collection of counter and gauge types ***/
+
+  typedef counter32 {
+    type uint32;
+    description
+     "The counter32 type represents a non-negative integer
+      that monotonically increases until it reaches a
+      maximum value of 2^32-1 (4294967295 decimal), when it
+      wraps around and starts increasing again from zero.
+
+      Counters have no defined 'initial' value, and thus, a
+      single value of a counter has (in general) no information
+      content.  Discontinuities in the monotonically increasing
+      value normally occur at re-initialization of the
+      management system, and at other times as specified in the
+      description of a schema node using this type.  If such
+      other times can occur, for example, the creation of
+      a schema node of type counter32 at times other than
+      re-initialization, then a corresponding schema node
+      should be defined, with an appropriate type, to indicate
+      the last discontinuity.
+
+      The counter32 type should not be used for configuration
+      schema nodes.  A default statement SHOULD NOT be used in
+      combination with the type counter32.
+
+      In the value set and its semantics, this type is equivalent
+      to the Counter32 type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef zero-based-counter32 {
+    type yang:counter32;
+    default "0";
+    description
+     "The zero-based-counter32 type represents a counter32
+      that has the defined 'initial' value zero.
+
+      A schema node of this type will be set to zero (0) on creation
+      and will thereafter increase monotonically until it reaches
+      a maximum value of 2^32-1 (4294967295 decimal), when it
+      wraps around and starts increasing again from zero.
+
+      Provided that an application discovers a new schema node
+      of this type within the minimum time to wrap, it can use the
+      'initial' value as a delta.  It is important for a management
+      station to be aware of this minimum time and the actual time
+      between polls, and to discard data if the actual time is too
+      long or there is no defined minimum time.
+
+      In the value set and its semantics, this type is equivalent
+      to the ZeroBasedCounter32 textual convention of the SMIv2.";
+    reference
+      "RFC 4502: Remote Network Monitoring Management Information
+                 Base Version 2";
+  }
+
+  typedef counter64 {
+    type uint64;
+    description
+     "The counter64 type represents a non-negative integer
+      that monotonically increases until it reaches a
+      maximum value of 2^64-1 (18446744073709551615 decimal),
+      when it wraps around and starts increasing again from zero.
+
+      Counters have no defined 'initial' value, and thus, a
+      single value of a counter has (in general) no information
+      content.  Discontinuities in the monotonically increasing
+      value normally occur at re-initialization of the
+      management system, and at other times as specified in the
+      description of a schema node using this type.  If such
+      other times can occur, for example, the creation of
+      a schema node of type counter64 at times other than
+      re-initialization, then a corresponding schema node
+      should be defined, with an appropriate type, to indicate
+      the last discontinuity.
+
+      The counter64 type should not be used for configuration
+      schema nodes.  A default statement SHOULD NOT be used in
+      combination with the type counter64.
+
+      In the value set and its semantics, this type is equivalent
+      to the Counter64 type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef zero-based-counter64 {
+    type yang:counter64;
+    default "0";
+    description
+     "The zero-based-counter64 type represents a counter64 that
+      has the defined 'initial' value zero.
+
+      A schema node of this type will be set to zero (0) on creation
+      and will thereafter increase monotonically until it reaches
+      a maximum value of 2^64-1 (18446744073709551615 decimal),
+      when it wraps around and starts increasing again from zero.
+
+      Provided that an application discovers a new schema node
+      of this type within the minimum time to wrap, it can use the
+      'initial' value as a delta.  It is important for a management
+      station to be aware of this minimum time and the actual time
+      between polls, and to discard data if the actual time is too
+      long or there is no defined minimum time.
+
+      In the value set and its semantics, this type is equivalent
+      to the ZeroBasedCounter64 textual convention of the SMIv2.";
+    reference
+     "RFC 2856: Textual Conventions for Additional High Capacity
+                Data Types";
+  }
+
+  typedef gauge32 {
+    type uint32;
+    description
+     "The gauge32 type represents a non-negative integer, which
+      may increase or decrease, but shall never exceed a maximum
+      value, nor fall below a minimum value.  The maximum value
+      cannot be greater than 2^32-1 (4294967295 decimal), and
+      the minimum value cannot be smaller than 0.  The value of
+      a gauge32 has its maximum value whenever the information
+      being modeled is greater than or equal to its maximum
+      value, and has its minimum value whenever the information
+      being modeled is smaller than or equal to its minimum value.
+      If the information being modeled subsequently decreases
+      below (increases above) the maximum (minimum) value, the
+      gauge32 also decreases (increases).
+
+      In the value set and its semantics, this type is equivalent
+      to the Gauge32 type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef gauge64 {
+    type uint64;
+    description
+     "The gauge64 type represents a non-negative integer, which
+      may increase or decrease, but shall never exceed a maximum
+      value, nor fall below a minimum value.  The maximum value
+      cannot be greater than 2^64-1 (18446744073709551615), and
+      the minimum value cannot be smaller than 0.  The value of
+      a gauge64 has its maximum value whenever the information
+      being modeled is greater than or equal to its maximum
+      value, and has its minimum value whenever the information
+      being modeled is smaller than or equal to its minimum value.
+      If the information being modeled subsequently decreases
+      below (increases above) the maximum (minimum) value, the
+      gauge64 also decreases (increases).
+
+      In the value set and its semantics, this type is equivalent
+      to the CounterBasedGauge64 SMIv2 textual convention defined
+      in RFC 2856";
+    reference
+     "RFC 2856: Textual Conventions for Additional High Capacity
+                Data Types";
+  }
+
+  /*** collection of identifier-related types ***/
+
+  typedef object-identifier {
+    type string {
+      pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+            + '(\.(0|([1-9]\d*)))*';
+    }
+    description
+     "The object-identifier type represents administratively
+      assigned names in a registration-hierarchical-name tree.
+
+      Values of this type are denoted as a sequence of numerical
+      non-negative sub-identifier values.  Each sub-identifier
+      value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers
+      are separated by single dots and without any intermediate
+      whitespace.
+
+      The ASN.1 standard restricts the value space of the first
+      sub-identifier to 0, 1, or 2.  Furthermore, the value space
+      of the second sub-identifier is restricted to the range
+      0 to 39 if the first sub-identifier is 0 or 1.  Finally,
+      the ASN.1 standard requires that an object identifier
+      has always at least two sub-identifiers.  The pattern
+      captures these restrictions.
+
+      Although the number of sub-identifiers is not limited,
+      module designers should realize that there may be
+      implementations that stick with the SMIv2 limit of 128
+      sub-identifiers.
+
+      This type is a superset of the SMIv2 OBJECT IDENTIFIER type
+      since it is not restricted to 128 sub-identifiers.  Hence,
+      this type SHOULD NOT be used to represent the SMIv2 OBJECT
+      IDENTIFIER type; the object-identifier-128 type SHOULD be
+      used instead.";
+    reference
+     "ISO9834-1: Information technology -- Open Systems
+      Interconnection -- Procedures for the operation of OSI
+      Registration Authorities: General procedures and top
+      arcs of the ASN.1 Object Identifier tree";
+  }
+
+  typedef object-identifier-128 {
+    type object-identifier {
+      pattern '\d*(\.\d*){1,127}';
+    }
+    description
+     "This type represents object-identifiers restricted to 128
+      sub-identifiers.
+
+      In the value set and its semantics, this type is equivalent
+      to the OBJECT IDENTIFIER type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef yang-identifier {
+    type string {
+      length "1..max";
+      pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
+      pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*';
+    }
+    description
+      "A YANG identifier string as defined by the 'identifier'
+       rule in Section 12 of RFC 6020.  An identifier must
+       start with an alphabetic character or an underscore
+       followed by an arbitrary sequence of alphabetic or
+       numeric characters, underscores, hyphens, or dots.
+
+       A YANG identifier MUST NOT start with any possible
+       combination of the lowercase or uppercase character
+       sequence 'xml'.";
+    reference
+      "RFC 6020: YANG - A Data Modeling Language for the Network
+                 Configuration Protocol (NETCONF)";
+  }
+
+  /*** collection of types related to date and time***/
+
+  typedef date-and-time {
+    type string {
+      pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+            + '(Z|[\+\-]\d{2}:\d{2})';
+    }
+    description
+     "The date-and-time type is a profile of the ISO 8601
+      standard for representation of dates and times using the
+      Gregorian calendar.  The profile is defined by the
+      date-time production in Section 5.6 of RFC 3339.
+
+      The date-and-time type is compatible with the dateTime XML
+      schema type with the following notable exceptions:
+
+      (a) The date-and-time type does not allow negative years.
+
+      (b) The date-and-time time-offset -00:00 indicates an unknown
+          time zone (see RFC 3339) while -00:00 and +00:00 and Z
+          all represent the same time zone in dateTime.
+
+      (c) The canonical format (see below) of data-and-time values
+          differs from the canonical format used by the dateTime XML
+          schema type, which requires all times to be in UTC using
+          the time-offset 'Z'.
+
+      This type is not equivalent to the DateAndTime textual
+      convention of the SMIv2 since RFC 3339 uses a different
+      separator between full-date and full-time and provides
+      higher resolution of time-secfrac.
+
+      The canonical format for date-and-time values with a known time
+      zone uses a numeric time zone offset that is calculated using
+      the device's configured known offset to UTC time.  A change of
+      the device's offset to UTC time will cause date-and-time values
+      to change accordingly.  Such changes might happen periodically
+      in case a server follows automatically daylight saving time
+      (DST) time zone offset changes.  The canonical format for
+      date-and-time values with an unknown time zone (usually
+      referring to the notion of local time) uses the time-offset
+      -00:00.";
+    reference
+     "RFC 3339: Date and Time on the Internet: Timestamps
+      RFC 2579: Textual Conventions for SMIv2
+      XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
+  }
+
+  typedef timeticks {
+    type uint32;
+    description
+     "The timeticks type represents a non-negative integer that
+      represents the time, modulo 2^32 (4294967296 decimal), in
+      hundredths of a second between two epochs.  When a schema
+      node is defined that uses this type, the description of
+      the schema node identifies both of the reference epochs.
+
+      In the value set and its semantics, this type is equivalent
+      to the TimeTicks type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef timestamp {
+    type yang:timeticks;
+    description
+     "The timestamp type represents the value of an associated
+      timeticks schema node at which a specific occurrence
+      happened.  The specific occurrence must be defined in the
+      description of any schema node defined using this type.  When
+      the specific occurrence occurred prior to the last time the
+      associated timeticks attribute was zero, then the timestamp
+      value is zero.  Note that this requires all timestamp values
+      to be reset to zero when the value of the associated timeticks
+      attribute reaches 497+ days and wraps around to zero.
+
+      The associated timeticks schema node must be specified
+      in the description of any schema node using this type.
+
+      In the value set and its semantics, this type is equivalent
+      to the TimeStamp textual convention of the SMIv2.";
+    reference
+     "RFC 2579: Textual Conventions for SMIv2";
+  }
+
+  /*** collection of generic address types ***/
+
+  typedef phys-address {
+    type string {
+      pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+    }
+
+    description
+     "Represents media- or physical-level addresses represented
+      as a sequence octets, each octet represented by two hexadecimal
+      numbers.  Octets are separated by colons.  The canonical
+      representation uses lowercase characters.
+
+      In the value set and its semantics, this type is equivalent
+      to the PhysAddress textual convention of the SMIv2.";
+    reference
+     "RFC 2579: Textual Conventions for SMIv2";
+  }
+
+  typedef mac-address {
+    type string {
+      pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+    }
+    description
+     "The mac-address type represents an IEEE 802 MAC address.
+      The canonical representation uses lowercase characters.
+
+      In the value set and its semantics, this type is equivalent
+      to the MacAddress textual convention of the SMIv2.";
+    reference
+     "IEEE 802: IEEE Standard for Local and Metropolitan Area
+                Networks: Overview and Architecture
+      RFC 2579: Textual Conventions for SMIv2";
+  }
+
+  /*** collection of XML-specific types ***/
+
+  typedef xpath1.0 {
+    type string;
+    description
+     "This type represents an XPATH 1.0 expression.
+
+      When a schema node is defined that uses this type, the
+      description of the schema node MUST specify the XPath
+      context in which the XPath expression is evaluated.";
+    reference
+     "XPATH: XML Path Language (XPath) Version 1.0";
+  }
+
+  /*** collection of string types ***/
+
+  typedef hex-string {
+    type string {
+      pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+    }
+    description
+     "A hexadecimal string with octets represented as hex digits
+      separated by colons.  The canonical representation uses
+      lowercase characters.";
+  }
+
+  typedef uuid {
+    type string {
+      pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-'
+            + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
+    }
+    description
+     "A Universally Unique IDentifier in the string representation
+      defined in RFC 4122.  The canonical representation uses
+      lowercase characters.
+
+      The following is an example of a UUID in string representation:
+      f81d4fae-7dec-11d0-a765-00a0c91e6bf6
+      ";
+    reference
+     "RFC 4122: A Universally Unique IDentifier (UUID) URN
+                Namespace";
+  }
+
+  typedef dotted-quad {
+    type string {
+      pattern
+        '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+      + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
+    }
+    description
+      "An unsigned 32-bit number expressed in the dotted-quad
+       notation, i.e., four octets written as decimal numbers
+       and separated with the '.' (full stop) character.";
+  }
+}
diff --git a/yang/yang-common/pom.xml b/yang/yang-common/pom.xml
new file mode 100644 (file)
index 0000000..24cfb66
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-common</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>ODL :: YANG Tools :: ${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/yang/yang-common/src/main/java/module-info.java b/yang/yang-common/src/main/java/module-info.java
new file mode 100644 (file)
index 0000000..4dc30b9
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+ */
+import org.opendaylight.yangtools.yang.common.CanonicalValueSupport;
+import org.opendaylight.yangtools.yang.common.Decimal64;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.common.Uint8;
+
+module org.opendaylight.yangtools.yang.common {
+    exports org.opendaylight.yangtools.yang.common;
+
+    provides CanonicalValueSupport with
+        Decimal64.Support,
+        Uint8.Support,
+        Uint16.Support,
+        Uint32.Support,
+        Uint64.Support;
+
+    requires transitive org.opendaylight.yangtools.concepts;
+    requires org.slf4j;
+
+    // Annotations
+    requires static com.github.spotbugs.annotations;
+    requires static org.eclipse.jdt.annotation;
+    requires static org.checkerframework.checker.qual;
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueImplementationValidator.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueImplementationValidator.java
new file mode 100644 (file)
index 0000000..9b3b2b4
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.common;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NonNullByDefault
+abstract class AbstractCanonicalValueImplementationValidator extends ClassValue<Boolean> {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractCanonicalValueImplementationValidator.class);
+
+    @Override
+    protected final Boolean computeValue(final @Nullable Class<?> type) {
+        // Every DerivedString representation class must:
+        checkArgument(CanonicalValue.class.isAssignableFrom(type), "%s is not a DerivedString", type);
+
+        // be non-final and public
+        final int modifiers = type.getModifiers();
+        checkArgument(Modifier.isPublic(modifiers), "%s must be public", type);
+        checkArgument(!Modifier.isFinal(modifiers), "%s must not be final", type);
+
+        // have at least one public or protected constructor (for subclasses)
+        checkArgument(Arrays.stream(type.getDeclaredConstructors()).mapToInt(Constructor::getModifiers)
+            .anyMatch(mod -> Modifier.isProtected(mod) || Modifier.isPublic(mod)),
+            "%s must declare at least one protected or public constructor", type);
+
+        try {
+            // have a non-final non-abstract validator() method
+            final int validator;
+            try {
+                validator = type.getMethod("validator").getModifiers();
+            } catch (NoSuchMethodException e) {
+                throw new IllegalArgumentException(type + " must have a non-abstract non-final validator() method",
+                    e);
+            }
+            checkArgument(!Modifier.isFinal(validator), "%s must not have final validator()", type);
+
+            // have final toCanonicalString(), support(), hashCode() and equals(Object), compare(T) methods
+            checkFinalMethod(type, "toCanonicalString");
+            checkFinalMethod(type, "support");
+            checkFinalMethod(type, "hashCode");
+            checkFinalMethod(type, "equals", Object.class);
+        } catch (SecurityException e) {
+            LOG.warn("Cannot completely validate {}", type, e);
+            return Boolean.FALSE;
+        }
+
+        return Boolean.TRUE;
+    }
+
+    abstract void checkCompareTo(Class<?> type);
+
+    static void checkFinalMethod(final Class<?> type, final String name) {
+        try {
+            checkFinalMethod(type.getMethod(name).getModifiers(), type, name, "");
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException(type + " must have a final " + name + "() method", e);
+        }
+    }
+
+    static void checkFinalMethod(final Class<?> type, final String name, final Class<?> arg) {
+        final String argName = arg.getSimpleName();
+        try {
+            checkFinalMethod(type.getMethod(name, arg).getModifiers(), type, name, argName);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException(type + " must have a final " + name + "(" + argName + ") method", e);
+        }
+    }
+
+    private static void checkFinalMethod(final int modifiers, final Class<?> type, final String name,
+            final String args) {
+        checkArgument(Modifier.isFinal(modifiers), "%s must have a final %s(%s) method", type, name, args);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueSupport.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueSupport.java
new file mode 100644 (file)
index 0000000..0f85090
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.common;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import java.lang.reflect.Modifier;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Variant;
+
+/**
+ * Base implementation of {@link CanonicalValueSupport}. This class should be used as superclass to all implementations
+ * of {@link CanonicalValueSupport}, as doing so provides a simpler base and enforces some aspects of the subclass.
+ *
+ * @param <T> canonical value type
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public abstract class AbstractCanonicalValueSupport<T extends CanonicalValue<T>> implements CanonicalValueSupport<T> {
+    private static final ClassValue<Boolean> SUPPORTS = new ClassValue<Boolean>() {
+        @Override
+        protected Boolean computeValue(final @Nullable Class<?> type) {
+            // Every DerivedStringSupport representation class must:
+            checkArgument(CanonicalValueSupport.class.isAssignableFrom(type), "%s is not a CanonicalValueSupport",
+                type);
+
+            // be final
+            final int modifiers = type.getModifiers();
+            checkArgument(Modifier.isFinal(modifiers), "%s must be final", type);
+
+            return Boolean.TRUE;
+        }
+    };
+    private static final ClassValue<Boolean> VALUES = new AbstractCanonicalValueImplementationValidator() {
+        @Override
+        void checkCompareTo(@NonNull final Class<?> type) {
+            checkFinalMethod(type, "compareTo", type);
+        }
+    };
+
+    private final Class<T> representationClass;
+
+    protected AbstractCanonicalValueSupport(final Class<T> representationClass) {
+        VALUES.get(representationClass);
+        this.representationClass = representationClass;
+        SUPPORTS.get(getClass());
+    }
+
+    @Override
+    public final Class<T> getRepresentationClass() {
+        return representationClass;
+    }
+
+    @Override
+    public final Class<T> getValidatedRepresentationClass() {
+        return representationClass;
+    }
+
+    @Override
+    public final Variant<T, CanonicalValueViolation> validateRepresentation(final T value) {
+        return Variant.ofFirst(value);
+    }
+
+    @Override
+    public final Variant<T, CanonicalValueViolation> validateRepresentation(final T value,
+            final String canonicalString) {
+        return Variant.ofFirst(value);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueValidator.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueValidator.java
new file mode 100644 (file)
index 0000000..2d6c247
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Variant;
+
+/**
+ * Abstract base class for implementing validators.
+ *
+ * @param <T> string representation class
+ * @param <V> validated string representation class
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public abstract class AbstractCanonicalValueValidator<T extends DerivedString<T>, V extends T>
+        implements CanonicalValueValidator<T, V> {
+    private static final ClassValue<Boolean> IMPLEMENTATIONS = new AbstractCanonicalValueImplementationValidator() {
+        @Override
+        void checkCompareTo(final Class<?> type) {
+            // Intentional no-op, as we'd need a type capture of the representation
+        }
+    };
+
+    private final CanonicalValueSupport<T> representationSupport;
+    private final Class<V> validatedClass;
+
+    protected AbstractCanonicalValueValidator(final CanonicalValueSupport<T> representationSupport,
+            final Class<V> validatedClass) {
+        this.representationSupport = requireNonNull(representationSupport);
+        IMPLEMENTATIONS.get(validatedClass);
+        this.validatedClass = validatedClass;
+    }
+
+    @Override
+    public final Class<T> getRepresentationClass() {
+        return representationSupport.getRepresentationClass();
+    }
+
+    @Override
+    public final Class<V> getValidatedRepresentationClass() {
+        return validatedClass;
+    }
+
+    @Override
+    public final Variant<T, CanonicalValueViolation> validateRepresentation(final T value) {
+        return validatedClass.isAssignableFrom(value.validator().getValidatedRepresentationClass())
+                ? Variant.ofFirst(validatedClass.cast(value)) : validate(value);
+    }
+
+    @Override
+    public final Variant<T, CanonicalValueViolation> validateRepresentation(final T value,
+            final String canonicalString) {
+        return validatedClass.isAssignableFrom(value.validator().getValidatedRepresentationClass())
+                ? Variant.ofFirst(validatedClass.cast(value)) : validate(value, requireNonNull(canonicalString));
+    }
+
+    /**
+     * Validate a {@link DerivedString} representation. Subclasses should override this method if they can
+     * provide a validation algorithm which does not rely on canonical strings but works on representation state only.
+     *
+     * @param value Representation value
+     * @return Validated representation or CanonicalValueViolation
+     * @throws NullPointerException if {@code value} is null
+     */
+    protected Variant<T, CanonicalValueViolation> validate(final T value) {
+        return validate(value, value.toCanonicalString());
+    }
+
+    /**
+     * Validate a {@link DerivedString} representation. Subclasses can chose whether they operate on representation
+     * state or canonical string -- both are considered equivalent.
+     *
+     * @param value Representation value
+     * @param canonicalString Canonical string matching the representation value
+     * @return Validated representation
+     * @throws NullPointerException if {@code value} or {@code canonicalString} is null.
+     */
+    protected abstract Variant<T, CanonicalValueViolation> validate(T value, String canonicalString);
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractQName.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractQName.java
new file mode 100644 (file)
index 0000000..da1521b
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.common;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.CharMatcher;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Identifier;
+import org.opendaylight.yangtools.concepts.WritableObject;
+
+/**
+ * Abstract superclass for sharing QName references, which can either be resolved {@link QName}s or unresolved
+ * {@link UnqualifiedQName} and {@link QualifiedQName}s.
+ */
+@Beta
+@NonNullByDefault
+public abstract class AbstractQName implements Identifier, WritableObject {
+    private static final long serialVersionUID = 1L;
+    private static final CharMatcher IDENTIFIER_START =
+            CharMatcher.inRange('A', 'Z').or(CharMatcher.inRange('a', 'z').or(CharMatcher.is('_'))).precomputed();
+    private static final CharMatcher NOT_IDENTIFIER_PART =
+            IDENTIFIER_START.or(CharMatcher.inRange('0', '9')).or(CharMatcher.anyOf("-.")).negate().precomputed();
+
+    private final String localName;
+
+    AbstractQName(final String localName) {
+        this.localName = requireNonNull(localName);
+    }
+
+    /**
+     * Returns YANG schema identifier which were defined for this node in the YANG module.
+     *
+     * @return YANG schema identifier which were defined for this node in the YANG module
+     */
+    public final String getLocalName() {
+        return localName;
+    }
+
+    /**
+     * Return an interned reference to an equivalent object.
+     *
+     * @return Interned reference, or this object if it was interned.
+     */
+    public abstract AbstractQName intern();
+
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals(@Nullable Object obj);
+
+    @Override
+    public abstract String toString();
+
+    abstract Object writeReplace();
+
+    static final String checkLocalName(final @Nullable String localName) {
+        checkArgument(localName != null, "Parameter 'localName' may not be null.");
+        checkArgument(!localName.isEmpty(), "Parameter 'localName' must be a non-empty string.");
+        checkArgument(IDENTIFIER_START.matches(localName.charAt(0)) && NOT_IDENTIFIER_PART.indexIn(localName, 1) == -1,
+                "String '%s' is not a valid identifier", localName);
+        return localName;
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/BiMapYangNamespaceContext.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/BiMapYangNamespaceContext.java
new file mode 100644 (file)
index 0000000..69f4414
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableBiMap.Builder;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.opendaylight.yangtools.concepts.WritableObject;
+
+/**
+ * A BiMap-based implementation of {@link YangNamespaceContext}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class BiMapYangNamespaceContext implements YangNamespaceContext, WritableObject {
+    private static final long serialVersionUID = 1L;
+
+    private final ImmutableBiMap<String, QNameModule> mapping;
+
+    public BiMapYangNamespaceContext(final BiMap<String, QNameModule> mapping) {
+        this.mapping = ImmutableBiMap.copyOf(mapping);
+    }
+
+    @Override
+    public Optional<QNameModule> findNamespaceForPrefix(final String prefix) {
+        return Optional.ofNullable(mapping.get(requireNonNull(prefix)));
+    }
+
+    @Override
+    public Optional<String> findPrefixForNamespace(final QNameModule namespace) {
+        return Optional.ofNullable(mapping.inverse().get(requireNonNull(namespace)));
+    }
+
+    @Override
+    public void writeTo(final DataOutput out) throws IOException {
+        out.writeInt(mapping.size());
+        for (Entry<String, QNameModule> entry : mapping.entrySet()) {
+            out.writeUTF(entry.getKey());
+            entry.getValue().writeTo(out);
+        }
+    }
+
+    public static BiMapYangNamespaceContext readFrom(final DataInput in) throws IOException {
+        final int size = in.readInt();
+        final Builder<String, QNameModule> builder = ImmutableBiMap.builder();
+        for (int i = 0; i < size; ++i) {
+            final String prefix = in.readUTF();
+            final QNameModule namespace = QNameModule.readFrom(in);
+            builder.put(prefix, namespace);
+        }
+
+        return new BiMapYangNamespaceContext(builder.build());
+    }
+
+    @Override
+    public int hashCode() {
+        return mapping.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return this == obj || obj instanceof BiMapYangNamespaceContext
+                && mapping.equals(((BiMapYangNamespaceContext) obj).mapping);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("mapping", mapping).toString();
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CachingDerivedString.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CachingDerivedString.java
new file mode 100644 (file)
index 0000000..4725196
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.common;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * A opportunistically-caching {@link DerivedString}. Canonical name is cached at first encounter.
+ *
+ * @param <T> derived string type
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public abstract class CachingDerivedString<T extends CachingDerivedString<T>> extends DerivedString<T> {
+    private static final long serialVersionUID = 1L;
+
+    private transient volatile @Nullable String str;
+
+    @SuppressFBWarnings("NP_STORE_INTO_NONNULL_FIELD")
+    protected CachingDerivedString() {
+        this.str = null;
+    }
+
+    protected CachingDerivedString(final String str) {
+        this.str = requireNonNull(str);
+    }
+
+    @Override
+    public final String toCanonicalString() {
+        String local;
+        return (local = this.str) != null ? local : (str = computeCanonicalString());
+    }
+
+    /**
+     * Return the canonical string representation of this object's value.
+     *
+     * @return Canonical string
+     */
+    protected abstract String computeCanonicalString();
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValue.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValue.java
new file mode 100644 (file)
index 0000000..3e8b470
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.common;
+
+import com.google.common.annotations.Beta;
+import java.io.Serializable;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * A typed value in its internal Java representation. Implementations of this interface are required to:
+ * <ul>
+ * <li>be immutable</li>
+ * <li>be {@link Serializable}</li>
+ * <li>accurately define total ordering of values</li>
+ * </ul>
+ *
+ * <p>
+ * Aside from providing the ability to hold a canonical value, this interface and its implementations support carrying
+ * additional information about how the value has been validated -- allowing efficient interchange of already-validated
+ * values between users. {@link #validator()} provides the link to a {@link CanonicalValueValidator} which has declared
+ * the value conform to it. Users can query the validator to establish whether the value needs to be further validated
+ * to conform to their requirement.
+ *
+ * @param <T> Canonical value type
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public interface CanonicalValue<T extends CanonicalValue<T>> extends Comparable<T>, Immutable, Serializable {
+    /**
+     * Return the canonical string representation of this value.
+     *
+     * @return Canonical string
+     */
+    String toCanonicalString();
+
+    /**
+     * Return the {@link CanonicalValue} associated with this type. It can be used to create new instances of this
+     * representation.
+     *
+     * @return A {@link CanonicalValue} instance.
+     */
+    CanonicalValueSupport<T> support();
+
+    /**
+     * Return a {@link CanonicalValueValidator} associated with this value's validated type.
+     *
+     * @return A {@link CanonicalValueValidator} instance.
+     */
+    default CanonicalValueValidator<T, ? extends T> validator() {
+        return support();
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueSupport.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueSupport.java
new file mode 100644 (file)
index 0000000..afa0a3d
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Variant;
+
+/**
+ * Support for a {@link CanonicalValue} subclasses. An implementation of this interface must be registered
+ * in the system and be available from each CanonicalValue object.
+ *
+ * <p>
+ * Note: never implement this interface directly, subclass {@link AbstractCanonicalValueSupport} instead.
+ *
+ * <p>
+ * This interface allows a {@link CanonicalValue} to be instantiated from a String. The implementation is expected
+ * to perform all checks implied by the corresponding YANG data model.
+ *
+ * @param <T> canonical value type
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public interface CanonicalValueSupport<T extends CanonicalValue<T>> extends CanonicalValueValidator<T, T> {
+    /**
+     * Create a instance for a string representation. Implementations of this method are required to perform checks
+     * equivalent to the YANG data model restrictions attached to the corresponding YANG type. Non-canonical format
+     * strings must be accepted and result in objects equal to objects obtained from the corresponding canonical format.
+     *
+     * @param str String representation
+     * @return A {@link CanonicalValue} instance or CanonicalValueViolation if {@code str} does not conform
+     * @throws NullPointerException if {@code str} is null
+     */
+    Variant<T, CanonicalValueViolation> fromString(String str);
+
+    /**
+     * Create a instance for the canonical string representation. Implementations of this method may perform
+     * optimizations based on the assumption the string is canonical, but should still report errors when a mismatch
+     * is detected.
+     *
+     * @param str String representation
+     * @return A {@link CanonicalValue} instance or CanonicalValueViolation if {@code str} does not conform
+     * @throws NullPointerException if {@code str} is null
+     */
+    default Variant<T, CanonicalValueViolation> fromCanonicalString(final String str) {
+        return fromString(requireNonNull(str));
+    }
+
+    /**
+     * Unsafe cast to a factory type.
+     *
+     * @return This instance cast to specified type
+     */
+    @SuppressWarnings("unchecked")
+    default <X extends CanonicalValue<X>> CanonicalValueSupport<X> unsafe() {
+        return (CanonicalValueSupport<X>) this;
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueValidator.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueValidator.java
new file mode 100644 (file)
index 0000000..9865923
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.common;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Variant;
+
+/**
+ * {@link CanonicalValue} validator interface. Implementations of this interface can perform further validation of
+ * representation state such that it conforms to a YANG type derived from a type with a {@link CanonicalValue}
+ * representation.
+ *
+ * <p>
+ * Note: this interface should not be directly implemented. Use {@link AbstractCanonicalValueValidator} instead.
+ *
+ * @param <T> canonical value type
+ * @param <V> validated canonical value type
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public interface CanonicalValueValidator<T extends CanonicalValue<T>, V extends T> extends Immutable {
+    /**
+     * Returns the instantiated representation class. The representation class is a {@link CanonicalValue} which
+     * understands the semantics of modeled data and has some internal representation of it. All {@link CanonicalValue}s
+     * which share the same representation class are considered equal if their internal state would result in the
+     * same canonical string representation as defined by the YANG data model.
+     *
+     * @return Representation {@link CanonicalValue} class.
+     */
+    Class<T> getRepresentationClass();
+
+    /**
+     * Return the class which captures the fact it was validated by this validator.
+     *
+     * @return Validated capture of the representation class.
+     */
+    Class<V> getValidatedRepresentationClass();
+
+    /**
+     * Validate a {@link CanonicalValue} representation. Implementations should override this method if they can
+     * provide a validation algorithm which does not rely on canonical strings but works on representation state only.
+     *
+     * @param value Representation value
+     * @return Validated representation or a {@link CanonicalValueViolation}
+     * @throws NullPointerException if {@code value} is null
+     */
+    default Variant<T, CanonicalValueViolation> validateRepresentation(final T value) {
+        return validateRepresentation(value, value.toCanonicalString());
+    }
+
+    /**
+     * Validate a {@link CanonicalValue} representation. Implementations can chose whether they operate on
+     * representation state or canonical string -- both are considered equivalent. Users should call this method if they
+     * have a representation readily available.
+     *
+     * @param value Representation value
+     * @param canonicalString Canonical string matching the representation value
+     * @return Validated representation or a {@link CanonicalValueViolation}
+     * @throws NullPointerException if {@code value} or {@code canonicalString} is null.
+     */
+    Variant<T, CanonicalValueViolation> validateRepresentation(T value, String canonicalString);
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueViolation.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueViolation.java
new file mode 100644 (file)
index 0000000..75be2d4
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.Serializable;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Variant;
+
+/**
+ * A violation of a {@link CanonicalValue} validation. Contains details as mandated by RFC7950 Section 8.3.1.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public abstract class CanonicalValueViolation implements Immutable, Serializable {
+    public static class Regular extends CanonicalValueViolation {
+        private static final long serialVersionUID = 1L;
+
+        private final @Nullable String appTag;
+        private final @Nullable String message;
+
+        Regular(final @Nullable String appTag, final @Nullable String message) {
+            this.appTag = appTag;
+            this.message = message;
+        }
+
+        @Override
+        @Nullable String appTag() {
+            return appTag;
+        }
+
+        @Override
+        @Nullable String message() {
+            return message;
+        }
+
+        @Override
+        ToStringHelper addToString(final ToStringHelper helper) {
+            return helper.omitNullValues().add("app-tag", appTag).add("message", message);
+        }
+    }
+
+    @SuppressFBWarnings("NM_CLASS_NOT_EXCEPTION")
+    public static class WithException extends CanonicalValueViolation {
+        private static final long serialVersionUID = 1L;
+
+        private final Exception cause;
+
+        WithException(final Exception cause) {
+            this.cause = requireNonNull(cause);
+        }
+
+        @Override
+        @SuppressFBWarnings("NP_NONNULL_RETURN_VIOLATION")
+        @Nullable String appTag() {
+            return null;
+        }
+
+        @Override
+        @Nullable String message() {
+            return cause.getMessage();
+        }
+
+        public final Exception getCause() {
+            return cause;
+        }
+
+        @Override
+        ToStringHelper addToString(final ToStringHelper helper) {
+            return helper.add("cause", cause);
+        }
+    }
+
+    private static final CanonicalValueViolation EMPTY = new Regular(null, null);
+    private static final Variant<?, CanonicalValueViolation> EMPTY_VARIANT = Variant.ofSecond(EMPTY);
+    private static final long serialVersionUID = 1L;
+
+    public static CanonicalValueViolation empty() {
+        return EMPTY;
+    }
+
+    public static CanonicalValueViolation of(final Exception cause) {
+        return new WithException(cause);
+    }
+
+    public static CanonicalValueViolation of(final @Nullable String appTag, final @Nullable String message) {
+        return appTag == null && message == null ? EMPTY : new Regular(appTag, message);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> Variant<T, CanonicalValueViolation> emptyVariant() {
+        return (Variant<T, CanonicalValueViolation>) EMPTY_VARIANT;
+    }
+
+    public static <T> Variant<T, CanonicalValueViolation> variantOf(final Exception cause) {
+        return Variant.ofSecond(CanonicalValueViolation.of(cause));
+    }
+
+    public static <T> Variant<T, CanonicalValueViolation> variantOf(final String message) {
+        return variantOf(null, message);
+    }
+
+    public static <T> Variant<T, CanonicalValueViolation> variantOf(final @Nullable String appTag,
+            final String message) {
+        return Variant.ofSecond(CanonicalValueViolation.of(appTag, message));
+    }
+
+    public final Optional<String> getAppTag() {
+        return nullableString(appTag());
+    }
+
+    public final Optional<String> getMessage() {
+        return nullableString(appTag());
+    }
+
+    abstract @Nullable String appTag();
+
+    abstract @Nullable String message();
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(appTag(), message());
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof CanonicalValueViolation)) {
+            return false;
+        }
+        final CanonicalValueViolation other = (CanonicalValueViolation) obj;
+        return Objects.equals(appTag(), other.appTag()) && Objects.equals(message(), other.message());
+    }
+
+    @Override
+    public final String toString() {
+        return addToString(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    abstract ToStringHelper addToString(ToStringHelper helper);
+
+    private static Optional<String> nullableString(@Nullable final String str) {
+        return str != null ? Optional.of(str) : Optional.empty();
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Decimal64.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Decimal64.java
new file mode 100644 (file)
index 0000000..78354f3
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.common;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
+import java.math.BigDecimal;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Variant;
+
+/**
+ * Dedicated type for YANG's 'type decimal64' type. This class is similar to {@link BigDecimal}, but provides more
+ * efficient storage, as it has fixed precision.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public class Decimal64 extends Number implements CanonicalValue<Decimal64> {
+    public static final class Support extends AbstractCanonicalValueSupport<Decimal64> {
+        public Support() {
+            super(Decimal64.class);
+        }
+
+        @Override
+        public Variant<Decimal64, CanonicalValueViolation> fromString(final String str) {
+            // https://tools.ietf.org/html/rfc6020#section-9.3.1
+            //
+            // A decimal64 value is lexically represented as an optional sign ("+"
+            // or "-"), followed by a sequence of decimal digits, optionally
+            // followed by a period ('.') as a decimal indicator and a sequence of
+            // decimal digits.  If no sign is specified, "+" is assumed.
+            if (str.isEmpty()) {
+                return CanonicalValueViolation.variantOf("Empty string is not a valid decimal64 representation");
+            }
+
+            // Deal with optional sign
+            final boolean negative;
+            int idx;
+            switch (str.charAt(0)) {
+                case '-':
+                    negative = true;
+                    idx = 1;
+                    break;
+                case '+':
+                    negative = false;
+                    idx = 1;
+                    break;
+                default:
+                    negative = false;
+                    idx = 0;
+            }
+
+            // Sanity check length
+            if (idx == str.length()) {
+                return CanonicalValueViolation.variantOf("Missing digits after sign");
+            }
+
+            // Character limit, used for caching and cutting trailing zeroes
+            int limit = str.length() - 1;
+
+            // Skip any leading zeroes, but leave at least one
+            for (; idx < limit && str.charAt(idx) == '0'; idx++) {
+                final char ch = str.charAt(idx + 1);
+                if (ch < '0' || ch > '9') {
+                    break;
+                }
+            }
+
+            // Integer part and its length
+            int intLen = 0;
+            long intPart = 0;
+
+            for (; idx <= limit; idx++, intLen++) {
+                final char ch = str.charAt(idx);
+                if (ch == '.') {
+                    // Fractions are next
+                    break;
+                }
+                if (intLen == MAX_FRACTION_DIGITS) {
+                    return CanonicalValueViolation.variantOf(
+                        "Integer part is longer than " + MAX_FRACTION_DIGITS + " digits");
+                }
+
+                intPart = 10 * intPart + toInt(ch, idx);
+            }
+
+            if (idx > limit) {
+                // No fraction digits, we are done
+                return Variant.ofFirst(new Decimal64((byte)1, intPart, 0, negative));
+            }
+
+            // Bump index to skip over period and check the remainder
+            idx++;
+            if (idx > limit) {
+                return CanonicalValueViolation.variantOf("Value '" + str + "' is missing fraction digits");
+            }
+
+            // Trim trailing zeroes, if any
+            while (idx < limit && str.charAt(limit) == '0') {
+                limit--;
+            }
+
+            final int fracLimit = MAX_FRACTION_DIGITS - intLen;
+            byte fracLen = 0;
+            long fracPart = 0;
+            for (; idx <= limit; idx++, fracLen++) {
+                final char ch = str.charAt(idx);
+                if (fracLen == fracLimit) {
+                    return CanonicalValueViolation.variantOf("Fraction part longer than " + fracLimit + " digits");
+                }
+
+                fracPart = 10 * fracPart + toInt(ch, idx);
+            }
+
+            return Variant.ofFirst(new Decimal64(fracLen, intPart, fracPart, negative));
+        }
+
+        private static int toInt(final char ch, final int index) {
+            if (ch < '0' || ch > '9') {
+                throw new NumberFormatException("Illegal character at offset " + index);
+            }
+            return ch - '0';
+        }
+    }
+
+    private static final CanonicalValueSupport<Decimal64> SUPPORT = new Support();
+    private static final long serialVersionUID = 1L;
+
+    private static final int MAX_FRACTION_DIGITS = 18;
+
+    private static final long[] SCALE = {
+        10,
+        100,
+        1000,
+        10000,
+        100000,
+        1000000,
+        10000000,
+        100000000,
+        1000000000,
+        10000000000L,
+        100000000000L,
+        1000000000000L,
+        10000000000000L,
+        100000000000000L,
+        1000000000000000L,
+        10000000000000000L,
+        100000000000000000L,
+        1000000000000000000L
+    };
+
+    static {
+        verify(SCALE.length == MAX_FRACTION_DIGITS);
+    }
+
+    private final byte scaleOffset;
+    private final long value;
+
+    @VisibleForTesting
+    Decimal64(final int fractionDigits, final long intPart, final long fracPart, final boolean negative) {
+        checkArgument(fractionDigits >= 1 && fractionDigits <= MAX_FRACTION_DIGITS);
+        this.scaleOffset = (byte) (fractionDigits - 1);
+
+        final long bits = intPart * SCALE[this.scaleOffset] + fracPart;
+        this.value = negative ? -bits : bits;
+    }
+
+    protected Decimal64(final Decimal64 other) {
+        this.scaleOffset = other.scaleOffset;
+        this.value = other.value;
+    }
+
+    public static Decimal64 valueOf(final byte byteVal) {
+        return byteVal < 0 ? new Decimal64(1, -byteVal, 0, true) : new Decimal64(1, byteVal, 0, false);
+    }
+
+    public static Decimal64 valueOf(final short shortVal) {
+        return shortVal < 0 ? new Decimal64(1, -shortVal, 0, true) : new Decimal64(1, shortVal, 0, false);
+    }
+
+    public static Decimal64 valueOf(final int intVal) {
+        return intVal < 0 ? new Decimal64(1, - (long)intVal, 0, true) : new Decimal64(1, intVal, 0, false);
+    }
+
+    public static Decimal64 valueOf(final long longVal) {
+        // XXX: we should be able to do something smarter here
+        return valueOf(Long.toString(longVal));
+    }
+
+    public static Decimal64 valueOf(final double doubleVal) {
+        // XXX: we should be able to do something smarter here
+        return valueOf(Double.toString(doubleVal));
+    }
+
+    public static Decimal64 valueOf(final BigDecimal decimalVal) {
+        // XXX: we should be able to do something smarter here
+        return valueOf(decimalVal.toPlainString());
+    }
+
+    /**
+     * Attempt to parse a String into a Decimal64. This method uses minimum fraction digits required to hold
+     * the entire value.
+     *
+     * @param str String to parser
+     * @return A Decimal64 instance
+     * @throws NullPointerException if value is null.
+     * @throws NumberFormatException if the string does not contain a parsable decimal64.
+     */
+    public static Decimal64 valueOf(final String str) {
+        final Variant<Decimal64, CanonicalValueViolation> variant = SUPPORT.fromString(str);
+        final Optional<Decimal64> value = variant.tryFirst();
+        if (value.isPresent()) {
+            return value.get();
+        }
+        final Optional<String> message = variant.getSecond().getMessage();
+        throw message.isPresent() ? new NumberFormatException(message.get()) : new NumberFormatException();
+    }
+
+    public final BigDecimal decimalValue() {
+        return BigDecimal.valueOf(value, scaleOffset + 1);
+    }
+
+    @Override
+    public final int intValue() {
+        return (int) intPart();
+    }
+
+    @Override
+    public final long longValue() {
+        return intPart();
+    }
+
+    @Override
+    public final float floatValue() {
+        return (float) doubleValue();
+    }
+
+    @Override
+    public final double doubleValue() {
+        return 1.0 * value / SCALE[scaleOffset];
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to a {@code byte}, checking for lost information. If this {@code Decimal64} has
+     * a nonzero fractional part or is out of the possible range for a {@code byte} result then
+     * an {@code ArithmeticException} is thrown.
+     *
+     * @return this {@code Decimal64} converted to a {@code byte}.
+     * @throws ArithmeticException if {@code this} has a nonzero fractional part, or will not fit in a {@code byte}.
+     */
+    public final byte byteValueExact() {
+        final long val = longValueExact();
+        final byte ret = (byte) val;
+        if (val != ret) {
+            throw new ArithmeticException("Value " + val + " is outside of byte range");
+        }
+        return ret;
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to a {@code short}, checking for lost information. If this {@code Decimal64} has
+     * a nonzero fractional part or is out of the possible range for a {@code short} result then
+     * an {@code ArithmeticException} is thrown.
+     *
+     * @return this {@code Decimal64} converted to a {@code short}.
+     * @throws ArithmeticException if {@code this} has a nonzero fractional part, or will not fit in a {@code short}.
+     */
+    public final short shortValueExact() {
+        final long val = longValueExact();
+        final short ret = (short) val;
+        if (val != ret) {
+            throw new ArithmeticException("Value " + val + " is outside of short range");
+        }
+        return ret;
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to an {@code int}, checking for lost information. If this {@code Decimal64} has
+     * a nonzero fractional part or is out of the possible range for an {@code int} result then
+     * an {@code ArithmeticException} is thrown.
+     *
+     * @return this {@code Decimal64} converted to an {@code int}.
+     * @throws ArithmeticException if {@code this} has a nonzero fractional part, or will not fit in an {@code int}.
+     */
+    public final int intValueExact() {
+        final long val = longValueExact();
+        final int ret = (int) val;
+        if (val != ret) {
+            throw new ArithmeticException("Value " + val + " is outside of integer range");
+        }
+        return ret;
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to a {@code long}, checking for lost information.  If this {@code Decimal64} has
+     * a nonzero fractional part then an {@code ArithmeticException} is thrown.
+     *
+     * @return this {@code Decimal64} converted to a {@code long}.
+     * @throws ArithmeticException if {@code this} has a nonzero fractional part.
+     */
+    public final long longValueExact() {
+        if (fracPart() != 0) {
+            throw new ArithmeticException("Conversion of " + this + " would lose fraction");
+        }
+        return intPart();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final int compareTo(final Decimal64 o) {
+        if (this == o) {
+            return 0;
+        }
+        if (scaleOffset == o.scaleOffset) {
+            return Long.compare(value, o.value);
+        }
+
+        // XXX: we could do something smarter here
+        return Double.compare(doubleValue(), o.doubleValue());
+    }
+
+    @Override
+    public final String toCanonicalString() {
+        // https://tools.ietf.org/html/rfc6020#section-9.3.2
+        //
+        // The canonical form of a positive decimal64 does not include the sign
+        // "+".  The decimal point is required.  Leading and trailing zeros are
+        // prohibited, subject to the rule that there MUST be at least one digit
+        // before and after the decimal point.  The value zero is represented as
+        // "0.0".
+        final StringBuilder sb = new StringBuilder(21).append(intPart()).append('.');
+        final long fracPart = fracPart();
+        if (fracPart != 0) {
+            // We may need to zero-pad the fraction part
+            sb.append(Strings.padStart(Long.toString(fracPart), scaleOffset + 1, '0'));
+        } else {
+            sb.append('0');
+        }
+
+        return sb.toString();
+    }
+
+    @Override
+    public final CanonicalValueSupport<Decimal64> support() {
+        return SUPPORT;
+    }
+
+    @Override
+    public final int hashCode() {
+        // We need to normalize the results in order to be consistent with equals()
+        return Long.hashCode(intPart()) * 31 + Long.hashCode(fracPart());
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        return this == obj || obj instanceof Decimal64 && equalsImpl((Decimal64) obj);
+    }
+
+    /**
+     * A slightly faster version of {@link #equals(Object)}.
+     *
+     * @param obj Decimal64 object
+     * @return {@code true} if this object is the same as the obj argument; {@code false} otherwise.
+     */
+    public final boolean equals(final @Nullable Decimal64 obj) {
+        return this == obj || obj != null && equalsImpl(obj);
+    }
+
+    @Override
+    public final String toString() {
+        return toCanonicalString();
+    }
+
+    private boolean equalsImpl(final Decimal64 other) {
+        return scaleOffset == other.scaleOffset ? value == other.value
+                // We need to normalize both
+                : intPart() == other.intPart() && fracPart() == other.fracPart();
+    }
+
+    private long intPart() {
+        return value / SCALE[scaleOffset];
+    }
+
+    private long fracPart() {
+        return Math.abs(value % SCALE[scaleOffset]);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/DerivedString.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/DerivedString.java
new file mode 100644 (file)
index 0000000..97f7e6a
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.common;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Abstract base class for objects which are string-equivalent to canonical string representation specified
+ * in a YANG model. Note that each subclass of {@link DerivedString} defines its own {@link #hashCode()} and
+ * {@link #equals(Object)} contracts based on implementation particulars.
+ *
+ * <p>
+ * Given the following YANG snippet:
+ * <pre>
+ *     typedef foo {
+ *         type string;
+ *         pattern "[1-9]?[0-9]";
+ *     }
+ *
+ *     typedef bar {
+ *         type foo;
+ *         patter "[1-9][0-9]";
+ *     }
+ *
+ *     typedef baz {
+ *         type foo;
+ *     }
+ * </pre>
+ * it is obvious we could use a storage class with 'int' as the internal representation of all three types and define
+ * operations on top of it. In this case we would define:
+ * <ul>
+ *     <li>{@code public class FooDerivedString extends DerivedString<FooDerivedString>}, which implements all abstract
+ *         methods of {@link DerivedString} as final methods. It will notably not override {@link #validator()} and
+ *         must not be final.</li>
+ *     <li>{@code public final class FooDerivedStringSupport extends DerivedStringSupport<FooDerivedString>}, which
+ *         forms the baseline validator and instantiation for {@code FooDerivedString}. It should be a singleton class
+ *         with a getInstance() method.</li>
+ *     <li>{@code public class BarDerivedString extends FooDerivedString}, which overrides {@link #validator()} to
+ *         indicate its contents have been validated to conform to bar -- it does that by returning the singleton
+ *         instance of {@code BarDerivedStringValidator}.</li>
+ *     <li>{@code public final class BarDerivedStringValidator extends DerivedStringValidator<FooDerivedString,
+ *         BarDerivedString}. This method needs to notably implement
+ *         {@link CanonicalValueValidator#validateRepresentation(CanonicalValue)} to hand out BarDerivedString
+ *         instances. This class needs to be a singleton with a getInstance() method, too.</li>
+ * </ul>
+ * Since {@code baz} is not defining any new restrictions, all instances of FooDerivedString are valid for it and we
+ * do not have to define any additional support.
+ *
+ * <p>
+ * It is important for {@link DerivedString} subclasses not to be final because any YANG type can be further extended
+ * and adding a final class in that hierarchy would prevent a proper class from being defined.
+ *
+ * @param <T> derived string representation
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public abstract class DerivedString<T extends DerivedString<T>> implements CanonicalValue<T> {
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals(@Nullable Object obj);
+
+    @Override
+    public final String toString() {
+        return toCanonicalString();
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Empty.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Empty.java
new file mode 100644 (file)
index 0000000..af85eb0
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.common;
+
+import com.google.common.annotations.Beta;
+import java.io.Serializable;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Dedicated singleton type for YANG's 'type empty' value.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public final class Empty implements Immutable, Serializable {
+    private static final long serialVersionUID = 1L;
+    private static final Empty INSTANCE = new Empty();
+
+    private Empty() {
+
+    }
+
+    public static Empty getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String toString() {
+        return "empty";
+    }
+
+    @SuppressWarnings("static-method")
+    private Object readResolve() {
+        return INSTANCE;
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/NSv1.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/NSv1.java
new file mode 100644 (file)
index 0000000..99a7e80
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.common;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+/**
+ * Externalizable proxy for {@link QNameModule}.
+ */
+final class NSv1 implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private QNameModule namespace;
+
+    @SuppressWarnings("checkstyle:redundantModifier")
+    public NSv1() {
+        // For Externalizable
+    }
+
+    NSv1(final QNameModule qname) {
+        this.namespace = requireNonNull(qname);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        namespace.writeTo(out);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        namespace = QNameModule.readFrom(in);
+    }
+
+    Object readResolve() {
+        return verifyNotNull(namespace);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/OperationFailedException.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/OperationFailedException.java
new file mode 100644 (file)
index 0000000..190b5a9
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.yang.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.List;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+/**
+ * A general base exception for an operation failure.
+ *
+ * @author Thomas Pantelis
+ */
+public class OperationFailedException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+
+    private final ImmutableList<RpcError> errorList;
+
+    /**
+     * Constructs a new instance with the specified detail message and errors.
+     *
+     * @param message the detail message
+     * @param errors {@link RpcError} instances that provide additional error information about
+     *               this exception
+     */
+    public OperationFailedException(final String message, final RpcError... errors) {
+        this(message, null, errors);
+    }
+
+    /**
+     * Constructs a new instance with the specified detail message, cause and errors.
+     *
+     * @param message the detail message
+     * @param cause the cause
+     * @param errors {@link RpcError} instances that provide additional error information about
+     *               this exception
+     */
+    public OperationFailedException(final String message, final Throwable cause,
+                                    final RpcError... errors) {
+        super(requireNonNull(message), cause);
+
+        if (errors != null && errors.length > 0) {
+            errorList = ImmutableList.copyOf(Arrays.asList(errors));
+        } else {
+            // Add a default RpcError.
+            errorList = ImmutableList.of(RpcResultBuilder.newError(ErrorType.APPLICATION, null,
+                    getMessage(), null, null, getCause()));
+        }
+    }
+
+    /**
+     * Returns additional error information about this exception.
+     *
+     * @return a List of RpcErrors. There is always at least one RpcError.
+     */
+    public List<RpcError> getErrorList() {
+        return errorList;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("message", getMessage())
+                .add("errorList", errorList).toString();
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QName.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QName.java
new file mode 100644 (file)
index 0000000..feea525
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * 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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.checkerframework.checker.regex.qual.Regex;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The QName from XML consists of local name of element and XML namespace, but for our use, we added module revision to
+ * it.
+ *
+ * <p>
+ * In YANG context QName is full name of defined node, type, procedure or notification. QName consists of XML namespace,
+ * YANG model revision and local name of defined type. It is used to prevent name clashes between nodes with same local
+ * name, but from different schemas.
+ *
+ * <p>
+ * The local name must conform to <a href="https://tools.ietf.org/html/rfc7950#section-6.2">RFC7950 Section 6.2</a>.
+ *
+ * <ul>
+ * <li><b>XMLNamespace</b> - {@link #getNamespace()} - the namespace assigned to the YANG module which
+ * defined element, type, procedure or notification.</li>
+ * <li><b>Revision</b> - {@link #getRevision()} - the revision of the YANG module which describes the
+ * element</li>
+ * <li><b>LocalName</b> - {@link #getLocalName()} - the YANG schema identifier which were defined for this
+ * node in the YANG module</li>
+ * </ul>
+ */
+public final class QName extends AbstractQName implements Comparable<QName> {
+    private static final Interner<QName> INTERNER = Interners.newWeakInterner();
+    // Note: 5398411242927766414L is used for versions < 3.0.0 without writeReplace
+    private static final long serialVersionUID = 1L;
+
+    static final String QNAME_REVISION_DELIMITER = "?revision=";
+    static final String QNAME_LEFT_PARENTHESIS = "(";
+    static final String QNAME_RIGHT_PARENTHESIS = ")";
+
+    @Regex
+    private static final String QNAME_STRING_FULL = "^\\((.+)\\?revision=(.+)\\)(.+)$";
+    private static final Pattern QNAME_PATTERN_FULL = Pattern.compile(QNAME_STRING_FULL);
+
+    @Regex
+    private static final String QNAME_STRING_NO_REVISION = "^\\((.+)\\)(.+)$";
+    private static final Pattern QNAME_PATTERN_NO_REVISION = Pattern.compile(QNAME_STRING_NO_REVISION);
+
+    private final @NonNull QNameModule module;
+    private transient int hash = 0;
+
+    QName(final QNameModule module, final @NonNull String localName) {
+        super(localName);
+        this.module = requireNonNull(module);
+    }
+
+    /**
+     * QName Constructor.
+     *
+     * @param namespace
+     *            the namespace assigned to the YANG module
+     * @param localName
+     *            YANG schema identifier
+     */
+    private QName(final URI namespace, final String localName) {
+        this(QNameModule.create(namespace), checkLocalName(localName));
+    }
+
+    public static @NonNull QName create(final String input) {
+        Matcher matcher = QNAME_PATTERN_FULL.matcher(input);
+        if (matcher.matches()) {
+            final String namespace = matcher.group(1);
+            final String revision = matcher.group(2);
+            final String localName = matcher.group(3);
+            return create(namespace, revision, localName);
+        }
+        matcher = QNAME_PATTERN_NO_REVISION.matcher(input);
+        if (matcher.matches()) {
+            final URI namespace = URI.create(matcher.group(1));
+            final String localName = matcher.group(2);
+            return new QName(namespace, localName);
+        }
+        throw new IllegalArgumentException("Invalid input: " + input);
+    }
+
+    public static @NonNull QName create(final QName base, final String localName) {
+        return create(base.getModule(), localName);
+    }
+
+    /**
+     * Creates new QName.
+     *
+     * @param qnameModule
+     *            Namespace and revision enclosed as a QNameModule
+     * @param localName
+     *            Local name part of QName. MUST NOT BE null.
+     * @return Instance of QName
+     */
+    public static @NonNull QName create(final QNameModule qnameModule, final String localName) {
+        return new QName(requireNonNull(qnameModule, "module may not be null"), checkLocalName(localName));
+    }
+
+    /**
+     * Creates new QName.
+     *
+     * @param namespace Namespace of QName or null if namespace is undefined.
+     * @param revision Revision of namespace or null if revision is unspecified.
+     * @param localName Local name part of QName. MUST NOT BE null.
+     * @return Instance of QName
+     */
+    public static @NonNull QName create(final URI namespace, final @Nullable Revision revision,
+            final String localName) {
+        return create(QNameModule.create(namespace, revision), localName);
+    }
+
+    /**
+     * Creates new QName.
+     *
+     * @param namespace Namespace of QName or null if namespace is undefined.
+     * @param revision Revision of namespace.
+     * @param localName Local name part of QName. MUST NOT BE null.
+     * @return Instance of QName
+     */
+    public static @NonNull QName create(final URI namespace, final Optional<Revision> revision,
+            final String localName) {
+        return create(QNameModule.create(namespace, revision), localName);
+    }
+
+    /**
+     * Creates new QName.
+     *
+     * @param namespace Namespace of QName or null if namespace is undefined.
+     * @param revision Revision of namespace or null if revision is unspecified.
+     * @param localName Local name part of QName. MUST NOT BE null.
+     * @return Instance of QName
+     */
+    public static @NonNull QName create(final String namespace, final String localName, final Revision revision) {
+        return create(QNameModule.create(parseNamespace(namespace), revision), localName);
+    }
+
+    /**
+     * Creates new QName.
+     *
+     * @param namespace Namespace of QName, MUST NOT BE Null.
+     * @param revision Revision of namespace / YANG module. MUST NOT BE null, MUST BE in format {@code YYYY-mm-dd}.
+     * @param localName Local name part of QName. MUST NOT BE null.
+     * @return A new QName
+     * @throws NullPointerException If any of parameters is null.
+     * @throws IllegalArgumentException If {@code namespace} is not valid URI or {@code revision} does not conform
+     *         to {@code YYYY-mm-dd}.
+     */
+    public static @NonNull QName create(final String namespace, final String revision, final String localName) {
+        return create(parseNamespace(namespace), Revision.of(revision), localName);
+    }
+
+    /**
+     * Creates new QName.
+     *
+     * @param namespace Namespace of QName, MUST NOT BE Null.
+     * @param localName Local name part of QName. MUST NOT BE null.
+     * @return A new QName
+     * @throws NullPointerException If any of parameters is null.
+     * @throws IllegalArgumentException If {@code namespace} is not valid URI.
+     */
+    public static @NonNull QName create(final String namespace, final String localName) {
+        return create(parseNamespace(namespace), localName);
+    }
+
+    /**
+     * Creates new QName.
+     *
+     * @param namespace Namespace of QName, MUST NOT BE null.
+     * @param localName Local name part of QName. MUST NOT BE null.
+     * @return A new QName
+     * @throws NullPointerException If any of parameters is null.
+     * @throws IllegalArgumentException If <code>namespace</code> is not valid URI.
+     */
+    public static @NonNull QName create(final URI namespace, final String localName) {
+        return new QName(namespace, localName);
+    }
+
+    /**
+     * Read a QName from a DataInput. The format is expected to match the output format of {@link #writeTo(DataOutput)}.
+     *
+     * @param in DataInput to read
+     * @return A QName instance
+     * @throws IOException if I/O error occurs
+     */
+    public static @NonNull QName readFrom(final DataInput in) throws IOException {
+        final QNameModule module = QNameModule.readFrom(in);
+        return new QName(module, checkLocalName(in.readUTF()));
+    }
+
+    /**
+     * Get the module component of the QName.
+     *
+     * @return Module component
+     */
+    public @NonNull QNameModule getModule() {
+        return module;
+    }
+
+    /**
+     * Returns XMLNamespace assigned to the YANG module.
+     *
+     * @return XMLNamespace assigned to the YANG module.
+     */
+    public @NonNull URI getNamespace() {
+        return module.getNamespace();
+    }
+
+    /**
+     * Returns revision of the YANG module if the module has defined revision.
+     *
+     * @return revision of the YANG module if the module has defined revision.
+     */
+    public @NonNull Optional<Revision> getRevision() {
+        return module.getRevision();
+    }
+
+    @Override
+    public @NonNull QName intern() {
+        // We also want to make sure we keep the QNameModule cached
+        final QNameModule cacheMod = module.intern();
+
+        // Identity comparison is here on purpose, as we are deciding whether to potentially store 'qname' into the
+        // interner. It is important that it does not hold user-supplied reference (such a String instance from
+        // parsing of an XML document).
+        final QName template = cacheMod == module ? this : new QName(cacheMod, getLocalName().intern());
+
+        return INTERNER.intern(template);
+    }
+
+    @Override
+    public int hashCode() {
+        if (hash == 0) {
+            hash = Objects.hash(module, getLocalName());
+        }
+        return hash;
+    }
+
+    /**
+     * Compares the specified object with this list for equality.  Returns {@code true} if and only if the specified
+     * object is also instance of {@link QName} and its {@link #getLocalName()}, {@link #getNamespace()} and
+     * {@link #getRevision()} are equals to same properties of this instance.
+     *
+     * @param obj the object to be compared for equality with this QName
+     * @return {@code true} if the specified object is equal to this QName
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof QName)) {
+            return false;
+        }
+        final QName other = (QName) obj;
+        return Objects.equals(getLocalName(), other.getLocalName()) && module.equals(other.module);
+    }
+
+    private static @NonNull URI parseNamespace(final String namespace) {
+        try {
+            return new URI(namespace);
+        } catch (final URISyntaxException ue) {
+            throw new IllegalArgumentException("Namespace '" + namespace + "' is not a valid URI", ue);
+        }
+    }
+
+    @Override
+    public @NonNull String toString() {
+        final StringBuilder sb = new StringBuilder();
+        if (getNamespace() != null) {
+            sb.append(QNAME_LEFT_PARENTHESIS).append(getNamespace());
+
+            final Optional<Revision> rev = getRevision();
+            if (rev.isPresent()) {
+                sb.append(QNAME_REVISION_DELIMITER).append(rev.get());
+            }
+            sb.append(QNAME_RIGHT_PARENTHESIS);
+        }
+        return sb.append(getLocalName()).toString();
+    }
+
+    /**
+     * Returns a QName with the specified QNameModule and the same localname as this one.
+     *
+     * @param newModule New QNameModule to use
+     * @return a QName with specified QNameModule and same local name as this one
+     */
+    public @NonNull QName withModule(final QNameModule newModule) {
+        return new QName(newModule, getLocalName());
+    }
+
+    /**
+     * Returns a QName with the same namespace and local name, but with no revision. If this QName does not have
+     * a Revision, this object is returned.
+     *
+     * @return a QName with the same namespace and local name, but with no revision.
+     */
+    public @NonNull QName withoutRevision() {
+        final QNameModule newModule;
+        return (newModule = module.withoutRevision()) == module ? this : new QName(newModule, getLocalName());
+    }
+
+    /**
+     * Formats {@link Revision} representing revision to format {@code YYYY-mm-dd}
+     *
+     * <p>
+     * YANG Specification defines format for {@code revision<} as YYYY-mm-dd. This format for revision is reused across
+     * multiple places such as capabilities URI, YANG modules, etc.
+     *
+     * @param revision Date object to format
+     * @return String representation or null if the input was null.
+     */
+    public static @Nullable String formattedRevision(final Optional<Revision> revision) {
+        return revision.map(Revision::toString).orElse(null);
+    }
+
+    /**
+     * Compares this QName to other, without comparing revision.
+     *
+     * <p>
+     * Compares instance of this to other instance of QName and returns true if both instances have equal
+     * {@code localName} ({@link #getLocalName()}) and @{code namespace} ({@link #getNamespace()}).
+     *
+     * @param other Other QName. Must not be null.
+     * @return true if this instance and other have equals localName and namespace.
+     * @throws NullPointerException if {@code other} is null.
+     */
+    public boolean isEqualWithoutRevision(final QName other) {
+        return getLocalName().equals(other.getLocalName()) && Objects.equals(getNamespace(), other.getNamespace());
+    }
+
+    // FIXME: this comparison function looks odd. We are sorting first by local name and then by module? What is
+    //        the impact on iteration order of SortedMap<QName, ?>?
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public int compareTo(final QName o) {
+        // compare mandatory localName parameter
+        int result = getLocalName().compareTo(o.getLocalName());
+        if (result != 0) {
+            return result;
+        }
+        return module.compareTo(o.module);
+    }
+
+    @Override
+    public void writeTo(final DataOutput out) throws IOException {
+        module.writeTo(out);
+        out.writeUTF(getLocalName());
+    }
+
+    @Override
+    Object writeReplace() {
+        return new QNv1(this);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QNameModule.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QNameModule.java
new file mode 100644 (file)
index 0000000..5da98d9
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Identifier;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.WritableObject;
+
+public final class QNameModule implements Comparable<QNameModule>, Immutable, Serializable, Identifier, WritableObject {
+    private static final Interner<QNameModule> INTERNER = Interners.newWeakInterner();
+    private static final long serialVersionUID = 3L;
+
+    private final @NonNull URI namespace;
+    private final @Nullable Revision revision;
+
+    private transient int hash = 0;
+
+    private QNameModule(final URI namespace, final @Nullable Revision revision) {
+        this.namespace = requireNonNull(namespace);
+        this.revision = revision;
+    }
+
+    /**
+     * Return an interned reference to a equivalent QNameModule.
+     *
+     * @return Interned reference, or this object if it was interned.
+     */
+    public @NonNull QNameModule intern() {
+        return INTERNER.intern(this);
+    }
+
+    /**
+     * Create a new QName module instance with specified namespace/revision.
+     *
+     * @param namespace Module namespace
+     * @param revision Module revision
+     * @return A new, potentially shared, QNameModule instance
+     * @throws NullPointerException if any argument is null
+     */
+    public static @NonNull QNameModule create(final URI namespace, final Optional<Revision> revision) {
+        return new QNameModule(namespace, revision.orElse(null));
+    }
+
+    /**
+     * Create a new QName module instance with specified namespace and no revision.
+     *
+     * @param namespace Module namespace
+     * @return A new, potentially shared, QNameModule instance
+     * @throws NullPointerException if {@code namespace} is null
+     */
+    public static @NonNull QNameModule create(final URI namespace) {
+        return new QNameModule(namespace, null);
+    }
+
+    /**
+     * Create a new QName module instance with specified namespace/revision.
+     *
+     * @param namespace Module namespace
+     * @param revision Module revision
+     * @return A new, potentially shared, QNameModule instance
+     * @throws NullPointerException if any argument is null
+     */
+    public static @NonNull QNameModule create(final URI namespace, final @Nullable Revision revision) {
+        return new QNameModule(namespace, revision);
+    }
+
+    /**
+     * Read a QNameModule from a DataInput. The format is expected to match the output format
+     * of {@link #writeTo(DataOutput)}.
+     *
+     * @param in DataInput to read
+     * @return A QNameModule instance
+     * @throws IOException if I/O error occurs
+     */
+    public static @NonNull QNameModule readFrom(final DataInput in) throws IOException {
+        final String namespace = in.readUTF();
+        final String revision = in.readUTF();
+        return new QNameModule(URI.create(namespace), revision.isEmpty() ? null : Revision.of(revision));
+    }
+
+    /**
+     * Returns the namespace of the module which is specified as argument of YANG Module {@code namespace} keyword.
+     *
+     * @return URI format of the namespace of the module
+     */
+    public @NonNull URI getNamespace() {
+        return namespace;
+    }
+
+    /**
+     * Returns the revision date for the module.
+     *
+     * @return date of the module revision which is specified as argument of YANG Module {@code revision} keyword
+     */
+    public @NonNull Optional<Revision> getRevision() {
+        return Optional.ofNullable(revision);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public int compareTo(final QNameModule o) {
+        int cmp = namespace.compareTo(o.namespace);
+        if (cmp != 0) {
+            return cmp;
+        }
+        return Revision.compare(revision, o.revision);
+    }
+
+    /**
+     * Returns a QNameModule with the same namespace, but with no revision. If this QNameModule does not have
+     * a revision, this object is returned.
+     *
+     * @return a QNameModule with the same namespace, but with no revision.
+     */
+    public @NonNull QNameModule withoutRevision() {
+        return revision == null ? this : new QNameModule(namespace, null);
+    }
+
+    @Override
+    public void writeTo(final DataOutput out) throws IOException {
+        out.writeUTF(namespace.toString());
+        out.writeUTF(revision == null ? "" : revision.toString());
+    }
+
+    @Override
+    public int hashCode() {
+        if (hash == 0) {
+            hash = Objects.hash(namespace, revision);
+        }
+        return hash;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof QNameModule)) {
+            return false;
+        }
+        final QNameModule other = (QNameModule) obj;
+        return Objects.equals(revision, other.revision) && namespace.equals(other.namespace);
+    }
+
+    /**
+     * Returns a namespace in form defined by section 5.6.4. of
+     * <a href=https://tools.ietf.org/html/rfc6020">RFC6020</a>, for example
+     * {@code http://example.acme.com/system?revision=2008-04-01}.
+     *
+     * @return Namespace in form defined by section 5.6.4. of RFC6020.
+     * @throws URISyntaxException on incorrect namespace definition
+     *
+     */
+    @NonNull URI getRevisionNamespace() throws URISyntaxException {
+        final String query = revision == null ? "" : "revision=" + revision.toString();
+        return new URI(namespace.getScheme(), namespace.getUserInfo(), namespace.getHost(), namespace.getPort(),
+            namespace.getPath(), query, namespace.getFragment());
+    }
+
+    @Override
+    public @NonNull String toString() {
+        return MoreObjects.toStringHelper(QNameModule.class).omitNullValues().add("ns", namespace)
+            .add("rev", revision).toString();
+    }
+
+    Object writeReplace() {
+        return new NSv1(this);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QNv1.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QNv1.java
new file mode 100644 (file)
index 0000000..674087f
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.common;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+/**
+ * Externalizable proxy for {@link QName}.
+ */
+final class QNv1 implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private QName qname;
+
+    @SuppressWarnings("checkstyle:redundantModifier")
+    public QNv1() {
+        // For Externalizable
+    }
+
+    QNv1(final QName qname) {
+        this.qname = requireNonNull(qname);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        qname.writeTo(out);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        qname = QName.readFrom(in);
+    }
+
+    Object readResolve() {
+        return verifyNotNull(qname);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QQNv1.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QQNv1.java
new file mode 100644 (file)
index 0000000..1623681
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.common;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+/**
+ * Externalizable proxy for {@link QualifiedQName}.
+ */
+final class QQNv1 implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private QualifiedQName qname;
+
+    @SuppressWarnings("checkstyle:redundantModifier")
+    public QQNv1() {
+        // For Externalizable
+    }
+
+    QQNv1(final QualifiedQName qname) {
+        this.qname = requireNonNull(qname);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        qname.writeTo(out);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        qname = QualifiedQName.readFrom(in);
+    }
+
+    Object readResolve() {
+        return verifyNotNull(qname);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QualifiedQName.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QualifiedQName.java
new file mode 100644 (file)
index 0000000..f6dce70
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * An unresolved, qualified {@link QName}. It is guaranteed to hold a valid {@link #getLocalName()} bound to a namespace
+ * identified through a prefix string, but remains unresolved. A resolved {@link QName} can be obtained through
+ * {@link #bindTo(YangNamespaceContext)}.
+ */
+@Beta
+@NonNullByDefault
+public final class QualifiedQName extends AbstractQName implements Comparable<QualifiedQName> {
+    private static final long serialVersionUID = 1L;
+    private static final Interner<QualifiedQName> INTERNER = Interners.newWeakInterner();
+
+    private final String prefix;
+
+    private QualifiedQName(final String prefix, final String localName) {
+        super(localName);
+        this.prefix = requireNonNull(prefix);
+    }
+
+    public static QualifiedQName of(final String prefix, final String localName) {
+        return new QualifiedQName(checkLocalName(prefix), checkLocalName(localName));
+    }
+
+    /**
+     * Read an UnboundQName from a DataInput. The format is expected to match the output format of
+     * {@link #writeTo(DataOutput)}.
+     *
+     * @param in DataInput to read
+     * @return An UnboundQName instance
+     * @throws IOException if I/O error occurs
+     */
+    public static QualifiedQName readFrom(final DataInput in) throws IOException {
+        return of(in.readUTF(), in.readUTF());
+    }
+
+    public Optional<QName> bindTo(final YangNamespaceContext namespaceContext) {
+        return namespaceContext.findNamespaceForPrefix(prefix).map(this::bindTo);
+    }
+
+    private QName bindTo(final QNameModule namespace) {
+        return new QName(namespace, getLocalName());
+    }
+
+    @Override
+    @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "Interning identity check")
+    public QualifiedQName intern() {
+        // Make sure to intern the string and check whether it refers to the same name as we are
+        final String name = getLocalName();
+        final String internedName = name.intern();
+        final QualifiedQName template = internedName == name ? this : new QualifiedQName(prefix.intern(), internedName);
+        return INTERNER.intern(template);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public int compareTo(final QualifiedQName o) {
+        return getLocalName().compareTo(o.getLocalName());
+    }
+
+    @Override
+    public void writeTo(final DataOutput out) throws IOException {
+        out.writeUTF(getLocalName());
+    }
+
+    @Override
+    public int hashCode() {
+        return getLocalName().hashCode();
+    }
+
+    @Override
+    public boolean equals(final @Nullable Object obj) {
+        return this == obj || obj instanceof QualifiedQName
+                && getLocalName().equals(((AbstractQName) obj).getLocalName());
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("localName", getLocalName()).toString();
+    }
+
+    @Override
+    Object writeReplace() {
+        return new QQNv1(this);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Revision.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Revision.java
new file mode 100644 (file)
index 0000000..a4788d3
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2016 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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import org.checkerframework.checker.regex.qual.Regex;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Dedicated object identifying a YANG module revision.
+ *
+ * <h3>API design note</h3>
+ * This class defines the contents of a revision statement, but modules do not require to have a revision (e.g. they
+ * have not started to keep track of revisions).
+ *
+ * <p>
+ * APIs which involve this class should always transfer instances via {@code Optional<Revision>}, which is
+ * the primary bridge data type. Implementations can use nullable fields with explicit conversions to/from
+ * {@link Optional}. Both patterns can take advantage of {@link #compare(Optional, Optional)} and
+ * {@link #compare(Revision, Revision)} respectively.
+ *
+ * @author Robert Varga
+ */
+public final class Revision implements Comparable<Revision>, Immutable, Serializable {
+    // Note: since we are using writeReplace() this version is not significant.
+    private static final long serialVersionUID = 1L;
+
+    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+    @Regex
+    // FIXME: we should improve this to filter incorrect dates -- see constructor.
+    private static final String STRING_FORMAT_PATTERN_STR = "\\d\\d\\d\\d\\-\\d\\d-\\d\\d";
+
+    /**
+     * String format pattern, which can be used to match parts of a string into components.
+     */
+    public static final Pattern STRING_FORMAT_PATTERN = Pattern.compile(STRING_FORMAT_PATTERN_STR);
+
+    /**
+     * Revision which compares as greater than any other valid revision.
+     */
+    public static final Revision MAX_VALUE = Revision.of("9999-12-31");
+
+    private final @NonNull String str;
+
+    private Revision(final @NonNull String str) {
+        /*
+         * According to RFC7950 (https://tools.ietf.org/html/rfc7950#section-7.1.9):
+         *
+         *   The "revision" statement specifies the editorial revision history of
+         *   the module, including the initial revision.  A series of "revision"
+         *   statements detail the changes in the module's definition.  The
+         *   argument is a date string in the format "YYYY-MM-DD", [...]
+         *
+         * Hence we use JDK-provided parsing faculties to parse the date.
+         */
+        FORMATTER.parse(str);
+        this.str = str;
+    }
+
+    /**
+     * Parse a revision string.
+     *
+     * @param str String to be parsed
+     * @return A Revision instance.
+     * @throws DateTimeParseException if the string format does not conform specification.
+     * @throws NullPointerException if the string is null
+     */
+    public static @NonNull Revision of(final @NonNull String str) {
+        return new Revision(str);
+    }
+
+    /**
+     * Parse a (potentially null) revision string. Null strings result result in {@link Optional#empty()}.
+     *
+     * @param str String to be parsed
+     * @return An optional Revision instance.
+     * @throws DateTimeParseException if the string format does not conform specification.
+     */
+    public static @NonNull Optional<Revision> ofNullable(final @Nullable String str) {
+        return str == null ? Optional.empty() : Optional.of(new Revision(str));
+    }
+
+    /**
+     * Compare two {@link Optional}s wrapping Revisions. Arguments and return value are consistent with
+     * {@link java.util.Comparator#compare(Object, Object)} interface contract. Missing revisions compare as lower
+     * than any other revision.
+     *
+     * @param first First optional revision
+     * @param second Second optional revision
+     * @return Positive, zero, or negative integer.
+     */
+    public static int compare(final @NonNull Optional<Revision> first, final @NonNull Optional<Revision> second) {
+        if (first.isPresent()) {
+            return second.isPresent() ? first.get().compareTo(second.get()) : 1;
+        }
+        return second.isPresent() ? -1 : 0;
+    }
+
+    /**
+     * Compare two explicitly nullable Revisions. Unlike {@link #compareTo(Revision)}, this handles both arguments
+     * being null such that total ordering is defined.
+     *
+     * @param first First revision
+     * @param second Second revision
+     * @return Positive, zero, or negative integer.
+     */
+    public static int compare(final @Nullable Revision first, final @Nullable Revision second) {
+        if (first != null) {
+            return second != null ? first.compareTo(second) : 1;
+        }
+        return second != null ? -1 : 0;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public int compareTo(final Revision o) {
+        // Since all strings conform to the format, we can use their comparable property to do the correct thing
+        // with respect to temporal ordering.
+        return str.compareTo(o.str);
+    }
+
+    @Override
+    public int hashCode() {
+        return str.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return this == obj || obj instanceof Revision && str.equals(((Revision)obj).str);
+    }
+
+    @Override
+    public String toString() {
+        return str;
+    }
+
+    Object writeReplace() {
+        return new Proxy(str);
+    }
+
+    private static final class Proxy implements Externalizable {
+        private static final long serialVersionUID = 1L;
+
+        private String str;
+
+        @SuppressWarnings("checkstyle:redundantModifier")
+        public Proxy() {
+            // For Externalizable
+        }
+
+        Proxy(final String str) {
+            this.str = requireNonNull(str);
+        }
+
+        @Override
+        public void writeExternal(final ObjectOutput out) throws IOException {
+            out.writeObject(str);
+        }
+
+        @Override
+        public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+            str = (String) in.readObject();
+        }
+
+        private Object readResolve() {
+            return Revision.of(requireNonNull(str));
+        }
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/RpcError.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/RpcError.java
new file mode 100644 (file)
index 0000000..b1a79f4
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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.common;
+
+/**
+ * Representation of an error.
+ *
+ */
+public interface RpcError {
+
+    enum ErrorSeverity {
+        ERROR,
+        WARNING
+    }
+
+    enum ErrorType {
+        /**
+         * Indicates an error occurred during transport of data, eg over the network.
+         */
+        TRANSPORT,
+
+        /**
+         * Indicates an error occurred during a remote procedure call.
+         */
+        RPC,
+
+        /**
+         * Indicates an error at a protocol layer, eg if invalid data was passed by the caller.
+         */
+        PROTOCOL,
+
+        /**
+         * Indicates an error occurred during internal processing.
+         */
+        APPLICATION
+    }
+
+    /**
+     * Returns the error severity, as determined by the application reporting the error.
+     *
+     * @return an {@link ErrorSeverity} enum.
+     */
+    ErrorSeverity getSeverity();
+
+    /**
+     * Returns a short string that identifies the general type of error condition.
+     *
+     * <p>
+     * The following outlines suggested values as defined by netconf (<a href="https://tools.ietf.org/html/rfc6241#page-89">RFC 6241</a>):
+     * <pre>
+     *    access-denied
+     *    bad-attribute
+     *    bad-element
+     *    data-exists
+     *    data-missing
+     *    in-use
+     *    invalid-value
+     *    lock-denied
+     *    malformed-message
+     *    missing-attribute
+     *    missing-element
+     *    operation-failed
+     *    operation-not-supported
+     *    resource-denied
+     *    rollback-failed
+     *    too-big
+     *    unknown-attribute
+     *    unknown-element
+     *    unknown-namespace
+     * </pre>
+     * @return a string if available or null otherwise.
+     */
+    String getTag();
+
+    /**
+     * Returns a short string that identifies the specific type of error condition as
+     * determined by the application reporting the error.
+     *
+     * @return a string if available or null otherwise.
+     */
+    String getApplicationTag();
+
+    /**
+     * Returns a string suitable for human display that describes the error
+     * condition.
+     *
+     * @return a message string.
+     */
+    String getMessage();
+
+    /**
+     * Returns a string containing additional information to provide extended
+     * and/or implementation-specific debugging information.
+     *
+     * @return a string if available or null otherwise.
+     */
+    String getInfo();
+
+    /**
+     * Returns an exception cause.
+     *
+     * @return a Throwable if the error was triggered by exception, null otherwise.
+     */
+    Throwable getCause();
+
+    /**
+     * Returns the conceptual layer at which the error occurred.
+     *
+     * @return an {@link ErrorType} enum.
+     */
+    ErrorType getErrorType();
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/RpcResult.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/RpcResult.java
new file mode 100644 (file)
index 0000000..1c4624a
--- /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.common;
+
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Represents a general result of a call, request, or operation.
+ *
+ * @param <T> the result value type
+ */
+public interface RpcResult<T> {
+    /**
+     * Returns whether or not processing of the call was successful.
+     *
+     * @return true if processing was successful, false otherwise.
+     */
+    boolean isSuccessful();
+
+    /**
+     * Returns the value result of the call or null if no result is available.
+     */
+    T getResult();
+
+    /**
+     * Returns a set of errors and warnings which occurred during processing the call.
+     *
+     * @return a list of {@link RpcError}s
+     */
+    @NonNull List<RpcError> getErrors();
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/RpcResultBuilder.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/RpcResultBuilder.java
new file mode 100644 (file)
index 0000000..c47052d
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * 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.yang.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.Futures;
+import java.io.Serializable;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+/**
+ * A builder for creating RpcResult instances.
+ *
+ * @author Thomas Pantelis
+ *
+ * @param <T> the result value type
+ */
+public final class RpcResultBuilder<T> implements Builder<RpcResult<T>> {
+
+    private static class RpcResultImpl<T> implements RpcResult<T>, Serializable {
+        private static final long serialVersionUID = 1L;
+
+        private final ImmutableList<RpcError> errors;
+        private final T result;
+        private final boolean successful;
+
+        RpcResultImpl(final boolean successful, final T result, final ImmutableList<RpcError> errors) {
+            this.successful = successful;
+            this.result = result;
+            this.errors = requireNonNull(errors);
+        }
+
+        @Override
+        public ImmutableList<RpcError> getErrors() {
+            return errors;
+        }
+
+        @Override
+        public T getResult() {
+            return result;
+        }
+
+        @Override
+        public boolean isSuccessful() {
+            return successful;
+        }
+
+        @Override
+        public String toString() {
+            return "RpcResult [successful=" + successful + ", result="
+                    + result + ", errors=" + errors + "]";
+        }
+    }
+
+    private static class RpcErrorImpl implements RpcError, Serializable {
+        private static final long serialVersionUID = 1L;
+
+        private final String applicationTag;
+        private final String tag;
+        private final String info;
+        private final ErrorSeverity severity;
+        private final String message;
+        private final ErrorType errorType;
+        private final Throwable cause;
+
+        RpcErrorImpl(final ErrorSeverity severity, final ErrorType errorType,
+                final String tag, final String message, final String applicationTag, final String info,
+                final Throwable cause) {
+            this.severity = severity;
+            this.errorType = errorType;
+            this.tag = tag;
+            this.message = message;
+            this.applicationTag = applicationTag;
+            this.info = info;
+            this.cause = cause;
+        }
+
+        @Override
+        public String getApplicationTag() {
+            return applicationTag;
+        }
+
+        @Override
+        public String getTag() {
+            return tag;
+        }
+
+        @Override
+        public String getInfo() {
+            return info;
+        }
+
+        @Override
+        public ErrorSeverity getSeverity() {
+            return severity;
+        }
+
+        @Override
+        public String getMessage() {
+            return message;
+        }
+
+        @Override
+        public ErrorType getErrorType() {
+            return errorType;
+        }
+
+        @Override
+        public Throwable getCause() {
+            return cause;
+        }
+
+        @Override
+        public String toString() {
+            return "RpcError [message=" + message + ", severity="
+                    + severity + ", errorType=" + errorType + ", tag=" + tag
+                    + ", applicationTag=" + applicationTag + ", info=" + info
+                    + ", cause=" + cause + "]";
+        }
+    }
+
+    private ImmutableList.Builder<RpcError> errors;
+    private T result;
+    private final boolean successful;
+
+    private RpcResultBuilder(final boolean successful, final T result) {
+        this.successful = successful;
+        this.result = result;
+    }
+
+    /**
+     * Returns a builder for a successful result.
+     */
+    public static <T> @NonNull RpcResultBuilder<T> success() {
+        return new RpcResultBuilder<>(true, null);
+    }
+
+    /**
+     * Returns a builder for a successful result.
+     *
+     * @param result the result value
+     */
+    public static <T> @NonNull RpcResultBuilder<T> success(final T result) {
+        return new RpcResultBuilder<>(true, result);
+    }
+
+    /**
+     * Returns a builder for a successful result.
+     *
+     * @param builder builder for the result value
+     */
+    public static <T> @NonNull RpcResultBuilder<T> success(final Builder<T> builder) {
+        return success(builder.build());
+    }
+
+    /**
+     * Returns a builder for a failed result.
+     */
+    public static <T> @NonNull RpcResultBuilder<T> failed() {
+        return new RpcResultBuilder<>(false, null);
+    }
+
+    /**
+     * Returns a builder based on the given status.
+     *
+     * @param success true if successful, false otherwise.
+     */
+    public static <T> @NonNull RpcResultBuilder<T> status(final boolean success) {
+        return new RpcResultBuilder<>(success, null);
+    }
+
+    /**
+     * Returns a builder from another RpcResult.
+     *
+     * @param other the other RpcResult.
+     */
+    public static <T> @NonNull RpcResultBuilder<T> from(final RpcResult<T> other) {
+        return new RpcResultBuilder<>(other.isSuccessful(), other.getResult())
+                                                      .withRpcErrors(other.getErrors());
+    }
+
+    /**
+     * Creates an RpcError with severity ERROR for reuse.
+     *
+     * @param errorType the conceptual layer at which the error occurred.
+     * @param tag a short string that identifies the general type of error condition. See
+     *        {@link RpcError#getTag} for a list of suggested values.
+     * @param message a string suitable for human display that describes the error condition.
+     *
+     * @return an RpcError
+     */
+    public static @NonNull RpcError newError(final ErrorType errorType, final String tag, final String message) {
+        return new RpcErrorImpl(ErrorSeverity.ERROR, errorType,
+                tag != null ? tag : "operation-failed", message, null, null, null);
+    }
+
+    /**
+     * Creates an RpcError with severity ERROR for reuse.
+     *
+     * @param errorType the conceptual layer at which the error occurred.
+     * @param tag a short string that identifies the general type of error condition. See
+     *        {@link RpcError#getTag} for a list of suggested values.
+     * @param message a string suitable for human display that describes the error condition.
+     * @param applicationTag a short string that identifies the specific type of error condition.
+     * @param info a string containing additional information to provide extended
+     *        and/or implementation-specific debugging information.
+     * @param cause the exception that triggered the error.
+     *
+     * @return an RpcError
+     */
+    public static @NonNull RpcError newError(final ErrorType errorType, final String tag, final String message,
+            final String applicationTag, final String info, final Throwable cause) {
+        return new RpcErrorImpl(ErrorSeverity.ERROR, errorType,
+                tag != null ? tag : "operation-failed", message, applicationTag, info, cause);
+    }
+
+    /**
+     * Creates an RpcError with severity WARNING for reuse.
+     *
+     * @param errorType the conceptual layer at which the warning occurred.
+     * @param tag a short string that identifies the general type of warning condition. See
+     *        {@link RpcError#getTag} for a list of suggested values.
+     * @param message a string suitable for human display that describes the warning condition.
+     *
+     * @return an RpcError
+     */
+    public static @NonNull RpcError newWarning(final ErrorType errorType, final String tag, final String message) {
+        return new RpcErrorImpl(ErrorSeverity.WARNING, errorType, tag, message, null, null, null);
+    }
+
+    /**
+     * Creates an RpcError with severity WARNING for reuse.
+     *
+     * @param errorType the conceptual layer at which the warning occurred.
+     * @param tag a short string that identifies the general type of warning condition. See
+     *        {@link RpcError#getTag} for a list of suggested values.
+     * @param message a string suitable for human display that describes the warning condition.
+     * @param applicationTag a short string that identifies the specific type of warning condition.
+     * @param info a string containing additional information to provide extended
+     *        and/or implementation-specific debugging information.
+     * @param cause the exception that triggered the warning.
+     *
+     * @return an RpcError
+     */
+    public static @NonNull RpcError newWarning(final ErrorType errorType, final String tag, final String message,
+            final String applicationTag, final String info, final Throwable cause) {
+        return new RpcErrorImpl(ErrorSeverity.WARNING, errorType, tag, message,
+                                 applicationTag, info, cause);
+    }
+
+    /**
+     * Sets the value of the result.
+     *
+     * @param result the result value
+     */
+    @SuppressWarnings("checkstyle:hiddenField")
+    public @NonNull RpcResultBuilder<T> withResult(final T result) {
+        this.result = result;
+        return this;
+    }
+
+    /**
+     * Sets the value of the result.
+     *
+     * @param builder builder for the result value
+     */
+    public @NonNull RpcResultBuilder<T> withResult(final Builder<T> builder) {
+        return withResult(builder.build());
+    }
+
+    private void addError(final ErrorSeverity severity, final ErrorType errorType,
+            final String tag, final String message, final String applicationTag, final String info,
+            final Throwable cause) {
+
+        addError(new RpcErrorImpl(severity, errorType,
+                                    tag != null ? tag : "operation-failed", message,
+                                    applicationTag, info, cause));
+    }
+
+    private void addError(final RpcError error) {
+
+        if (errors == null) {
+            errors = new ImmutableList.Builder<>();
+        }
+
+        errors.add(error);
+    }
+
+    /**
+     * Adds a warning to the result.
+     *
+     * @param errorType the conceptual layer at which the warning occurred.
+     * @param tag a short string that identifies the general type of warning condition. See
+     *        {@link RpcError#getTag} for a list of suggested values.
+     * @param message a string suitable for human display that describes the warning condition.
+     */
+    public @NonNull RpcResultBuilder<T> withWarning(final ErrorType errorType, final String tag, final String message) {
+        addError(ErrorSeverity.WARNING, errorType, tag, message, null, null, null);
+        return this;
+    }
+
+    /**
+     * Adds a warning to the result.
+     *
+     * @param errorType the conceptual layer at which the warning occurred.
+     * @param tag a short string that identifies the general type of warning condition. See
+     *        {@link RpcError#getTag} for a list of suggested values.
+     * @param message a string suitable for human display that describes the warning condition.
+     * @param applicationTag a short string that identifies the specific type of warning condition.
+     * @param info a string containing additional information to provide extended
+     *        and/or implementation-specific debugging information.
+     * @param cause the exception that triggered the warning.
+     */
+    public @NonNull RpcResultBuilder<T> withWarning(final ErrorType errorType, final String tag, final String message,
+            final String applicationTag, final String info, final Throwable cause) {
+        addError(ErrorSeverity.WARNING, errorType, tag, message, applicationTag, info, cause);
+        return this;
+    }
+
+    /**
+     * Adds an error to the result. The general error tag defaults to "operation-failed".
+     *
+     * @param errorType the conceptual layer at which the error occurred.
+     * @param message a string suitable for human display that describes the error condition.
+     */
+    public @NonNull RpcResultBuilder<T> withError(final ErrorType errorType, final String message) {
+        addError(ErrorSeverity.ERROR, errorType, null, message, null, null, null);
+        return this;
+    }
+
+    /**
+     * Adds an error to the result.
+     *
+     * @param errorType the conceptual layer at which the error occurred.
+     * @param tag a short string that identifies the general type of error condition. See
+     *        {@link RpcError#getTag} for a list of suggested values.
+     * @param message a string suitable for human display that describes the error condition.
+     */
+    public @NonNull RpcResultBuilder<T> withError(final ErrorType errorType, final String tag, final String message) {
+        addError(ErrorSeverity.ERROR, errorType, tag, message, null, null, null);
+        return this;
+    }
+
+    /**
+     * Adds an error to the result. The general error tag defaults to "operation-failed".
+     *
+     * @param errorType the conceptual layer at which the error occurred.
+     * @param message a string suitable for human display that describes the error condition.
+     * @param cause the exception that triggered the error.
+     */
+    public @NonNull RpcResultBuilder<T> withError(final ErrorType errorType, final String message,
+                                          final Throwable cause) {
+        addError(ErrorSeverity.ERROR, errorType, null, message, null, null, cause);
+        return this;
+    }
+
+    /**
+     * Adds an error to the result.
+     *
+     * @param errorType the conceptual layer at which the error occurred.
+     * @param tag a short string that identifies the general type of error condition. See
+     *        {@link RpcError#getTag} for a list of suggested values.
+     * @param message a string suitable for human display that describes the error condition.
+     * @param applicationTag a short string that identifies the specific type of error condition.
+     * @param info a string containing additional information to provide extended
+     *        and/or implementation-specific debugging information.
+     * @param cause the exception that triggered the error.
+     */
+    public @NonNull RpcResultBuilder<T> withError(final ErrorType errorType, final String tag, final String message,
+            final String applicationTag, final String info, final Throwable cause) {
+        addError(ErrorSeverity.ERROR, errorType, tag, message, applicationTag, info, cause);
+        return this;
+    }
+
+    /**
+     * Adds an RpcError.
+     *
+     * @param error the RpcError
+     */
+    public @NonNull RpcResultBuilder<T> withRpcError(final RpcError error) {
+        addError(error);
+        return this;
+    }
+
+    /**
+     * Adds RpcErrors.
+     *
+     * @param rpcErrors the list of RpcErrors
+     */
+    public RpcResultBuilder<T> withRpcErrors(final Collection<? extends RpcError> rpcErrors) {
+        if (rpcErrors != null) {
+            for (RpcError error : rpcErrors) {
+                addError(error);
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public RpcResult<T> build() {
+        return new RpcResultImpl<>(successful, result, errors != null ? errors.build() : ImmutableList.of());
+    }
+
+    /**
+     * Builds RpcResult and wraps it in a Future.
+     *
+     * <p>
+     * This is a convenience method to assist those writing RPCs that produce immediate results. It allows you to
+     * replace {@code FluentFuture.from(Futures.immediateFuture(rpcResult.build()))} with
+     * {@code rpcResult.buildFuture()}
+     *
+     * @return Future for RpcResult built by RpcResultBuilder
+     */
+    public @NonNull FluentFuture<RpcResult<T>> buildFuture() {
+        return FluentFuture.from(Futures.immediateFuture(build()));
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/UQNv1.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/UQNv1.java
new file mode 100644 (file)
index 0000000..a0955ae
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.common;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+/**
+ * Externalizable proxy for {@link UnqualifiedQName}.
+ */
+final class UQNv1 implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private UnqualifiedQName qname;
+
+    @SuppressWarnings("checkstyle:redundantModifier")
+    public UQNv1() {
+        // For Externalizable
+    }
+
+    UQNv1(final UnqualifiedQName qname) {
+        this.qname = requireNonNull(qname);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        qname.writeTo(out);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        qname = UnqualifiedQName.readFrom(in);
+    }
+
+    Object readResolve() {
+        return verifyNotNull(qname);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Uint16.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Uint16.java
new file mode 100644 (file)
index 0000000..4915598
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Variant;
+
+/**
+ * Dedicated type for YANG's 'type uint16' type.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public class Uint16 extends Number implements CanonicalValue<Uint16> {
+    public static final class Support extends AbstractCanonicalValueSupport<Uint16> {
+        public Support() {
+            super(Uint16.class);
+        }
+
+        @Override
+        public Variant<Uint16, CanonicalValueViolation> fromString(final String str) {
+            try {
+                return Variant.ofFirst(Uint16.valueOf(str));
+            } catch (IllegalArgumentException e) {
+                return CanonicalValueViolation.variantOf(e);
+            }
+        }
+    }
+
+    private static final CanonicalValueSupport<Uint16> SUPPORT = new Support();
+    private static final long serialVersionUID = 1L;
+    private static final int MAX_VALUE_INT = 65535;
+    private static final String MAX_VALUE_STR = "65535";
+
+    private static final String CACHE_SIZE_PROPERTY = "org.opendaylight.yangtools.yang.common.Uint16.cache.size";
+    private static final int DEFAULT_CACHE_SIZE = 256;
+
+    /**
+     * Tunable cache for values. By default it holds {@value #DEFAULT_CACHE_SIZE} entries. This can be
+     * changed via {@value #CACHE_SIZE_PROPERTY} system property.
+     */
+    private static final int CACHE_SIZE;
+
+    static {
+        final int p = Integer.getInteger(CACHE_SIZE_PROPERTY, DEFAULT_CACHE_SIZE);
+        CACHE_SIZE = p >= 0 ? Math.min(p, MAX_VALUE_INT + 1) : DEFAULT_CACHE_SIZE;
+    }
+
+    private static final @NonNull Uint16[] CACHE;
+
+    static {
+        final Uint16[] c = new Uint16[CACHE_SIZE];
+        for (int i = 0; i < c.length; ++i) {
+            c[i] = new Uint16((short) i);
+        }
+        CACHE = c;
+    }
+
+    private static final Interner<Uint16> INTERNER = Interners.newWeakInterner();
+
+    /**
+     * Value of {@code 0}.
+     */
+    public static final Uint16 ZERO = valueOf(0).intern();
+    /**
+     * Value of {@code 1}.
+     */
+    public static final Uint16 ONE = valueOf(1).intern();
+    /**
+     * Value of {@code 2}.
+     */
+    public static final Uint16 TWO = valueOf(2).intern();
+    /**
+     * Value of {@code 10}.
+     */
+    public static final Uint16 TEN = valueOf(10).intern();
+    /**
+     * Value of {@code 65535}.
+     */
+    public static final Uint16 MAX_VALUE = valueOf(MAX_VALUE_INT).intern();
+
+    private final short value;
+
+    private Uint16(final short value) {
+        this.value = value;
+    }
+
+    protected Uint16(final Uint16 other) {
+        this(other.value);
+    }
+
+    private static Uint16 instanceFor(final short value) {
+        final int slot = Short.toUnsignedInt(value);
+        return slot < CACHE.length ? CACHE[slot] : new Uint16(value);
+    }
+
+    /**
+     * Returns an {@code Uint16} corresponding to a given bit representation. The argument is interpreted as an
+     * unsigned 16-bit value.
+     *
+     * @param bits unsigned bit representation
+     * @return A Uint16 instance
+     */
+    public static Uint16 fromShortBits(final short bits) {
+        return instanceFor(bits);
+    }
+
+    /**
+     * Returns an {@code Uint16} corresponding to a given {@code byteVal}. The inverse operation is
+     * {@link #byteValue()}.
+     *
+     * @param byteVal byte value
+     * @return A Uint16 instance
+     * @throws IllegalArgumentException if byteVal is less than zero
+     */
+    public static Uint16 valueOf(final byte byteVal) {
+        UintConversions.checkNonNegative(byteVal, MAX_VALUE_STR);
+        return instanceFor(byteVal);
+    }
+
+    /**
+     * Returns an {@code Uint16} corresponding to a given {@code shortVal}. The inverse operation is
+     * {@link #shortValue()}.
+     *
+     * @param shortVal short value
+     * @return A Uint16 instance
+     * @throws IllegalArgumentException if shortVal is less than zero.
+     */
+    public static Uint16 valueOf(final short shortVal) {
+        UintConversions.checkNonNegative(shortVal, MAX_VALUE_STR);
+        return instanceFor(shortVal);
+    }
+
+    /**
+     * Returns an {@code Uint16} corresponding to a given {@code intVal}. The inverse operation is {@link #intValue()}.
+     *
+     * @param intVal int value
+     * @return A Uint16 instance
+     * @throws IllegalArgumentException if intVal is less than zero or greater than 65535.
+     */
+    public static Uint16 valueOf(final int intVal) {
+        UintConversions.checkRange(intVal, MAX_VALUE_INT);
+        return instanceFor((short)intVal);
+    }
+
+    /**
+     * Returns an {@code Uint16} corresponding to a given {@code longVal}. The inverse operation is
+     * {@link #longValue()}.
+     *
+     * @param longVal long value
+     * @return A Uint16 instance
+     * @throws IllegalArgumentException if intVal is less than zero or greater than 65535.
+     */
+    public static Uint16 valueOf(final long longVal) {
+        UintConversions.checkRange(longVal, MAX_VALUE_INT);
+        return instanceFor((short)longVal);
+    }
+
+    /**
+     * Returns an {@code Uint16} corresponding to a given {@code uint}.
+     *
+     * @param uint Uint8 value
+     * @return A Uint16 instance
+     * @throws NullPointerException if uint is null
+     */
+    public static Uint16 valueOf(final Uint8 uint) {
+        return instanceFor(uint.shortValue());
+    }
+
+    /**
+     * Returns an {@code Uint8} corresponding to a given {@code uint}.
+     *
+     * @param uint Uint32 value
+     * @return A Uint16 instance
+     * @throws NullPointerException if uint is null
+     * @throws IllegalArgumentException if uint is greater than 65535.
+     */
+    public static Uint16 valueOf(final Uint32 uint) {
+        return valueOf(uint.longValue());
+    }
+
+    /**
+     * Returns an {@code Uint8} corresponding to a given {@code uint}.
+     *
+     * @param uint Uint64 value
+     * @return A Uint16 instance
+     * @throws NullPointerException if uint is null
+     * @throws IllegalArgumentException if uint is greater than 65535.
+     */
+    public static Uint16 valueOf(final Uint64 uint) {
+        return valueOf(uint.longValue());
+    }
+
+    /**
+     * Returns an {@code Uint16} holding the value of the specified {@code String}, parsed as an unsigned {@code int}
+     * value.
+     *
+     * @param string String to parse
+     * @return A Uint16 instance
+     * @throws NullPointerException if string is null
+     * @throws IllegalArgumentException if the parsed value is less than zero or greater than 65535
+     * @throws NumberFormatException if the string does not contain a parsable unsigned {@code int} value.
+     */
+    public static Uint16 valueOf(final String string) {
+        return valueOf(string, 10);
+    }
+
+    /**
+     * Returns an {@code Uint16} holding the value of the specified {@code String}, parsed as an unsigned {@code int}
+     * value.
+     *
+     * @param string String to parse
+     * @param radix Radix to use
+     * @return A Uint16 instance
+     * @throws NullPointerException if string is null
+     * @throws IllegalArgumentException if the parsed value is less than zero or greater than 65535
+     * @throws NumberFormatException if the string does not contain a parsable unsigned {@code int} value, or if the
+     *                               {@code radix} is outside of allowed range.
+     */
+    public static Uint16 valueOf(final String string, final int radix) {
+        return valueOf(Integer.parseInt(requireNonNull(string), radix));
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>
+     * The inverse operation is {@link #fromShortBits(short)}. In case this value is greater than
+     * {@link Short#MAX_VALUE}, the returned value will be equal to {@code this - 2^16}.
+     */
+    @Override
+    public final short shortValue() {
+        return value;
+    }
+
+    @Override
+    public final int intValue() {
+        return Short.toUnsignedInt(value);
+    }
+
+    @Override
+    public final long longValue() {
+        return Short.toUnsignedLong(value);
+    }
+
+    @Override
+    public final float floatValue() {
+        return intValue();
+    }
+
+    @Override
+    public final double doubleValue() {
+        return intValue();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final int compareTo(final Uint16 o) {
+        return Short.compareUnsigned(value, o.value);
+    }
+
+    @Override
+    public final String toCanonicalString() {
+        return Integer.toString(intValue());
+    }
+
+    @Override
+    public final CanonicalValueSupport<Uint16> support() {
+        return SUPPORT;
+    }
+
+    /**
+     * Return an interned (shared) instance equivalent to this object. This may return the same object.
+     *
+     * @return A shared instance.
+     */
+    public final Uint16 intern() {
+        return intValue() < CACHE_SIZE ? this : INTERNER.intern(this);
+    }
+
+    /**
+     * Convert this value to an {@code int}.
+     *
+     * @return An int
+     */
+    public final int toJava() {
+        return intValue();
+    }
+
+    /**
+     * Convert this value to a {@code Uint8}.
+     *
+     * @return A Uint8
+     * @throws IllegalArgumentException if this value is greater than 255.
+     */
+    public final Uint8 toUint8() {
+        return Uint8.valueOf(toJava());
+    }
+
+    /**
+     * Convert this value to a {@code Uint32}.
+     *
+     * @return A Uint32
+     */
+    public final Uint32 toUint32() {
+        return Uint32.fromIntBits(intValue());
+    }
+
+    /**
+     * Convert this value to a {@code Uint64}.
+     *
+     * @return A Uint64
+     */
+    public final Uint64 toUint64() {
+        return Uint64.fromLongBits(longValue());
+    }
+
+    @Override
+    public final int hashCode() {
+        return Short.hashCode(value);
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        return this == obj || obj instanceof Uint16 && value == ((Uint16)obj).value;
+    }
+
+    /**
+     * A slightly faster version of {@link #equals(Object)}.
+     *
+     * @param obj Uint16 object
+     * @return  {@code true} if this object is the same as the obj argument; {@code false} otherwise.
+     */
+    public final boolean equals(final @Nullable Uint16 obj) {
+        return this == obj || obj != null && value == obj.value;
+    }
+
+    @Override
+    public final String toString() {
+        return toCanonicalString();
+    }
+
+    private Object readResolve() {
+        return instanceFor(value);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Uint32.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Uint32.java
new file mode 100644 (file)
index 0000000..6b3b392
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import com.google.common.primitives.UnsignedInteger;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Variant;
+
+/**
+ * Dedicated type for YANG's 'type uint32' type.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public class Uint32 extends Number implements CanonicalValue<Uint32> {
+    public static final class Support extends AbstractCanonicalValueSupport<Uint32> {
+        public Support() {
+            super(Uint32.class);
+        }
+
+        @Override
+        public Variant<Uint32, CanonicalValueViolation> fromString(final String str) {
+            try {
+                return Variant.ofFirst(Uint32.valueOf(str));
+            } catch (IllegalArgumentException e) {
+                return CanonicalValueViolation.variantOf(e);
+            }
+        }
+    }
+
+    private static final CanonicalValueSupport<Uint32> SUPPORT = new Support();
+    private static final long serialVersionUID = 1L;
+    private static final long MAX_VALUE_LONG = 4294967295L;
+    private static final String MAX_VALUE_STR = "4294967295";
+
+    private static final String CACHE_SIZE_PROPERTY = "org.opendaylight.yangtools.yang.common.Uint32.cache.size";
+    private static final int DEFAULT_CACHE_SIZE = 256;
+
+    /**
+     * Tunable cache for values. By default it holds {@value #DEFAULT_CACHE_SIZE} entries. This can be
+     * changed via {@value #CACHE_SIZE_PROPERTY} system property.
+     */
+    private static final int CACHE_SIZE;
+
+    static {
+        final int p = Integer.getInteger(CACHE_SIZE_PROPERTY, DEFAULT_CACHE_SIZE);
+        CACHE_SIZE = p >= 0 ? Math.min(p, Integer.MAX_VALUE) : DEFAULT_CACHE_SIZE;
+    }
+
+    private static final @NonNull Uint32[] CACHE;
+
+    static {
+        final Uint32[] c = new Uint32[CACHE_SIZE];
+        for (int i = 0; i < c.length; ++i) {
+            c[i] = new Uint32(i);
+        }
+        CACHE = c;
+    }
+
+    private static final Interner<Uint32> INTERNER = Interners.newWeakInterner();
+
+    /**
+     * Value of {@code 0}.
+     */
+    public static final Uint32 ZERO = valueOf(0).intern();
+    /**
+     * Value of {@code 1}.
+     */
+    public static final Uint32 ONE = valueOf(1).intern();
+    /**
+     * Value of {@code 2}.
+     */
+    public static final Uint32 TWO = valueOf(2).intern();
+    /**
+     * Value of {@code 10}.
+     */
+    public static final Uint32 TEN = valueOf(10).intern();
+    /**
+     * Value of {@code 4294967295}.
+     */
+    public static final Uint32 MAX_VALUE = valueOf(MAX_VALUE_LONG).intern();
+
+    private final int value;
+
+    private Uint32(final int value) {
+        this.value = value;
+    }
+
+    protected Uint32(final Uint32 other) {
+        this(other.value);
+    }
+
+    private static Uint32 instanceFor(final int value) {
+        final long longSlot = Integer.toUnsignedLong(value);
+        return longSlot < CACHE.length ? CACHE[(int)longSlot] : new Uint32(value);
+    }
+
+    /**
+     * Returns an {@code Uint32} corresponding to a given bit representation. The argument is interpreted as an
+     * unsigned 32-bit value.
+     *
+     * @param bits unsigned bit representation
+     * @return A Uint32 instance
+     */
+    public static Uint32 fromIntBits(final int bits) {
+        return instanceFor(bits);
+    }
+
+    /**
+     * Returns an {@code Uint32} corresponding to a given {@code byteVal}. The inverse operation is
+     * {@link #byteValue()}.
+     *
+     * @param byteVal byte value
+     * @return A Uint32 instance
+     * @throws IllegalArgumentException if byteVal is less than zero
+     */
+    public static Uint32 valueOf(final byte byteVal) {
+        UintConversions.checkNonNegative(byteVal, MAX_VALUE_STR);
+        return instanceFor(byteVal);
+    }
+
+    /**
+     * Returns an {@code Uint32} corresponding to a given {@code shortVal}. The inverse operation is
+     * {@link #shortValue()}.
+     *
+     * @param shortVal short value
+     * @return A Uint32 instance
+     * @throws IllegalArgumentException if shortVal is less than zero
+     */
+    public static Uint32 valueOf(final short shortVal) {
+        UintConversions.checkNonNegative(shortVal, MAX_VALUE_STR);
+        return instanceFor(shortVal);
+    }
+
+    /**
+     * Returns an {@code Uint32} corresponding to a given {@code intVal}. The inverse operation is {@link #intValue()}.
+     *
+     * @param intVal int value
+     * @return A Uint32 instance
+     * @throws IllegalArgumentException if intVal is less than zero
+     */
+    public static Uint32 valueOf(final int intVal) {
+        UintConversions.checkNonNegative(intVal, MAX_VALUE_STR);
+        return instanceFor(intVal);
+    }
+
+    /**
+     * Returns an {@code Uint32} corresponding to a given {@code longVal}. The inverse operation is
+     * {@link #longValue()}.
+     *
+     * @param longVal long value
+     * @return A Uint8 instance
+     * @throws IllegalArgumentException if longVal is less than zero or greater than 4294967295
+     */
+    public static Uint32 valueOf(final long longVal) {
+        UintConversions.checkRange(longVal, MAX_VALUE_LONG);
+        return instanceFor((int)longVal);
+    }
+
+    /**
+     * Returns an {@code Uint32} corresponding to a given {@code uint}.
+     *
+     * @param uint Uint8 value
+     * @return A Uint32 instance
+     * @throws NullPointerException if uint is null
+     */
+    public static Uint32 valueOf(final Uint8 uint) {
+        return instanceFor(uint.shortValue());
+    }
+
+    /**
+     * Returns an {@code Uint32} corresponding to a given {@code uint}.
+     *
+     * @param uint Uint16 value
+     * @return A Uint32 instance
+     * @throws NullPointerException if uint is null
+     */
+    public static Uint32 valueOf(final Uint16 uint) {
+        return instanceFor(uint.intValue());
+    }
+
+    /**
+     * Returns an {@code Uint32} corresponding to a given {@code uint}.
+     *
+     * @param uint Uint64 value
+     * @return A Uint32 instance
+     * @throws NullPointerException if uint is null
+     * @throws IllegalArgumentException if uint is greater than 4294967295
+     */
+    public static Uint32 valueOf(final Uint64 uint) {
+        return valueOf(uint.longValue());
+    }
+
+    /**
+     * Returns an {@code Uint32} corresponding to a given {@code uint}.
+     *
+     * @param uint UnsignedInteger value
+     * @return A Uint32 instance
+     * @throws NullPointerException if uint is null
+     */
+    public static Uint32 valueOf(final UnsignedInteger uint) {
+        return instanceFor(uint.intValue());
+    }
+
+    /**
+     * Returns an {@code Uint32} holding the value of the specified {@code String}, parsed as an unsigned {@code long}
+     * value.
+     *
+     * @param string String to parse
+     * @return A Uint32 instance
+     * @throws NullPointerException if string is null
+     * @throws IllegalArgumentException if the parsed value is less than zero or greater than 4294967295
+     * @throws NumberFormatException if the string does not contain a parsable unsigned {@code long} value.
+     */
+    public static Uint32 valueOf(final String string) {
+        return valueOf(string, 10);
+    }
+
+    /**
+     * Returns an {@code Uint32} holding the value of the specified {@code String}, parsed as an unsigned {@code long}
+     * value.
+     *
+     * @param string String to parse
+     * @param radix Radix to use
+     * @return A Uint32 instance
+     * @throws NullPointerException if string is null
+     * @throws IllegalArgumentException if the parsed value is less than zero or greater than 4294967295
+     * @throws NumberFormatException if the string does not contain a parsable unsigned {@code long} value, or if the
+     *                               {@code radix} is outside of allowed range.
+     */
+    public static Uint32 valueOf(final String string, final int radix) {
+        return instanceFor(Integer.parseUnsignedInt(requireNonNull(string), radix));
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>
+     * The inverse operation is {@link #fromIntBits(int)}. In case this value is greater than {@link Integer#MAX_VALUE},
+     * the returned value will be equal to {@code this - 2^32}.
+     */
+    @Override
+    public final int intValue() {
+        return value;
+    }
+
+    @Override
+    public final long longValue() {
+        return Integer.toUnsignedLong(value);
+    }
+
+    @Override
+    public final float floatValue() {
+        return longValue();
+    }
+
+    @Override
+    public final double doubleValue() {
+        return longValue();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final int compareTo(final Uint32 o) {
+        return Integer.compareUnsigned(value, o.value);
+    }
+
+    @Override
+    public final String toCanonicalString() {
+        return Integer.toUnsignedString(value);
+    }
+
+    @Override
+    public final CanonicalValueSupport<Uint32> support() {
+        return SUPPORT;
+    }
+
+    /**
+     * Return an interned (shared) instance equivalent to this object. This may return the same object.
+     *
+     * @return A shared instance.
+     */
+    public final Uint32 intern() {
+        return value >= 0 && value < CACHE_SIZE ? this : INTERNER.intern(this);
+    }
+
+    /**
+     * Convert this value to a {@code long}.
+     *
+     * @return A long
+     */
+    public final long toJava() {
+        return longValue();
+    }
+
+    /**
+     * Convert this value to an {@link UnsignedInteger}.
+     *
+     * @return An UnsignedInteger instance
+     */
+    public final UnsignedInteger toGuava() {
+        return UnsignedInteger.fromIntBits(value);
+    }
+
+    /**
+     * Convert this value to a {@code Uint8}.
+     *
+     * @return A Uint8
+     * @throws IllegalArgumentException if this value is greater than 255.
+     */
+    public final Uint8 toUint8() {
+        return Uint8.valueOf(toJava());
+    }
+
+    /**
+     * Convert this value to a {@code Uint16}.
+     *
+     * @return A Uint16
+     * @throws IllegalArgumentException if this value is greater than 65535.
+     */
+    public final Uint16 toUint16() {
+        return Uint16.valueOf(toJava());
+    }
+
+    /**
+     * Convert this value to a {@code Uint64}.
+     *
+     * @return A Uint64
+     */
+    public final Uint64 toUint64() {
+        return Uint64.fromLongBits(longValue());
+    }
+
+    @Override
+    public final int hashCode() {
+        return Integer.hashCode(value);
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        return this == obj || obj instanceof Uint32 && value == ((Uint32)obj).value;
+    }
+
+    /**
+     * A slightly faster version of {@link #equals(Object)}.
+     *
+     * @param obj Uint32 object
+     * @return {@code true} if this object is the same as the obj argument; {@code false} otherwise.
+     */
+    public final boolean equals(final @Nullable Uint32 obj) {
+        return this == obj || obj != null && value == obj.value;
+    }
+
+    @Override
+    public final String toString() {
+        return toCanonicalString();
+    }
+
+    private Object readResolve() {
+        return instanceFor(value);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Uint64.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Uint64.java
new file mode 100644 (file)
index 0000000..6d537fd
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import com.google.common.primitives.UnsignedLong;
+import java.math.BigInteger;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Variant;
+
+/**
+ * Dedicated type for YANG's 'type uint64' type.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public class Uint64 extends Number implements CanonicalValue<Uint64> {
+    public static final class Support extends AbstractCanonicalValueSupport<Uint64> {
+        public Support() {
+            super(Uint64.class);
+        }
+
+        @Override
+        public Variant<Uint64, CanonicalValueViolation> fromString(final String str) {
+            try {
+                return Variant.ofFirst(Uint64.valueOf(str));
+            } catch (IllegalArgumentException e) {
+                return CanonicalValueViolation.variantOf(e);
+            }
+        }
+    }
+
+    private static final CanonicalValueSupport<Uint64> SUPPORT = new Support();
+    private static final long serialVersionUID = 1L;
+    private static final String MAX_VALUE_STR = Long.toUnsignedString(-1);
+
+    private static final String CACHE_SIZE_PROPERTY = "org.opendaylight.yangtools.yang.common.Uint64.cache.size";
+    private static final int DEFAULT_CACHE_SIZE = 256;
+
+    /**
+     * Tunable cache for values. By default it holds {@value #DEFAULT_CACHE_SIZE} entries. This can be
+     * changed via {@value #CACHE_SIZE_PROPERTY} system property.
+     */
+    private static final long CACHE_SIZE;
+
+    static {
+        final int p = Integer.getInteger(CACHE_SIZE_PROPERTY, DEFAULT_CACHE_SIZE);
+        CACHE_SIZE = p >= 0 ? Math.min(p, Integer.MAX_VALUE) : DEFAULT_CACHE_SIZE;
+    }
+
+    private static final @NonNull Uint64[] CACHE;
+
+    static {
+        final Uint64[] c = new Uint64[(int) CACHE_SIZE];
+        for (int i = 0; i < c.length; ++i) {
+            c[i] = new Uint64(i);
+        }
+        CACHE = c;
+    }
+
+    private static final Interner<Uint64> INTERNER = Interners.newWeakInterner();
+
+    /**
+     * Value of {@code 0}.
+     */
+    public static final Uint64 ZERO = valueOf(0).intern();
+    /**
+     * Value of {@code 1}.
+     */
+    public static final Uint64 ONE = valueOf(1).intern();
+    /**
+     * Value of {@code 2}.
+     */
+    public static final Uint64 TWO = valueOf(2).intern();
+    /**
+     * Value of {@code 10}.
+     */
+    public static final Uint64 TEN = valueOf(10).intern();
+    /**
+     * Value of {@code 18446744073709551615}.
+     */
+    public static final Uint64 MAX_VALUE = fromLongBits(-1).intern();
+
+    private final long value;
+
+    private Uint64(final long value) {
+        this.value = value;
+    }
+
+    protected Uint64(final Uint64 other) {
+        this(other.value);
+    }
+
+    private static Uint64 instanceFor(final long value) {
+        return value >= 0 && value < CACHE.length ? CACHE[(int) value] : new Uint64(value);
+    }
+
+    /**
+     * Returns an {@code Uint64} corresponding to a given bit representation. The argument is interpreted as an
+     * unsigned 64-bit value.
+     *
+     * @param bits unsigned bit representation
+     * @return A Uint64 instance
+     */
+    public static Uint64 fromLongBits(final long bits) {
+        return instanceFor(bits);
+    }
+
+    /**
+     * Returns an {@code Uint64} corresponding to a given {@code byteVal}. The inverse operation is
+     * {@link #byteValue()}.
+     *
+     * @param byteVal byte value
+     * @return A Uint64 instance
+     * @throws IllegalArgumentException if byteVal is less than zero
+     */
+    public static Uint64 valueOf(final byte byteVal) {
+        UintConversions.checkNonNegative(byteVal, MAX_VALUE_STR);
+        return instanceFor(byteVal);
+    }
+
+    /**
+     * Returns an {@code Uint64} corresponding to a given {@code shortVal}. The inverse operation is
+     * {@link #shortValue()}.
+     *
+     * @param shortVal short value
+     * @return A Uint64 instance
+     * @throws IllegalArgumentException if shortVal is less than zero
+     */
+    public static Uint64 valueOf(final short shortVal) {
+        UintConversions.checkNonNegative(shortVal, MAX_VALUE_STR);
+        return instanceFor(shortVal);
+    }
+
+    /**
+     * Returns an {@code Uint64} corresponding to a given {@code intVal}. The inverse operation is {@link #intValue()}.
+     *
+     * @param intVal int value
+     * @return A Uint64 instance
+     * @throws IllegalArgumentException if intVal is less than zero
+     */
+    public static Uint64 valueOf(final int intVal) {
+        UintConversions.checkNonNegative(intVal, MAX_VALUE_STR);
+        return instanceFor(intVal);
+    }
+
+    /**
+     * Returns an {@code Uint64} corresponding to a given {@code longVal}, which is checked for range.
+     * See also {@link #longValue()} and {@link #fromLongBits(long)}.
+     *
+     * @param longVal long value
+     * @return A Uint8 instance
+     * @throws IllegalArgumentException if longVal is less than zero
+     */
+    public static Uint64 valueOf(final long longVal) {
+        if (longVal >= 0) {
+            return instanceFor(longVal);
+        }
+        throw new IllegalArgumentException("Invalid range: " + longVal + ", expected: [[0..18446744073709551615]].");
+    }
+
+    /**
+     * Returns an {@code Uint64} corresponding to a given {@code uint}.
+     *
+     * @param uint Uint8 value
+     * @return A Uint64 instance
+     * @throws NullPointerException if uint is null
+     */
+    public static Uint64 valueOf(final Uint8 uint) {
+        return instanceFor(uint.shortValue());
+    }
+
+    /**
+     * Returns an {@code Uint64} corresponding to a given {@code uint}.
+     *
+     * @param uint Uint16 value
+     * @return A Uint64 instance
+     * @throws NullPointerException if uint is null
+     */
+    public static Uint64 valueOf(final Uint16 uint) {
+        return instanceFor(uint.intValue());
+    }
+
+    /**
+     * Returns an {@code Uint64} corresponding to a given {@code uint}.
+     *
+     * @param uint Uint32 value
+     * @return A Uint64 instance
+     * @throws NullPointerException if uint is null
+     */
+    public static Uint64 valueOf(final Uint32 uint) {
+        return instanceFor(uint.longValue());
+    }
+
+    /**
+     * Returns an {@code Uint64} corresponding to a given {@code ulong}.
+     *
+     * @param ulong UnsignedLong value
+     * @return A Uint64 instance
+     * @throws NullPointerException if ulong is null
+     */
+    public static Uint64 valueOf(final UnsignedLong ulong) {
+        return instanceFor(ulong.longValue());
+    }
+
+    /**
+     * Returns an {@code Uint64} corresponding to a given {@code bigInt}.
+     *
+     * @param bigInt BigInteger value
+     * @return A Uint64 instance
+     * @throws NullPointerException if bigInt is null
+     * @throws IllegalArgumentException if bigInt is less than zero or greater than 18446744073709551615
+     */
+    public static Uint64 valueOf(final BigInteger bigInt) {
+        if (bigInt.signum() >= 0 && bigInt.bitLength() <= Long.SIZE) {
+            return instanceFor(bigInt.longValue());
+        }
+        throw new IllegalArgumentException("Invalid range: " + bigInt + ", expected: [[0..18446744073709551615]].");
+    }
+
+    /**
+     * Returns an {@code Uint32} holding the value of the specified {@code String}, parsed as an unsigned {@code long}
+     * value.
+     *
+     * @param string String to parse
+     * @return A Uint64 instance
+     * @throws NullPointerException if string is null
+     * @throws IllegalArgumentException if the parsed value is less than zero or greater than 18446744073709551615
+     * @throws NumberFormatException if the string does not contain a parsable unsigned {@code long} value.
+     */
+    public static Uint64 valueOf(final String string) {
+        return valueOf(string, 10);
+    }
+
+    /**
+     * Returns an {@code Uint64} holding the value of the specified {@code String}, parsed as an unsigned {@code long}
+     * value.
+     *
+     * @param string String to parse
+     * @param radix Radix to use
+     * @return A Uint64 instance
+     * @throws NullPointerException if string is null
+     * @throws IllegalArgumentException if the parsed value is less than zero or greater than 18446744073709551615
+     * @throws NumberFormatException if the string does not contain a parsable unsigned {@code long} value, or if the
+     *                               {@code radix} is outside of allowed range.
+     */
+    public static Uint64 valueOf(final String string, final int radix) {
+        return instanceFor(Long.parseUnsignedLong(requireNonNull(string), radix));
+    }
+
+    @Override
+    public final int intValue() {
+        return (int)value;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>
+     * The inverse operation is {@link #fromLongBits(long)}. In case this value is greater than {@link Long#MAX_VALUE},
+     * the returned value will be equal to {@code this - 2^64}.
+     */
+    @Override
+    public final long longValue() {
+        return value;
+    }
+
+    @Override
+    public final float floatValue() {
+        // TODO: ditch Guava
+        return UnsignedLong.fromLongBits(value).floatValue();
+    }
+
+    @Override
+    public final double doubleValue() {
+        // TODO: ditch Guava
+        return UnsignedLong.fromLongBits(value).doubleValue();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final int compareTo(final Uint64 o) {
+        return Long.compareUnsigned(value, o.value);
+    }
+
+    @Override
+    public final String toCanonicalString() {
+        return Long.toUnsignedString(value);
+    }
+
+    @Override
+    public final CanonicalValueSupport<Uint64> support() {
+        return SUPPORT;
+    }
+
+    /**
+     * Return an interned (shared) instance equivalent to this object. This may return the same object.
+     *
+     * @return A shared instance.
+     */
+    public final Uint64 intern() {
+        return value >= 0 && value < CACHE_SIZE ? this : INTERNER.intern(this);
+    }
+
+    /**
+     * Convert this value to a {@link BigInteger}.
+     *
+     * @return A BigInteger instance
+     */
+    public final BigInteger toJava() {
+        // FIXME: ditch the Guava transition
+        return toGuava().bigIntegerValue();
+    }
+
+    /**
+     * Convert this value to an {@link UnsignedLong}.
+     *
+     * @return An UnsignedLong instance
+     */
+    public final UnsignedLong toGuava() {
+        return UnsignedLong.fromLongBits(value);
+    }
+
+    /**
+     * Convert this value to a {@code Uint8}.
+     *
+     * @return A Uint8
+     * @throws IllegalArgumentException if this value is greater than 255.
+     */
+    public final Uint8 toUint8() {
+        if ((value & 0xFFFFFFFFFFFFFF00L) != 0) {
+            UintConversions.throwIAE(toString(), 255);
+        }
+        return Uint8.fromByteBits((byte) value);
+    }
+
+    /**
+     * Convert this value to a {@code Uint16}.
+     *
+     * @return A Uint16
+     * @throws IllegalArgumentException if this value is greater than 65535.
+     */
+    public final Uint16 toUint16() {
+        if ((value & 0xFFFFFFFFFFFF0000L) != 0) {
+            UintConversions.throwIAE(toString(), 65535);
+        }
+        return Uint16.fromShortBits((short) value);
+    }
+
+    /**
+     * Convert this value to a {@code Uint64}.
+     *
+     * @return A Uint32
+     * @throws IllegalArgumentException if this value is greater than 4294967295.
+     */
+    public final Uint32 toUint32() {
+        if ((value & 0xFFFFFFFF00000000L) != 0) {
+            UintConversions.throwIAE(toString(), 4294967295L);
+        }
+        return Uint32.fromIntBits((int) value);
+    }
+
+    @Override
+    public final int hashCode() {
+        return Long.hashCode(value);
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        return this == obj || obj instanceof Uint64 && value == ((Uint64)obj).value;
+    }
+
+    /**
+     * A slightly faster version of {@link #equals(Object)}.
+     *
+     * @param obj Uint64 object
+     * @return {@code true} if this object is the same as the obj argument; {@code false} otherwise.
+     */
+    public final boolean equals(final @Nullable Uint64 obj) {
+        return this == obj || obj != null && value == obj.value;
+    }
+
+    @Override
+    public final String toString() {
+        return toCanonicalString();
+    }
+
+    private Object readResolve() {
+        return instanceFor(value);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Uint8.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Uint8.java
new file mode 100644 (file)
index 0000000..99548c3
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Variant;
+
+/**
+ * Dedicated type for YANG's 'type uint8' type.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public class Uint8 extends Number implements CanonicalValue<Uint8> {
+    public static final class Support extends AbstractCanonicalValueSupport<Uint8> {
+        public Support() {
+            super(Uint8.class);
+        }
+
+        @Override
+        public Variant<Uint8, CanonicalValueViolation> fromString(final String str) {
+            try {
+                return Variant.ofFirst(Uint8.valueOf(str));
+            } catch (IllegalArgumentException e) {
+                return CanonicalValueViolation.variantOf(e);
+            }
+        }
+    }
+
+    private static final CanonicalValueSupport<Uint8> SUPPORT = new Support();
+
+    private static final short MAX_VALUE_SHORT = 255;
+    private static final String MAX_VALUE_STR = "255";
+
+    private static final long serialVersionUID = 1L;
+
+    private static final @NonNull Uint8[] CACHE;
+
+    static {
+        final Uint8[] c = new Uint8[MAX_VALUE_SHORT + 1];
+        for (int i = 0; i <= MAX_VALUE_SHORT; ++i) {
+            c[i] = new Uint8((byte)i);
+        }
+        CACHE = c;
+    }
+
+    /**
+     * Value of {@code 0}.
+     */
+    public static final Uint8 ZERO = valueOf(0);
+    /**
+     * Value of {@code 1}.
+     */
+    public static final Uint8 ONE = valueOf(1);
+    /**
+     * Value of {@code 2}.
+     */
+    public static final Uint8 TWO = valueOf(2);
+    /**
+     * Value of {@code 10}.
+     */
+    public static final Uint8 TEN = valueOf(10);
+    /**
+     * Value of {@code 255}.
+     */
+    public static final Uint8 MAX_VALUE = valueOf(MAX_VALUE_SHORT);
+
+    private final byte value;
+
+    private Uint8(final byte value) {
+        this.value = value;
+    }
+
+    protected Uint8(final Uint8 other) {
+        this(other.value);
+    }
+
+    private static Uint8 instanceFor(final byte value) {
+        return CACHE[Byte.toUnsignedInt(value)];
+    }
+
+    /**
+     * Returns an {@code Uint8} corresponding to a given bit representation. The argument is interpreted as an
+     * unsigned 8-bit value.
+     *
+     * @param bits unsigned bit representation
+     * @return A Uint8 instance
+     */
+    public static Uint8 fromByteBits(final byte bits) {
+        return instanceFor(bits);
+    }
+
+    /**
+     * Returns an {@code Uint8} corresponding to a given {@code byteVal}. The inverse operation is {@link #byteValue()}.
+     *
+     * @param byteVal byte value
+     * @return A Uint8 instance
+     * @throws IllegalArgumentException if byteVal is less than zero
+     */
+    public static Uint8 valueOf(final byte byteVal) {
+        UintConversions.checkNonNegative(byteVal, MAX_VALUE_STR);
+        return instanceFor(byteVal);
+    }
+
+    /**
+     * Returns an {@code Uint8} corresponding to a given {@code shortVal}. The inverse operation is
+     * {@link #shortValue()}.
+     *
+     * @param shortVal short value
+     * @return A Uint8 instance
+     * @throws IllegalArgumentException if shortVal is less than zero or greater than 255.
+     */
+    public static Uint8 valueOf(final short shortVal) {
+        UintConversions.checkRange(shortVal, MAX_VALUE_SHORT);
+        return instanceFor((byte)shortVal);
+    }
+
+    /**
+     * Returns an {@code Uint8} corresponding to a given {@code intVal}. The inverse operation is {@link #intValue()}.
+     *
+     * @param intVal int value
+     * @return A Uint8 instance
+     * @throws IllegalArgumentException if intVal is less than zero or greater than 255.
+     */
+    public static Uint8 valueOf(final int intVal) {
+        UintConversions.checkRange(intVal, MAX_VALUE_SHORT);
+        return instanceFor((byte)intVal);
+    }
+
+    /**
+     * Returns an {@code Uint8} corresponding to a given {@code longVal}. The inverse operation is
+     * {@link #longValue()}.
+     *
+     * @param longVal long value
+     * @return A Uint8 instance
+     * @throws IllegalArgumentException if intVal is less than zero or greater than 255.
+     */
+    public static Uint8 valueOf(final long longVal) {
+        UintConversions.checkRange(longVal, MAX_VALUE_SHORT);
+        return instanceFor((byte)longVal);
+    }
+
+    /**
+     * Returns an {@code Uint8} corresponding to a given {@code uint}.
+     *
+     * @param uint Uint16 value
+     * @return A Uint8 instance
+     * @throws NullPointerException if uint is null
+     * @throws IllegalArgumentException if uint is greater than 255.
+     */
+    public static Uint8 valueOf(final Uint16 uint) {
+        return valueOf(uint.intValue());
+    }
+
+    /**
+     * Returns an {@code Uint8} corresponding to a given {@code uint}.
+     *
+     * @param uint Uint32 value
+     * @return A Uint8 instance
+     * @throws NullPointerException if uint is null
+     * @throws IllegalArgumentException if uint is greater than 255.
+     */
+    public static Uint8 valueOf(final Uint32 uint) {
+        return valueOf(uint.longValue());
+    }
+
+    /**
+     * Returns an {@code Uint8} corresponding to a given {@code uint}.
+     *
+     * @param uint Uint64 value
+     * @return A Uint8 instance
+     * @throws NullPointerException if uint is null
+     * @throws IllegalArgumentException if uint is greater than 255.
+     */
+    public static Uint8 valueOf(final Uint64 uint) {
+        return valueOf(uint.longValue());
+    }
+
+    /**
+     * Returns an {@code Uint8} holding the value of the specified {@code String}, parsed as an unsigned {@code short}
+     * value.
+     *
+     * @param string String to parse
+     * @return A Uint8 instance
+     * @throws NullPointerException if string is null
+     * @throws IllegalArgumentException if the parsed value is less than zero or greater than 255
+     * @throws NumberFormatException if the string does not contain a parsable unsigned {@code short} value.
+     */
+    public static Uint8 valueOf(final String string) {
+        return valueOf(string, 10);
+    }
+
+    /**
+     * Returns an {@code Uint8} holding the value of the specified {@code String}, parsed as an unsigned {@code short}
+     * value.
+     *
+     * @param string String to parse
+     * @param radix Radix to use
+     * @return A Uint8 instance
+     * @throws NullPointerException if string is null
+     * @throws IllegalArgumentException if the parsed value is less than zero or greater than 255
+     * @throws NumberFormatException if the string does not contain a parsable unsigned {@code short} value, or if the
+     *                               {@code radix} is outside of allowed range.
+     */
+    public static Uint8 valueOf(final String string, final int radix) {
+        return valueOf(Short.parseShort(requireNonNull(string), radix));
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>
+     * The inverse operation is {@link #fromByteBits(byte)}. In case this value is greater than {@link Byte#MAX_VALUE},
+     * the returned value will be equal to {@code this - 2^8}.
+     */
+    @Override
+    public final byte byteValue() {
+        return value;
+    }
+
+    @Override
+    public final int intValue() {
+        return Byte.toUnsignedInt(value);
+    }
+
+    @Override
+    public final long longValue() {
+        return Byte.toUnsignedLong(value);
+    }
+
+    @Override
+    public final float floatValue() {
+        return intValue();
+    }
+
+    @Override
+    public final double doubleValue() {
+        return intValue();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final int compareTo(final Uint8 o) {
+        return Byte.compareUnsigned(value, o.value);
+    }
+
+    @Override
+    public final String toCanonicalString() {
+        return Integer.toString(intValue());
+    }
+
+    @Override
+    public final CanonicalValueSupport<Uint8> support() {
+        return SUPPORT;
+    }
+
+    /**
+     * Convert this value to a {@code short}.
+     *
+     * @return A short
+     */
+    public final short toJava() {
+        return shortValue();
+    }
+
+    /**
+     * Convert this value to a {@code Uint16}.
+     *
+     * @return A Uint16
+     */
+    public final Uint16 toUint16() {
+        return Uint16.fromShortBits(shortValue());
+    }
+
+    /**
+     * Convert this value to a {@code Uint32}.
+     *
+     * @return A Uint32
+     */
+    public final Uint32 toUint32() {
+        return Uint32.fromIntBits(intValue());
+    }
+
+    /**
+     * Convert this value to a {@code Uint64}.
+     *
+     * @return A Uint64
+     */
+    public final Uint64 toUint64() {
+        return Uint64.fromLongBits(longValue());
+    }
+
+    @Override
+    public final int hashCode() {
+        return Byte.hashCode(value);
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        return this == obj || obj instanceof Uint8 && value == ((Uint8)obj).value;
+    }
+
+    /**
+     * A slightly faster version of {@link #equals(Object)}.
+     *
+     * @param obj Uint8 object
+     * @return {@code true} if this object is the same as the obj argument; {@code false} otherwise.
+     */
+    public final boolean equals(final @Nullable Uint8 obj) {
+        return this == obj || obj != null && value == obj.value;
+    }
+
+    @Override
+    public final String toString() {
+        return toCanonicalString();
+    }
+
+    private Object readResolve() {
+        return instanceFor(value);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/UintConversions.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/UintConversions.java
new file mode 100644 (file)
index 0000000..7b8c223
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.common;
+
+import com.google.common.annotations.Beta;
+import com.google.common.primitives.UnsignedInteger;
+import com.google.common.primitives.UnsignedLong;
+import java.math.BigInteger;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Utility methods for converting Java and Guava integer types to their {@link Uint8}, {@link Uint16}, {@link Uint32}
+ * and {@link Uint64} equivalents. While individual types provide these through their {@code valueOf()} methods, this
+ * class allows dealing with multiple types through a static import:
+ *
+ * <pre>
+ *   <code>
+ *     import static org.opendaylight.yangtools.yang.common.UintConversions.fromJava;
+ *
+ *     Uint16 two = fromJava(32);
+ *     Uint32 one = fromJava(32L);
+ *   </code>
+ * </pre>
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public final class UintConversions {
+    private UintConversions() {
+        // Hidden on purpose
+    }
+
+    /**
+     * Convert a {@code short} in range 0-255 to an Uint8.
+     *
+     * @param value value
+     * @return Uint8 object
+     * @throws IllegalArgumentException if value is less than zero or greater than 255
+     */
+    public static Uint8 fromJava(final short value) {
+        return Uint8.valueOf(value);
+    }
+
+    /**
+     * Convert an {@code int} in range 0-65535 to a Uint16.
+     *
+     * @param value value
+     * @return Uint16 object
+     * @throws IllegalArgumentException if value is less than zero or greater than 65535.
+     */
+    public static Uint16 fromJava(final int value) {
+        return Uint16.valueOf(value);
+    }
+
+    /**
+     * Convert a {@code long} in range 0-4294967295 to a Uint32.
+     *
+     * @param value value
+     * @return Uint32 object
+     * @throws IllegalArgumentException if value is less than zero or greater than 4294967295
+     */
+    public static Uint32 fromJava(final long value) {
+        return Uint32.valueOf(value);
+    }
+
+    /**
+     * Convert a {@link BigInteger} in range 0-18446744073709551615 to an Uint64.
+     *
+     * @param value value
+     * @return Uint64 object
+     * @throws NullPointerException if value is null
+     * @throws IllegalArgumentException if value is less than zero or greater than 18446744073709551615
+     */
+    public static Uint64 fromJava(final BigInteger value) {
+        return Uint64.valueOf(value);
+    }
+
+    /**
+     * Convert an {@link UnsignedInteger} to a Uint32.
+     *
+     * @param value value
+     * @return Uint32 object
+     * @throws NullPointerException if value is null
+     */
+    public static Uint32 fromGuava(final UnsignedInteger value) {
+        return Uint32.valueOf(value);
+    }
+
+    /**
+     * Convert an {@link UnsignedLong} to a Uint64.
+     *
+     * @param value value
+     * @return Uint64 object
+     * @throws NullPointerException if value is null
+     */
+    public static Uint64 fromGuava(final UnsignedLong value) {
+        return Uint64.valueOf(value);
+    }
+
+    static void checkNonNegative(final byte value, final String maxValue) {
+        if (value < 0) {
+            throwIAE(value, maxValue);
+        }
+    }
+
+    static void checkNonNegative(final short value, final String maxStr) {
+        if (value < 0) {
+            throwIAE(value, maxStr);
+        }
+    }
+
+    static void checkNonNegative(final int value, final String maxStr) {
+        if (value < 0) {
+            throwIAE(value, maxStr);
+        }
+    }
+
+    static void checkRange(final short value, final short max) {
+        if (value < 0 || value > max) {
+            throwIAE(value, max);
+        }
+    }
+
+    static void checkRange(final int value, final int max) {
+        if (value < 0 || value > max) {
+            throwIAE(value, max);
+        }
+    }
+
+    static void checkRange(final long value, final long max) {
+        if (value < 0 || value > max) {
+            throwIAE(value, max);
+        }
+    }
+
+    static void throwIAE(final String value, final long max) {
+        // "Invalid range: 65536, expected: [[0..65535]]."
+        throw new IllegalArgumentException("Invalid range: " + value + ", expected: [[0.." + max + "]].");
+    }
+
+    private static void throwIAE(final int value, final int max) {
+        // "Invalid range: 65536, expected: [[0..65535]]."
+        throw new IllegalArgumentException("Invalid range: " + value + ", expected: [[0.." + max + "]].");
+    }
+
+    private static void throwIAE(final long value, final long max) {
+        // "Invalid range: 65536, expected: [[0..65535]]."
+        throw new IllegalArgumentException("Invalid range: " + value + ", expected: [[0.." + max + "]].");
+    }
+
+    private static void throwIAE(final int value, final String max) {
+        // "Invalid range: 65536, expected: [[0..65535]]."
+        throw new IllegalArgumentException("Invalid range: " + value + ", expected: [[0.." + max + "]].");
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/UnqualifiedQName.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/UnqualifiedQName.java
new file mode 100644 (file)
index 0000000..5fb3438
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.common;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * An unresolved, unqualified {@link QName}. It is guaranteed to hold a valid {@link #getLocalName()}, in the default
+ * namespace, which is not resolved. A resolved {@link QName} can be constructed through {@link #bindTo(QNameModule)}.
+ */
+@Beta
+@NonNullByDefault
+public final class UnqualifiedQName extends AbstractQName implements Comparable<UnqualifiedQName> {
+    private static final long serialVersionUID = 1L;
+    private static final Interner<UnqualifiedQName> INTERNER = Interners.newWeakInterner();
+
+    private UnqualifiedQName(final String localName) {
+        super(localName);
+    }
+
+    public static UnqualifiedQName of(final String localName) {
+        return new UnqualifiedQName(checkLocalName(localName));
+    }
+
+    /**
+     * Read an UnboundQName from a DataInput. The format is expected to match the output format of
+     * {@link #writeTo(DataOutput)}.
+     *
+     * @param in DataInput to read
+     * @return An UnboundQName instance
+     * @throws IOException if I/O error occurs
+     */
+    public static UnqualifiedQName readFrom(final DataInput in) throws IOException {
+        return of(in.readUTF());
+    }
+
+    public QName bindTo(final QNameModule namespace) {
+        return new QName(namespace, getLocalName());
+    }
+
+    @Override
+    @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "Interning identity check")
+    public UnqualifiedQName intern() {
+        // Make sure to intern the string and check whether it refers to the same name as we are
+        final String name = getLocalName();
+        final String internedName = name.intern();
+        final UnqualifiedQName template = internedName == name ? this : new UnqualifiedQName(internedName);
+        return INTERNER.intern(template);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public int compareTo(final UnqualifiedQName o) {
+        return getLocalName().compareTo(o.getLocalName());
+    }
+
+    @Override
+    public void writeTo(final DataOutput out) throws IOException {
+        out.writeUTF(getLocalName());
+    }
+
+    @Override
+    public int hashCode() {
+        return getLocalName().hashCode();
+    }
+
+    @Override
+    public boolean equals(final @Nullable Object obj) {
+        return this == obj || obj instanceof UnqualifiedQName
+                && getLocalName().equals(((AbstractQName) obj).getLocalName());
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("localName", getLocalName()).toString();
+    }
+
+    @Override
+    Object writeReplace() {
+        return new UQNv1(this);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangConstants.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangConstants.java
new file mode 100644 (file)
index 0000000..c4e82b5
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2015 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.common;
+
+import java.net.URI;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Constant definitions present in RFC documents related to the YANG language.
+ */
+@NonNullByDefault
+public final class YangConstants {
+    /**
+     * YANG File Extension, as defined in https://tools.ietf.org/html/rfc6020#section-14.1.
+     */
+    public static final String RFC6020_YANG_FILE_EXTENSION = ".yang";
+
+    /**
+     * YANG Media Type, as defined in https://tools.ietf.org/html/rfc6020#section-14.1.
+     */
+    public static final String RFC6020_YANG_MAC_FILE_TYPE = "TEXT";
+
+
+    /**
+     * YANG Media Type, as defined in https://tools.ietf.org/html/rfc6020#section-14.1.
+     */
+    public static final String RFC6020_YANG_MEDIA_TYPE = "application/yang";
+
+    /**
+     * YANG namespace, as defined in https://tools.ietf.org/html/rfc6020#section-14, in String format.
+     */
+    public static final String RFC6020_YANG_NAMESPACE_STRING = "urn:ietf:params:xml:ns:yang:1";
+
+    /**
+     * YANG namespace, as defined in https://tools.ietf.org/html/rfc6020#section-14, in URI format.
+     */
+    public static final URI RFC6020_YANG_NAMESPACE = URI.create(RFC6020_YANG_NAMESPACE_STRING);
+
+    /**
+     * Base QNameModule for all YANG statements.
+     */
+    public static final QNameModule RFC6020_YANG_MODULE = QNameModule.create(RFC6020_YANG_NAMESPACE).intern();
+
+    /**
+     * YIN File Extension, as defined in https://tools.ietf.org/html/rfc6020#section-14.2.
+     */
+    public static final String RFC6020_YIN_FILE_EXTENSION = ".yin";
+
+    /**
+     * YANG Media Type, as defined in https://tools.ietf.org/html/rfc6020#section-14.1.
+     */
+    public static final String RFC6020_MAC_FILE_TYPE = "TEXT";
+
+    /**
+     * YANG Media Type, as defined in https://tools.ietf.org/html/rfc6020#section-14.1.
+     */
+    public static final String RFC6020_YIN_MEDIA_TYPE = "application/xml+yin";
+
+    /**
+     * YIN namespace, as defined in https://tools.ietf.org/html/rfc6020#section-14, in String format.
+     */
+    public static final String RFC6020_YIN_NAMESPACE_STRING = "urn:ietf:params:xml:ns:yang:yin:1";
+
+    /**
+     * YIN namespace, as defined in https://tools.ietf.org/html/rfc6020#section-14, in URI format.
+     */
+    public static final URI RFC6020_YIN_NAMESPACE = URI.create(RFC6020_YIN_NAMESPACE_STRING);
+
+    /**
+     * Base QNameModule for all YIN statements.
+     */
+    public static final QNameModule RFC6020_YIN_MODULE = QNameModule.create(RFC6020_YIN_NAMESPACE).intern();
+
+    /**
+     * YANG Library NETCONF Capability, as defined in https://tools.ietf.org/html/rfc7950#section-16.
+     */
+    public static final URI RFC7950_YANG_LIBRARY_CAPABILITY =
+        URI.create("urn:ietf:params:netconf:capability:yang-library:1.0");
+
+    /**
+     * Prefix for YANG-specific XPath functions.
+     */
+    public static final String YANG_XPATH_FUNCTIONS_PREFIX = "yang";
+
+    // Dummy template QNames. These are never leaked, but are used for efficient instantiation via QName#withModule()
+    private static final QName DUMMY_OPERATION_INPUT = QName.create("DUMMY", "input");
+    private static final QName DUMMY_OPERATION_OUTPUT = QName.create("DUMMY", "output");
+
+    private YangConstants() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    /**
+     * Create a {@link QName} representing the 'input' statement of an operation (RPC or action) within specified
+     * {@link QNameModule}.
+     *
+     * @param module Desired module
+     * @return A QName representing action or RPC input.
+     * @throws NullPointerException if {@code module} is null
+     */
+    public static QName operationInputQName(final QNameModule module) {
+        return DUMMY_OPERATION_INPUT.withModule(module);
+    }
+
+    /**
+     * Create a {@link QName} representing the 'output' statement of an operation (RPC or action) within specified
+     * {@link QNameModule}.
+     *
+     * @param module Desired module
+     * @return A QName representing action or RPC output.
+     * @throws NullPointerException if {@code module} is null
+     */
+    public static QName operationOutputQName(final QNameModule module) {
+        return DUMMY_OPERATION_OUTPUT.withModule(module);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangNames.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangNames.java
new file mode 100644 (file)
index 0000000..0830ef6
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 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.common;
+
+import com.google.common.annotations.Beta;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Map.Entry;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Utility class for handling various naming conventions mentioned in YANG and related specifications.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public final class YangNames {
+    private YangNames() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Parse a file name according to rules outlined in https://tools.ietf.org/html/rfc6020#section-5.2. Input string
+     * should be the base path with file extension stripped.
+     *
+     * @param baseName file base name
+     * @return A tuple containing the module name and parsed revision, if present.
+     * @throws NullPointerException if {@code baseName} is null
+     */
+    public static Entry<String, @Nullable String> parseFilename(final String baseName) {
+        final int zavinac = baseName.lastIndexOf('@');
+        if (zavinac < 0) {
+            return new SimpleEntry<>(baseName, null);
+        }
+
+        return new SimpleEntry<>(baseName.substring(0, zavinac), baseName.substring(zavinac + 1));
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangNamespaceContext.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangNamespaceContext.java
new file mode 100644 (file)
index 0000000..9986eb8
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.common;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import java.io.Serializable;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Interface for mapping between {@link String} prefixes and {@link QNameModule} namespaces. The conceptual model
+ * matches prefix mapping inside a YANG {@code module} as defined through the use of {@code prefix} and {@code import}
+ * statements and detailed in <a href="https://tools.ietf.org/html/rfc7950#section-7.1.4">RFC7950 Section 7.1.4</a>.
+ *
+ * <p>
+ * Each namespace context has a set of prefix/namespace mappings. A namespace can be bound to multiple prefixes at the
+ * same time.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface YangNamespaceContext extends Immutable, Serializable {
+    /**
+     * Return QNameModule to which a particular prefix is bound.
+     *
+     * @param prefix Prefix to look up
+     * @return QNameModule bound to specified prefix
+     * @throws NullPointerException if {@code prefix} is null
+     */
+    @NonNull Optional<QNameModule> findNamespaceForPrefix(String prefix);
+
+    /**
+     * Return a prefix to which a particular QNameModule is bound. If a namespace is bound to multiple prefixes, it is
+     * left unspecified which of those prefixes is returned.
+     *
+     * @param namespace QNameModule to look up
+     * @return Prefix to which the QNameModule is bound
+     * @throws NullPointerException if {@code module} is null
+     */
+    @NonNull Optional<String> findPrefixForNamespace(QNameModule namespace);
+
+    /**
+     * Create a {@link QName} by resolving a prefix against currently-bound prefixes and combining it with specified
+     * local name.
+     *
+     * @param prefix Namespace prefix
+     * @param localName QName local name
+     * @return A QName.
+     * @throws NullPointerException if any argument is null
+     * @throws IllegalArgumentException if {@code localName} does not conform to local name requirements or if the
+     *                                  prefix is not bound in this context.
+     */
+    default @NonNull QName createQName(final String prefix, final String localName) {
+        final Optional<QNameModule> namespace = findNamespaceForPrefix(prefix);
+        checkArgument(namespace.isPresent(), "Prefix %s is not bound", prefix);
+        return QName.create(namespace.get(), localName);
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangVersion.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangVersion.java
new file mode 100644 (file)
index 0000000..1924027
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2016 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.common;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Maps;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Enumeration of supported YANG versions.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public enum YangVersion {
+    /**
+     * Version 1, as defined in RFC6020.
+     */
+    VERSION_1("1", "RFC6020"),
+    /**
+     * Version 1.1, as defined in RFC7950.
+     */
+    VERSION_1_1("1.1", "RFC7950");
+
+    private static final Map<String, YangVersion> YANG_VERSION_MAP = Maps.uniqueIndex(Arrays.asList(values()),
+        YangVersion::toString);
+
+    private final @NonNull String str;
+    private final @NonNull String reference;
+
+    YangVersion(final @NonNull String str, final @NonNull String reference) {
+        this.str = requireNonNull(str);
+        this.reference = requireNonNull(reference);
+    }
+
+    /**
+     * Parse a YANG version from its textual representation.
+     *
+     * @param str String to parse
+     * @return YANG version
+     * @throws NullPointerException if the string is null
+     */
+    public static Optional<YangVersion> parse(final @NonNull String str) {
+        return Optional.ofNullable(YANG_VERSION_MAP.get(requireNonNull(str)));
+    }
+
+    /**
+     * Return the normative reference defining this YANG version.
+     *
+     * @return Normative reference.
+     */
+    public @NonNull String getReference() {
+        return reference;
+    }
+
+    @Override
+    public @NonNull String toString() {
+        return str;
+    }
+}
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/pkg-info.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/pkg-info.java
new file mode 100644 (file)
index 0000000..081e1f0
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies s.r.o. 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
+ */
+/**
+ * Common YANG concepts and constants expressed in terms of Java. This notably includes the concepts of
+ * <ul>
+ * <li>a {@link YangVersion}</li>
+ * <li>a {@link Revision}</li>
+ * <li>a {@link QNameModule}</li>
+ * <li>a {@link QName}</li>
+ * <li>a Java-native representation of a YANG string, {@link DerivedString}</li>
+ * <li>a Java-native representations of numeric YANG types, like {@link Uint64}, {@link Decimal64} and
+ *     {@link Empty}</li>
+ * </ul>
+ */
+package org.opendaylight.yangtools.yang.common;
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/BiMapYangNamespaceContextTest.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/BiMapYangNamespaceContextTest.java
new file mode 100644 (file)
index 0000000..f48f619
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableBiMap;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Optional;
+import org.junit.Test;
+
+public class BiMapYangNamespaceContextTest {
+    private static final QNameModule FOO = QNameModule.create(URI.create("foo"));
+    private static final QNameModule BAR = QNameModule.create(URI.create("bar"));
+    private static final QNameModule BAZ = QNameModule.create(URI.create("baz"));
+
+    private final BiMapYangNamespaceContext context = new BiMapYangNamespaceContext(
+        ImmutableBiMap.of("foo", FOO, "bar", BAR));
+
+    @Test
+    public void testEquals() {
+        assertTrue(context.equals(context));
+        assertTrue(context.equals(new BiMapYangNamespaceContext(ImmutableBiMap.of("foo", FOO, "bar", BAR))));
+        assertFalse(context.equals(null));
+        assertFalse(context.equals(new BiMapYangNamespaceContext(ImmutableBiMap.of("foo", FOO))));
+        assertFalse(context.equals(new BiMapYangNamespaceContext(ImmutableBiMap.of("bar", BAR))));
+    }
+
+    @Test
+    public void testPrefixForNamespace() {
+        assertEquals(Optional.of("foo"), context.findPrefixForNamespace(FOO));
+        assertEquals(Optional.of("bar"), context.findPrefixForNamespace(BAR));
+        assertEquals(Optional.empty(), context.findPrefixForNamespace(BAZ));
+    }
+
+    @Test
+    public void testNamespaceForPrefix() {
+        assertEquals(Optional.of(FOO), context.findNamespaceForPrefix("foo"));
+        assertEquals(Optional.of(BAR), context.findNamespaceForPrefix("bar"));
+        assertEquals(Optional.empty(), context.findNamespaceForPrefix("baz"));
+    }
+
+    @Test
+    public void testReadWrite() throws IOException {
+        final byte[] bytes;
+        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
+            try (DataOutputStream dos = new DataOutputStream(bos)) {
+                context.writeTo(dos);
+            }
+            bytes = bos.toByteArray();
+        }
+
+        final BiMapYangNamespaceContext other;
+        try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes))) {
+            other = BiMapYangNamespaceContext.readFrom(dis);
+        }
+
+        assertEquals(context, other);
+    }
+
+    @Test
+    public void testCreateQName() {
+        assertEquals(QName.create(FOO, "some"), context.createQName("foo", "some"));
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Decimal64Test.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Decimal64Test.java
new file mode 100644 (file)
index 0000000..45b1234
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import org.junit.Test;
+
+public class Decimal64Test {
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseEmpty() {
+        Decimal64.valueOf("");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseSingleIllegal() {
+        Decimal64.valueOf("a");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseSingleHighIllegal() {
+        Decimal64.valueOf(":");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseZeroIllegal() {
+        Decimal64.valueOf("0a");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseZeroHighIllegal() {
+        Decimal64.valueOf("0:");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseZeroPointIllegal() {
+        Decimal64.valueOf("0.a");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseZeroPointHighIllegal() {
+        Decimal64.valueOf("0.:");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParsePointIllegal() {
+        Decimal64.valueOf(".a");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseMinus() {
+        Decimal64.valueOf("-");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParsePlus() {
+        Decimal64.valueOf("+");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParsePeriod() {
+        Decimal64.valueOf(".");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseTwoPeriods() {
+        Decimal64.valueOf("..");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseTrailingPeriod() {
+        Decimal64.valueOf("0.");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseMultiplePeriods() {
+        Decimal64.valueOf("0.1.");
+    }
+
+    @Test
+    public void testParseLongString() {
+        Decimal64.valueOf("123456789012345678");
+    }
+
+    @Test
+    public void testParseLongDecimal() {
+        Decimal64.valueOf("0.12345678901234568");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseTooLongString() {
+        Decimal64.valueOf("1234567890123456789");
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testParseTooLongDecimal() {
+        Decimal64.valueOf("0.123456789012345689");
+    }
+
+    @Test
+    public void testParse() {
+        assertParsedVariants("0", 0, 0, 1);
+        assertParsedVariants("0.00", 0, 0, 1);
+        assertParsedVariants("00.0", 0, 0, 1);
+        assertParsedVariants("000.0", 0, 0, 1);
+        assertCanonicalVariants("10.0", 10, 0, 1);
+        assertCanonicalVariants("10.09", 10, 9, 2);
+        assertParsedVariants("10.0900900", 10, 9009, 5);
+        assertParsedVariants("0002210.09", 2210, 9, 2);
+
+        Decimal64 parsed = assertParsedString("0.0", 0, 0, 1, false);
+        parsed = assertParsedString("+0.0", 0, 0, 1, false);
+        assertEquals("0.0", parsed.toString());
+        assertEquals("0.0", parsed.toString());
+        parsed = assertParsedString("-0.0", 0, 0, 1, true);
+        assertEquals("0.0", parsed.toString());
+
+        assertCanonicalVariants("1.0", 1, 0, 1);
+        assertCanonicalVariants("2.3", 2, 3, 1);
+    }
+
+    @Test
+    public void testCompare() {
+        final Decimal64 one = Decimal64.valueOf("1");
+        final Decimal64 two = Decimal64.valueOf("2");
+        final Decimal64 three = Decimal64.valueOf("3");
+        final Decimal64 negOne = Decimal64.valueOf("-1");
+        final Decimal64 anotherOne = Decimal64.valueOf("1");
+
+        assertEquals(0, one.compareTo(one));
+        assertEquals(0, one.compareTo(anotherOne));
+        assertEquals(-1, one.compareTo(two));
+        assertEquals(-1, one.compareTo(three));
+        assertEquals(1, one.compareTo(negOne));
+
+        assertEquals(1, two.compareTo(one));
+        assertEquals(1, two.compareTo(anotherOne));
+        assertEquals(-1, two.compareTo(three));
+        assertEquals(1, one.compareTo(negOne));
+    }
+
+    @Test
+    public void testEquals() {
+        final Decimal64 one = Decimal64.valueOf("1");
+        final Decimal64 two = Decimal64.valueOf("2");
+        final Decimal64 anotherOne = Decimal64.valueOf("1");
+
+        assertTrue(one.equals(one));
+        assertTrue(one.equals(anotherOne));
+        assertFalse(one.equals(two));
+        assertTrue(two.equals(two));
+        assertFalse(two.equals(one));
+
+        assertFalse(one.equals(new Object()));
+    }
+
+    @Test
+    public void testConversions() {
+        assertEquals(new BigDecimal("0.12"), Decimal64.valueOf("0.12").decimalValue());
+        assertEquals(new BigDecimal("-0.12"), Decimal64.valueOf("-0.12").decimalValue());
+        assertEquals(new BigDecimal("0.12"), Decimal64.valueOf("+0.12").decimalValue());
+        assertEquals(new BigDecimal("123.456"), Decimal64.valueOf("123.456").decimalValue());
+        assertEquals(new BigDecimal("-123.456"), Decimal64.valueOf("-123.456").decimalValue());
+
+        assertEquals(0.12, Decimal64.valueOf("0.12").doubleValue(), 0);
+        assertEquals(-0.12, Decimal64.valueOf("-0.12").doubleValue(), 0);
+
+        assertEquals((float) 0.12, Decimal64.valueOf("0.12").floatValue(), 0);
+        assertEquals((float) -0.12, Decimal64.valueOf("-0.12").floatValue(), 0);
+
+        assertEquals(12345678901L, Decimal64.valueOf("12345678901").longValue());
+        assertEquals(-12345678901L, Decimal64.valueOf("-12345678901").longValue());
+    }
+
+    @Test
+    public void testFactories() {
+        assertEquals("0.0", Decimal64.valueOf((byte) 0).toString());
+        assertEquals("1.0", Decimal64.valueOf((byte) 1).toString());
+        assertEquals("-1.0", Decimal64.valueOf((byte) -1).toString());
+
+        assertEquals("0.0", Decimal64.valueOf((short) 0).toString());
+        assertEquals("1.0", Decimal64.valueOf((short) 1).toString());
+        assertEquals("-1.0", Decimal64.valueOf((short) -1).toString());
+
+        assertEquals("0.0", Decimal64.valueOf(0).toString());
+        assertEquals("1.0", Decimal64.valueOf(1).toString());
+        assertEquals("-1.0", Decimal64.valueOf(-1).toString());
+
+        assertEquals("0.0", Decimal64.valueOf(0L).toString());
+        assertEquals("1.0", Decimal64.valueOf(1L).toString());
+        assertEquals("-1.0", Decimal64.valueOf(-1L).toString());
+
+        assertEquals("0.0", Decimal64.valueOf(0.0).toString());
+        assertEquals("1.0", Decimal64.valueOf(1.0).toString());
+        assertEquals("-1.0", Decimal64.valueOf(-1.0).toString());
+
+        assertEquals("0.0", Decimal64.valueOf(BigDecimal.ZERO).toString());
+        assertEquals("1.0", Decimal64.valueOf(BigDecimal.ONE).toString());
+        assertEquals("-1.0", Decimal64.valueOf(BigDecimal.ONE.negate()).toString());
+    }
+
+    @Test
+    public void testBoundaries() {
+        assertEquals(-128L, Decimal64.valueOf(Byte.MIN_VALUE).longValue());
+        assertEquals(127L, Decimal64.valueOf(Byte.MAX_VALUE).longValue());
+        assertEquals(-32768L, Decimal64.valueOf(Short.MIN_VALUE).longValue());
+        assertEquals(32767L, Decimal64.valueOf(Short.MAX_VALUE).longValue());
+        assertEquals(-2147483648L, Decimal64.valueOf(Integer.MIN_VALUE).longValue());
+        assertEquals(2147483647L, Decimal64.valueOf(Integer.MAX_VALUE).longValue());
+    }
+
+    @Test
+    public void testByteValueExact() {
+        assertEquals(Byte.MIN_VALUE, Decimal64.valueOf(Byte.MIN_VALUE).byteValueExact());
+        assertEquals(Byte.MAX_VALUE, Decimal64.valueOf(Byte.MAX_VALUE).byteValueExact());
+    }
+
+    @Test(expected = ArithmeticException.class)
+    public void testByteValueExactFrac() {
+        Decimal64.valueOf("1.1").byteValueExact();
+    }
+
+    @Test(expected = ArithmeticException.class)
+    public void testByteValueExactRange() {
+        Decimal64.valueOf(Byte.MAX_VALUE + 1).byteValueExact();
+    }
+
+    @Test
+    public void testShortValueExact() {
+        assertEquals(Short.MIN_VALUE, Decimal64.valueOf(Short.MIN_VALUE).shortValueExact());
+        assertEquals(Short.MAX_VALUE, Decimal64.valueOf(Short.MAX_VALUE).shortValueExact());
+    }
+
+    @Test(expected = ArithmeticException.class)
+    public void testShortValueExactFrac() {
+        Decimal64.valueOf("1.1").shortValueExact();
+    }
+
+    @Test(expected = ArithmeticException.class)
+    public void testShortValueExactRange() {
+        Decimal64.valueOf(Short.MAX_VALUE + 1).shortValueExact();
+    }
+
+    @Test
+    public void testIntValueExact() {
+        assertEquals(Integer.MIN_VALUE, Decimal64.valueOf(Integer.MIN_VALUE).intValueExact());
+        assertEquals(Integer.MAX_VALUE, Decimal64.valueOf(Integer.MAX_VALUE).intValueExact());
+    }
+
+    @Test(expected = ArithmeticException.class)
+    public void testIntValueExactFrac() {
+        Decimal64.valueOf("1.1").intValueExact();
+    }
+
+    @Test(expected = ArithmeticException.class)
+    public void testIntValueExactRange() {
+        Decimal64.valueOf(Integer.MAX_VALUE + 1L).intValueExact();
+    }
+
+    @Test(expected = ArithmeticException.class)
+    public void testLongValueExactFrac() {
+        Decimal64.valueOf("1.1").longValueExact();
+    }
+
+    private static void assertCanonicalVariants(final String str, final long intPart, final long fracPart,
+            final int digits) {
+        assertCanonicalString(str, intPart, fracPart, digits, false);
+        assertCanonicalString("-" + str, intPart, fracPart, digits, true);
+
+        final Decimal64 parsed = assertParsedString("+" + str, intPart, fracPart, digits, false);
+        assertEquals(str, parsed.toString());
+    }
+
+    private static void assertParsedVariants(final String str, final long intPart, final long fracPart,
+            final int digits) {
+        assertParsedString(str, intPart, fracPart, digits, false);
+        assertParsedString("-" + str, intPart, fracPart, digits, true);
+        assertParsedString("+" + str, intPart, fracPart, digits, false);
+    }
+
+    private static void assertCanonicalString(final String str, final long intPart, final long fracPart,
+            final int digits, final boolean negative) {
+        final Decimal64 parsed = assertParsedString(str, intPart, fracPart, digits, negative);
+        assertEquals(str, parsed.toString());
+    }
+
+    private static Decimal64 assertParsedString(final String str, final long intPart, final long fracPart,
+            final int digits, final boolean negative) {
+        final Decimal64 parsed = Decimal64.valueOf(str);
+        assertEquals(new Decimal64((byte) digits, intPart, fracPart, negative), parsed);
+        return parsed;
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/DerivedStringTest.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/DerivedStringTest.java
new file mode 100644 (file)
index 0000000..c96d8c6
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.Variant;
+
+@NonNullByDefault
+public class DerivedStringTest {
+    public static class EagerDerivedString extends CachingDerivedString<EagerDerivedString> {
+        private static final long serialVersionUID = 1L;
+
+        protected EagerDerivedString(final String str) {
+            super(str);
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:parameterName")
+        public final int compareTo(final EagerDerivedString o) {
+            return toCanonicalString().compareTo(o.toCanonicalString());
+        }
+
+        @Override
+        public final CanonicalValueSupport<EagerDerivedString> support() {
+            return EAGER_SUPPORT;
+        }
+
+        @Override
+        public final int hashCode() {
+            return toCanonicalString().hashCode();
+        }
+
+        @Override
+        public final boolean equals(@Nullable final Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            if (obj instanceof String) {
+                return toCanonicalString().equals(obj);
+            }
+
+            return obj instanceof DerivedString
+                    && toCanonicalString().equals(((DerivedString<?>)obj).toCanonicalString());
+        }
+
+        @Override
+        protected final String computeCanonicalString() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    public static class LazyDerivedString extends CachingDerivedString<LazyDerivedString> {
+        private static final long serialVersionUID = 1L;
+
+        private final String str;
+
+        protected LazyDerivedString(final String str) {
+            this.str = str;
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:parameterName")
+        public final int compareTo(final LazyDerivedString o) {
+            return str.compareTo(o.str);
+        }
+
+        @Override
+        public final CanonicalValueSupport<LazyDerivedString> support() {
+            return LAZY_SUPPORT;
+        }
+
+        @Override
+        public final int hashCode() {
+            return str.hashCode();
+        }
+
+        @Override
+        public final boolean equals(@Nullable final Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            if (obj instanceof String) {
+                return str.equals(obj);
+            }
+
+            return obj instanceof DerivedString && str.equals(((DerivedString<?>)obj).toCanonicalString());
+        }
+
+        @Override
+        protected final String computeCanonicalString() {
+            return str;
+        }
+    }
+
+    public static final class EagerDerivedStringSupport extends AbstractCanonicalValueSupport<EagerDerivedString> {
+        EagerDerivedStringSupport() {
+            super(EagerDerivedString.class);
+        }
+
+        @Override
+        public Variant<EagerDerivedString, CanonicalValueViolation> fromString(final String str) {
+            return Variant.ofFirst(new EagerDerivedString(str));
+        }
+    }
+
+    public static final class LazyDerivedStringSupport extends AbstractCanonicalValueSupport<LazyDerivedString> {
+        LazyDerivedStringSupport() {
+            super(LazyDerivedString.class);
+        }
+
+        @Override
+        public Variant<LazyDerivedString, CanonicalValueViolation> fromString(final String str) {
+            return Variant.ofFirst(new LazyDerivedString(str));
+        }
+    }
+
+    private static final CanonicalValueSupport<EagerDerivedString> EAGER_SUPPORT = new EagerDerivedStringSupport();
+    private static final CanonicalValueSupport<LazyDerivedString> LAZY_SUPPORT = new LazyDerivedStringSupport();
+
+    @Test
+    public void testEager() {
+        final DerivedString<?> foo = new EagerDerivedString("foo");
+        assertSame("foo", foo.toString());
+    }
+
+    @Test
+    public void testLazy() {
+        final DerivedString<?> foo = new LazyDerivedString("foo");
+        final String first = foo.toString();
+        assertEquals("foo", first);
+        assertSame(first, foo.toString());
+    }
+
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/EmptyTest.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/EmptyTest.java
new file mode 100644 (file)
index 0000000..4df61f9
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import org.junit.Test;
+
+public class EmptyTest {
+
+    @Test
+    public void testInstanceNotNull() {
+        assertNotNull(Empty.getInstance());
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals("empty", Empty.getInstance().toString());
+    }
+
+    @Test
+    public void testSerialization() throws IOException, ClassNotFoundException {
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+            oos.writeObject(Empty.getInstance());
+        }
+
+        final Object read;
+        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
+            read = ois.readObject();
+        }
+
+        assertSame(Empty.getInstance(), read);
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/OperationFailedExceptionTest.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/OperationFailedExceptionTest.java
new file mode 100644 (file)
index 0000000..27e6c22
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2016 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.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class OperationFailedExceptionTest {
+
+    @Test
+    public void testOperationFailedException() {
+        final Throwable cause = new Throwable("mock cause");
+        final RpcError rpcErrorShort = RpcResultBuilder.newError(RpcError.ErrorType.RPC, "tag", "msg");
+        final OperationFailedException operationFailedException1 = new OperationFailedException("error msg", cause,
+                rpcErrorShort);
+        final OperationFailedException operationFailedException2 = new OperationFailedException("error msg",
+                rpcErrorShort);
+        assertEquals(operationFailedException1.getErrorList(), operationFailedException2.getErrorList());
+        assertTrue(operationFailedException1.toString().contains("error msg"));
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/QNameTest.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/QNameTest.java
new file mode 100644 (file)
index 0000000..055de3f
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import org.junit.Test;
+
+public class QNameTest {
+    private static final String NAMESPACE = "urn:foo";
+    private static final String REVISION = "2013-12-24";
+    private static final String LOCALNAME = "bar";
+    private static final URI NS = URI.create(NAMESPACE);
+
+    @Test
+    public void testStringSerialization() throws Exception {
+        {
+            QName qname = QName.create(NAMESPACE, REVISION, LOCALNAME);
+            assertEquals(QName.QNAME_LEFT_PARENTHESIS + NAMESPACE + QName.QNAME_REVISION_DELIMITER
+                    + REVISION + QName.QNAME_RIGHT_PARENTHESIS + LOCALNAME, qname.toString());
+            QName copied = QName.create(qname.toString());
+            assertEquals(qname, copied);
+        }
+        // no revision
+        {
+            QName qname = QName.create(NS, LOCALNAME);
+            assertEquals(QName.QNAME_LEFT_PARENTHESIS + NAMESPACE + QName.QNAME_RIGHT_PARENTHESIS
+                    + LOCALNAME, qname.toString());
+            QName copied = QName.create(qname.toString());
+            assertEquals(qname, copied);
+        }
+    }
+
+    @Test
+    public void testIllegalLocalNames() {
+        assertLocalNameFails(null);
+        assertLocalNameFails("");
+        assertLocalNameFails("(");
+        assertLocalNameFails(")");
+        assertLocalNameFails("?");
+        assertLocalNameFails("&");
+    }
+
+    @Test
+    public void testCompareTo() throws Exception {
+        final String A = "a";
+        final String B = "b";
+
+        // compare with namespace
+        QName qa = QName.create(A, REVISION, A);
+        QName qb = QName.create(B, REVISION, A);
+        assertTrue(qa.compareTo(qb) < 0);
+        assertTrue(qb.compareTo(qa) > 0);
+
+        // compare with revision
+        qa = QName.create(A, "2013-12-24", A);
+        qb = QName.create(A, "2013-12-25", A);
+        assertTrue(qa.compareTo(qb) < 0);
+        assertTrue(qb.compareTo(qa) > 0);
+
+        // compare with 1 null revision
+        qa = QName.create(URI.create(A), A);
+        qb = QName.create(URI.create(A), Revision.of(REVISION), A);
+        assertTrue(qa.compareTo(qb) < 0);
+        assertTrue(qb.compareTo(qa) > 0);
+
+        // compare with both null revision
+        qb = QName.create(URI.create(A), A);
+        assertTrue(qa.compareTo(qb) == 0);
+        assertTrue(qb.compareTo(qa) == 0);
+    }
+
+    @Test
+    public void testQName() {
+        final QName qname = QName.create(NAMESPACE, REVISION, LOCALNAME);
+        final QName qname1 = QName.create(NAMESPACE, LOCALNAME);
+        final QName qname2 = QName.create(qname1, LOCALNAME);
+        assertEquals(qname1, qname.withoutRevision());
+        assertEquals(qname1, qname2);
+        assertTrue(qname.isEqualWithoutRevision(qname1));
+        assertNotNull(QName.formattedRevision(Revision.ofNullable("2000-01-01")));
+        assertNotNull(qname.hashCode());
+        assertEquals(qname, qname.intern());
+    }
+
+    @Test
+    public void testQNameModule() throws URISyntaxException {
+        final QNameModule qnameModule = QNameModule.create(NS, Revision.of("2000-01-01"));
+        assertNotNull(qnameModule.toString());
+        assertNotNull(qnameModule.getRevisionNamespace());
+    }
+
+    private static void assertLocalNameFails(final String localName) {
+        try {
+            QName.create(NS, localName);
+            fail("Local name should fail:" + localName);
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/RevisionTest.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/RevisionTest.java
new file mode 100644 (file)
index 0000000..2a43314
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.time.format.DateTimeParseException;
+import java.util.Optional;
+import org.junit.Test;
+
+public class RevisionTest {
+    @Test
+    public void testOf() {
+        assertEquals("2017-12-25", Revision.of("2017-12-25").toString());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testOfNull() {
+        Revision.of(null);
+    }
+
+    @Test(expected = DateTimeParseException.class)
+    public void testOfEmpty() {
+        Revision.of("");
+    }
+
+    @Test(expected = DateTimeParseException.class)
+    public void testOfInvalid() {
+        Revision.of("invalid");
+    }
+
+    @Test(expected = DateTimeParseException.class)
+    public void testOfInvalidDate1() {
+        Revision.of("2017-13-01");
+    }
+
+    @Test(expected = DateTimeParseException.class)
+    public void testOfInvalidDate2() {
+        Revision.of("2017-12-00");
+    }
+
+    @Test(expected = DateTimeParseException.class)
+    public void testOfInvalidDate3() {
+        Revision.of("2017-12-32");
+    }
+
+    @Test
+    public void testEquals() {
+        final Revision rev1 = Revision.of("2017-12-25");
+        final Revision rev1dup = Revision.of("2017-12-25");
+        final Revision rev2 = Revision.of("2017-12-26");
+
+        assertFalse(rev1.equals(null));
+        assertTrue(rev1.equals(rev1));
+        assertTrue(rev1.equals(rev1dup));
+        assertTrue(rev1dup.equals(rev1));
+        assertFalse(rev1.equals(rev2));
+        assertFalse(rev2.equals(rev1));
+    }
+
+    @Test
+    public void testOfNullable() {
+        assertEquals(Optional.empty(), Revision.ofNullable(null));
+
+        final Optional<Revision> opt = Revision.ofNullable("2017-12-25");
+        assertTrue(opt.isPresent());
+        assertEquals("2017-12-25", opt.get().toString());
+    }
+
+    @Test
+    public void testCompareOptional() {
+        assertEquals(0, Revision.compare(Optional.empty(), Optional.empty()));
+        assertEquals(0, Revision.compare(Revision.ofNullable("2017-12-25"), Revision.ofNullable("2017-12-25")));
+        assertEquals(-1, Revision.compare(Optional.empty(), Revision.ofNullable("2017-12-25")));
+        assertEquals(1, Revision.compare(Revision.ofNullable("2017-12-25"), Optional.empty()));
+    }
+
+    @Test
+    public void testSerialization() throws IOException, ClassNotFoundException {
+        final Revision source = Revision.of("2017-12-25");
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+            oos.writeObject(source);
+        }
+
+        final Object read;
+        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
+            read = ois.readObject();
+        }
+
+        assertEquals(source, read);
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/RpcResultBuilderTest.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/RpcResultBuilderTest.java
new file mode 100644 (file)
index 0000000..74166df
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * 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.yang.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+/**
+ * Unit tests for RpcResultBuilder.
+ *
+ * @author Thomas Pantelis
+ */
+public class RpcResultBuilderTest {
+
+    @Test
+    public void testSuccess() {
+        RpcResult<String> result = RpcResultBuilder.<String>success().withResult("foo").build();
+        verifyRpcResult(result, true, "foo");
+        assertNotNull("getErrors returned null", result.getErrors());
+        assertEquals("getErrors size", 0, result.getErrors().size());
+
+        result = RpcResultBuilder.success("bar").build();
+        verifyRpcResult(result, true, "bar");
+    }
+
+    @Test
+    public void testFailed() {
+        Throwable cause = new Throwable("mock cause");
+        Throwable cause2 = new Throwable("mock cause2");
+        RpcResult<String> result = RpcResultBuilder.<String>failed()
+                  .withError(ErrorType.PROTOCOL, "error message 1")
+                  .withError(ErrorType.APPLICATION, "lock_denied", "error message 2")
+                  .withError(ErrorType.RPC, "in-use", "error message 3", "my-app-tag", "my-info", cause)
+                  .withError(ErrorType.TRANSPORT, "error message 4", cause2)
+                  .build();
+        verifyRpcResult(result, false, null);
+        verifyRpcError(result, 0, ErrorSeverity.ERROR, ErrorType.PROTOCOL, "operation-failed",
+                        "error message 1", null, null, null);
+        verifyRpcError(result, 1, ErrorSeverity.ERROR, ErrorType.APPLICATION, "lock_denied",
+                        "error message 2", null, null, null);
+        verifyRpcError(result, 2, ErrorSeverity.ERROR, ErrorType.RPC, "in-use",
+                        "error message 3", "my-app-tag", "my-info", cause);
+        verifyRpcError(result, 3, ErrorSeverity.ERROR, ErrorType.TRANSPORT, "operation-failed",
+                        "error message 4", null, null, cause2);
+        assertEquals("getErrors size", 4, result.getErrors().size());
+    }
+
+    @Test
+    public void testWithWarnings() {
+        Throwable cause = new Throwable("mock cause");
+        RpcResult<String> result = RpcResultBuilder.<String>success()
+                  .withWarning(ErrorType.APPLICATION, "lock_denied", "message 1")
+                  .withWarning(ErrorType.RPC, "in-use", "message 2", "my-app-tag", "my-info", cause)
+                  .build();
+        verifyRpcResult(result, true, null);
+        verifyRpcError(result, 0, ErrorSeverity.WARNING, ErrorType.APPLICATION, "lock_denied",
+                        "message 1", null, null, null);
+        verifyRpcError(result, 1, ErrorSeverity.WARNING, ErrorType.RPC, "in-use",
+                        "message 2", "my-app-tag", "my-info", cause);
+        assertEquals("getErrors size", 2, result.getErrors().size());
+    }
+
+    @Test
+    public void testFrom() {
+        Throwable cause = new Throwable("mock cause");
+        RpcResult<String> result = RpcResultBuilder.<String>success()
+                .withResult("foo")
+                .withWarning(ErrorType.RPC, "in-use", "message", "my-app-tag", "my-info", cause)
+                .build();
+
+        RpcResult<String> copy = RpcResultBuilder.from(result)
+                .withError(ErrorType.PROTOCOL, "error message")
+                .build();
+        verifyRpcResult(copy, true, "foo");
+        verifyRpcError(copy, 0, ErrorSeverity.WARNING, ErrorType.RPC, "in-use",
+                        "message", "my-app-tag", "my-info", cause);
+        verifyRpcError(copy, 1, ErrorSeverity.ERROR, ErrorType.PROTOCOL, "operation-failed",
+                        "error message", null, null, null);
+    }
+
+    @Test
+    public void testWithRpcErrors() {
+        Throwable cause = new Throwable("mock cause");
+        RpcResult<String> result = RpcResultBuilder.<String>failed()
+                .withWarning(ErrorType.RPC, "in-use", "message", "my-app-tag", "my-info", cause)
+                .withError(ErrorType.PROTOCOL, "error message")
+                .build();
+
+        RpcResult<String> result2 = RpcResultBuilder.<String>failed()
+                .withRpcErrors(result.getErrors())
+                .build();
+        verifyRpcError(result2, 0, ErrorSeverity.WARNING, ErrorType.RPC, "in-use",
+                        "message", "my-app-tag", "my-info", cause);
+        verifyRpcError(result2, 1, ErrorSeverity.ERROR, ErrorType.PROTOCOL, "operation-failed",
+                        "error message", null, null, null);
+    }
+
+    @Test
+    public void testErrors() {
+        final RpcResultBuilder<Object> rpcResultBuilder = RpcResultBuilder.status(true);
+        final RpcError rpcErrorShort = RpcResultBuilder.newError(RpcError.ErrorType.RPC, "tag", "msg");
+        final RpcError rpcErrorLong = RpcResultBuilder.newError(RpcError.ErrorType.RPC, "tag", "msg", "applicationTag",
+                "info", null);
+        final RpcError rpcErrorShortWarn = RpcResultBuilder.newWarning(RpcError.ErrorType.RPC, "tag", "msg");
+        final RpcError rpcErrorLongWarn = RpcResultBuilder.newWarning(RpcError.ErrorType.RPC, "tag", "msg",
+                "applicationTag",
+                "info", null);
+        rpcResultBuilder.withRpcError(rpcErrorShort);
+        final RpcResult<Object> rpcResult = rpcResultBuilder.build();
+        final RpcResultBuilder<RpcResult<Object>> rpcResultRpcResultBuilder1 = RpcResultBuilder.success(
+                rpcResultBuilder);
+        final RpcResultBuilder<RpcResult<Object>> rpcResultRpcResultBuilder2 = rpcResultRpcResultBuilder1.withResult(
+                rpcResultBuilder);
+
+        assertEquals(rpcErrorShort.getErrorType(), rpcErrorShortWarn.getErrorType());
+        assertEquals(rpcErrorLong.getErrorType(), rpcErrorLongWarn.getErrorType());
+        assertEquals(rpcResultRpcResultBuilder1, rpcResultRpcResultBuilder2);
+        assertNotNull(rpcResultBuilder.buildFuture());
+        assertEquals("RpcResult [successful=true, result=null, errors=[RpcError [message=msg, severity=ERROR, "
+                + "errorType=RPC, tag=tag, applicationTag=null, info=null, cause=null]]]", rpcResult.toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialization() throws Exception {
+        RpcResult<String> result = RpcResultBuilder.<String>success().withResult("foo").build();
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        ObjectOutputStream out = new ObjectOutputStream(bos);
+        out.writeObject(result);
+
+        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
+        RpcResult<String> clone = (RpcResult<String>) in.readObject();
+
+        verifyRpcResult(clone, true, "foo");
+
+        Throwable cause = new Throwable("mock cause");
+        result = RpcResultBuilder.<String>failed()
+                .withError(ErrorType.RPC, "in-use", "error message", "my-app-tag", "my-info", cause)
+                .build();
+
+        bos = new ByteArrayOutputStream();
+        out = new ObjectOutputStream(bos);
+        out.writeObject(result);
+
+        in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
+        clone = (RpcResult<String>) in.readObject();
+
+        verifyRpcResult(clone, false, null);
+        verifyRpcError(result, 0, ErrorSeverity.ERROR, ErrorType.RPC, "in-use",
+                "error message", "my-app-tag", "my-info", cause);
+    }
+
+    void verifyRpcError(final RpcResult<?> result, final int errorIndex, final ErrorSeverity expSeverity,
+            final ErrorType expErrorType, final String expTag, final String expMessage, final String expAppTag,
+            final String expInfo, final Throwable expCause) {
+
+        List<RpcError> errors = new ArrayList<>(result.getErrors());
+        assertTrue("Expected error at index " + errorIndex + " not found",
+                    errorIndex < errors.size());
+        RpcError error = errors.get(errorIndex);
+        assertEquals("getSeverity", expSeverity, error.getSeverity());
+        assertEquals("getErrorType", expErrorType, error.getErrorType());
+        assertEquals("getTag", expTag, error.getTag());
+        assertEquals("getMessage", expMessage, error.getMessage());
+        assertEquals("getApplicationTag", expAppTag, error.getApplicationTag());
+        assertEquals("getInfo", expInfo, error.getInfo());
+        assertEquals("getCause", expCause, error.getCause());
+    }
+
+    void verifyRpcResult(final RpcResult<?> result, final boolean expSuccess, final Object expValue) {
+        assertEquals("isSuccessful", expSuccess, result.isSuccessful());
+        assertEquals("getResult", expValue, result.getResult());
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Uint16Test.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Uint16Test.java
new file mode 100644 (file)
index 0000000..767780b
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.common;
+
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.lessThan;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import org.junit.Test;
+
+public class Uint16Test {
+    @Test
+    public void testValueOf() {
+        assertEquals(127, Uint16.valueOf(Byte.MAX_VALUE).byteValue());
+        assertEquals(32767, Uint16.valueOf(Short.MAX_VALUE).shortValue());
+        assertEquals(65535, Uint16.valueOf(65535).intValue());
+        assertEquals(65535L, Uint16.valueOf(65535L).longValue());
+        assertEquals(0, Uint16.valueOf("0").intValue());
+    }
+
+    @Test
+    public void testCompareTo() {
+        final Uint16 five = Uint16.valueOf(5);
+        final Uint16 zero = Uint16.valueOf(0);
+        final Uint16 max = Uint16.valueOf(65535);
+
+        assertEquals(0, zero.compareTo(zero));
+        assertThat(zero.compareTo(five), lessThan(0));
+        assertThat(zero.compareTo(max), lessThan(0));
+
+        assertThat(five.compareTo(zero), greaterThan(0));
+        assertEquals(0, five.compareTo(five));
+        assertThat(five.compareTo(max), lessThan(0));
+
+        assertThat(max.compareTo(zero), greaterThan(0));
+        assertThat(max.compareTo(five), greaterThan(0));
+        assertEquals(0, max.compareTo(max));
+    }
+
+    @Test
+    public void testEquals() {
+        final Uint16 five = Uint16.valueOf(5);
+        final Uint16 zero = Uint16.valueOf(0);
+        final Uint16 max = Uint16.valueOf(65535);
+
+        final Uint16 test = new Uint16(five);
+        assertFalse(test.equals(zero));
+        assertFalse(test.equals(new Object()));
+        assertFalse(test.equals(max));
+        assertTrue(test.equals(test));
+        assertTrue(test.equals(five));
+        assertTrue(five.equals(test));
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals("0", Uint16.valueOf(0).toString());
+        assertEquals("32767", Uint16.valueOf(32767).toString());
+        assertEquals("32768", Uint16.valueOf(32768).toString());
+        assertEquals("65535", Uint16.valueOf(65535).toString());
+    }
+
+    @Test
+    public void testHashCode() {
+        assertEquals(Short.hashCode((short)-63), Uint16.fromShortBits((short)-63).hashCode());
+    }
+
+    @Test
+    public void testFloatValue() {
+        assertEquals(0, Uint16.valueOf(0).floatValue(), 0);
+    }
+
+    @Test
+    public void testDoubleValue() {
+        assertEquals(0, Uint16.valueOf(0).doubleValue(), 0);
+    }
+
+    @Test
+    public void testConversions() {
+        assertSame(Uint16.valueOf(5), Uint16.valueOf(Uint8.valueOf(5)));
+        assertSame(Uint16.valueOf(10), Uint16.valueOf(Uint32.valueOf(10)));
+        assertSame(Uint16.valueOf(20), Uint16.valueOf(Uint64.valueOf(20)));
+
+        assertEquals(Uint8.TEN, Uint16.TEN.toUint8());
+        assertEquals(Uint32.valueOf(65535), Uint16.MAX_VALUE.toUint32());
+        assertEquals(Uint64.valueOf(65535), Uint16.MAX_VALUE.toUint64());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testToUint8() {
+        Uint16.MAX_VALUE.toUint8();
+    }
+
+    @Test
+    public void testSerialization() throws IOException, ClassNotFoundException {
+        final Uint16 source = Uint16.valueOf(255);
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+            oos.writeObject(source);
+        }
+
+        final Object read;
+        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
+            read = ois.readObject();
+        }
+
+        assertSame(source, read);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeByte() {
+        Uint16.valueOf((byte)-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeShort() {
+        Uint16.valueOf((short)-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeInt() {
+        Uint16.valueOf(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeLong() {
+        Uint16.valueOf(-1L);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBigInt() {
+        Uint16.valueOf(65536);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBigLong() {
+        Uint16.valueOf(65536L);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testNullValueOfString() {
+        Uint16.valueOf((String) null);
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Uint32Test.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Uint32Test.java
new file mode 100644 (file)
index 0000000..2706dc0
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.primitives.UnsignedInteger;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import org.junit.Test;
+
+public class Uint32Test {
+    @Test
+    public void testValueOf() {
+        assertEquals(127, Uint32.valueOf(Byte.MAX_VALUE).byteValue());
+        assertEquals(32767, Uint32.valueOf(Short.MAX_VALUE).shortValue());
+        assertEquals(2147483647, Uint32.valueOf(Integer.MAX_VALUE).intValue());
+        assertEquals(4294967295L, Uint32.valueOf(4294967295L).longValue());
+        assertEquals(0, Uint32.valueOf("0").intValue());
+    }
+
+    @Test
+    public void testCompareTo() {
+        final Uint32 five = Uint32.valueOf(5);
+        final Uint32 zero = Uint32.valueOf(0);
+        final Uint32 max = Uint32.valueOf(4294967295L);
+
+        assertEquals(0, zero.compareTo(zero));
+        assertEquals(-1, zero.compareTo(five));
+        assertEquals(-1, zero.compareTo(max));
+
+        assertEquals(1, five.compareTo(zero));
+        assertEquals(0, five.compareTo(five));
+        assertEquals(-1, five.compareTo(max));
+
+        assertEquals(1, max.compareTo(zero));
+        assertEquals(1, max.compareTo(five));
+        assertEquals(0, max.compareTo(max));
+    }
+
+    @Test
+    public void testEquals() {
+        final Uint32 five = Uint32.valueOf(5);
+        final Uint32 zero = Uint32.valueOf(0);
+        final Uint32 max = Uint32.valueOf(4294967295L);
+
+        final Uint32 test = new Uint32(five);
+        assertFalse(test.equals(zero));
+        assertFalse(test.equals(new Object()));
+        assertFalse(test.equals(max));
+        assertTrue(test.equals(test));
+        assertTrue(test.equals(five));
+        assertTrue(five.equals(test));
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals("0", Uint32.valueOf(0).toString());
+        assertEquals("2147483647", Uint32.valueOf(2147483647L).toString());
+        assertEquals("2147483648", Uint32.valueOf(2147483648L).toString());
+        assertEquals("4294967295", Uint32.valueOf(4294967295L).toString());
+    }
+
+    @Test
+    public void testHashCode() {
+        assertEquals(Integer.hashCode(-63), Uint32.fromIntBits(-63).hashCode());
+    }
+
+    @Test
+    public void testFloatValue() {
+        assertEquals(0, Uint32.valueOf(0).floatValue(), 0);
+    }
+
+    @Test
+    public void testDoubleValue() {
+        assertEquals(0, Uint32.valueOf(0).doubleValue(), 0);
+    }
+
+    @Test
+    public void testConversions() {
+        assertSame(Uint32.valueOf(5), Uint32.valueOf(Uint8.valueOf(5)));
+        assertSame(Uint32.valueOf(10), Uint32.valueOf(Uint16.valueOf(10)));
+        assertSame(Uint32.valueOf(20), Uint32.valueOf(Uint64.valueOf(20)));
+
+        assertSame(Uint32.valueOf(5), Uint32.valueOf(UnsignedInteger.fromIntBits(5)));
+        assertEquals(UnsignedInteger.fromIntBits(5), Uint32.valueOf(5).toGuava());
+
+        assertEquals(Uint8.TEN, Uint32.TEN.toUint8());
+        assertEquals(Uint16.TEN, Uint32.TEN.toUint16());
+        assertEquals(Uint64.valueOf(4294967295L), Uint32.MAX_VALUE.toUint64());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testToUint8() {
+        Uint32.MAX_VALUE.toUint8();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testToUint16() {
+        Uint32.MAX_VALUE.toUint16();
+    }
+
+    @Test
+    public void testSerialization() throws IOException, ClassNotFoundException {
+        final Uint32 source = Uint32.valueOf(255);
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+            oos.writeObject(source);
+        }
+
+        final Object read;
+        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
+            read = ois.readObject();
+        }
+
+        assertSame(source, read);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeByte() {
+        Uint32.valueOf((byte)-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeShort() {
+        Uint32.valueOf((short)-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeInt() {
+        Uint32.valueOf(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeLong() {
+        Uint32.valueOf(-1L);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBigLong() {
+        Uint32.valueOf(4294967296L);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testNullValueOfString() {
+        Uint32.valueOf((String) null);
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Uint64Test.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Uint64Test.java
new file mode 100644 (file)
index 0000000..dfc5e84
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.primitives.UnsignedLong;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import org.junit.Test;
+
+public class Uint64Test {
+    @Test
+    public void testValueOf() {
+        assertEquals(127, Uint64.valueOf(Byte.MAX_VALUE).byteValue());
+        assertEquals(32767, Uint64.valueOf(Short.MAX_VALUE).shortValue());
+        assertEquals(2147483647, Uint64.valueOf(Integer.MAX_VALUE).intValue());
+        assertEquals(9223372036854775807L, Uint64.valueOf(Long.MAX_VALUE).longValue());
+        assertEquals(0, Uint64.valueOf("0").intValue());
+        assertEquals(2170205184637009920L, Uint64.valueOf(2170205184637009920L).longValue());
+        assertEquals(2170205184637009920L, Uint64.valueOf(new BigInteger("2170205184637009920")).longValue());
+    }
+
+    @Test
+    public void testCompareTo() {
+        final Uint64 five = Uint64.valueOf(5);
+        final Uint64 zero = Uint64.valueOf(0);
+        final Uint64 max = Uint64.valueOf(4294967295L);
+
+        assertEquals(0, zero.compareTo(zero));
+        assertEquals(-1, zero.compareTo(five));
+        assertEquals(-1, zero.compareTo(max));
+
+        assertEquals(1, five.compareTo(zero));
+        assertEquals(0, five.compareTo(five));
+        assertEquals(-1, five.compareTo(max));
+
+        assertEquals(1, max.compareTo(zero));
+        assertEquals(1, max.compareTo(five));
+        assertEquals(0, max.compareTo(max));
+    }
+
+    @Test
+    public void testEquals() {
+        final Uint64 five = Uint64.valueOf(5);
+        final Uint64 zero = Uint64.valueOf(0);
+        final Uint64 max = Uint64.valueOf(4294967295L);
+
+        final Uint64 test = new Uint64(five);
+        assertFalse(test.equals(zero));
+        assertFalse(test.equals(new Object()));
+        assertFalse(test.equals(max));
+        assertTrue(test.equals(test));
+        assertTrue(test.equals(five));
+        assertTrue(five.equals(test));
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals("0", Uint64.valueOf(0).toString());
+        assertEquals("2147483647", Uint64.valueOf(2147483647L).toString());
+        assertEquals("2147483648", Uint64.valueOf(2147483648L).toString());
+        assertEquals("4294967295", Uint64.valueOf(4294967295L).toString());
+    }
+
+    @Test
+    public void testHashCode() {
+        assertEquals(Long.hashCode(-63), Uint64.fromLongBits(-63L).hashCode());
+    }
+
+    @Test
+    public void testFloatValue() {
+        assertEquals(0, Uint64.valueOf(0).floatValue(), 0);
+    }
+
+    @Test
+    public void testDoubleValue() {
+        assertEquals(0, Uint64.valueOf(0).doubleValue(), 0);
+    }
+
+    @Test
+    public void testConversions() {
+        assertSame(Uint64.valueOf(5), Uint64.valueOf(Uint8.valueOf(5)));
+        assertSame(Uint64.valueOf(10), Uint64.valueOf(Uint16.valueOf(10)));
+        assertSame(Uint64.valueOf(20), Uint64.valueOf(Uint32.valueOf(20)));
+        assertEquals(Uint64.valueOf(30), Uint64.valueOf(new BigInteger("30")));
+
+        assertSame(Uint64.valueOf(5), Uint64.valueOf(UnsignedLong.fromLongBits(5)));
+        assertEquals(UnsignedLong.fromLongBits(5), Uint64.valueOf(5).toGuava());
+
+        assertEquals(Uint8.TEN, Uint64.TEN.toUint8());
+        assertEquals(Uint16.TEN, Uint64.TEN.toUint16());
+        assertEquals(Uint32.TEN, Uint64.TEN.toUint32());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testToUint8() {
+        Uint64.MAX_VALUE.toUint8();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testToUint16() {
+        Uint64.MAX_VALUE.toUint16();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testToUint32() {
+        Uint64.MAX_VALUE.toUint32();
+    }
+
+    @Test
+    public void testSerialization() throws IOException, ClassNotFoundException {
+        final Uint64 source = Uint64.valueOf(255);
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+            oos.writeObject(source);
+        }
+
+        final Object read;
+        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
+            read = ois.readObject();
+        }
+
+        assertSame(source, read);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeByte() {
+        Uint64.valueOf((byte)-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeShort() {
+        Uint64.valueOf((short)-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeInt() {
+        Uint64.valueOf(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeLong() {
+        Uint64.valueOf(-1L);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeBigInteger() {
+        Uint64.valueOf(new BigInteger("-1"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBigBigInteger() {
+        Uint64.valueOf(new BigInteger("0x10000000000000000"));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testNullValueOfString() {
+        Uint64.valueOf((String) null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testNullValueOfBigInteger() {
+        Uint64.valueOf((BigInteger) null);
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Uint8Test.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Uint8Test.java
new file mode 100644 (file)
index 0000000..a231e96
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import org.junit.Test;
+
+public class Uint8Test {
+    @Test
+    public void testValueOf() {
+        assertEquals(127, Uint8.valueOf(Byte.MAX_VALUE).byteValue());
+        assertEquals(255, Uint8.valueOf(255).intValue());
+        assertEquals(255L, Uint8.valueOf(255L).longValue());
+        assertEquals(0, Uint8.valueOf("0").intValue());
+    }
+
+    @Test
+    public void testCompareTo() {
+        final Uint8 five = Uint8.valueOf(5);
+        final Uint8 zero = Uint8.valueOf(0);
+        final Uint8 max = Uint8.valueOf(255);
+
+        assertEquals(0, zero.compareTo(zero));
+        assertEquals(-5, zero.compareTo(five));
+        assertEquals(-255, zero.compareTo(max));
+
+        assertEquals(5, five.compareTo(zero));
+        assertEquals(0, five.compareTo(five));
+        assertEquals(-250, five.compareTo(max));
+
+        assertEquals(255, max.compareTo(zero));
+        assertEquals(250, max.compareTo(five));
+        assertEquals(0, max.compareTo(max));
+    }
+
+    @Test
+    public void testEquals() {
+        final Uint8 five = Uint8.valueOf(5);
+        final Uint8 zero = Uint8.valueOf(0);
+        final Uint8 max = Uint8.valueOf(255);
+
+        final Uint8 test = new Uint8(five);
+        assertFalse(test.equals(zero));
+        assertFalse(test.equals(new Object()));
+        assertFalse(test.equals(max));
+        assertTrue(test.equals(test));
+        assertTrue(test.equals(five));
+        assertTrue(five.equals(test));
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals("0", Uint8.valueOf(0).toString());
+        assertEquals("127", Uint8.valueOf(127).toString());
+        assertEquals("128", Uint8.valueOf(128).toString());
+        assertEquals("255", Uint8.valueOf(255).toString());
+    }
+
+    @Test
+    public void testHashCode() {
+        assertEquals(Byte.hashCode((byte)-63), Uint8.fromByteBits((byte)-63).hashCode());
+    }
+
+    @Test
+    public void testFloatValue() {
+        assertEquals(0, Uint8.valueOf(0).floatValue(), 0);
+    }
+
+    @Test
+    public void testDoubleValue() {
+        assertEquals(0, Uint8.valueOf(0).doubleValue(), 0);
+    }
+
+    @Test
+    public void testConversions() {
+        assertSame(Uint8.valueOf(5), Uint8.valueOf(Uint16.valueOf(5)));
+        assertSame(Uint8.valueOf(10), Uint8.valueOf(Uint32.valueOf(10)));
+        assertSame(Uint8.valueOf(20), Uint8.valueOf(Uint64.valueOf(20)));
+
+        assertEquals(Uint16.valueOf(255), Uint8.MAX_VALUE.toUint16());
+        assertEquals(Uint32.valueOf(255), Uint8.MAX_VALUE.toUint32());
+        assertEquals(Uint64.valueOf(255), Uint8.MAX_VALUE.toUint64());
+    }
+
+    @Test
+    public void testSerialization() throws IOException, ClassNotFoundException {
+        final Uint8 source = Uint8.valueOf(255);
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+            oos.writeObject(source);
+        }
+
+        final Object read;
+        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
+            read = ois.readObject();
+        }
+
+        assertSame(source, read);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeByte() {
+        Uint8.valueOf((byte)-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeShort() {
+        Uint8.valueOf((short)-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeInt() {
+        Uint8.valueOf(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeLong() {
+        Uint8.valueOf(-1L);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBigShort() {
+        Uint8.valueOf((short)256);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBigInt() {
+        Uint8.valueOf(256);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testBigLong() {
+        Uint8.valueOf(256L);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testNullValueOfString() {
+        Uint8.valueOf((String) null);
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/YangConstantsTest.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/YangConstantsTest.java
new file mode 100644 (file)
index 0000000..26bf20b
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016 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.common;
+
+import static org.junit.Assert.assertEquals;
+
+import java.net.URI;
+import org.junit.Test;
+
+public class YangConstantsTest {
+
+    @Test
+    public void testYangConstants() {
+        final URI uriYang = YangConstants.RFC6020_YANG_NAMESPACE;
+        final URI uriYin = YangConstants.RFC6020_YIN_NAMESPACE;
+        assertEquals(URI.create("urn:ietf:params:xml:ns:yang:1"), uriYang);
+        assertEquals(URI.create("urn:ietf:params:xml:ns:yang:yin:1"), uriYin);
+        assertEquals(QNameModule.create(uriYang).intern(), YangConstants.RFC6020_YANG_MODULE);
+        assertEquals(QNameModule.create(uriYin).intern(), YangConstants.RFC6020_YIN_MODULE);
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/YangNamesTest.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/YangNamesTest.java
new file mode 100644 (file)
index 0000000..c957b61
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.common;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.AbstractMap.SimpleImmutableEntry;
+import org.junit.Test;
+
+public class YangNamesTest {
+    @Test
+    public void testParseFileName() {
+        assertEquals(new SimpleImmutableEntry<>("foo", null), YangNames.parseFilename("foo"));
+        assertEquals(new SimpleImmutableEntry<>("foo", "bar"), YangNames.parseFilename("foo@bar"));
+        assertEquals(new SimpleImmutableEntry<>("foo@bar", "baz"), YangNames.parseFilename("foo@bar@baz"));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testParseFileNameNull() {
+        YangNames.parseFilename(null);
+    }
+}
diff --git a/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/YangVersionTest.java b/yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/YangVersionTest.java
new file mode 100644 (file)
index 0000000..dff3cfb
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.common;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Optional;
+import org.junit.Test;
+
+public class YangVersionTest {
+    @Test
+    public void testGetReference() {
+        assertEquals("RFC6020", YangVersion.VERSION_1.getReference());
+        assertEquals("RFC7950", YangVersion.VERSION_1_1.getReference());
+    }
+
+    @Test
+    public void testParse() {
+        assertEquals(Optional.empty(), YangVersion.parse(""));
+        assertEquals(Optional.empty(), YangVersion.parse("1."));
+        assertEquals(Optional.of(YangVersion.VERSION_1), YangVersion.parse("1"));
+        assertEquals(Optional.of(YangVersion.VERSION_1_1), YangVersion.parse("1.1"));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testParseNull() {
+        YangVersion.parse(null);
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals("1", YangVersion.VERSION_1.toString());
+        assertEquals("1.1", YangVersion.VERSION_1_1.toString());
+    }
+}
diff --git a/yang/yang-data-api/pom.xml b/yang/yang-data-api/pom.xml
new file mode 100644 (file)
index 0000000..50c6917
--- /dev/null
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-data-api</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.yangtools</groupId>
+          <artifactId>yang-model-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.data.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/AIv1.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/AIv1.java
new file mode 100644 (file)
index 0000000..939599c
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+
+/**
+ * Externalizable proxy for {@link AugmentationIdentifier}.
+ */
+final class AIv1 implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private AugmentationIdentifier ai;
+
+    @SuppressWarnings("checkstyle:redundantModifier")
+    public AIv1() {
+        // For Externalizable
+    }
+
+    AIv1(final AugmentationIdentifier ai) {
+        this.ai = requireNonNull(ai);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        out.writeInt(ai.getPossibleChildNames().size());
+        for (QName qname : ai.getPossibleChildNames()) {
+            qname.writeTo(out);
+        }
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        final int count = in.readInt();
+        final QName[] qnames = new QName[count];
+        for (int i = 0; i < count; ++i) {
+            qnames[i] = QName.readFrom(in);
+        }
+        ai = new AugmentationIdentifier(ImmutableSet.copyOf(qnames));
+    }
+
+    private Object readResolve() {
+        return ai;
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/DSIv1.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/DSIv1.java
new file mode 100644 (file)
index 0000000..d2348e0
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+final class DSIv1 implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private QName qname;
+
+    @SuppressWarnings("checkstyle:redundantModifier")
+    public DSIv1() {
+        // For Externalizable
+    }
+
+    DSIv1(final @NonNull QName qname) {
+        this.qname = requireNonNull(qname);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        qname.writeTo(out);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        qname = QName.readFrom(in);
+    }
+
+    private Object readResolve() {
+        return DatastoreIdentifier.create(qname);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/DatastoreIdentifier.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/DatastoreIdentifier.java
new file mode 100644 (file)
index 0000000..3359025
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+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 java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.net.URI;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.WritableObject;
+import org.opendaylight.yangtools.util.AbstractIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Identifier of a RFC8342 (NMDA) datastore. This class is backed by the QName of the datastore, i.e.
+ * the {@code identity} which defines the datastore. This class does not allow creation of identifiers which are
+ * defined as abstract, that is "datastore", "conventional" and "dynamic" in the namespace of {@code ietf-datastores}.
+ */
+@Beta
+@NonNullByDefault
+public final class DatastoreIdentifier extends AbstractIdentifier<QName> implements WritableObject {
+    private static final long serialVersionUID = 1L;
+
+    private static final URI IETF_DATASTORES_NAMESPACE = URI.create("urn:ietf:params:xml:ns:yang:ietf-datastores");
+    private static final ImmutableSet<String> KNOWN_ABSTRACTS = ImmutableSet.of("datastore", "conventional", "dynamic");
+
+    private static final LoadingCache<QName, DatastoreIdentifier> CACHE = CacheBuilder.newBuilder().weakValues()
+            .build(new CacheLoader<QName, DatastoreIdentifier>() {
+                @Override
+                public DatastoreIdentifier load(final QName key) {
+                    return of(key);
+                }
+            });
+
+    private DatastoreIdentifier(final QName qname) {
+        super(qname);
+        if (IETF_DATASTORES_NAMESPACE.equals(qname.getNamespace())) {
+            checkArgument(!KNOWN_ABSTRACTS.contains(qname.getLocalName()), "%s refers to a known-abstract datastore",
+                qname);
+        }
+    }
+
+    public static DatastoreIdentifier of(final QName qname) {
+        return new DatastoreIdentifier(qname);
+    }
+
+    public static DatastoreIdentifier create(final QName qname) {
+        final DatastoreIdentifier existing = CACHE.getIfPresent(qname);
+        return existing != null ? existing : CACHE.getUnchecked(qname.intern());
+    }
+
+    public static DatastoreIdentifier readFrom(final DataInput in) throws IOException {
+        return create(QName.readFrom(in));
+    }
+
+    @Override
+    public void writeTo(final DataOutput out) throws IOException {
+        getValue().writeTo(out);
+    }
+
+    private Object writeReplace() {
+        return new DSIv1(getValue());
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/FixedYangInstanceIdentifier.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/FixedYangInstanceIdentifier.java
new file mode 100644 (file)
index 0000000..ff7499d
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2015 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.io.ObjectStreamException;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.HashCodeBuilder;
+
+final class FixedYangInstanceIdentifier extends YangInstanceIdentifier implements Cloneable {
+    static final @NonNull FixedYangInstanceIdentifier EMPTY_INSTANCE = new FixedYangInstanceIdentifier(
+        ImmutableList.of(), new HashCodeBuilder<>().build());
+    private static final long serialVersionUID = 1L;
+
+    private final ImmutableList<PathArgument> path;
+    private transient volatile YangInstanceIdentifier parent;
+
+    private FixedYangInstanceIdentifier(final ImmutableList<PathArgument> path, final int hash) {
+        super(hash);
+        this.path = requireNonNull(path, "path must not be null.");
+    }
+
+    static @NonNull FixedYangInstanceIdentifier create(final Iterable<? extends PathArgument> path, final int hash) {
+        return new FixedYangInstanceIdentifier(ImmutableList.copyOf(path), hash);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return path.isEmpty();
+    }
+
+    @Override
+    public FixedYangInstanceIdentifier clone() {
+        try {
+            return (FixedYangInstanceIdentifier) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new IllegalStateException("clone() should be supported", e);
+        }
+    }
+
+    @Override
+    public YangInstanceIdentifier getParent() {
+        if (path.isEmpty()) {
+            return null;
+        }
+
+        YangInstanceIdentifier ret = parent;
+        if (ret == null) {
+            ret = YangInstanceIdentifier.create(path.subList(0, path.size() - 1));
+            parent = ret;
+        }
+
+        return ret;
+    }
+
+    @Override
+    public YangInstanceIdentifier getAncestor(final int depth) {
+        checkArgument(depth >= 0, "Negative depth is not allowed");
+        checkArgument(depth <= path.size(), "Depth %s exceeds maximum depth %s", depth, path.size());
+
+        if (depth == path.size()) {
+            return this;
+        }
+        if (depth == path.size() - 1) {
+            // Use the parent cache
+            return getParent();
+        }
+        return YangInstanceIdentifier.create(path.subList(0, depth));
+    }
+
+    @Override
+    public List<PathArgument> getPathArguments() {
+        return path;
+    }
+
+    @Override
+    public List<PathArgument> getReversePathArguments() {
+        return path.reverse();
+    }
+
+    @Override
+    @NonNull List<PathArgument> tryPathArguments() {
+        return path;
+    }
+
+    @Override
+    @NonNull List<PathArgument> tryReversePathArguments() {
+        return path.reverse();
+    }
+
+    @Override
+    public PathArgument getLastPathArgument() {
+        return path.isEmpty() ? null : path.get(path.size() - 1);
+    }
+
+    @Override
+    YangInstanceIdentifier createRelativeIdentifier(final int skipFromRoot) {
+        if (skipFromRoot == path.size()) {
+            return EMPTY_INSTANCE;
+        }
+
+        final ImmutableList<PathArgument> newPath = path.subList(skipFromRoot, path.size());
+        final HashCodeBuilder<PathArgument> hash = new HashCodeBuilder<>();
+        for (PathArgument a : newPath) {
+            hash.addArgument(a);
+        }
+
+        return new FixedYangInstanceIdentifier(newPath, hash.build());
+    }
+
+    private Object readResolve() throws ObjectStreamException {
+        return path.isEmpty() ? EMPTY_INSTANCE : this;
+    }
+
+    @Override
+    boolean pathArgumentsEqual(final YangInstanceIdentifier other) {
+        if (other instanceof FixedYangInstanceIdentifier) {
+            return path.equals(((FixedYangInstanceIdentifier) other).path);
+        }
+        return super.pathArgumentsEqual(other);
+    }
+
+    @Override
+    public FixedYangInstanceIdentifier toOptimized() {
+        return this;
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NIPv1.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NIPv1.java
new file mode 100644 (file)
index 0000000..de051e5
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+
+/**
+ * Externalizable proxy for {@link NodeIdentifierWithPredicates}.
+ *
+ * @deprecated Since 4.0.0 in favor of {@link NIPv2}.
+ */
+@Deprecated(since = "4.0.0", forRemoval = true)
+final class NIPv1 implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private NodeIdentifierWithPredicates nip;
+
+    @SuppressWarnings("checkstyle:redundantModifier")
+    public NIPv1() {
+        // For Externalizable
+    }
+
+    NIPv1(final NodeIdentifierWithPredicates nid) {
+        this.nip = requireNonNull(nid);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        nip.getNodeType().writeTo(out);
+        out.writeObject(nip.asMap());
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        final QName qname = QName.readFrom(in);
+        nip = NodeIdentifierWithPredicates.of(qname, (Map<QName, Object>) in.readObject());
+    }
+
+    private Object readResolve() {
+        return nip;
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NIPv2.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NIPv2.java
new file mode 100644 (file)
index 0000000..cf73b48
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Map.Entry;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+
+/**
+ * Externalizable proxy for {@link NodeIdentifierWithPredicates}.
+ */
+final class NIPv2 implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private NodeIdentifierWithPredicates nip;
+
+    @SuppressWarnings("checkstyle:redundantModifier")
+    public NIPv2() {
+        // For Externalizable
+    }
+
+    NIPv2(final NodeIdentifierWithPredicates nid) {
+        this.nip = requireNonNull(nid);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        nip.getNodeType().writeTo(out);
+
+        out.writeInt(nip.size());
+        for (Entry<QName, Object> entry : nip.entrySet()) {
+            entry.getKey().writeTo(out);
+            out.writeObject(entry.getValue());
+        }
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        final QName qname = QName.readFrom(in);
+        final int size = in.readInt();
+        switch (size) {
+            case 0:
+                nip = NodeIdentifierWithPredicates.of(qname);
+                break;
+            case 1:
+                nip = NodeIdentifierWithPredicates.of(qname, QName.readFrom(in), in.readObject());
+                break;
+            default:
+                final Builder<QName, Object> keys = ImmutableMap.builderWithExpectedSize(size);
+                for (int i = 0; i < size; ++i) {
+                    keys.put(QName.readFrom(in), in.readObject());
+                }
+                nip = NodeIdentifierWithPredicates.of(qname, keys.build());
+        }
+    }
+
+    private Object readResolve() {
+        return nip;
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NIVv1.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NIVv1.java
new file mode 100644 (file)
index 0000000..a79adee
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+
+/**
+ * Externalizable proxy for {@link NodeWithValue}.
+ */
+final class NIVv1 implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private NodeWithValue<?> niv;
+
+    @SuppressWarnings("checkstyle:redundantModifier")
+    public NIVv1() {
+        // For Externalizable
+    }
+
+    NIVv1(final NodeWithValue<?> niv) {
+        this.niv = requireNonNull(niv);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        niv.getNodeType().writeTo(out);
+        out.writeObject(niv.getValue());
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        niv = new NodeWithValue<>(QName.readFrom(in), in.readObject());
+    }
+
+    private Object readResolve() {
+        return niv;
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NIv1.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NIv1.java
new file mode 100644 (file)
index 0000000..bc58788
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+
+/**
+ * Externalizable proxy for {@link NodeIdentifier}.
+ */
+final class NIv1 implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private NodeIdentifier nid;
+
+    @SuppressWarnings("checkstyle:redundantModifier")
+    public NIv1() {
+        // For Externalizable
+    }
+
+    NIv1(final NodeIdentifier nid) {
+        this.nid = requireNonNull(nid);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        nid.getNodeType().writeTo(out);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        nid = new NodeIdentifier(QName.readFrom(in));
+    }
+
+    private Object readResolve() {
+        return nid;
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/PathArgumentList.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/PathArgumentList.java
new file mode 100644 (file)
index 0000000..e41d79b
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 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;
+
+import com.google.common.collect.UnmodifiableIterator;
+import java.util.AbstractList;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+abstract class PathArgumentList extends AbstractList<PathArgument> {
+    @Override
+    public abstract @NonNull UnmodifiableIterator<PathArgument> iterator();
+
+    @Override
+    public final boolean isEmpty() {
+        return false;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final boolean remove(final Object o) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final boolean addAll(final Collection<? extends PathArgument> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final boolean addAll(final int index, final Collection<? extends PathArgument> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final boolean removeAll(final Collection<?> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final boolean retainAll(final Collection<?> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final void clear() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/StackedPathArguments.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/StackedPathArguments.java
new file mode 100644 (file)
index 0000000..022aee6
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2015 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;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.UnmodifiableIterator;
+import java.util.Iterator;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+final class StackedPathArguments extends PathArgumentList {
+    private final List<PathArgument> base;
+    private final List<PathArgument> stack;
+
+    StackedPathArguments(final @NonNull YangInstanceIdentifier base, final @NonNull List<PathArgument> stack) {
+        verify(!stack.isEmpty());
+        this.base = base.getPathArguments();
+        this.stack = stack;
+    }
+
+    @Override
+    public int size() {
+        return stack.size() + base.size();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean contains(final Object o) {
+        final PathArgument srch = (PathArgument) requireNonNull(o);
+        return stack.contains(srch) || base.contains(srch);
+    }
+
+    @Override
+    public PathArgument get(final int index) {
+        if (index < base.size()) {
+            return base.get(index);
+        }
+        return stack.get(index - base.size());
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public int indexOf(final Object o) {
+        final PathArgument srch = (PathArgument) requireNonNull(o);
+
+        int ret = base.indexOf(srch);
+        if (ret == -1) {
+            ret = stack.indexOf(srch);
+            if (ret != -1) {
+                return base.size() + ret;
+            }
+        }
+        return ret;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public int lastIndexOf(final Object o) {
+        final PathArgument srch = (PathArgument) requireNonNull(o);
+
+        final int ret = stack.lastIndexOf(srch);
+        if (ret != -1) {
+            return base.size() + ret;
+        }
+
+        return base.lastIndexOf(srch);
+    }
+
+    @Override
+    public UnmodifiableIterator<PathArgument> iterator() {
+        return new IteratorImpl(base, stack);
+    }
+
+    private static final class IteratorImpl extends UnmodifiableIterator<PathArgument> {
+        private final Iterator<PathArgument> stack;
+        private final Iterator<PathArgument> base;
+
+        IteratorImpl(final Iterable<PathArgument> base, final Iterable<PathArgument> stack) {
+            this.base = base.iterator();
+            this.stack = stack.iterator();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return stack.hasNext();
+        }
+
+        @Override
+        public PathArgument next() {
+            if (base.hasNext()) {
+                return base.next();
+            }
+            return stack.next();
+        }
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/StackedReversePathArguments.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/StackedReversePathArguments.java
new file mode 100644 (file)
index 0000000..bcf2122
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2015 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;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.Iterators;
+import com.google.common.collect.UnmodifiableIterator;
+import java.util.Collection;
+import java.util.Iterator;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+final class StackedReversePathArguments extends PathArgumentList {
+    private final StackedYangInstanceIdentifier identifier;
+    private int size;
+    private volatile boolean haveSize;
+
+    StackedReversePathArguments(final StackedYangInstanceIdentifier identifier) {
+        this.identifier = requireNonNull(identifier);
+    }
+
+    private static int calculateSize(final YangInstanceIdentifier parent) {
+        YangInstanceIdentifier current = parent;
+        for (int i = 1;; ++i) {
+            final Collection<PathArgument> args = current.tryReversePathArguments();
+            if (args != null) {
+                return i + args.size();
+            }
+
+            verify(current instanceof StackedYangInstanceIdentifier);
+            current = current.getParent();
+        }
+    }
+
+    @Override
+    public int size() {
+        int ret = size;
+        if (!haveSize) {
+            ret = calculateSize(identifier.getParent());
+            size = ret;
+            haveSize = true;
+        }
+
+        return ret;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public boolean contains(final Object o) {
+        final PathArgument srch = (PathArgument) requireNonNull(o);
+        return Iterators.contains(iterator(), srch);
+    }
+
+    @Override
+    public PathArgument get(final int index) {
+        return Iterators.get(iterator(), index);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public int indexOf(final Object o) {
+        final PathArgument srch = (PathArgument) requireNonNull(o);
+        return super.indexOf(srch);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public int lastIndexOf(final Object o) {
+        final PathArgument srch = (PathArgument) requireNonNull(o);
+
+        int ret = -1;
+        final Iterator<PathArgument> it = iterator();
+        for (int i = 0; it.hasNext(); ++i) {
+            if (srch.equals(it.next())) {
+                ret = i;
+            }
+        }
+
+        return ret;
+    }
+
+    @Override
+    public UnmodifiableIterator<PathArgument> iterator() {
+        return new IteratorImpl(identifier);
+    }
+
+    private static final class IteratorImpl extends UnmodifiableIterator<PathArgument> {
+        private StackedYangInstanceIdentifier identifier;
+        private Iterator<PathArgument> tail;
+
+        IteratorImpl(final StackedYangInstanceIdentifier identifier) {
+            this.identifier = requireNonNull(identifier);
+        }
+
+        @Override
+        public boolean hasNext() {
+            return tail == null || tail.hasNext();
+        }
+
+        @Override
+        public PathArgument next() {
+            if (tail != null) {
+                return tail.next();
+            }
+
+            final PathArgument ret = identifier.getLastPathArgument();
+            final YangInstanceIdentifier next = identifier.getParent();
+            final Iterable<PathArgument> args = next.tryReversePathArguments();
+            if (args != null) {
+                tail = args.iterator();
+                identifier = null;
+            } else {
+                verify(next instanceof StackedYangInstanceIdentifier);
+                identifier = (StackedYangInstanceIdentifier) next;
+            }
+
+            return ret;
+        }
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/StackedYangInstanceIdentifier.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/StackedYangInstanceIdentifier.java
new file mode 100644 (file)
index 0000000..0fb647e
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2015 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Field;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+
+final class StackedYangInstanceIdentifier extends YangInstanceIdentifier implements Cloneable {
+    private static final long serialVersionUID = 1L;
+    private static final Field PARENT_FIELD;
+
+    static {
+        final Field f;
+        try {
+            f = StackedYangInstanceIdentifier.class.getDeclaredField("parent");
+        } catch (NoSuchFieldException | SecurityException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+
+        AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+            f.setAccessible(true);
+            return null;
+        });
+
+        PARENT_FIELD = f;
+    }
+
+    private final YangInstanceIdentifier parent;
+    private final PathArgument pathArgument;
+
+    private transient volatile StackedPathArguments pathArguments;
+    private transient volatile StackedReversePathArguments reversePathArguments;
+
+    StackedYangInstanceIdentifier(final YangInstanceIdentifier parent, final PathArgument pathArgument,
+            final int hash) {
+        super(hash);
+        this.parent = requireNonNull(parent);
+        this.pathArgument = requireNonNull(pathArgument);
+    }
+
+    @Override
+    public StackedYangInstanceIdentifier clone() {
+        try {
+            return (StackedYangInstanceIdentifier) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new IllegalStateException("clone() should be supported", e);
+        }
+    }
+
+    @Override
+    public YangInstanceIdentifier getParent() {
+        return parent;
+    }
+
+    @Override
+    public YangInstanceIdentifier getAncestor(final int depth) {
+        checkArgument(depth >= 0, "Steps cannot be negative");
+
+        // Calculate how far up our FixedYangInstanceIdentifier ancestor is
+        int stackedDepth = 1;
+        YangInstanceIdentifier wlk = getParent();
+        while (wlk instanceof StackedYangInstanceIdentifier) {
+            wlk = wlk.getParent();
+            stackedDepth++;
+        }
+
+        // Guaranteed to come from FixedYangInstanceIdentifier
+        final int fixedDepth = wlk.getPathArguments().size();
+        if (fixedDepth >= depth) {
+            return wlk.getAncestor(depth);
+        }
+
+        // Calculate our depth and check argument
+        final int ourDepth = stackedDepth + fixedDepth;
+        checkArgument(depth <= ourDepth, "Depth %s exceeds maximum depth %s", depth, ourDepth);
+
+        // Requested depth is covered by the stack, traverse up for specified number of steps
+        final int toWalk = ourDepth - depth;
+        YangInstanceIdentifier result = this;
+        for (int i = 0; i < toWalk; ++i) {
+            result = result.getParent();
+        }
+
+        return result;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return false;
+    }
+
+    @Override
+    public List<PathArgument> getPathArguments() {
+        StackedPathArguments ret = tryPathArguments();
+        if (ret == null) {
+            final List<PathArgument> stack = new ArrayList<>();
+            YangInstanceIdentifier current = this;
+            do {
+                verify(current instanceof StackedYangInstanceIdentifier);
+                final StackedYangInstanceIdentifier stacked = (StackedYangInstanceIdentifier) current;
+                stack.add(stacked.getLastPathArgument());
+                current = stacked.getParent();
+            } while (current.tryPathArguments() == null);
+
+            ret = new StackedPathArguments(current, Lists.reverse(stack));
+            pathArguments = ret;
+        }
+
+        return ret;
+    }
+
+    @Override
+    public List<PathArgument> getReversePathArguments() {
+        StackedReversePathArguments ret = tryReversePathArguments();
+        if (ret == null) {
+            ret = new StackedReversePathArguments(this);
+            reversePathArguments = ret;
+        }
+        return ret;
+    }
+
+    @Override
+    public PathArgument getLastPathArgument() {
+        return pathArgument;
+    }
+
+    @Override
+    StackedPathArguments tryPathArguments() {
+        return pathArguments;
+    }
+
+    @Override
+    StackedReversePathArguments tryReversePathArguments() {
+        return reversePathArguments;
+    }
+
+    @Override
+    YangInstanceIdentifier createRelativeIdentifier(final int skipFromRoot) {
+        // TODO: can we optimize this one?
+        return YangInstanceIdentifier.create(Iterables.skip(getPathArguments(), skipFromRoot));
+    }
+
+    @Override
+    boolean pathArgumentsEqual(final YangInstanceIdentifier other) {
+        if (other instanceof StackedYangInstanceIdentifier) {
+            final StackedYangInstanceIdentifier stacked = (StackedYangInstanceIdentifier) other;
+            return pathArgument.equals(stacked.pathArgument) && parent.equals(stacked.parent);
+        }
+        return super.pathArgumentsEqual(other);
+    }
+
+    private void readObject(final ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
+        inputStream.defaultReadObject();
+
+        final FixedYangInstanceIdentifier p = (FixedYangInstanceIdentifier) inputStream.readObject();
+        try {
+            PARENT_FIELD.set(this, p);
+        } catch (IllegalArgumentException | IllegalAccessException e) {
+            throw new IOException("Failed to set parent", e);
+        }
+    }
+
+    private void writeObject(final ObjectOutputStream outputStream) throws IOException {
+        outputStream.defaultWriteObject();
+
+        final FixedYangInstanceIdentifier p;
+        if (parent instanceof FixedYangInstanceIdentifier) {
+            p = (FixedYangInstanceIdentifier) parent;
+        } else {
+            p = FixedYangInstanceIdentifier.create(parent.getPathArguments(), parent.hashCode());
+        }
+        outputStream.writeObject(p);
+    }
+
+    @Override
+    public YangInstanceIdentifier toOptimized() {
+        return FixedYangInstanceIdentifier.create(getPathArguments());
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YIDv1.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YIDv1.java
new file mode 100644 (file)
index 0000000..58d914d
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.List;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Externalizable proxy for {@link YangInstanceIdentifier}.
+ */
+final class YIDv1 implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    private YangInstanceIdentifier yid;
+
+    @SuppressWarnings("checkstyle:redundantModifier")
+    public YIDv1() {
+        // For Externalizable
+    }
+
+    YIDv1(final YangInstanceIdentifier yid) {
+        this.yid = requireNonNull(yid);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        final List<PathArgument> args = yid.getPathArguments();
+        out.writeInt(args.size());
+        for (PathArgument arg : args) {
+            // Unfortunately PathArgument is an interface and we do not have control over all its implementations,
+            // hence we did not bother with making them WritableObjects. This works reasonably well, though.
+            out.writeObject(arg);
+        }
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        final int size = in.readInt();
+        final Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(size);
+        for (int i = 0; i < size; ++i) {
+            builder.add((PathArgument) in.readObject());
+        }
+        yid = YangInstanceIdentifier.create(builder.build());
+    }
+
+    private Object readResolve() {
+        return yid;
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java
new file mode 100644 (file)
index 0000000..d02b7bc
--- /dev/null
@@ -0,0 +1,1121 @@
+/*
+ * 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import java.util.function.Function;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.util.HashCodeBuilder;
+import org.opendaylight.yangtools.util.ImmutableOffsetMap;
+import org.opendaylight.yangtools.util.SharedSingletonMap;
+import org.opendaylight.yangtools.util.SingletonSet;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+
+/**
+ * Unique identifier of a particular node instance in the data tree.
+ *
+ * <p>
+ * Java representation of YANG Built-in type <code>instance-identifier</code>,
+ * which conceptually is XPath expression minimized to uniquely identify element
+ * in data tree which conforms to constraints maintained by YANG Model,
+ * effectively this makes Instance Identifier a path to element in data tree.
+ *
+ * <p>
+ * Constraints put in YANG specification on instance-identifier allowed it to be
+ * effectively represented in Java and it's evaluation does not require
+ * full-blown XPath processor.
+ *
+ * <p>
+ * <h3>Path Arguments</h3>
+ * Path to the node represented in instance identifier consists of
+ * {@link PathArgument} which carries necessary information to uniquely identify
+ * node on particular level in the subtree.
+ *
+ * <ul>
+ * <li>{@link NodeIdentifier} - Identifier of node, which has cardinality
+ * <code>0..1</code> in particular subtree in data tree.</li>
+ * <li>{@link NodeIdentifierWithPredicates} - Identifier of node (list item),
+ * which has cardinality <code>0..n</code>.</li>
+ * <li>{@link NodeWithValue} - Identifier of instance <code>leaf</code> node or
+ * <code>leaf-list</code> node.</li>
+ * <li>{@link AugmentationIdentifier} - Identifier of instance of
+ * <code>augmentation</code> node.</li>
+ * </ul>
+ *
+ * @see <a href="http://tools.ietf.org/html/rfc6020#section-9.13">RFC6020</a>
+ */
+// FIXME: 5.0.0: this concept needs to be moved to yang-common, as parser components need the ability to refer
+//               to data nodes -- most notably XPath expressions and {@code default} statement arguments need to be able
+//               to represent these.
+public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentifier>, Immutable, Serializable {
+    private static final AtomicReferenceFieldUpdater<YangInstanceIdentifier, String> TOSTRINGCACHE_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(YangInstanceIdentifier.class, String.class, "toStringCache");
+    private static final long serialVersionUID = 4L;
+
+    private final int hash;
+    private transient volatile String toStringCache = null;
+
+    // Package-private to prevent outside subclassing
+    YangInstanceIdentifier(final int hash) {
+        this.hash = hash;
+    }
+
+    /**
+     * Return An empty {@link YangInstanceIdentifier}. It corresponds to the path of the conceptual root of the YANG
+     * namespace.
+     *
+     * @return An empty YangInstanceIdentifier
+     */
+    public static @NonNull YangInstanceIdentifier empty() {
+        return FixedYangInstanceIdentifier.EMPTY_INSTANCE;
+    }
+
+    abstract @NonNull YangInstanceIdentifier createRelativeIdentifier(int skipFromRoot);
+
+    abstract @Nullable Collection<PathArgument> tryPathArguments();
+
+    abstract @Nullable Collection<PathArgument> tryReversePathArguments();
+
+    /**
+     * Check if this instance identifier has empty path arguments, e.g. it is
+     * empty and corresponds to {@link #empty()}.
+     *
+     * @return True if this instance identifier is empty, false otherwise.
+     */
+    public abstract boolean isEmpty();
+
+    /**
+     * Return an optimized version of this identifier, useful when the identifier
+     * will be used very frequently.
+     *
+     * @return A optimized equivalent instance.
+     */
+    @Beta
+    public abstract @NonNull YangInstanceIdentifier toOptimized();
+
+    /**
+     * Return the conceptual parent {@link YangInstanceIdentifier}, which has
+     * one item less in {@link #getPathArguments()}.
+     *
+     * @return Parent {@link YangInstanceIdentifier}, or null if this object is {@link #empty()}.
+     */
+    public abstract @Nullable YangInstanceIdentifier getParent();
+
+    /**
+     * Return the ancestor {@link YangInstanceIdentifier} with a particular depth, e.g. number of path arguments.
+     *
+     * @param depth Ancestor depth
+     * @return Ancestor {@link YangInstanceIdentifier}
+     * @throws IllegalArgumentException if the specified depth is negative or is greater than the depth of this object.
+     */
+    public abstract @NonNull YangInstanceIdentifier getAncestor(int depth);
+
+    /**
+     * Returns an ordered iteration of path arguments.
+     *
+     * @return Immutable iteration of path arguments.
+     */
+    public abstract @NonNull List<PathArgument> getPathArguments();
+
+    /**
+     * Returns an iterable of path arguments in reverse order. This is useful
+     * when walking up a tree organized this way.
+     *
+     * @return Immutable iterable of path arguments in reverse order.
+     */
+    public abstract @NonNull List<PathArgument> getReversePathArguments();
+
+    /**
+     * Returns the last PathArgument. This is equivalent of iterating
+     * to the last element of the iterable returned by {@link #getPathArguments()}.
+     *
+     * @return The last past argument, or null if there are no PathArguments.
+     */
+    public abstract PathArgument getLastPathArgument();
+
+    public static @NonNull YangInstanceIdentifier create(final Iterable<? extends PathArgument> path) {
+        if (Iterables.isEmpty(path)) {
+            return empty();
+        }
+
+        final HashCodeBuilder<PathArgument> hash = new HashCodeBuilder<>();
+        for (PathArgument a : path) {
+            hash.addArgument(a);
+        }
+
+        return FixedYangInstanceIdentifier.create(path, hash.build());
+    }
+
+    public static @NonNull YangInstanceIdentifier create(final PathArgument... path) {
+        // We are forcing a copy, since we cannot trust the user
+        return create(Arrays.asList(path));
+    }
+
+    /**
+     * Create a {@link YangInstanceIdentifier} by taking a snapshot of provided path and iterating it backwards.
+     *
+     * @param pathTowardsRoot Path towards root
+     * @return A {@link YangInstanceIdentifier} instance
+     * @throws NullPointerException if {@code pathTowardsRoot} or any of its members is null
+     */
+    public static @NonNull YangInstanceIdentifier createReverse(final Deque<PathArgument> pathTowardsRoot) {
+        final ImmutableList.Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(
+            pathTowardsRoot.size());
+        pathTowardsRoot.descendingIterator().forEachRemaining(builder::add);
+        return YangInstanceIdentifier.create(builder.build());
+    }
+
+    /**
+     * Create a {@link YangInstanceIdentifier} by walking specified stack backwards and extracting path components
+     * from it.
+     *
+     * @param stackTowardsRoot Stack towards root,
+     * @return A {@link YangInstanceIdentifier} instance
+     * @throws NullPointerException if {@code pathTowardsRoot} is null
+     */
+    public static <T> @NonNull YangInstanceIdentifier createReverse(final Deque<? extends T> stackTowardsRoot,
+            final Function<T, PathArgument> function) {
+        final ImmutableList.Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(
+            stackTowardsRoot.size());
+        final Iterator<? extends T> it = stackTowardsRoot.descendingIterator();
+        while (it.hasNext()) {
+            builder.add(function.apply(it.next()));
+        }
+        return YangInstanceIdentifier.create(builder.build());
+    }
+
+    boolean pathArgumentsEqual(final YangInstanceIdentifier other) {
+        return Iterables.elementsEqual(getPathArguments(), other.getPathArguments());
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof YangInstanceIdentifier)) {
+            return false;
+        }
+        YangInstanceIdentifier other = (YangInstanceIdentifier) obj;
+        if (this.hashCode() != obj.hashCode()) {
+            return false;
+        }
+
+        return pathArgumentsEqual(other);
+    }
+
+    /**
+     * Constructs a new Instance Identifier with new {@link NodeIdentifier} added to the end of path arguments.
+     *
+     * @param name QName of {@link NodeIdentifier}
+     * @return Instance Identifier with additional path argument added to the end.
+     */
+    public final @NonNull YangInstanceIdentifier node(final QName name) {
+        return node(new NodeIdentifier(name));
+    }
+
+    /**
+     * Constructs a new Instance Identifier with new {@link PathArgument} added to the end of path arguments.
+     *
+     * @param arg Path argument which should be added to the end
+     * @return Instance Identifier with additional path argument added to the end.
+     */
+    public final @NonNull YangInstanceIdentifier node(final PathArgument arg) {
+        return new StackedYangInstanceIdentifier(this, arg, HashCodeBuilder.nextHashCode(hash, arg));
+    }
+
+    /**
+     * Get the relative path from an ancestor. This method attempts to perform
+     * the reverse of concatenating a base (ancestor) and a path.
+     *
+     * @param ancestor
+     *            Ancestor against which the relative path should be calculated
+     * @return This object's relative path from parent, or Optional.absent() if
+     *         the specified parent is not in fact an ancestor of this object.
+     */
+    public Optional<YangInstanceIdentifier> relativeTo(final YangInstanceIdentifier ancestor) {
+        if (this == ancestor) {
+            return Optional.of(empty());
+        }
+        if (ancestor.isEmpty()) {
+            return Optional.of(this);
+        }
+
+        final Iterator<PathArgument> lit = getPathArguments().iterator();
+        final Iterator<PathArgument> oit = ancestor.getPathArguments().iterator();
+        int common = 0;
+
+        while (oit.hasNext()) {
+            // Ancestor is not really an ancestor
+            if (!lit.hasNext() || !lit.next().equals(oit.next())) {
+                return Optional.empty();
+            }
+
+            ++common;
+        }
+
+        if (common == 0) {
+            return Optional.of(this);
+        }
+        if (!lit.hasNext()) {
+            return Optional.of(empty());
+        }
+
+        return Optional.of(createRelativeIdentifier(common));
+    }
+
+    @Override
+    public final boolean contains(final YangInstanceIdentifier other) {
+        if (this == other) {
+            return true;
+        }
+
+        checkArgument(other != null, "other should not be null");
+        final Iterator<PathArgument> lit = getPathArguments().iterator();
+        final Iterator<PathArgument> oit = other.getPathArguments().iterator();
+
+        while (lit.hasNext()) {
+            if (!oit.hasNext()) {
+                return false;
+            }
+
+            if (!lit.next().equals(oit.next())) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public final String toString() {
+        /*
+         * The toStringCache is safe, since the object contract requires
+         * immutability of the object and all objects referenced from this
+         * object.
+         * Used lists, maps are immutable. Path Arguments (elements) are also
+         * immutable, since the PathArgument contract requires immutability.
+         * The cache is thread-safe - if multiple computations occurs at the
+         * same time, cache will be overwritten with same result.
+         */
+        String ret = toStringCache;
+        if (ret == null) {
+            final StringBuilder builder = new StringBuilder("/");
+            PathArgument prev = null;
+            for (PathArgument argument : getPathArguments()) {
+                if (prev != null) {
+                    builder.append('/');
+                }
+                builder.append(argument.toRelativeString(prev));
+                prev = argument;
+            }
+
+            ret = builder.toString();
+            TOSTRINGCACHE_UPDATER.lazySet(this, ret);
+        }
+        return ret;
+    }
+
+    @Override
+    public final int hashCode() {
+        /*
+         * The caching is safe, since the object contract requires
+         * immutability of the object and all objects referenced from this
+         * object.
+         * Used lists, maps are immutable. Path Arguments (elements) are also
+         * immutable, since the PathArgument contract requires immutability.
+         */
+        return hash;
+    }
+
+    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+            justification = "https://github.com/spotbugs/spotbugs/issues/811")
+    private static int hashCode(final Object value) {
+        if (value == null) {
+            return 0;
+        }
+
+        if (byte[].class.equals(value.getClass())) {
+            return Arrays.hashCode((byte[]) value);
+        }
+
+        if (value.getClass().isArray()) {
+            int hash = 0;
+            int length = Array.getLength(value);
+            for (int i = 0; i < length; i++) {
+                hash += Objects.hashCode(Array.get(value, i));
+            }
+
+            return hash;
+        }
+
+        return Objects.hashCode(value);
+    }
+
+    final Object writeReplace() {
+        return new YIDv1(this);
+    }
+
+    // Static factories & helpers
+
+    /**
+     * Returns a new InstanceIdentifier with only one path argument of type {@link NodeIdentifier} with supplied
+     * QName.
+     *
+     * @param name QName of first node identifier
+     * @return Instance Identifier with only one path argument of type {@link NodeIdentifier}
+     */
+    public static @NonNull YangInstanceIdentifier of(final QName name) {
+        return create(new NodeIdentifier(name));
+    }
+
+    /**
+     * Returns new builder for InstanceIdentifier with empty path arguments.
+     *
+     * @return new builder for InstanceIdentifier with empty path arguments.
+     */
+    public static @NonNull InstanceIdentifierBuilder builder() {
+        return new YangInstanceIdentifierBuilder();
+    }
+
+    /**
+     * Returns new builder for InstanceIdentifier with path arguments copied from original instance identifier.
+     *
+     * @param origin InstanceIdentifier from which path arguments are copied.
+     * @return new builder for InstanceIdentifier with path arguments copied from original instance identifier.
+     */
+    public static @NonNull InstanceIdentifierBuilder builder(final YangInstanceIdentifier origin) {
+        return new YangInstanceIdentifierBuilder(origin.getPathArguments(), origin.hashCode());
+    }
+
+    /**
+     * Path argument / component of InstanceIdentifier.
+     * Path argument uniquely identifies node in data tree on particular
+     * level.
+     *
+     * <p>
+     * This interface itself is used as common parent for actual
+     * path arguments types and should not be implemented by user code.
+     *
+     * <p>
+     * Path arguments SHOULD contain only minimum of information
+     * required to uniquely identify node on particular subtree level.
+     *
+     * <p>
+     * For actual path arguments types see:
+     * <ul>
+     * <li>{@link NodeIdentifier} - Identifier of container or leaf
+     * <li>{@link NodeIdentifierWithPredicates} - Identifier of list entries, which have key defined
+     * <li>{@link AugmentationIdentifier} - Identifier of augmentation
+     * <li>{@link NodeWithValue} - Identifier of leaf-list entry
+     * </ul>
+     */
+    public interface PathArgument extends Comparable<PathArgument>, Immutable, Serializable {
+        /**
+         * Returns unique QName of data node as defined in YANG Schema, if available.
+         *
+         * @return Node type
+         * @throws UnsupportedOperationException if node type is not applicable, for example in case of an augmentation.
+         */
+        @NonNull QName getNodeType();
+
+        /**
+         * Return the string representation of this object for use in context
+         * provided by a previous object. This method can be implemented in
+         * terms of {@link #toString()}, but implementations are encourage to
+         * reuse any context already emitted by the previous object.
+         *
+         * @param previous Previous path argument
+         * @return String representation
+         */
+        @NonNull String toRelativeString(PathArgument previous);
+    }
+
+    private abstract static class AbstractPathArgument implements PathArgument {
+        private static final long serialVersionUID = -4546547994250849340L;
+        private final @NonNull QName nodeType;
+        private transient volatile int hashValue;
+
+        protected AbstractPathArgument(final QName nodeType) {
+            this.nodeType = requireNonNull(nodeType);
+        }
+
+        @Override
+        public final QName getNodeType() {
+            return nodeType;
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:parameterName")
+        public int compareTo(final PathArgument o) {
+            return nodeType.compareTo(o.getNodeType());
+        }
+
+        protected int hashCodeImpl() {
+            return 31 + getNodeType().hashCode();
+        }
+
+        @Override
+        public final int hashCode() {
+            int local;
+            return (local = hashValue) != 0 ? local : (hashValue = hashCodeImpl());
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null || this.getClass() != obj.getClass()) {
+                return false;
+            }
+
+            return getNodeType().equals(((AbstractPathArgument)obj).getNodeType());
+        }
+
+        @Override
+        public String toString() {
+            return getNodeType().toString();
+        }
+
+        @Override
+        public String toRelativeString(final PathArgument previous) {
+            if (previous instanceof AbstractPathArgument) {
+                final QNameModule mod = previous.getNodeType().getModule();
+                if (getNodeType().getModule().equals(mod)) {
+                    return getNodeType().getLocalName();
+                }
+            }
+
+            return getNodeType().toString();
+        }
+
+        abstract Object writeReplace();
+    }
+
+    /**
+     * Simple path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.ContainerNode} or
+     * {@link org.opendaylight.yangtools.yang.data.api.schema.LeafNode} leaf in particular subtree.
+     */
+    public static final class NodeIdentifier extends AbstractPathArgument {
+        private static final long serialVersionUID = -2255888212390871347L;
+        private static final LoadingCache<QName, NodeIdentifier> CACHE = CacheBuilder.newBuilder().weakValues()
+                .build(new CacheLoader<QName, NodeIdentifier>() {
+                    @Override
+                    public NodeIdentifier load(final QName key) {
+                        return new NodeIdentifier(key);
+                    }
+                });
+
+        public NodeIdentifier(final QName node) {
+            super(node);
+        }
+
+        /**
+         * Return a NodeIdentifier for a particular QName. Unlike the constructor, this factory method uses a global
+         * instance cache, resulting in object reuse for equal inputs.
+         *
+         * @param node Node's QName
+         * @return A {@link NodeIdentifier}
+         */
+        public static @NonNull NodeIdentifier create(final QName node) {
+            return CACHE.getUnchecked(node);
+        }
+
+        @Override
+        Object writeReplace() {
+            return new NIv1(this);
+        }
+    }
+
+    /**
+     * Composite path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode} leaf
+     * overall data tree.
+     */
+    public abstract static class NodeIdentifierWithPredicates extends AbstractPathArgument {
+        @Beta
+        public static final class Singleton extends NodeIdentifierWithPredicates {
+            private static final long serialVersionUID = 1L;
+
+            private final @NonNull QName key;
+            private final @NonNull Object value;
+
+            Singleton(final QName node, final QName key, final Object value) {
+                super(node);
+                this.key = requireNonNull(key);
+                this.value = requireNonNull(value);
+            }
+
+            @Override
+            public SingletonSet<Entry<QName, Object>> entrySet() {
+                return SingletonSet.of(singleEntry());
+            }
+
+            @Override
+            public SingletonSet<QName> keySet() {
+                return SingletonSet.of(key);
+            }
+
+            @Override
+            public boolean containsKey(final QName qname) {
+                return key.equals(requireNonNull(qname));
+            }
+
+            @Override
+            public SingletonSet<Object> values() {
+                return SingletonSet.of(value);
+            }
+
+            @Override
+            public int size() {
+                return 1;
+            }
+
+            @Override
+            public ImmutableMap<QName, Object> asMap() {
+                return ImmutableMap.of(key, value);
+            }
+
+            /**
+             * Return the single entry contained in this object. This is equivalent to
+             * {@code entrySet().iterator().next()}.
+             *
+             * @return A single entry.
+             */
+            public @NonNull Entry<QName, Object> singleEntry() {
+                return new SimpleImmutableEntry<>(key, value);
+            }
+
+            @Override
+            boolean equalMapping(final NodeIdentifierWithPredicates other) {
+                final Singleton single = (Singleton) other;
+                return key.equals(single.key) && Objects.deepEquals(value, single.value);
+            }
+
+            @Override
+            Object keyValue(final QName qname) {
+                return key.equals(qname) ? value : null;
+            }
+        }
+
+        private static final class Regular extends NodeIdentifierWithPredicates {
+            private static final long serialVersionUID = 1L;
+
+            private final @NonNull Map<QName, Object> keyValues;
+
+            Regular(final QName node, final Map<QName, Object> keyValues) {
+                super(node);
+                this.keyValues = requireNonNull(keyValues);
+            }
+
+            @Override
+            public Set<Entry<QName, Object>> entrySet() {
+                return keyValues.entrySet();
+            }
+
+            @Override
+            public Set<QName> keySet() {
+                return keyValues.keySet();
+            }
+
+            @Override
+            public boolean containsKey(final QName qname) {
+                return keyValues.containsKey(requireNonNull(qname));
+            }
+
+            @Override
+            public Collection<Object> values() {
+                return keyValues.values();
+            }
+
+            @Override
+            public int size() {
+                return keyValues.size();
+            }
+
+            @Override
+            public Map<QName, Object> asMap() {
+                return keyValues;
+            }
+
+            @Override
+            Object keyValue(final QName qname) {
+                return keyValues.get(qname);
+            }
+
+            @Override
+            boolean equalMapping(final NodeIdentifierWithPredicates other) {
+                final Map<QName, Object> otherKeyValues = ((Regular) other).keyValues;
+                // TODO: benchmark to see if just calling equals() on the two maps is not faster
+                if (keyValues == otherKeyValues) {
+                    return true;
+                }
+                if (keyValues.size() != otherKeyValues.size()) {
+                    return false;
+                }
+
+                for (Entry<QName, Object> entry : entrySet()) {
+                    final Object otherValue = otherKeyValues.get(entry.getKey());
+                    if (otherValue == null || !Objects.deepEquals(entry.getValue(), otherValue)) {
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+        }
+
+        private static final long serialVersionUID = -4787195606494761540L;
+
+        NodeIdentifierWithPredicates(final QName node) {
+            super(node);
+        }
+
+        public static @NonNull NodeIdentifierWithPredicates of(final QName node) {
+            return new Regular(node, ImmutableMap.of());
+        }
+
+        public static @NonNull NodeIdentifierWithPredicates of(final QName node, final QName key, final Object value) {
+            return new Singleton(node, key, value);
+        }
+
+        public static @NonNull NodeIdentifierWithPredicates of(final QName node, final Entry<QName, Object> entry) {
+            return of(node, entry.getKey(), entry.getValue());
+        }
+
+        public static @NonNull NodeIdentifierWithPredicates of(final QName node, final Map<QName, Object> keyValues) {
+            return keyValues.size() == 1 ? of(keyValues, node)
+                    // Retains ImmutableMap for empty maps. For larger sizes uses a shared key set.
+                    : new Regular(node, ImmutableOffsetMap.unorderedCopyOf(keyValues));
+        }
+
+        public static @NonNull NodeIdentifierWithPredicates of(final QName node,
+                final ImmutableOffsetMap<QName, Object> keyValues) {
+            return keyValues.size() == 1 ? of(keyValues, node) : new Regular(node, keyValues);
+        }
+
+        @Deprecated
+        public static @NonNull NodeIdentifierWithPredicates of(final QName node,
+                final SharedSingletonMap<QName, Object> keyValues) {
+            return of(node, keyValues.getEntry());
+        }
+
+        private static @NonNull NodeIdentifierWithPredicates of(final Map<QName, Object> keyValues, final QName node) {
+            return of(node, keyValues.entrySet().iterator().next());
+        }
+
+        /**
+         * Return the set of predicates keys and values. Keys are guaranteeed to be unique.
+         *
+         * @return Predicate set.
+         */
+        @Beta
+        public abstract @NonNull Set<Entry<QName, Object>> entrySet();
+
+        /**
+         * Return the predicate key in the iteration order of {@link #entrySet()}.
+         *
+         * @return Predicate values.
+         */
+        @Beta
+        public abstract @NonNull Set<QName> keySet();
+
+        /**
+         * Determine whether a particular predicate key is present.
+         *
+         * @param key Predicate key
+         * @return True if the predicate is present, false otherwise
+         * @throws NullPointerException if {@code key} is null
+         */
+        @Beta
+        public abstract boolean containsKey(QName key);
+
+        /**
+         * Return the predicate values in the iteration order of {@link #entrySet()}.
+         *
+         * @return Predicate values.
+         */
+        @Beta
+        public abstract @NonNull Collection<Object> values();
+
+        @Beta
+        public final @Nullable Object getValue(final QName key) {
+            return keyValue(requireNonNull(key));
+        }
+
+        @Beta
+        public final <T> @Nullable T getValue(final QName key, final Class<T> valueClass) {
+            return valueClass.cast(getValue(key));
+        }
+
+        /**
+         * Return the number of predicates present.
+         *
+         * @return The number of predicates present.
+         */
+        @Beta
+        public abstract int size();
+
+        /**
+         * A Map-like view of this identifier's predicates. The view is expected to be stable and effectively-immutable.
+         *
+         * @return Map of predicates.
+         * @deprecated This method in a provisional one. It can be used in the code base, but users requiring it should
+         *             contact <a href="mailto:yangtools-dev@lists.opendaylight.org">yangtools-dev</a> for migration
+         *             guidelines. Callers are strongly encouraged to explore {@link #entrySet()}, {@link #size()},
+         *             {@link #values()} and {@link #keySet()} as an alternative.
+         */
+        @Beta
+        @Deprecated
+        // FIXME: 5.0.0: evaluate the real usefulness of this. The problem here is Map.hashCode() and Map.equals(),
+        //               which limits our options.
+        public abstract @NonNull Map<QName, Object> asMap();
+
+        @Override
+        protected final int hashCodeImpl() {
+            int result = 31 * super.hashCodeImpl();
+            for (Entry<QName, Object> entry : entrySet()) {
+                result += entry.getKey().hashCode() + YangInstanceIdentifier.hashCode(entry.getValue());
+            }
+            return result;
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:equalsHashCode")
+        public final boolean equals(final Object obj) {
+            return super.equals(obj) && equalMapping((NodeIdentifierWithPredicates) obj);
+        }
+
+        abstract boolean equalMapping(NodeIdentifierWithPredicates other);
+
+        abstract @Nullable Object keyValue(@NonNull QName qname);
+
+        @Override
+        public final String toString() {
+            return super.toString() + '[' + asMap() + ']';
+        }
+
+        @Override
+        public final String toRelativeString(final PathArgument previous) {
+            return super.toRelativeString(previous) + '[' + asMap() + ']';
+        }
+
+        @Override
+        final Object writeReplace() {
+            return new NIPv2(this);
+        }
+    }
+
+    /**
+     * Simple path argument identifying a {@link LeafSetEntryNode} leaf
+     * overall data tree.
+     */
+    public static final class NodeWithValue<T> extends AbstractPathArgument {
+        private static final long serialVersionUID = -3637456085341738431L;
+
+        private final T value;
+
+        public NodeWithValue(final QName node, final T value) {
+            super(node);
+            this.value = value;
+        }
+
+        public T getValue() {
+            return value;
+        }
+
+        @Override
+        protected int hashCodeImpl() {
+            final int prime = 31;
+            int result = super.hashCodeImpl();
+            result = prime * result + YangInstanceIdentifier.hashCode(value);
+            return result;
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:equalsHashCode")
+        public boolean equals(final Object obj) {
+            if (!super.equals(obj)) {
+                return false;
+            }
+            final NodeWithValue<?> other = (NodeWithValue<?>) obj;
+            return Objects.deepEquals(value, other.value);
+        }
+
+        @Override
+        public String toString() {
+            return super.toString() + '[' + value + ']';
+        }
+
+        @Override
+        public String toRelativeString(final PathArgument previous) {
+            return super.toRelativeString(previous) + '[' + value + ']';
+        }
+
+        @Override
+        Object writeReplace() {
+            return new NIVv1(this);
+        }
+    }
+
+    /**
+     * Composite path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode}
+     * node in particular subtree.
+     *
+     * <p>
+     * Augmentation is uniquely identified by set of all possible child nodes.
+     * This is possible
+     * to identify instance of augmentation,
+     * since RFC6020 states that <code>augment</code> that augment
+     * statement must not add multiple nodes from same namespace
+     * / module to the target node.
+     *
+     * @see <a href="http://tools.ietf.org/html/rfc6020#section-7.15">RFC6020</a>
+     */
+    public static final class AugmentationIdentifier implements PathArgument {
+        private static final long serialVersionUID = -8122335594681936939L;
+
+        private static final LoadingCache<ImmutableSet<QName>, AugmentationIdentifier> CACHE = CacheBuilder.newBuilder()
+                .weakValues().build(new CacheLoader<ImmutableSet<QName>, AugmentationIdentifier>() {
+                    @Override
+                    public AugmentationIdentifier load(final ImmutableSet<QName> key) {
+                        return new AugmentationIdentifier(key);
+                    }
+                });
+
+        private final @NonNull ImmutableSet<QName> childNames;
+
+        @Override
+        public QName getNodeType() {
+            // This should rather throw exception than return always null
+            throw new UnsupportedOperationException("Augmentation node has no QName");
+        }
+
+        /**
+         * Construct new augmentation identifier using supplied set of possible
+         * child nodes.
+         *
+         * @param childNames
+         *            Set of possible child nodes.
+         */
+        public AugmentationIdentifier(final ImmutableSet<QName> childNames) {
+            this.childNames = requireNonNull(childNames);
+        }
+
+        /**
+         * Construct new augmentation identifier using supplied set of possible
+         * child nodes.
+         *
+         * @param childNames
+         *            Set of possible child nodes.
+         */
+        public AugmentationIdentifier(final Set<QName> childNames) {
+            this.childNames = ImmutableSet.copyOf(childNames);
+        }
+
+        /**
+         * Return an AugmentationIdentifier for a particular set of QNames. Unlike the constructor, this factory method
+         * uses a global instance cache, resulting in object reuse for equal inputs.
+         *
+         * @param childNames Set of possible child nodes
+         * @return An {@link AugmentationIdentifier}
+         */
+        public static @NonNull AugmentationIdentifier create(final ImmutableSet<QName> childNames) {
+            return CACHE.getUnchecked(childNames);
+        }
+
+        /**
+         * Return an AugmentationIdentifier for a particular set of QNames. Unlike the constructor, this factory method
+         * uses a global instance cache, resulting in object reuse for equal inputs.
+         *
+         * @param childNames Set of possible child nodes
+         * @return An {@link AugmentationIdentifier}
+         */
+        public static @NonNull AugmentationIdentifier create(final Set<QName> childNames) {
+            final AugmentationIdentifier existing = CACHE.getIfPresent(childNames);
+            return existing != null ? existing : create(ImmutableSet.copyOf(childNames));
+        }
+
+        /**
+         * Returns set of all possible child nodes.
+         *
+         * @return set of all possible child nodes.
+         */
+        public @NonNull Set<QName> getPossibleChildNames() {
+            return childNames;
+        }
+
+        @Override
+        public String toString() {
+            return "AugmentationIdentifier{" + "childNames=" + childNames + '}';
+        }
+
+        @Override
+        public String toRelativeString(final PathArgument previous) {
+            return toString();
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof AugmentationIdentifier)) {
+                return false;
+            }
+
+            AugmentationIdentifier that = (AugmentationIdentifier) obj;
+            return childNames.equals(that.childNames);
+        }
+
+        @Override
+        public int hashCode() {
+            return childNames.hashCode();
+        }
+
+        @Override
+        @SuppressWarnings("checkstyle:parameterName")
+        public int compareTo(final PathArgument o) {
+            if (!(o instanceof AugmentationIdentifier)) {
+                return -1;
+            }
+            AugmentationIdentifier other = (AugmentationIdentifier) o;
+            Set<QName> otherChildNames = other.getPossibleChildNames();
+            int thisSize = childNames.size();
+            int otherSize = otherChildNames.size();
+            if (thisSize == otherSize) {
+                // Quick Set-based comparison
+                if (childNames.equals(otherChildNames)) {
+                    return 0;
+                }
+
+                // We already know the sets are not equal, but have equal size, hence the sets differ in their elements,
+                // but potentially share a common set of elements. The most consistent way of comparing them is using
+                // total ordering defined by QName's compareTo. Hence convert both sets to lists ordered
+                // by QName.compareTo() and decide on the first differing element.
+                final List<QName> diff = new ArrayList<>(Sets.symmetricDifference(childNames, otherChildNames));
+                verify(!diff.isEmpty(), "Augmentation identifiers %s and %s report no difference", this, o);
+                diff.sort(QName::compareTo);
+                return childNames.contains(diff.get(0)) ? -1 : 1;
+            } else if (thisSize < otherSize) {
+                return 1;
+            } else {
+                return -1;
+            }
+        }
+
+        private Object writeReplace() {
+            return new AIv1(this);
+        }
+    }
+
+    /**
+     * Fluent Builder of Instance Identifier instances.
+     */
+    public interface InstanceIdentifierBuilder extends Builder<YangInstanceIdentifier> {
+        /**
+         * Adds a {@link PathArgument} to path arguments of resulting instance identifier.
+         *
+         * @param arg A {@link PathArgument} to be added
+         * @return this builder
+         */
+        @NonNull InstanceIdentifierBuilder node(PathArgument arg);
+
+        /**
+         * Adds {@link NodeIdentifier} with supplied QName to path arguments of resulting instance identifier.
+         *
+         * @param nodeType QName of {@link NodeIdentifier} which will be added
+         * @return this builder
+         */
+        @NonNull InstanceIdentifierBuilder node(QName nodeType);
+
+        /**
+         * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key values to path arguments of resulting
+         * instance identifier.
+         *
+         * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
+         * @param keyValues Map of key components and their respective values for {@link NodeIdentifierWithPredicates}
+         * @return this builder
+         */
+        @NonNull InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
+
+        /**
+         * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key, value.
+         *
+         * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
+         * @param key QName of key which will be added
+         * @param value value of key which will be added
+         * @return this builder
+         */
+        @NonNull InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
+
+        /**
+         * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
+         *
+         * @param args {@link PathArgument}s to be added
+         * @return this builder
+         * @throws NullPointerException if any of the arguments is null
+         */
+        @Beta
+        @NonNull InstanceIdentifierBuilder append(Collection<? extends PathArgument> args);
+
+        /**
+         * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
+         *
+         * @param args {@link PathArgument}s to be added
+         * @return this builder
+         * @throws NullPointerException if any of the arguments is null
+         */
+        @Beta
+        default @NonNull InstanceIdentifierBuilder append(final PathArgument... args) {
+            return append(Arrays.asList(args));
+        }
+
+        /**
+         * Builds an {@link YangInstanceIdentifier} with path arguments from this builder.
+         *
+         * @return {@link YangInstanceIdentifier}
+         */
+        @Override
+        YangInstanceIdentifier build();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifierBuilder.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifierBuilder.java
new file mode 100644 (file)
index 0000000..57a0a61
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015 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;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.HashCodeBuilder;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+final class YangInstanceIdentifierBuilder implements InstanceIdentifierBuilder {
+    private final HashCodeBuilder<PathArgument> hash;
+    private final List<PathArgument> path;
+
+    YangInstanceIdentifierBuilder() {
+        this.hash = new HashCodeBuilder<>();
+        this.path = new ArrayList<>();
+    }
+
+    YangInstanceIdentifierBuilder(final List<PathArgument> prefix, final int hash) {
+        this.path = new ArrayList<>(prefix);
+        this.hash = new HashCodeBuilder<>(hash);
+    }
+
+    private @NonNull InstanceIdentifierBuilder addArgument(final PathArgument arg) {
+        path.add(arg);
+        hash.addArgument(arg);
+        return this;
+    }
+
+    @Override
+    public InstanceIdentifierBuilder node(final PathArgument arg) {
+        return addArgument(requireNonNull(arg));
+    }
+
+    @Override
+    public InstanceIdentifierBuilder node(final QName nodeType) {
+        return addArgument(new NodeIdentifier(nodeType));
+    }
+
+    @Override
+    public InstanceIdentifierBuilder append(final Collection<? extends PathArgument> args) {
+        path.addAll(args);
+        args.forEach(hash::addArgument);
+        return this;
+    }
+
+    @Override
+    public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final QName key, final Object value) {
+        return addArgument(NodeIdentifierWithPredicates.of(nodeType, key, value));
+    }
+
+    @Override
+    public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final Map<QName, Object> keyValues) {
+        return addArgument(NodeIdentifierWithPredicates.of(nodeType, keyValues));
+    }
+
+    @Override
+    public YangInstanceIdentifier build() {
+        return FixedYangInstanceIdentifier.create(path, hash.build());
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/BinaryCodec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/BinaryCodec.java
new file mode 100644 (file)
index 0000000..09004c5
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+public interface BinaryCodec<T> extends IllegalArgumentCodec<T, byte[]> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/BitsCodec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/BitsCodec.java
new file mode 100644 (file)
index 0000000..896ec41
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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.codec;
+
+import java.util.Set;
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+public interface BitsCodec<T> extends IllegalArgumentCodec<T, Set<String>> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/BooleanCodec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/BooleanCodec.java
new file mode 100644 (file)
index 0000000..105b33b
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+public interface BooleanCodec<T> extends IllegalArgumentCodec<T, Boolean> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/DecimalCodec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/DecimalCodec.java
new file mode 100644 (file)
index 0000000..b42a6d9
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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.codec;
+
+import java.math.BigDecimal;
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+public interface DecimalCodec<T> extends IllegalArgumentCodec<T, BigDecimal> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/EmptyCodec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/EmptyCodec.java
new file mode 100644 (file)
index 0000000..fe3bfc2
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+import org.opendaylight.yangtools.yang.common.Empty;
+
+public interface EmptyCodec<T> extends IllegalArgumentCodec<T, Empty> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/EnumCodec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/EnumCodec.java
new file mode 100644 (file)
index 0000000..9327cca
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+public interface EnumCodec<T> extends IllegalArgumentCodec<T, String> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/IdentityrefCodec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/IdentityrefCodec.java
new file mode 100644 (file)
index 0000000..639fbc2
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface IdentityrefCodec<T> extends IllegalArgumentCodec<T, QName> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/InstanceIdentifierCodec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/InstanceIdentifierCodec.java
new file mode 100644 (file)
index 0000000..d395026
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Codec which serializes / deserializes InstanceIdentifier.
+ *
+ * @param <T> Target type
+ */
+public interface InstanceIdentifierCodec<T> extends IllegalArgumentCodec<T, YangInstanceIdentifier> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Int16Codec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Int16Codec.java
new file mode 100644 (file)
index 0000000..7bd24d0
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+public interface Int16Codec<T> extends IllegalArgumentCodec<T, Short> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Int32Codec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Int32Codec.java
new file mode 100644 (file)
index 0000000..afc6819
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+public interface Int32Codec<T> extends IllegalArgumentCodec<T,Integer> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Int64Codec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Int64Codec.java
new file mode 100644 (file)
index 0000000..3cb7f92
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+public interface Int64Codec<T> extends IllegalArgumentCodec<T, Long> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Int8Codec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Int8Codec.java
new file mode 100644 (file)
index 0000000..b3d5d72
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+public interface Int8Codec<T> extends IllegalArgumentCodec<T, Byte> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/LeafrefCodec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/LeafrefCodec.java
new file mode 100644 (file)
index 0000000..056d560
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+public interface LeafrefCodec<T> extends IllegalArgumentCodec<T, Object> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/StringCodec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/StringCodec.java
new file mode 100644 (file)
index 0000000..b458469
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+public interface StringCodec<T> extends IllegalArgumentCodec<T, String> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Uint16Codec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Uint16Codec.java
new file mode 100644 (file)
index 0000000..c811fa2
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+import org.opendaylight.yangtools.yang.common.Uint16;
+
+/**
+ * A codec between Uint16 and some other representation.
+ *
+ * @param <T> Output type of serialization.
+ */
+public interface Uint16Codec<T> extends IllegalArgumentCodec<T, Uint16> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Uint32Codec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Uint32Codec.java
new file mode 100644 (file)
index 0000000..8a96072
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+import org.opendaylight.yangtools.yang.common.Uint32;
+
+/**
+ * A codec between Uint32 and some other representation.
+ *
+ * @param <T> Output type of serialization.
+ */
+public interface Uint32Codec<T> extends IllegalArgumentCodec<T, Uint32> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Uint64Codec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Uint64Codec.java
new file mode 100644 (file)
index 0000000..247cb0c
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+import org.opendaylight.yangtools.yang.common.Uint64;
+
+/**
+ * A codec between Uint64 and some other representation.
+ *
+ * @param <T> Output type of serialization.
+ */
+public interface Uint64Codec<T> extends IllegalArgumentCodec<T, Uint64> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Uint8Codec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/Uint8Codec.java
new file mode 100644 (file)
index 0000000..0bac80f
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+import org.opendaylight.yangtools.yang.common.Uint8;
+
+/**
+ * A codec between Uint8 and some other representation.
+ *
+ * @param <T> Output type of serialization.
+ */
+public interface Uint8Codec<T> extends IllegalArgumentCodec<T, Uint8> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/UnionCodec.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/codec/UnionCodec.java
new file mode 100644 (file)
index 0000000..853bf57
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.codec;
+
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+public interface UnionCodec<T> extends IllegalArgumentCodec<T,Object> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AnydataNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AnydataNode.java
new file mode 100644 (file)
index 0000000..080c236
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+
+/**
+ * A NormalizedNode holding the contents of an {@code anydata} node in some object model. While no guarantees are placed
+ * on object models, there are related interfaces available for data interchange:
+ *
+ * <ul>
+ *   <li>{@link NormalizedAnydata}, which exposes the contents as a {@link NormalizedNode} with attached schema
+ *       information</li>
+ *   <li>{@link NormalizableAnydata}, which is trait optionally implemented by object models and allows the opaque,
+ *       implementation-specific representation to be interpreted in a the context of provided schema information,
+ *       potentially forming a NormalizedAnydata node.
+ * </ul>
+ *
+ * @param <V> Value type, uniquely identifying the object model used for values
+ */
+@Beta
+public interface AnydataNode<V> extends ForeignDataNode<NodeIdentifier, V> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AnydataNormalizationException.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AnydataNormalizationException.java
new file mode 100644 (file)
index 0000000..60c19a5
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public class AnydataNormalizationException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public AnydataNormalizationException(final String message) {
+        super(requireNonNull(message));
+    }
+
+    public AnydataNormalizationException(final String message, final Throwable cause) {
+        super(requireNonNull(message), cause);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AnyxmlNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AnyxmlNode.java
new file mode 100644 (file)
index 0000000..30134a6
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+
+/**
+ * A NormalizedNode holding the contents of an {@code anyxml} node in some object model. This interface is a common
+ * capture for all object model specializations.
+ *
+ * @param <V> Value type, uniquely identifying the object model used for values
+ */
+@Beta
+public interface AnyxmlNode<V> extends ForeignDataNode<NodeIdentifier, V> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AugmentationNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AugmentationNode.java
new file mode 100644 (file)
index 0000000..c85a6ce
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+
+/**
+ * Data instance of <code>augment</code> associated with parent node.
+ *
+ * <p>
+ * Augmentation is addition of subtree defined by other external YANG Model and is schema for subtree is described
+ * by an instance of {@link AugmentationSchemaNode} associated with parent node of this node.
+ *
+ * <p>
+ * Augmentation node MUST NOT be direct child of other augmentation node.
+ */
+public interface AugmentationNode extends MixinNode, DataContainerNode<AugmentationIdentifier>,
+    DataContainerChild<AugmentationIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> {
+
+    /**
+     * Gets identifier of augmentation node. Returned identifier of augmentation node contains all possible
+     * direct child QNames.
+     *
+     * <p>
+     * This is sufficient to identify instance of augmentation, since RFC6020 states that <code>augment</code>
+     * that augment statement must not add multiple nodes from same namespace / module to the target node.
+     *
+     * @return Identifier which uniquely identifies augmentation in particular subtree.
+     */
+    @Override
+    AugmentationIdentifier getIdentifier();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ChoiceNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ChoiceNode.java
new file mode 100644 (file)
index 0000000..d5cc5b4
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Node representing data instance of <code>choice</code>.
+ *
+ * <p>
+ * Choice node is instance of one of possible alternatives, from which only one is allowed to exist at one time in
+ * particular context of parent node.
+ *
+ * <p>
+ * YANG Model and schema for choice is described by instance of
+ * {@link org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode}.
+ *
+ * <p>
+ * Valid alternatives of subtree are described by instances of
+ * {@link org.opendaylight.yangtools.yang.model.api.CaseSchemaNode}, which are retrieved via
+ * {@link org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode#getCases()}.
+ */
+public interface ChoiceNode extends MixinNode, DataContainerNode<NodeIdentifier>,
+        DataContainerChild<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ContainerNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ContainerNode.java
new file mode 100644 (file)
index 0000000..d201d7d
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Data subtree with cardinality 0..1 in the context of parent node.
+ *
+ * <p>
+ * Node which does not have value but contains valid {@link DataContainerChild} nodes.
+ *
+ * <p>
+ * Schema of this node is described by instance of
+ * {@link org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode}.
+ */
+public interface ContainerNode extends DataContainerNode<NodeIdentifier>,
+        DataContainerChild<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DOMSourceAnyxmlNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DOMSourceAnyxmlNode.java
new file mode 100644 (file)
index 0000000..b5be957
--- /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.data.api.schema;
+
+import javax.xml.transform.dom.DOMSource;
+
+/**
+ * An AnyxmlNode with data in {@link DOMSource} format.
+ */
+public interface DOMSourceAnyxmlNode extends AnyxmlNode<DOMSource> {
+    @Override
+    default Class<DOMSource> getValueObjectModel() {
+        return DOMSource.class;
+    }
+
+    /**
+     * Return value represented as a DOMSource. Returned source contains top level element
+     * that duplicates the anyxml node.
+     *
+     * @return anyxml node value represented as DOMSource.
+     */
+    @Override
+    DOMSource getValue();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DataContainerChild.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DataContainerChild.java
new file mode 100644 (file)
index 0000000..9939492
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Marker interface for direct children of {@link DataContainerNode}.
+ *
+ * <h3>Implementation notes</h3>
+ * This interface should not be implemented directly, but rather using one
+ * of its subinterfaces:
+ *
+ * <ul>
+ * <li>{@link LeafNode}
+ * <li>{@link ContainerNode}
+ * <li>{@link ChoiceNode}
+ * <li>{@link MapNode}
+ * <li>{@link AugmentationNode}
+ * </ul>
+ *
+ * @param <K> Path Argument Type which is used to identify node
+ * @param <V> Value type
+ */
+public interface DataContainerChild<K extends PathArgument,V> extends NormalizedNode<K, V> {
+    @Override
+    K getIdentifier();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DataContainerNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DataContainerNode.java
new file mode 100644 (file)
index 0000000..c99b19b
--- /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.api.schema;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Abstract node which does not have value but contains valid {@link DataContainerChild} nodes.
+ * Schema of this node is described by instance of {@link org.opendaylight.yangtools.yang.model.api.DataNodeContainer}.
+ *
+ * <p>
+ * <h2>Implementation notes</h2>
+ * This interface should not be implemented directly, but rather implementing one of it's subclasses
+ * <ul>
+ * <li>{@link ContainerNode}
+ * <li>{@link MapEntryNode}
+ * <li>{@link UnkeyedListEntryNode}
+ * <li>{@link ChoiceNode}
+ * <li>{@link AugmentationNode}
+ * </ul>
+ *
+ * @param <K> {@link PathArgument} which identifies instance of {@link DataContainerNode}
+ */
+public interface DataContainerNode<K extends PathArgument> extends
+        NormalizedNodeContainer<K, PathArgument, DataContainerChild<? extends PathArgument, ?>> {
+    /**
+     * Returns iteration of all child nodes.
+     * Order of returned child nodes may be defined by subinterfaces.
+     *
+     * <p>
+     * <b>Implementation Notes:</b>
+     * All nodes returned in this iterable, MUST also be accessible via
+     * {@link #getChild(PathArgument)} using their associated identifier.
+     *
+     * @return Iteration of all child nodes
+     */
+    @Override
+    Collection<DataContainerChild<? extends PathArgument, ?>> getValue();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DuplicateEntry.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DuplicateEntry.java
new file mode 100644 (file)
index 0000000..382c6c5
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015 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;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Entry describing a duplicate found in a {@link NormalizedNode} tree. Note this
+ * class leaks mutable collections, so users are advised either not to share it.
+ */
+public final class DuplicateEntry implements Identifiable<YangInstanceIdentifier> {
+    private final List<YangInstanceIdentifier> hardLinks = new ArrayList<>(1);
+    private List<YangInstanceIdentifier> duplicates = Collections.emptyList();
+
+    DuplicateEntry(final YangInstanceIdentifier path) {
+        hardLinks.add(path);
+    }
+
+    void addDuplicate(final YangInstanceIdentifier path) {
+        if (duplicates.isEmpty()) {
+            duplicates = new ArrayList<>();
+        }
+        duplicates.add(path);
+    }
+
+    void addHardLink(final YangInstanceIdentifier path) {
+        hardLinks.add(path);
+    }
+
+    @Override
+    public YangInstanceIdentifier getIdentifier() {
+        return hardLinks.get(0);
+    }
+
+    public List<YangInstanceIdentifier> getHardLinks() {
+        return hardLinks;
+    }
+
+    public List<YangInstanceIdentifier> getDuplicates() {
+        return duplicates;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DuplicateFinder.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/DuplicateFinder.java
new file mode 100644 (file)
index 0000000..ad4190e
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015 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;
+
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+final class DuplicateFinder {
+    private final Map<NormalizedNode<?, ?>, DuplicateEntry> identities = new IdentityHashMap<>();
+    private final Map<NormalizedNode<?, ?>, DuplicateEntry> duplicates = new HashMap<>();
+
+    private DuplicateFinder() {
+        // Hidden on purpose
+    }
+
+    private void findDuplicates(final YangInstanceIdentifier path, final NormalizedNode<?, ?> node) {
+        final DuplicateEntry i = identities.get(node);
+        if (i == null) {
+            final DuplicateEntry d = duplicates.get(node);
+            if (d == null) {
+                final DuplicateEntry n = new DuplicateEntry(path);
+                identities.put(node, n);
+                duplicates.put(node, n);
+            } else {
+                d.addDuplicate(path);
+            }
+
+            if (node instanceof NormalizedNodeContainer<?, ?, ?>) {
+                final NormalizedNodeContainer<?, ?, ?> container = (NormalizedNodeContainer<?, ?, ?>) node;
+
+                for (NormalizedNode<?, ?> c : container.getValue()) {
+                    findDuplicates(path.node(c.getIdentifier()), c);
+                }
+            }
+        } else {
+            i.addHardLink(path);
+        }
+    }
+
+    /**
+     * Recursively scan a {@link NormalizedNode} instance and its children and
+     * produce a collection of {@link DuplicateEntry} objects. Each holds the
+     * original definition path and a list of hard/softlinks.
+     *
+     * @param node Root node, may not be null.
+     * @return List of entries
+     */
+    static Map<NormalizedNode<?, ?>, DuplicateEntry> findDuplicates(final NormalizedNode<?, ?> node) {
+        final DuplicateFinder finder = new DuplicateFinder();
+        finder.findDuplicates(YangInstanceIdentifier.empty(), node);
+        return finder.identities;
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ForeignDataNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ForeignDataNode.java
new file mode 100644 (file)
index 0000000..0b9d7c4
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * A NormalizedNode holding a value in some foreign object model. The object model is identified by a single class,
+ * which must be the superclass of (or interface implemented by) all objects used to anchor that object model into
+ * NormalizedNode model.
+ *
+ * <p>
+ * This interface should not be implemented directly, but rather further specialized, like {@link AnyxmlNode}.
+ *
+ * @param <K> Local identifier of node
+ * @param <V> Value type, uniquely identifying the object model used for values
+ */
+@Beta
+public interface ForeignDataNode<K extends PathArgument, V> extends DataContainerChild<K, V> {
+    /**
+     * Return the object model class, which identifies it. For example {@link DOMSourceAnyxmlNode}
+     * uses {@link DOMSource} as its value object model.
+     *
+     * @return Object model class
+     */
+    @NonNull Class<V> getValueObjectModel();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/LeafNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/LeafNode.java
new file mode 100644 (file)
index 0000000..8c62072
--- /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.data.api.schema;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+
+/**
+ * Leaf node with multiplicity 0..1.
+ *
+ * <p>
+ * Leaf node has a value, but no child nodes in the data tree, schema
+ * for leaf node and its value is described by {@link org.opendaylight.yangtools.yang.model.api.LeafSchemaNode}.
+ *
+ * @param <T> Value type
+ */
+public interface LeafNode<T> extends DataContainerChild<NodeIdentifier, T>, ValueNode<NodeIdentifier, T> {
+    /**
+     * Returns value of this leaf node.
+     *
+     * @return Returned value of this leaf node. Value SHOULD meet criteria defined by schema.
+     */
+    @Override
+    T getValue();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/LeafSetEntryNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/LeafSetEntryNode.java
new file mode 100644 (file)
index 0000000..63f9127
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+
+/**
+ * Leaf node with multiplicity 0...n.
+ *
+ * <p>
+ * Leaf node has a value, but no child nodes in the data tree, schema
+ * for leaf node and its value is described by
+ * {@link org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode}.
+ *
+ * @param <T>
+ *            Value type
+ */
+public interface LeafSetEntryNode<T> extends NormalizedNode<NodeWithValue, T>, ValueNode<NodeWithValue, T> {
+    /**
+     * Returns {@link NodeWithValue} which identifies this leaf set entry.
+     * Returned {@link NodeWithValue} contains same value as this node.
+     *
+     * <p>
+     * <h3>Implementation notes</h3> Invocation of
+     * {@link NodeWithValue#getValue()} on returned instance of
+     * {@link NodeWithValue} must returns
+     * same value as invocation of {@link #getValue()}, such as
+     * following condition is allways met:
+     * <code>true == this.getIdentifier().getValue().equals(this.getValue())</code>
+     *
+     * @return {@link NodeWithValue} which identifies this leaf set entry.
+     */
+    @Override
+    NodeWithValue getIdentifier();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/LeafSetNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/LeafSetNode.java
new file mode 100644 (file)
index 0000000..77d4519
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+
+/**
+ * Node representing set of simple leaf nodes. Node containing instances of {@link LeafSetEntryNode}.
+ *
+ * <p>
+ * Schema and semantics of this node are described by instance of
+ * {@link org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode}.
+ *
+ * @param <T> Type of leaf node values.
+ */
+public interface LeafSetNode<T> extends MixinNode, DataContainerChild<NodeIdentifier, Collection<LeafSetEntryNode<T>>>,
+    NormalizedNodeContainer<NodeIdentifier, NodeWithValue, LeafSetEntryNode<T>> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/MapEntryNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/MapEntryNode.java
new file mode 100644 (file)
index 0000000..11a3170
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+
+/**
+ * Instance of Map entry, this node does not contains value, but child nodes.
+ */
+public interface MapEntryNode extends DataContainerNode<NodeIdentifierWithPredicates> {
+    /**
+     * Returns identifier of this node in parent map node
+     *
+     * <p>
+     * Contents of identifier is defined by <code>key</code> (
+     * {@link org.opendaylight.yangtools.yang.model.api.ListSchemaNode#getKeyDefinition()}
+     * ) statement in YANG schema for associated list item and child {@link LeafNode}s
+     * values with {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier}
+     * as defined in the schema.
+     *
+     * @return identifier of this node in the context of parent node
+     */
+    @Override
+    NodeIdentifierWithPredicates getIdentifier();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/MapNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/MapNode.java
new file mode 100644 (file)
index 0000000..fc602d5
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+
+/**
+ * Containment node, which contains {@link MapEntryNode} of the same type, which may
+ * be quickly retrieved using key.
+ *
+ * <p>
+ * This node maps to the list node in YANG schema, schema and semantics of this node,
+ * its children and key construction is  defined by YANG <code>list</code>
+ * statement and its <code>key</code> and <code>ordered-by</code> substatements.
+ */
+public interface MapNode extends MixinNode, DataContainerChild<NodeIdentifier, Collection<MapEntryNode>>,
+        NormalizedNodeContainer<NodeIdentifier, NodeIdentifierWithPredicates, MapEntryNode> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/MixinNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/MixinNode.java
new file mode 100644 (file)
index 0000000..d39bdf9
--- /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.data.api.schema;
+
+/**
+ * Marker interface for nodes, which are mixins - their content
+ * belongs to parent node and in serialized form this node
+ * does not exists, but it's children are present.
+ */
+public interface MixinNode {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizableAnydata.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizableAnydata.java
new file mode 100644 (file)
index 0000000..6afe18a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * An {@link AnydataNode#getValueObjectModel() anydata value object model} which can be normalized to
+ * {@link NormalizedAnydata} when provided with proper context.
+ */
+@Beta
+@NonNullByDefault
+public interface NormalizableAnydata {
+    /**
+     * Attempt to interpret this anydata content in the context of specified tree and node.
+     *
+     * @param schemaContext Schema context
+     * @param contextNode Corresponding schema node
+     * @return Normalized anydata instance
+     * @throws NullPointerException if any argument is null
+     * @throws AnydataNormalizationException if this data cannot be interpreted in the requested context
+     */
+    NormalizedAnydata normalizeTo(SchemaContext schemaContext, DataSchemaNode contextNode)
+            throws AnydataNormalizationException;
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedAnydata.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedAnydata.java
new file mode 100644 (file)
index 0000000..b231e91
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+/**
+ * The contents of an {@code anydata} node in a normalized format. This representation acts as a schema-bound bridge
+ * between the various (mostly parser-based) representations. Implementations of this interface are usually created
+ * from an instance of {@link NormalizableAnydata}.
+ *
+ * <p>
+ * Note this interface does not have an equality contract and implementations are expected to default to identity
+ * equality (or in Valhalla-speak: be plain data).
+ */
+@Beta
+@NonNullByDefault
+public interface NormalizedAnydata extends Immutable, SchemaContextProvider {
+
+    DataSchemaNode getContextNode();
+
+    NormalizedNode<?, ?> getData();
+
+    default void writeTo(final NormalizedNodeStreamWriter writer) throws IOException {
+        writeTo(writer, true);
+    }
+
+    default void writeTo(final NormalizedNodeStreamWriter writer, final boolean orderKeyLeaves) throws IOException {
+        NormalizedNodeWriter.forStreamWriter(writer, orderKeyLeaves).write(getData()).flush();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedNode.java
new file mode 100644 (file)
index 0000000..ac3e46a
--- /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.data.api.schema;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Node which is normalized according to the YANG schema
+ * is identifiable by a {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier}.
+ *
+ * <p>
+ * See subinterfaces of this interface for concretization of node.
+ *
+ * @param <K> Local identifier of node
+ * @param <V> Value of node
+ */
+/*
+ * FIXME: 5.0.0: NormalizedNode represents the perfectly-compliant view of the data, as evaluated by an implementation,
+ *               which is currently singular, with respect of its interpretation of a SchemaContext. This includes
+ *               leaf values, which are required to hold normalized representation for a particular implementation,
+ *               which may be affected by its understanding of any YANG extensions present -- such as optional type
+ *               handling hints and bindings.
+ *
+ *               Implementations (i.e. the reference implementation and parsers) will need to start using
+ *               yang.common.Uint8 and friends and, if possible, express data validation in terms
+ *               of yang.common.CanonicalValue and yang.common.CanonicalValueValidator.
+ *
+ *               This notably means that to efficiently implement any sort of lenient parsing, we need a separate
+ *               concept which contains an unverified, potentially non-conformant data tree, which the consumer needs
+ *               to check/fixup if it wishes to use it as a NormalizedNode. Such a concept should be called
+ *               "UnverifiedData".
+ */
+/*
+ * FIXME: 5.0.0: Once we have UnverifiedData, we should really rename this to "NormalizedData" or similar to unload
+ *               some "Node" ambiguity. "Node" should be a generic term reserved for a particular domain -- hence 'node'
+ *               can be used to refer to either a 'schema node' in context of yang.model.api, or to
+ *               a 'normalized data node' in context of yang.data.api.
+ *
+ * FIXME: 5.0.0: Well, not quite. The structure of unverified data is really codec specific -- and JSON and XML
+ *               do not agree on details. Furthermore things get way more complicated when we have a cross-schema
+ *               boundary -- like RFC8528. Hence we cannot really have a reasonably-structured concept of unverified
+ *               data. Nevertheless, this interface should be named 'NormalizedData'.
+ */
+public interface NormalizedNode<K extends PathArgument, V> extends Identifiable<K> {
+    /**
+     * QName of the node as defined in YANG schema.
+     *
+     * @return QName of this node, non-null.
+     */
+    QName getNodeType();
+
+    /**
+     * Locally unique identifier of the node.
+     *
+     * @return Node identifier, non-null.
+     */
+    @Override
+    K getIdentifier();
+
+    /**
+     * Value of node.
+     *
+     * @return Value of the node, may be null.
+     */
+    @NonNull V getValue();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedNodeContainer.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedNodeContainer.java
new file mode 100644 (file)
index 0000000..df81ece
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Node which is not leaf, but has child {@link NormalizedNode}s as its valzue.
+ *
+ * <p>
+ * NormalizedNodeContainer does not have a value, but it has a child
+ * nodes. Definition of possible and valid child nodes is introduced
+ * in subclasses of this interface.
+ *
+ * <p>
+ * This interface should not be used directly, but rather use of of derived subinterfaces
+ * such as {@link DataContainerNode}, {@link MapNode}, {@link LeafSetNode}.
+ *
+ * @param <I>
+ *            Node Identifier type
+ * @param <K>
+ *            Child Node Identifier type
+ * @param <V>
+ *            Child Node type
+ */
+public interface NormalizedNodeContainer<I extends PathArgument, K extends PathArgument,
+       V extends NormalizedNode<? extends K, ?>> extends NormalizedNode<I, Collection<V>> {
+
+    @Override
+    I getIdentifier();
+
+    /**
+     * Returns immutable iteration of child nodes of this node.
+     *
+     */
+    @Override
+    Collection<V> getValue();
+
+    /**
+     * Returns child node identified by provided key.
+     *
+     * @param child
+     *            Path argument identifying child node
+     * @return Optional with child node if child exists.
+     *         {@link Optional#empty()} if child does not exists.
+     */
+    Optional<V> getChild(K child);
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedNodes.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedNodes.java
new file mode 100644 (file)
index 0000000..1f11849
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * A set of utility methods for interacting with {@link NormalizedNode} objects.
+ */
+@Beta
+public final class NormalizedNodes {
+    private static final int STRINGTREE_INDENT = 4;
+
+    private NormalizedNodes() {
+        throw new UnsupportedOperationException("Utility class should not be instantiated");
+    }
+
+    /**
+     * Find duplicate NormalizedNode instances within a subtree. Duplicates are those, which compare
+     * as equal, but do not refer to the same object.
+     *
+     * @param node A normalized node subtree, may not be null
+     * @return A Map of NormalizedNode/DuplicateEntry relationships.
+     */
+    public static Map<NormalizedNode<?, ?>, DuplicateEntry> findDuplicates(final @NonNull NormalizedNode<?, ?> node) {
+        return Maps.filterValues(DuplicateFinder.findDuplicates(node), input -> !input.getDuplicates().isEmpty());
+    }
+
+    public static Optional<NormalizedNode<?, ?>> findNode(final YangInstanceIdentifier rootPath,
+            final NormalizedNode<?, ?> rootNode, final YangInstanceIdentifier childPath) {
+        final Optional<YangInstanceIdentifier> relativePath = childPath.relativeTo(rootPath);
+        return relativePath.isPresent() ? findNode(rootNode, relativePath.get()) : Optional.empty();
+    }
+
+    public static Optional<NormalizedNode<?, ?>> findNode(final Optional<NormalizedNode<?, ?>> parent,
+            final Iterable<PathArgument> relativePath) {
+        final Iterator<PathArgument> pathIterator = requireNonNull(relativePath, "Relative path must not be null")
+                .iterator();
+        Optional<NormalizedNode<?, ?>> currentNode = requireNonNull(parent, "Parent must not be null");
+        while (currentNode.isPresent() && pathIterator.hasNext()) {
+            currentNode = getDirectChild(currentNode.get(), pathIterator.next());
+        }
+        return currentNode;
+    }
+
+    public static Optional<NormalizedNode<?, ?>> findNode(final Optional<NormalizedNode<?, ?>> parent,
+            final PathArgument... relativePath) {
+        return findNode(parent, Arrays.asList(relativePath));
+    }
+
+    public static Optional<NormalizedNode<?, ?>> findNode(final NormalizedNode<?, ?> parent,
+            final Iterable<PathArgument> relativePath) {
+        return findNode(Optional.ofNullable(parent), relativePath);
+    }
+
+    public static Optional<NormalizedNode<?, ?>> findNode(final NormalizedNode<?, ?> parent,
+            final SchemaPath relativePath) {
+        checkArgument(!relativePath.isAbsolute(), "%s is not a relative path", relativePath);
+        return findNode(Optional.ofNullable(parent), Iterables.transform(relativePath.getPathFromRoot(),
+            NodeIdentifier::new));
+    }
+
+    public static Optional<NormalizedNode<?, ?>> findNode(final NormalizedNode<?, ?> parent,
+            final PathArgument... relativePath) {
+        return findNode(parent, Arrays.asList(relativePath));
+    }
+
+    public static Optional<NormalizedNode<?, ?>> findNode(final NormalizedNode<?, ?> tree,
+            final YangInstanceIdentifier path) {
+        return findNode(Optional.of(requireNonNull(tree, "Tree must not be null")),
+            requireNonNull(path, "Path must not be null").getPathArguments());
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static Optional<NormalizedNode<?, ?>> getDirectChild(final NormalizedNode<?, ?> node,
+            final PathArgument pathArg) {
+        if (node instanceof ValueNode) {
+            return Optional.empty();
+        } else if (node instanceof DataContainerNode) {
+            return (Optional) ((DataContainerNode<?>) node).getChild(pathArg);
+        } else if (node instanceof MapNode && pathArg instanceof NodeIdentifierWithPredicates) {
+            return (Optional) ((MapNode) node).getChild((NodeIdentifierWithPredicates) pathArg);
+        } else if (node instanceof LeafSetNode && pathArg instanceof NodeWithValue) {
+            return (Optional) ((LeafSetNode<?>) node).getChild((NodeWithValue) pathArg);
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * Convert a data subtree under a node into a human-readable string format.
+     *
+     * @param node Data subtree root
+     * @return String containing a human-readable form of the subtree.
+     */
+    public static String toStringTree(final NormalizedNode<?, ?> node) {
+        final StringBuilder builder = new StringBuilder();
+        toStringTree(builder, node, 0);
+        return builder.toString();
+    }
+
+    private static void toStringTree(final StringBuilder builder, final NormalizedNode<?, ?> node, final int offset) {
+        final String prefix = " ".repeat(offset);
+
+        builder.append(prefix).append(toStringTree(node.getIdentifier()));
+        if (node instanceof NormalizedNodeContainer) {
+            final NormalizedNodeContainer<?, ?, ?> container = (NormalizedNodeContainer<?, ?, ?>) node;
+
+            builder.append(" {\n");
+            for (NormalizedNode<?, ?> child : container.getValue()) {
+                toStringTree(builder, child, offset + STRINGTREE_INDENT);
+            }
+
+            builder.append(prefix).append('}');
+        } else {
+            builder.append(' ').append(node.getValue());
+        }
+        builder.append('\n');
+    }
+
+    private static String toStringTree(final PathArgument identifier) {
+        if (identifier instanceof NodeIdentifierWithPredicates) {
+            return identifier.getNodeType().getLocalName() + ((NodeIdentifierWithPredicates) identifier).values();
+        } else if (identifier instanceof AugmentationIdentifier) {
+            return "augmentation";
+        } else {
+            return identifier.getNodeType().getLocalName();
+        }
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/OrderedLeafSetNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/OrderedLeafSetNode.java
new file mode 100644 (file)
index 0000000..062ea08
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+/**
+ * Leaf set node which preserves user-supplied ordering.
+ *
+ * <p>
+ * This node represents a data instance of <code>leaf-list</code> with
+ * <code>ordered-by user;</code> substatement.
+ *
+ * <p>
+ * Except preserving user-ordering all other semantics and behaviour is same
+ * as in {@link LeafSetNode}.
+ *
+ * @param <T> Value type of Leaf entries
+ */
+public interface OrderedLeafSetNode<T> extends LeafSetNode<T>, OrderedNodeContainer<LeafSetEntryNode<T>> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/OrderedMapNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/OrderedMapNode.java
new file mode 100644 (file)
index 0000000..bfe6dae
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema;
+
+/**
+ * Map node which preserves user-supplied ordering.
+ *
+ * <p>
+ * This node represents a data instance of <code>list</code> with
+ * <code>ordered-by user;</code> substatement and <code>key</code> definition.
+ *
+ * <p>
+ * Except preserving user-ordering all other semantics and behaviour is same as
+ * in {@link MapNode}.
+ */
+public interface OrderedMapNode extends MapNode, OrderedNodeContainer<MapEntryNode> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/OrderedNodeContainer.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/OrderedNodeContainer.java
new file mode 100644 (file)
index 0000000..4516d77
--- /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.data.api.schema;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+
+/**
+ * Normalized Node container which preserves user supplied ordering
+ * and allows addressing of child elements by position.
+ *
+ * @param <V> child type
+ */
+public interface OrderedNodeContainer<V extends NormalizedNode<?, ?>> extends MixinNode,
+       NormalizedNode<NodeIdentifier, Collection<V>> {
+    /**
+     * Returns child node by position.
+     *
+     * @param position Position of child node
+     * @return Child Node
+     * @throws IndexOutOfBoundsException Out of bound Exception
+     */
+    V getChild(int position);
+
+    /**
+     * Returns count of child nodes.
+     *
+     * @return count of child nodes.
+     */
+    int getSize();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/UnkeyedListEntryNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/UnkeyedListEntryNode.java
new file mode 100644 (file)
index 0000000..4f7ef2e
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+
+/**
+ * List entry node, which does not have value, but child nodes.
+ * Represents an instance of data, which schema is instance of
+ * {@link org.opendaylight.yangtools.yang.model.api.ListSchemaNode} with key undefined.
+ *
+ * <p>
+ * This nodes itself does not contain any ordering information, user supplied
+ * ordering is preserved by parent node, which is instance of {@link UnkeyedListNode}.
+ */
+public interface UnkeyedListEntryNode extends DataContainerNode<NodeIdentifier> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/UnkeyedListNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/UnkeyedListNode.java
new file mode 100644 (file)
index 0000000..63ab2cd
--- /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.data.api.schema;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+
+/**
+ * Containment node, which contains {@link UnkeyedListEntryNode} of the same type, which may
+ * be quickly retrieved using key.
+ *
+ * <p>
+ * This node maps to the <code>list</code> statement in YANG schema,
+ * which did not define <code>key</code> substatement.
+ *
+ * <p>
+ * Ordering of the elements is user-defined during construction of instance of this
+ * interface. Ordered view of elements (iteration)
+ * is provided by {@link #getValue()} call.
+ */
+public interface UnkeyedListNode extends DataContainerChild<NodeIdentifier, Collection<UnkeyedListEntryNode>>,
+    OrderedNodeContainer<UnkeyedListEntryNode> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ValueNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ValueNode.java
new file mode 100644 (file)
index 0000000..eb70bfa
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016 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;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Interface holding the common trait of {@link LeafSetEntryNode} and {@link LeafNode}, which both hold a value.
+ *
+ * @author Robert Varga
+ *
+ * @param <K> Local identifier of node
+ * @param <V> Value of node
+ */
+public interface ValueNode<K extends PathArgument, V> extends NormalizedNode<K, V> {
+    /**
+     * Returns value of held by this node.
+     *
+     * <h3>Implementation notes</h3> Invocation of {@link #getValue()} must
+     * provides same value as value in {@link #getIdentifier()}.
+     * <code>true == this.getIdentifier().getValue().equals(this.getValue())</code>
+     *
+     * @return Returned value of this node. Value SHOULD meet criteria
+     *         defined by schema.
+     *
+     */
+    @Override
+    V getValue();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/YangModeledAnyXmlNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/YangModeledAnyXmlNode.java
new file mode 100644 (file)
index 0000000..2550718
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 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;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+
+/**
+ * AnyXML node with schema of contained XML data.
+ */
+@Beta
+public interface YangModeledAnyXmlNode extends DataContainerNode<NodeIdentifier>,
+        DataContainerChild<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> {
+
+    /**
+     * Returns the schema corresponding to the node's datea.
+     *
+     * @return schema of contained XML data
+     */
+    @NonNull ContainerSchemaNode getSchemaOfAnyXmlData();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/package-info.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/package-info.java
new file mode 100644 (file)
index 0000000..12a2e50
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2015 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
+ */
+
+/**
+ * Definition of normalized YANG DOM Model. Normalized DOM Model brings more direct mapping between YANG Model, DOM
+ * representation of data.
+ *
+ * <h2>Normalized DOM Model</h2>
+ *
+ * <h3>Node Types</h3>
+ * <ul>
+ * <li> {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode} -
+ * Base type representing a node in a tree structure; all nodes are derived from
+ * it, it contains a leaf identifier and a value.
+ * <ul>
+ * <li>
+ * {@link org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode} -
+ * Node which contains multiple leafs; it does not have a direct representation
+ * in the YANG syntax.
+ * <ul>
+ * <li> {@link org.opendaylight.yangtools.yang.data.api.schema.ContainerNode} -
+ * Node, which represents a leaf which can occur only once per parent node; it
+ * contains multiple child leaves and maps to the <i>container</i> statement in
+ * YANG.</li>
+ * <li> {@link org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode} -
+ * Node which represents a leaf, which can occur multiple times; a leave is
+ * uniquely identified by the value of its key. A MapEntryNode may contain
+ * multiple child leaves. MapEntryNode maps to the instance of <i>list</i> in
+ * YANG.</li>
+ * <li>
+ * {@link org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode}
+ * - Node which represents a leaf, which can occur multiple times; a leave is
+ * uniquely identified by the value of its key. A MapEntryNode may contain
+ * multiple child leaves. MapEntryNode maps to the instance of <i>list</i> in
+ * YANG.</li>
+ * <li> {@link org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode} - Node
+ * which represents a leaf, which occurs mostly once per parent node, but
+ * possible values could have different types. Maps to <i>choice</i> statement.
+ * Types maps to the <i>case</i> statements for that <i>choice</i>.</li>
+ * <li> {@link org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode}
+ * - Node which represents a leaf, which occurs mostly once per parent node.</li>
+ * </ul>
+ * </li>
+ * <li> {@link org.opendaylight.yangtools.yang.data.api.schema.LeafNode} - Node
+ * which represents a leaf, which occurs mostly once per parent node. Contains
+ * simple value.</li>
+ * <li> {@link org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode}
+ * - Node which represents a leaf, which type could occurs multiple times per
+ * parent node. Maps to to the instances of <i>leaf-list</i> in YANG.</li>
+ * <li> {@link org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode} -
+ * Special node, which can occur only once per parent node; its leaves are
+ * LeafSetEntryNode nodes of specified type. Maps into the <i>leaf-list</i> in
+ * YANG.</li>
+ * <li> {@link org.opendaylight.yangtools.yang.data.api.schema.MapNode} - Special
+ * node, which can occur only once per parent node; its leaves are MapEntryNode
+ * nodes.
+ * <ul>
+ * <li> {@link org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode} -
+ * Special node, which can occur only once per parent node; its leaves are
+ * MapEntryNode nodes.</li>
+ * </ul>
+ * </li>
+ * <li> {@link org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode} -
+ * Special node, which can occur only once per parent node; its leaves are
+ * MapEntryNode nodes.</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * <h3>Tree / subtree structure</h3> <h4>Grammar representation</h4>
+ *
+ * <pre>
+ *  {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier} =
+ *    {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument}*
+ *  {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument} =
+ *    {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier}
+ *    | {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates}
+ *    | {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue}
+ *    | {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier}
+ *
+ *  TreeRoot = {@link org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode}
+ *  {@link org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode} =
+ *    ( {@link org.opendaylight.yangtools.yang.data.api.schema.LeafNode}
+ *     | {@link org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode}
+ *     | {@link org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode}
+ *     | {@link org.opendaylight.yangtools.yang.data.api.schema.MapNode}
+ *     | {@link org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode})*
+ *  ContainerDataNode =
+ *    {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier}
+ *    {@link org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode}
+ *
+ *  {@link org.opendaylight.yangtools.yang.data.api.schema.LeafNode} =
+ *    {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier} SimpleValue
+ *  {@link org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode} =
+ *    {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier}
+ *    {@link org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode}
+ *  {@link org.opendaylight.yangtools.yang.data.api.schema.MapNode} =
+ *    {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier}
+ *    {@link org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode}
+ *  {@link org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode} =
+ *    {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates}
+ *    {@link org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode}
+ *
+ *  // Special nodes
+ *  {@link org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode} =
+ *    {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier}
+ *    {@link org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode}*
+ *  {@link org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode} =
+ *    {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier}
+ *    {@link org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode}
+ *  {@link org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode} =
+ *    {@link org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue} SimpleValue
+ * </pre>
+ *
+ * <p>
+ * The resulting tree organization is following:
+ *
+ * <ul>
+ * <li>(DataContainerNode)
+ * <ul>
+ * <li>(0..n) LeafNode</li>
+ * <li>(0..n) LeafSetNode
+ * <ul>
+ * <li>(0..n) LeafSetEntryNode</li>
+ * </ul>
+ * </li>
+ * <li>(0..n) ContainerNode
+ * <ul>
+ * <li>(Same as DataContainerNode)</li>
+ * </ul>
+ * </li>
+ * <li>(0..n) ContainerNode
+ * <ul>
+ * <li>(Same as DataContainerNode)</li>
+ * </ul>
+ * </li>
+ * <li>(0..n) MapNode
+ * <ul>
+ * <li>(0..n) MapEntryNode
+ * <ul>
+ * <li>(Same as DataContainerNode)</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * </li>
+ * <li>(0..n) AugmentationNode
+ * <ul>
+ * <li>(Same as DataContainerNode)</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * <h3>Ordering of child nodes</h3>
+ * Ordering of child nodes is not enforced by this API definition, unless
+ * explicitly stated by subclasses of
+ * {@link org.opendaylight.yangtools.yang.data.api.schema.OrderedNodeContainer}
+ * which marks nodes with semantic constrain to preserve user-supplied ordering.
+ *
+ * <p>
+ * Clients should not expect any specific ordering of child nodes for interfaces
+ * from this package which does not extend
+ * {@link org.opendaylight.yangtools.yang.data.api.schema.OrderedNodeContainer},
+ * since implementations are not required to have well-defined order, which
+ * allows for more efficient implementations. If such ordering is required by
+ * clients for serialization / debugability it SHOULD be done externally in
+ * code using these interfaces.
+ *
+ */
+package org.opendaylight.yangtools.yang.data.api.schema;
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/ForwardingNormalizedNodeStreamWriter.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/ForwardingNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..e5f5ca3
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2015 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 com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ForwardingObject;
+import java.io.IOException;
+import javax.xml.transform.dom.DOMSource;
+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.NodeWithValue;
+
+public abstract class ForwardingNormalizedNodeStreamWriter extends ForwardingObject
+        implements NormalizedNodeStreamWriter {
+    @Override
+    protected abstract NormalizedNodeStreamWriter delegate();
+
+    @Override
+    public ClassToInstanceMap<NormalizedNodeStreamWriterExtension> getExtensions() {
+        return delegate().getExtensions();
+    }
+
+    @Override
+    public void startLeafNode(final NodeIdentifier name) throws IOException {
+        delegate().startLeafNode(name);
+    }
+
+    @Override
+    public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        delegate().startLeafSet(name, childSizeHint);
+    }
+
+    @Override
+    public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        delegate().startOrderedLeafSet(name, childSizeHint);
+    }
+
+    @Override
+    public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
+        delegate().startLeafSetEntryNode(name);
+    }
+
+    @Override
+    public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        delegate().startContainerNode(name, childSizeHint);
+    }
+
+    @Override
+    public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        delegate().startUnkeyedList(name, childSizeHint);
+    }
+
+    @Override
+    public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        delegate().startUnkeyedListItem(name, childSizeHint);
+    }
+
+    @Override
+    public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        delegate().startMapNode(name, childSizeHint);
+    }
+
+    @Override
+    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+            throws IOException {
+        delegate().startMapEntryNode(identifier, childSizeHint);
+    }
+
+    @Override
+    public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        delegate().startOrderedMapNode(name, childSizeHint);
+    }
+
+    @Override
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        delegate().startChoiceNode(name, childSizeHint);
+    }
+
+    @Override
+    public void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
+        delegate().startAugmentationNode(identifier);
+    }
+
+    @Override
+    public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        return delegate().startAnyxmlNode(name, objectModel);
+    }
+
+    @Override
+    public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        delegate().startYangModeledAnyXmlNode(name, childSizeHint);
+    }
+
+    @Override
+    public boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        return delegate().startAnydataNode(name, objectModel);
+    }
+
+    @Override
+    public void endNode() throws IOException {
+        delegate().endNode();
+    }
+
+    @Override
+    public void scalarValue(final Object value) throws IOException {
+        delegate().scalarValue(value);
+    }
+
+    @Override
+    public void domSourceValue(final DOMSource value) throws IOException {
+        delegate().domSourceValue(value);
+    }
+
+    @Override
+    public void close() throws IOException {
+        delegate().close();
+    }
+
+    @Override
+    public void flush() throws IOException {
+        delegate().flush();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/LoggingNormalizedNodeStreamWriter.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/LoggingNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..c510011
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2015 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 static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import javax.xml.transform.dom.DOMSource;
+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.NodeWithValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link NormalizedNodeStreamWriter} which logs the events into a {@link Logger}.
+ */
+@Beta
+public final class LoggingNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+    private static final Logger LOG = LoggerFactory.getLogger(LoggingNormalizedNodeStreamWriter.class);
+    private static final int DEFAULT_INDENT_SIZE = 2;
+
+    private final Deque<String> indent = new ArrayDeque<>();
+    private final String indentStr;
+
+    public LoggingNormalizedNodeStreamWriter() {
+        this(DEFAULT_INDENT_SIZE);
+    }
+
+    public LoggingNormalizedNodeStreamWriter(final int indentSize) {
+        this.indentStr = " ".repeat(indentSize);
+        indent.push("");
+    }
+
+    private String ind() {
+        return indent.peek();
+    }
+
+    private void decIndent() {
+        indent.pop();
+    }
+
+    private void incIndent() {
+        indent.push(ind() + indentStr);
+    }
+
+    @Override
+    public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) {
+        LOG.debug("{}{}[](no key)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
+        LOG.debug("{}{}(no key)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
+        startMapNode(name, childSizeHint);
+    }
+
+    @Override
+    public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
+        LOG.debug("{}{}(key)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) {
+        LOG.debug("{}{}[](key)", ind(), identifier);
+        incIndent();
+    }
+
+    @Override
+    public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
+        LOG.debug("{}{}(leaf-list)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
+        startLeafSet(name, childSizeHint);
+    }
+
+    @Override
+    public void startContainerNode(final NodeIdentifier name, final int childSizeHint) {
+        LOG.debug("{}{}(container)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
+        LOG.debug("{}{}(choice)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void startAugmentationNode(final AugmentationIdentifier identifier) {
+        LOG.debug("{}{}(augmentation)", ind(), identifier);
+        incIndent();
+    }
+
+    @Override
+    public void startLeafSetEntryNode(final NodeWithValue<?> name) {
+        LOG.debug("{}{}(entry}", ind(), name.getNodeType());
+        incIndent();
+    }
+
+    @Override
+    public void startLeafNode(final NodeIdentifier name) {
+        LOG.debug("{}{}(leaf)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void endNode() {
+        decIndent();
+        LOG.debug("{}(end)", ind());
+    }
+
+    @Override
+    public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) {
+        LOG.debug("{}{}(anyxml)", ind(), name);
+        incIndent();
+        return true;
+    }
+
+    @Override
+    public boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) {
+        LOG.debug("{}{}(anydata)", ind(), name);
+        incIndent();
+        return true;
+    }
+
+    @Override
+    public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) {
+        LOG.debug("{}{}(yangModeledAnyXml)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void flush() {
+        LOG.trace("<<FLUSH>>");
+    }
+
+    @Override
+    public void close() {
+        LOG.debug("<<END-OF-STREAM>>");
+    }
+
+    @Override
+    @SuppressFBWarnings("SLF4J_SIGN_ONLY_FORMAT")
+    public void scalarValue(final Object value) {
+        LOG.debug("{}({})={}", ind(), requireNonNull(value).getClass().getSimpleName(), value);
+    }
+
+    @Override
+    public void domSourceValue(final DOMSource value) {
+        scalarValue(value);
+    }
+}
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..a9d2451
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * 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 static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.ExtensibleObject;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * Event Stream Writer based on Normalized Node tree representation.
+ *
+ * <h3>Writing Event Stream</h3>
+ * Each entity is emitted by invoking its corresponding {@code start*} event, optionally followed by interior events and
+ * invoking {@link #endNode()}. Some entities supported nested entities, some do not, see below for restrictions.
+ *
+ * <p>
+ * While this interface defines basic events, the event stream may be extended through
+ * {@link NormalizedNodeStreamWriterExtension}s discoverable through {@link #getExtensions()} method. The set of these
+ * extensions is immutable during the lifetime of a writer and may be freely cached.
+ *
+ * <ul>
+ * <li>{@code container} - Container node representation, start event is emitted using
+ * {@link #startContainerNode(NodeIdentifier, int)}.
+ * </li>
+ *
+ * <li>{@code list} - YANG list statement has two representation in event stream - unkeyed list and map. An unkeyed
+ * list is YANG list which did not specify a {@code key} statement. A map is a {@code list} with a {@code key}
+ * statement.
+ * <ul>
+ * <li>{@code Map} - Map start event is emitted using {@link #startMapNode(NodeIdentifier, int)}. Each map entry start
+ * is emitted using {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
+ * </li>
+ * <li>{@code UnkeyedList} - Unkeyed list represent list without keys, unkeyed list start is emitted using
+ * {@link #startUnkeyedList(NodeIdentifier, int)}. Each list item is emitted using
+ * {@link #startUnkeyedListItem(NodeIdentifier, int)}.</li>
+ * </ul>
+ * </li>
+ *
+ * <li>{@code leaf} - Leaf node start event is emitted using {@link #startLeafNode(NodeIdentifier)}. Leaf node values
+ * need to be emitted through {@link #scalarValue(Object)}.
+ * </li>
+ *
+ * <li>{@code leaf-list} - Leaf list start is emitted using {@link #startLeafSet(NodeIdentifier, int)}. Individual
+ * leaf-list entries are emitted using {@link #startLeafSetEntryNode(NodeWithValue)}.
+ *
+ * <li>{@code anyxml} - An anyxml node event is emitted using {@link #startAnyxmlNode(NodeIdentifier, Class)}.</li>
+ *
+ * <li>{@code choice} - Choice node event is emitted by {@link #startChoiceNode(NodeIdentifier, int)} event.</li>
+ *
+ * <li>{@code augment} - Represents augmentation, augmentation node is started by invoking
+ * {@link #startAugmentationNode(AugmentationIdentifier)}.
+ * </li>
+ * </ul>
+ *
+ * <h3>Implementation notes</h3>
+ *
+ * <p>
+ * Implementations of this interface must not hold user suppled objects and resources needlessly.
+ */
+public interface NormalizedNodeStreamWriter extends Closeable, Flushable,
+        ExtensibleObject<NormalizedNodeStreamWriter, NormalizedNodeStreamWriterExtension> {
+    /**
+     * Methods in this interface allow users to hint the underlying implementation about the sizing of container-like
+     * constructors (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).
+     *
+     * <p>
+     * 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.
+     */
+    int UNKNOWN_SIZE = -1;
+
+    /**
+     * Emits a start of leaf node event.
+     *
+     * @param name name of node as defined in schema, namespace and revision are derived from parent node.
+     * @throws NullPointerException if {@code name} is null
+     * @throws IllegalArgumentException If emitted leaf node was emitted multiple times.
+     * @throws IllegalStateException If node was emitted inside {@code map}, {@code choice} or a {@code unkeyed list}
+     *                               node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startLeafNode(NodeIdentifier name) throws IOException;
+
+    /**
+     * Emits a start of system-ordered leaf set (leaf-list). While this entity is open,
+     * only {@link #startLeafSetEntryNode(NodeWithValue)} calls are valid. Implementations are free to reorder entries
+     * within the leaf-list.
+     *
+     * @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 NullPointerException if {@code name} is null
+     * @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 choice} or a {@code unkeyed list}
+     *                               node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startLeafSet(NodeIdentifier name, int childSizeHint) throws IOException;
+
+    /**
+     * Emits a start of a user-ordered leaf set (leaf-list). While this entity is open, only
+     * {@link #startLeafSetEntryNode(NodeWithValue)} calls are valid. Implementations must retain the same entry order.
+     *
+     * @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 NullPointerException if {@code name} is null
+     * @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 choice} or a {@code unkeyed list}
+     *                               node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startOrderedLeafSet(NodeIdentifier name, int childSizeHint) throws IOException;
+
+    /**
+     * Emits a leaf set entry node.
+     *
+     * @param name name of the node as defined in the schema.
+     * @throws NullPointerException if {@code name} is null
+     * @throws IllegalArgumentException if {@code name} does not match enclosing leaf set entity
+     * @throws IllegalStateException If node was emitted outside {@code leaf set} node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startLeafSetEntryNode(NodeWithValue<?> name) throws IOException;
+
+    /**
+     * Emits start of new container. Valid sub-events are:
+     * <ul>
+     * <li>{@link #startLeafNode}</li>
+     * <li>{@link #startAnyxmlNode(NodeIdentifier, Class)}</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 NullPointerException if {@code name} is null
+     * @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 choice} or a {@code unkeyed list}
+     *                               node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startContainerNode(NodeIdentifier name, int childSizeHint) throws IOException;
+
+    /**
+     * Emits start of unkeyed list node event. Valid subevents is only
+     * {@link #startUnkeyedListItem(NodeIdentifier, int)}.
+     *
+     * @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 NullPointerException if {@code name} is null
+     * @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 choice} or a {@code unkeyed list}
+     *                               node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startUnkeyedList(NodeIdentifier name, int childSizeHint) throws IOException;
+
+    /**
+     * Emits start of new unkeyed list item. Valid sub-events are:
+     * <ul>
+     * <li>{@link #startLeafNode}</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 NullPointerException if {@code name} is null
+     * @throws IllegalStateException If node was emitted outside <code>unkeyed list</code> node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startUnkeyedListItem(NodeIdentifier name, int childSizeHint) throws IOException;
+
+    /**
+     * Emits start of map node event. Valid subevent is only
+     * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
+     *
+     * @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 NullPointerException if {@code name} is null
+     * @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 choice} or a {@code unkeyed list}
+     *                               node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startMapNode(NodeIdentifier name, int childSizeHint) throws IOException;
+
+    /**
+     * Emits start of map entry. Valid sub-events are:
+     * <ul>
+     * <li>{@link #startLeafNode}</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.
+     * @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 NullPointerException if {@code name} is null
+     * @throws IllegalArgumentException If key contains incorrect value.
+     * @throws IllegalStateException If node was emitted outside {@code map entry} node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startMapEntryNode(NodeIdentifierWithPredicates identifier, int childSizeHint) throws IOException;
+
+    /**
+     * Emits start of map node event.  Valid subevent is only
+     * {@link #startMapEntryNode(NodeIdentifierWithPredicates, int)}.
+     *
+     * @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 NullPointerException if {@code name} is null
+     * @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 choice} or a {@code unkeyed list}
+     *                               node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startOrderedMapNode(NodeIdentifier name, int childSizeHint) throws IOException;
+
+    /**
+     * Emits start of a choice node event.
+     *
+     * @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 NullPointerException if {@code name} is null
+     * @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 choice} or a {@code unkeyed list}
+     *                               node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startChoiceNode(NodeIdentifier name, int childSizeHint) throws IOException;
+
+    /**
+     * Emits start of augmentation node. Valid sub-events are:
+     * <ul>
+     * <li>{@link #startLeafNode}</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 NullPointerException if {@code identifier} is null
+     * @throws IllegalArgumentException If augmentation is invalid in current context.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startAugmentationNode(AugmentationIdentifier identifier) throws IOException;
+
+    /**
+     * Start emitting a new anydata node identified by name.
+     *
+     * @param name The name of the anydata element
+     * @param objectModel The object model of anydata content
+     * @return True if the specified object model is supported by this extension and the process of emitting the node
+     *         has started. False if the object model is not supported and the node has not started to be emitted.
+     * @throws NullPointerException if any argument is null
+     * @throws IOException if an underlying IO error occurs
+     */
+    @Beta
+    boolean startAnydataNode(NodeIdentifier name, Class<?> objectModel) throws IOException;
+
+    /**
+     * Emits a start of anyxml node event.
+     *
+     * @param name name of node as defined in schema, namespace and revision are derived from parent node.
+     * @param objectModel The object model of anyxml content
+     * @return True if the specified object model is supported by this extension and the process of emitting the node
+     *         has started. False if the object model is not supported and the node has not started to be emitted.
+     * @throws NullPointerException if any argument is null
+     * @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 choice} or a {@code unkeyed list}
+     *                               node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    boolean startAnyxmlNode(NodeIdentifier name, Class<?> objectModel) throws IOException;
+
+    /**
+     * Set the value of current anyxml node. This call is only valid within the context in which an anyxml node is open.
+     *
+     * @param value node value
+     * @throws NullPointerException if the argument is null
+     * @throws IllegalArgumentException if the argument does not represents a valid value
+     * @throws IllegalStateException if an anyxml node is not open or if it's value has already been set and this
+     *                               implementation does not allow resetting the value.
+     * @throws IOException if an underlying IO error occurs
+     */
+    // FIXME: 5.0.0: this probably should integrated with scalarValue()
+    void domSourceValue(DOMSource value) throws IOException;
+
+    /**
+     * Emits start of new YANG-modeled anyxml node. Valid sub-events are:
+     * <ul>
+     * <li>{@link #startLeafNode}</li>
+     * <li>{@link #startContainerNode}</li>
+     * <li>{@link #startLeafSet}</li>
+     * <li>{@link #startMapNode}</li>
+     * <li>{@link #startUnkeyedList}</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 NullPointerException if {@code name} is null
+     * @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 choice} or a {@code unkeyed list}
+     *                               node.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void startYangModeledAnyXmlNode(NodeIdentifier name, int childSizeHint) throws IOException;
+
+    /**
+     * Emits end event for node.
+     *
+     * @throws IllegalStateException If there is no start* event to be closed.
+     * @throws IOException if an underlying IO error occurs
+     */
+    void endNode() throws IOException;
+
+    /**
+     * Attach the specified {@link DataSchemaNode} to the next node which will get started or emitted. The default
+     * implementation does nothing.
+     *
+     * @param schema DataSchemaNode
+     * @throws NullPointerException if the argument is null
+     */
+    default void nextDataSchemaNode(final @NonNull DataSchemaNode schema) {
+        requireNonNull(schema);
+    }
+
+    /**
+     * Set the value of current node. This call is only valid within the context in which a value-bearing node is open,
+     * such as a LeafNode, LeafSetEntryNode.
+     *
+     * @param value node value, must be effectively immutable
+     * @throws NullPointerException if the argument is null
+     * @throws IllegalArgumentException if the argument does not represents a valid value
+     * @throws IllegalStateException if a value-bearing node is not open or if it's value has already been set and this
+     *                               implementation does not allow resetting the value.
+     */
+    void scalarValue(@NonNull Object value) throws IOException;
+
+    @Override
+    void close() throws IOException;
+
+    @Override
+    void flush() throws IOException;
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeStreamWriterExtension.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeStreamWriterExtension.java
new file mode 100644 (file)
index 0000000..34d32d3
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o. 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 com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.ObjectExtension;
+
+/**
+ * Extension interface for {@link NormalizedNodeStreamWriter}. Extensions should extend this interface and their
+ * instances should be made available through {@link NormalizedNodeStreamWriter#getExtensions()}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface NormalizedNodeStreamWriterExtension extends
+    ObjectExtension<NormalizedNodeStreamWriter, NormalizedNodeStreamWriterExtension> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java
new file mode 100644 (file)
index 0000000..87aa4d6
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.stream;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Iterables;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Set;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
+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;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is an experimental iterator over a {@link NormalizedNode}. This is essentially
+ * the opposite of a {@link XMLStreamReader} -- unlike instantiating an iterator over
+ * the backing data, this encapsulates a {@link NormalizedNodeStreamWriter} and allows
+ * us to write multiple nodes.
+ */
+@Beta
+public class NormalizedNodeWriter implements Closeable, Flushable {
+    private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodeWriter.class);
+
+    private final @NonNull NormalizedNodeStreamWriter writer;
+
+    protected NormalizedNodeWriter(final NormalizedNodeStreamWriter writer) {
+        this.writer = requireNonNull(writer);
+    }
+
+    protected final NormalizedNodeStreamWriter getWriter() {
+        return writer;
+    }
+
+    /**
+     * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
+     *
+     * @param writer Back-end writer
+     * @return A new instance.
+     */
+    public static NormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer) {
+        return forStreamWriter(writer, true);
+    }
+
+    /**
+     * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. Unlike the simple
+     * {@link #forStreamWriter(NormalizedNodeStreamWriter)} method, this allows the caller to switch off RFC6020 XML
+     * compliance, providing better throughput. The reason is that the XML mapping rules in RFC6020 require
+     * the encoding to emit leaf nodes which participate in a list's key first and in the order in which they are
+     * defined in the key. For JSON, this requirement is completely relaxed and leaves can be ordered in any way we
+     * see fit. The former requires a bit of work: first a lookup for each key and then for each emitted node we need
+     * to check whether it was already emitted.
+     *
+     * @param writer Back-end writer
+     * @param orderKeyLeaves whether the returned instance should be RFC6020 XML compliant.
+     * @return A new instance.
+     */
+    public static NormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer,
+            final boolean orderKeyLeaves) {
+        return orderKeyLeaves ? new OrderedNormalizedNodeWriter(writer) : new NormalizedNodeWriter(writer);
+    }
+
+    /**
+     * Iterate over the provided {@link NormalizedNode} and emit write
+     * events to the encapsulated {@link NormalizedNodeStreamWriter}.
+     *
+     * @param node Node
+     * @return NormalizedNodeWriter this
+     * @throws IOException when thrown from the backing writer.
+     */
+    public NormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
+        if (wasProcessedAsCompositeNode(node)) {
+            return this;
+        }
+
+        if (wasProcessAsSimpleNode(node)) {
+            return this;
+        }
+
+        throw new IllegalStateException("It wasn't possible to serialize node " + node);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        writer.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        writer.flush();
+        writer.close();
+    }
+
+    /**
+     * Emit a best guess of a hint for a particular set of children. It evaluates the
+     * iterable to see if the size can be easily gotten to. If it is, we hint at the
+     * real number of child nodes. Otherwise we emit UNKNOWN_SIZE.
+     *
+     * @param children Child nodes
+     * @return Best estimate of the collection size required to hold all the children.
+     */
+    protected static int childSizeHint(final Iterable<?> children) {
+        return children instanceof Collection ? ((Collection<?>) children).size() : UNKNOWN_SIZE;
+    }
+
+    protected boolean wasProcessAsSimpleNode(final NormalizedNode<?, ?> node) throws IOException {
+        if (node instanceof LeafSetEntryNode) {
+            final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>)node;
+            writer.startLeafSetEntryNode(nodeAsLeafList.getIdentifier());
+            writer.scalarValue(nodeAsLeafList.getValue());
+            writer.endNode();
+            return true;
+        } else if (node instanceof LeafNode) {
+            final LeafNode<?> nodeAsLeaf = (LeafNode<?>)node;
+            writer.startLeafNode(nodeAsLeaf.getIdentifier());
+            writer.scalarValue(nodeAsLeaf.getValue());
+            writer.endNode();
+            return true;
+        } else if (node instanceof AnyxmlNode) {
+            final AnyxmlNode<?> anyxmlNode = (AnyxmlNode<?>)node;
+            final Class<?> model = anyxmlNode.getValueObjectModel();
+            if (writer.startAnyxmlNode(anyxmlNode.getIdentifier(), model)) {
+                final Object value = node.getValue();
+                if (DOMSource.class.isAssignableFrom(model)) {
+                    verify(value instanceof DOMSource, "Inconsistent anyxml node %s", anyxmlNode);
+                    writer.domSourceValue((DOMSource) value);
+                } else {
+                    writer.scalarValue(value);
+                }
+                writer.endNode();
+                return true;
+            }
+
+            LOG.debug("Ignoring unhandled anyxml node {}", anyxmlNode);
+        } else if (node instanceof AnydataNode) {
+            final AnydataNode<?> anydata = (AnydataNode<?>) node;
+            final Class<?> model = anydata.getValueObjectModel();
+            if (writer.startAnydataNode(anydata.getIdentifier(), model)) {
+                writer.scalarValue(anydata.getValue());
+                writer.endNode();
+                return true;
+            }
+
+            LOG.debug("Writer {} does not support anydata in form of {}", writer, model);
+        }
+
+        return false;
+    }
+
+    /**
+     * Emit events for all children and then emit an endNode() event.
+     *
+     * @param children Child iterable
+     * @return True
+     * @throws IOException when the writer reports it
+     */
+    protected boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children) throws IOException {
+        for (final NormalizedNode<?, ?> child : children) {
+            write(child);
+        }
+
+        writer.endNode();
+        return true;
+    }
+
+    protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
+        writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
+        return writeChildren(node.getValue());
+    }
+
+    protected boolean wasProcessedAsCompositeNode(final NormalizedNode<?, ?> node) throws IOException {
+        if (node instanceof ContainerNode) {
+            final ContainerNode n = (ContainerNode) node;
+            writer.startContainerNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof YangModeledAnyXmlNode) {
+            final YangModeledAnyXmlNode n = (YangModeledAnyXmlNode) node;
+            writer.startYangModeledAnyXmlNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof MapEntryNode) {
+            return writeMapEntryNode((MapEntryNode) node);
+        }
+        if (node instanceof UnkeyedListEntryNode) {
+            final UnkeyedListEntryNode n = (UnkeyedListEntryNode) node;
+            writer.startUnkeyedListItem(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof ChoiceNode) {
+            final ChoiceNode n = (ChoiceNode) node;
+            writer.startChoiceNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof AugmentationNode) {
+            final AugmentationNode n = (AugmentationNode) node;
+            writer.startAugmentationNode(n.getIdentifier());
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof UnkeyedListNode) {
+            final UnkeyedListNode n = (UnkeyedListNode) node;
+            writer.startUnkeyedList(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof OrderedMapNode) {
+            final OrderedMapNode n = (OrderedMapNode) node;
+            writer.startOrderedMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof MapNode) {
+            final MapNode n = (MapNode) node;
+            writer.startMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof OrderedLeafSetNode) {
+            final LeafSetNode<?> n = (LeafSetNode<?>) node;
+            writer.startOrderedLeafSet(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof LeafSetNode) {
+            final LeafSetNode<?> n = (LeafSetNode<?>) node;
+            writer.startLeafSet(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+
+        return false;
+    }
+
+    private static final class OrderedNormalizedNodeWriter extends NormalizedNodeWriter {
+        private static final Logger LOG = LoggerFactory.getLogger(OrderedNormalizedNodeWriter.class);
+
+        OrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer) {
+            super(writer);
+        }
+
+        @Override
+        protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
+            final NormalizedNodeStreamWriter nnWriter = getWriter();
+            nnWriter.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
+
+            final Set<QName> qnames = node.getIdentifier().keySet();
+            // Write out all the key children
+            for (final QName qname : qnames) {
+                final Optional<? extends NormalizedNode<?, ?>> child = node.getChild(new NodeIdentifier(qname));
+                if (child.isPresent()) {
+                    write(child.get());
+                } else {
+                    LOG.info("No child for key element {} found", qname);
+                }
+            }
+
+            // Write all the rest
+            return writeChildren(Iterables.filter(node.getValue(), input -> {
+                if (input instanceof AugmentationNode) {
+                    return true;
+                }
+                if (!qnames.contains(input.getNodeType())) {
+                    return true;
+                }
+
+                LOG.debug("Skipping key child {}", input);
+                return false;
+            }));
+        }
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/ReusableStreamReceiver.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/ReusableStreamReceiver.java
new file mode 100644 (file)
index 0000000..ac13ccc
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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 com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Utility marker interface for {@link NormalizedNodeStreamWriter} implementations which can be reused multiple times
+ * and can expose a {@link NormalizedNode} result of each complete streaming use.
+ *
+ * <p>
+ * An example of use would be:
+ * <pre>
+ *   ReusableStreamReceiver writer;
+ *   final NormalizedNode result;
+ *
+ *   try {
+ *       // pipe events into writer:
+ *       writer.startContainer(...);
+ *       ...
+ *
+ *       result = writer.getResult();
+ *   } finally {
+ *       writer.reset();
+ *   }
+ * </pre>
+ *
+ * <p>
+ * Note the writer should always be {@link #reset()} in a {@code finally} block, so that any streaming state is
+ * properly discarded.
+ */
+@Beta
+public interface ReusableStreamReceiver extends NormalizedNodeStreamWriter {
+    /**
+     * Acquire the result of the last streaming session.
+     *
+     * @return Result of streaming.
+     */
+    NormalizedNode<?, ?> getResult();
+
+    /**
+     * Reset this writer to initial state.
+     */
+    void reset();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractAvailableLeafCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractAvailableLeafCandidateNode.java
new file mode 100644 (file)
index 0000000..71ee5b7
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+abstract class AbstractAvailableLeafCandidateNode extends AbstractLeafCandidateNode {
+    AbstractAvailableLeafCandidateNode(final NormalizedNode<?, ?> dataAfter) {
+        super(dataAfter);
+    }
+
+    @Override
+    public final Optional<NormalizedNode<?, ?>> getDataAfter() {
+        return dataOptional();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractDataTreeCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractDataTreeCandidateNode.java
new file mode 100644 (file)
index 0000000..89b1d05
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+abstract class AbstractDataTreeCandidateNode implements DataTreeCandidateNode {
+    private final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?,?>> data;
+
+    AbstractDataTreeCandidateNode(final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data) {
+        this.data = requireNonNull(data);
+    }
+
+    @Override
+    public final PathArgument getIdentifier() {
+        return data.getIdentifier();
+    }
+
+    final @NonNull Optional<NormalizedNode<?, ?>> dataOptional() {
+        return Optional.of(data);
+    }
+
+    final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data() {
+        return data;
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getSimpleName() + "{data = " + this.data + "}";
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractLeafCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractLeafCandidateNode.java
new file mode 100644 (file)
index 0000000..65ee8f8
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+abstract class AbstractLeafCandidateNode implements DataTreeCandidateNode {
+    private final NormalizedNode<?, ?> data;
+
+    AbstractLeafCandidateNode(final NormalizedNode<?, ?> data) {
+        this.data = requireNonNull(data);
+    }
+
+    final @NonNull Optional<NormalizedNode<?, ?>> dataOptional() {
+        return Optional.of(data);
+    }
+
+    @Override
+    public final Collection<DataTreeCandidateNode> getChildNodes() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public final PathArgument getIdentifier() {
+        return data.getIdentifier();
+    }
+
+    @Override
+    public final Optional<DataTreeCandidateNode> getModifiedChild(final PathArgument identifier) {
+        requireNonNull(identifier);
+        return Optional.empty();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractRecursiveCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractRecursiveCandidateNode.java
new file mode 100644 (file)
index 0000000..a98b0fe
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.collect.Collections2;
+import java.util.Collection;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+abstract class AbstractRecursiveCandidateNode extends AbstractDataTreeCandidateNode {
+    AbstractRecursiveCandidateNode(final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data) {
+        super(data);
+    }
+
+    @Override
+    public final Optional<DataTreeCandidateNode> getModifiedChild(final PathArgument identifier) {
+        return data().getChild(identifier).map(this::createChild);
+    }
+
+    @Override
+    public final Collection<DataTreeCandidateNode> getChildNodes() {
+        return Collections2.transform(data().getValue(), this::createChild);
+    }
+
+    abstract DataTreeCandidateNode createContainer(
+            NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> childData);
+
+    abstract DataTreeCandidateNode createLeaf(NormalizedNode<?,?> childData);
+
+    @SuppressWarnings("unchecked")
+    private DataTreeCandidateNode createChild(final NormalizedNode<?, ?> childData) {
+        if (childData instanceof NormalizedNodeContainer) {
+            return createContainer((NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>>) childData);
+        }
+        return createLeaf(childData);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractWriteCandidate.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/AbstractWriteCandidate.java
new file mode 100644 (file)
index 0000000..b85a4eb
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+abstract class AbstractWriteCandidate extends AbstractAvailableLeafCandidateNode {
+    AbstractWriteCandidate(final NormalizedNode<?, ?> dataAfter) {
+        super(dataAfter);
+    }
+
+    @Override
+    public final ModificationType getModificationType() {
+        return ModificationType.WRITE;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/BackendFailedException.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/BackendFailedException.java
new file mode 100644 (file)
index 0000000..e683947
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Exception thrown when the backed of a {@link DataTreeSnapshotCursor}
+ * detects an errors which prevents it from completing the requested operation.
+ */
+@Beta
+public class BackendFailedException extends IllegalStateException {
+    private static final long serialVersionUID = 1L;
+
+    public BackendFailedException(final String message) {
+        super(message);
+    }
+
+    public BackendFailedException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ConflictingModificationAppliedException.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ConflictingModificationAppliedException.java
new file mode 100644 (file)
index 0000000..c14ff17
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Exception thrown when a proposed change fails validation before being
+ * applied into the Data Tree because the Data Tree has been modified
+ * in way that a conflicting node is present.
+ */
+public class ConflictingModificationAppliedException extends DataValidationFailedException {
+    private static final long serialVersionUID = 1L;
+
+    public ConflictingModificationAppliedException(final YangInstanceIdentifier path, final String message,
+            final Throwable cause) {
+        super(path, message, cause);
+    }
+
+    public ConflictingModificationAppliedException(final YangInstanceIdentifier path, final String message) {
+        super(path, message);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/CursorAwareDataTreeModification.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/CursorAwareDataTreeModification.java
new file mode 100644 (file)
index 0000000..3b0b743
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * A {@link DataTreeModification} which allows creation of a {@link DataTreeModificationCursor}.
+ */
+@Beta
+public interface CursorAwareDataTreeModification extends DataTreeModification, CursorAwareDataTreeSnapshot {
+    /**
+     * Create a new {@link DataTreeModificationCursor} at specified path. May fail
+     * if specified path does not exist.
+     *
+     * @param path Path at which the cursor is to be anchored
+     * @return A new cursor, or empty if the path does not exist.
+     * @throws IllegalStateException if there is another cursor currently open,
+     *                               or the modification is already {@link #ready()}.
+     */
+    @Override
+    Optional<? extends DataTreeModificationCursor> openCursor(YangInstanceIdentifier path);
+
+    /**
+     * Create a new {@link DataTreeModificationCursor} at the root of the modification.
+     *
+     * @return A new cursor
+     * @throws IllegalStateException if there is another cursor currently open.
+     */
+    @Override
+    default DataTreeModificationCursor openCursor() {
+        return openCursor(YangInstanceIdentifier.empty()).get();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/CursorAwareDataTreeSnapshot.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/CursorAwareDataTreeSnapshot.java
new file mode 100644 (file)
index 0000000..10a6dfd
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * A {@link DataTreeSnapshot} which allows creation of a {@link DataTreeSnapshotCursor}.
+ */
+@Beta
+public interface CursorAwareDataTreeSnapshot extends DataTreeSnapshot {
+    /**
+     * Create a new {@link DataTreeSnapshotCursor} at specified path. May fail
+     * if specified path does not exist.
+     *
+     * @param path Path at which the cursor is to be anchored
+     * @return A new cursor, or empty if the path does not exist.
+     * @throws IllegalStateException if there is another cursor currently open.
+     */
+    Optional<? extends DataTreeSnapshotCursor> openCursor(@NonNull YangInstanceIdentifier path);
+
+    /**
+     * Create a new {@link DataTreeSnapshotCursor} at the root of the modification.
+     *
+     * @return A new cursor
+     * @throws IllegalStateException if there is another cursor currently open.
+     */
+    default DataTreeSnapshotCursor openCursor() {
+        return openCursor(YangInstanceIdentifier.empty()).get();
+    }
+
+    @Override
+    CursorAwareDataTreeModification newModification();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTree.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTree.java
new file mode 100644 (file)
index 0000000..378b980
--- /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.api.schema.tree;
+
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Interface representing a data tree which can be modified in an MVCC fashion.
+ */
+public interface DataTree extends DataTreeTip, ReadOnlyDataTree {
+    /**
+     * Make the data tree use a new schema context. The context will be used
+     * only by subsequent operations.
+     *
+     * @param newSchemaContext new SchemaContext
+     * @throws IllegalArgumentException if the new context is incompatible
+     * @throws NullPointerException if newSchemaContext is null
+     * @deprecated Prefer {@link #setEffectiveModelContext(EffectiveModelContext)}.
+     */
+    @Deprecated
+    void setSchemaContext(SchemaContext newSchemaContext);
+
+    /**
+     * Make the data tree use a new schema context. The context will be used
+     * only by subsequent operations.
+     *
+     * @param newModelContext new EffectiveModelContext
+     * @throws IllegalArgumentException if the new context is incompatible
+     * @throws NullPointerException if newModelContext is null
+     */
+    void setEffectiveModelContext(EffectiveModelContext newModelContext);
+
+    /**
+     * Commit a data tree candidate.
+     *
+     * @param candidate data tree candidate
+     */
+    void commit(DataTreeCandidate candidate);
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidate.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidate.java
new file mode 100644 (file)
index 0000000..063302c
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * An encapsulation of a validated data tree modification. This candidate
+ * is ready for atomic commit to the datastore. It allows access to before-
+ * and after-state as it will be seen in to subsequent commit. This capture
+ * can be accessed for reference, but cannot be modified and the content
+ * is limited to nodes which were affected by the modification from which
+ * this instance originated.
+ */
+public interface DataTreeCandidate {
+    /**
+     * Get the candidate tree root node.
+     *
+     * @return Candidate tree root node
+     */
+    DataTreeCandidateNode getRootNode();
+
+    /**
+     * Get the candidate tree root path. This is the path of the root node
+     * relative to the root of InstanceIdentifier namespace.
+     *
+     * @return Relative path of the root node
+     */
+    YangInstanceIdentifier getRootPath();
+
+    /**
+     * {@inheritDoc}
+     *
+     * {@link DataTreeCandidate} implementations must not override the default identity hashCode method.
+     */
+    @Override
+    int hashCode();
+
+    /**
+     * {@inheritDoc}
+     *
+     * {@link DataTreeCandidate} implementations must not override the default identity hashCode method, meaning their
+     * equals implementation must result in identity comparison.
+     */
+    @Override
+    boolean equals(Object obj);
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateNode.java
new file mode 100644 (file)
index 0000000..48c2684
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A single node within a {@link DataTreeCandidate}. The nodes are organized in tree hierarchy, reflecting
+ * the modification from which this candidate was created. The node itself exposes the before- and after-image
+ * of the tree restricted to the modified nodes.
+ */
+// FIXME: 5.0.0: Use @NonNullByDefault
+public interface DataTreeCandidateNode {
+    /**
+     * Get the node identifier.
+     *
+     * @return The node identifier.
+     */
+    @NonNull PathArgument getIdentifier();
+
+    /**
+     * Get an unmodifiable collection of modified child nodes. Note that the collection may include
+     * {@link ModificationType#UNMODIFIED} nodes, which the caller is expected to handle as if they were not present.
+     *
+     * @return Unmodifiable collection of modified child nodes.
+     */
+    @NonNull Collection<DataTreeCandidateNode> getChildNodes();
+
+    /**
+     * Returns modified child or empty. Note that this method may return an {@link ModificationType#UNMODIFIED} node
+     * when there is evidence of the node or its parent being involved in modification which has turned out not to
+     * modify the node's contents.
+     *
+     * @param childIdentifier Identifier of child node
+     * @return Modified child or empty.
+     * @throws NullPointerException if {@code childIdentifier} is null
+     */
+    @NonNull Optional<DataTreeCandidateNode> getModifiedChild(PathArgument childIdentifier);
+
+    /**
+     * Return the type of modification this node is undergoing.
+     *
+     * @return Node modification type.
+     */
+    @NonNull ModificationType getModificationType();
+
+    /**
+     * Return the after-image of data corresponding to the node.
+     *
+     * @return Node data as they will be present in the tree after
+     *         the modification is applied.
+     */
+    @NonNull Optional<NormalizedNode<?, ?>> getDataAfter();
+
+    /**
+     * Return the before-image of data corresponding to the node.
+     *
+     * @return Node data as they were present in the tree before
+     *         the modification was applied.
+     */
+    @NonNull Optional<NormalizedNode<?, ?>> getDataBefore();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateNodes.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateNodes.java
new file mode 100644 (file)
index 0000000..6b16365
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+@Beta
+public final class DataTreeCandidateNodes {
+    private DataTreeCandidateNodes() {
+
+    }
+
+    /**
+     * Return an empty {@link DataTreeCandidateNode} identified by specified {@link PathArgument}.
+     *
+     * @param identifier Node identifier
+     * @return An empty DataTreeCandidateNode
+     */
+    public static @NonNull DataTreeCandidateNode empty(final PathArgument identifier) {
+        return new EmptyDataTreeCandidateNode(identifier);
+    }
+
+    /**
+     * Return an unmodified {@link DataTreeCandidateNode} identified by specified {@link NormalizedNode}.
+     *
+     * @param node Unchanged normalized node
+     * @return An empty DataTreeCandidateNode
+     */
+    public static @NonNull DataTreeCandidateNode unmodified(final NormalizedNode<?, ?> node) {
+        if (node instanceof NormalizedNodeContainer) {
+            return new RecursiveUnmodifiedCandidateNode(
+                (NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>>) node);
+        }
+        return new UnmodifiedLeafCandidateNode(node);
+    }
+
+    /**
+     * Return a {@link DataTreeCandidateNode} pretending specified node was written without the data exsting beforehand.
+     *
+     * @param node Unchanged normalized node
+     * @return An empty DataTreeCandidateNode
+     * @throws NullPointerException if {@code node} is null
+     */
+    public static @NonNull DataTreeCandidateNode written(final NormalizedNode<?, ?> node) {
+        return new NormalizedNodeDataTreeCandidateNode(node);
+    }
+
+    /**
+     * Return a collection of {@link DataTreeCandidateNode}s summarizing the changes between the contents of two
+     * {@link NormalizedNodeContainer}s.
+     *
+     * @param oldData Old data container, may be null
+     * @param newData New data container, may be null
+     * @return Collection of changes
+     */
+    public static @NonNull Collection<DataTreeCandidateNode> containerDelta(
+            final @Nullable NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> oldData,
+            final @Nullable NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> newData) {
+        if (newData == null) {
+            return oldData == null ? ImmutableList.of()
+                    : Collections2.transform(oldData.getValue(), DataTreeCandidateNodes::deleteNode);
+        }
+        if (oldData == null) {
+            return Collections2.transform(newData.getValue(), DataTreeCandidateNodes::writeNode);
+        }
+
+        /*
+         * This is slightly inefficient, as it requires N*F(M)+M*F(N) lookup operations, where
+         * F is dependent on the implementation of NormalizedNodeContainer.getChild().
+         *
+         * We build the return collection by iterating over new data and looking each child up
+         * in old data. Based on that we construct replaced/written nodes. We then proceed to
+         * iterate over old data and looking up each child in new data.
+         */
+        final Collection<DataTreeCandidateNode> result = new ArrayList<>();
+        for (NormalizedNode<?, ?> child : newData.getValue()) {
+            final DataTreeCandidateNode node;
+            final Optional<NormalizedNode<?, ?>> maybeOldChild = oldData.getChild(child.getIdentifier());
+
+            if (maybeOldChild.isPresent()) {
+                // This does not find children which have not in fact been modified, as doing that
+                // reliably would require us running a full equals() on the two nodes.
+                node = replaceNode(maybeOldChild.get(), child);
+            } else {
+                node = writeNode(child);
+            }
+
+            result.add(node);
+        }
+
+        // Process removals next, looking into new data to see if we processed it
+        for (NormalizedNode<?, ?> child : oldData.getValue()) {
+            if (newData.getChild(child.getIdentifier()).isEmpty()) {
+                result.add(deleteNode(child));
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Return a collection of {@link DataTreeCandidateNode}s summarizing the change in a child, identified by a
+     * {@link PathArgument}, between two {@link NormalizedNodeContainer}s.
+     *
+     * @param oldData Old data container, may be null
+     * @param newData New data container, may be null
+     * @return A {@link DataTreeCandidateNode} describing the change, or empty if the node is not present
+     */
+    public static @NonNull Optional<DataTreeCandidateNode> containerDelta(
+            final @Nullable NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> oldData,
+            final @Nullable NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> newData,
+            final @NonNull PathArgument child) {
+        final Optional<NormalizedNode<?, ?>> maybeNewChild = getChild(newData, child);
+        final Optional<NormalizedNode<?, ?>> maybeOldChild = getChild(oldData, child);
+        if (maybeOldChild.isPresent()) {
+            final NormalizedNode<?, ?> oldChild = maybeOldChild.get();
+            return Optional.of(maybeNewChild.isPresent() ? replaceNode(oldChild, maybeNewChild.get())
+                    : deleteNode(oldChild));
+        }
+
+        return maybeNewChild.map(DataTreeCandidateNodes::writeNode);
+    }
+
+    /**
+     * Applies the {@code node} to the {@code cursor}, note that if the top node of (@code node} is RootNode
+     * you need to use {@link #applyRootedNodeToCursor(DataTreeModificationCursor, YangInstanceIdentifier,
+     * DataTreeCandidateNode) applyRootedNodeToCursor} method that works with rooted node candidates.
+     *
+     * @param cursor cursor from the modification we want to apply the {@code node} to
+     * @param node candidate tree to apply
+     */
+    public static void applyToCursor(final DataTreeModificationCursor cursor, final DataTreeCandidateNode node) {
+        switch (node.getModificationType()) {
+            case DELETE:
+                cursor.delete(node.getIdentifier());
+                break;
+            case SUBTREE_MODIFIED:
+                cursor.enter(node.getIdentifier());
+                AbstractNodeIterator iterator = new ExitingNodeIterator(null, node.getChildNodes().iterator());
+                do {
+                    iterator = iterator.next(cursor);
+                } while (iterator != null);
+                break;
+            case UNMODIFIED:
+                // No-op
+                break;
+            case WRITE:
+                cursor.write(node.getIdentifier(), node.getDataAfter().get());
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
+        }
+    }
+
+    /**
+     * Applies the {@code node} that is rooted(doesn't have an identifier) in tree A to tree B's {@code cursor}
+     * at location specified by {@code rootPath}.
+     *
+     * @param cursor cursor from the modification we want to apply the {@code node} to
+     * @param rootPath path in the {@code cursor}'s tree we want to apply to candidate to
+     * @param node candidate tree to apply
+     */
+    public static void applyRootedNodeToCursor(final DataTreeModificationCursor cursor,
+            final YangInstanceIdentifier rootPath, final DataTreeCandidateNode node) {
+        switch (node.getModificationType()) {
+            case DELETE:
+                cursor.delete(rootPath.getLastPathArgument());
+                break;
+            case SUBTREE_MODIFIED:
+                cursor.enter(rootPath.getLastPathArgument());
+                AbstractNodeIterator iterator = new ExitingNodeIterator(null, node.getChildNodes().iterator());
+                do {
+                    iterator = iterator.next(cursor);
+                } while (iterator != null);
+                break;
+            case UNMODIFIED:
+                // No-op
+                break;
+            case WRITE:
+                cursor.write(rootPath.getLastPathArgument(), node.getDataAfter().get());
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
+        }
+    }
+
+    public static void applyRootToCursor(final DataTreeModificationCursor cursor, final DataTreeCandidateNode node) {
+        switch (node.getModificationType()) {
+            case DELETE:
+                throw new IllegalArgumentException("Can not delete root.");
+            case WRITE:
+            case SUBTREE_MODIFIED:
+                AbstractNodeIterator iterator = new RootNonExitingIterator(node.getChildNodes().iterator());
+                do {
+                    iterator = iterator.next(cursor);
+                } while (iterator != null);
+                break;
+            case UNMODIFIED:
+                // No-op
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
+        }
+    }
+
+    private static Optional<NormalizedNode<?, ?>> getChild(
+            final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> container,
+                    final PathArgument identifier) {
+        return container == null ? Optional.empty() : container.getChild(identifier);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static @NonNull DataTreeCandidateNode deleteNode(final NormalizedNode<?, ?> data) {
+        if (data instanceof NormalizedNodeContainer) {
+            return new RecursiveDeleteCandidateNode(
+                (NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>>) data);
+        }
+        return new DeleteLeafCandidateNode(data);
+    }
+
+
+    @SuppressWarnings("unchecked")
+    private static @NonNull DataTreeCandidateNode replaceNode(final NormalizedNode<?, ?> oldData,
+            final NormalizedNode<?, ?> newData) {
+        if (oldData instanceof NormalizedNodeContainer) {
+            return new RecursiveReplaceCandidateNode(
+                (NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>>) oldData,
+                (NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>>) newData);
+        }
+        return new ReplaceLeafCandidateNode(oldData, newData);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static @NonNull DataTreeCandidateNode writeNode(final NormalizedNode<?, ?> data) {
+        if (data instanceof NormalizedNodeContainer) {
+            return new RecursiveWriteCandidateNode(
+                (NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>>) data);
+        }
+        return new WriteLeafCandidateNode(data);
+    }
+
+    private abstract static class AbstractNodeIterator {
+        private final Iterator<DataTreeCandidateNode> iterator;
+
+        AbstractNodeIterator(final Iterator<DataTreeCandidateNode> iterator) {
+            this.iterator = requireNonNull(iterator);
+        }
+
+        final AbstractNodeIterator next(final DataTreeModificationCursor cursor) {
+            while (iterator.hasNext()) {
+                final DataTreeCandidateNode node = iterator.next();
+                switch (node.getModificationType()) {
+                    case DELETE:
+                        cursor.delete(node.getIdentifier());
+                        break;
+                    case APPEARED:
+                    case DISAPPEARED:
+                    case SUBTREE_MODIFIED:
+                        final Collection<DataTreeCandidateNode> children = node.getChildNodes();
+                        if (!children.isEmpty()) {
+                            cursor.enter(node.getIdentifier());
+                            return new ExitingNodeIterator(this, children.iterator());
+                        }
+                        break;
+                    case UNMODIFIED:
+                        // No-op
+                        break;
+                    case WRITE:
+                        cursor.write(node.getIdentifier(), node.getDataAfter().get());
+                        break;
+                    default:
+                        throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
+                }
+            }
+            exitNode(cursor);
+            return getParent();
+        }
+
+        abstract @Nullable AbstractNodeIterator getParent();
+
+        abstract void exitNode(DataTreeModificationCursor cursor);
+    }
+
+    private static final class RootNonExitingIterator extends AbstractNodeIterator {
+        RootNonExitingIterator(final Iterator<DataTreeCandidateNode> iterator) {
+            super(iterator);
+        }
+
+        @Override
+        void exitNode(final DataTreeModificationCursor cursor) {
+            // Intentional noop.
+        }
+
+        @Override
+        AbstractNodeIterator getParent() {
+            return null;
+        }
+    }
+
+    private static final class ExitingNodeIterator extends AbstractNodeIterator {
+        private final AbstractNodeIterator parent;
+
+        ExitingNodeIterator(final AbstractNodeIterator parent, final Iterator<DataTreeCandidateNode> iterator) {
+            super(iterator);
+            this.parent = parent;
+        }
+
+        @Override
+        AbstractNodeIterator getParent() {
+            return parent;
+        }
+
+        @Override
+        void exitNode(final DataTreeModificationCursor cursor) {
+            cursor.exit();
+        }
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateTip.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateTip.java
new file mode 100644 (file)
index 0000000..7045a9e
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * A {@link DataTreeCandidate} which is also a {@link DataTreeTip}. This indicates that
+ * a {@link DataTreeModification} can be prepared for commit in sequence after this candidate.
+ * DataTree implementations are encouraged to provide this instead of the basic
+ * {@link DataTreeCandidate}, as it allows users to amortize latency in systems where
+ * a candidate commit needs to be coordinated across distributed parties.
+ */
+@Beta
+public interface DataTreeCandidateTip extends DataTreeCandidate, DataTreeTip {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidates.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidates.java
new file mode 100644 (file)
index 0000000..51521f1
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Iterator;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class holding methods useful when dealing with {@link DataTreeCandidate} instances.
+ */
+@Beta
+public final class DataTreeCandidates {
+    private static final Logger LOG = LoggerFactory.getLogger(DataTreeCandidates.class);
+
+    private DataTreeCandidates() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static @NonNull DataTreeCandidate newDataTreeCandidate(final YangInstanceIdentifier rootPath,
+            final DataTreeCandidateNode rootNode) {
+        return new DefaultDataTreeCandidate(rootPath, rootNode);
+    }
+
+    public static @NonNull DataTreeCandidate fromNormalizedNode(final YangInstanceIdentifier rootPath,
+            final NormalizedNode<?, ?> node) {
+        return new DefaultDataTreeCandidate(rootPath, new NormalizedNodeDataTreeCandidateNode(node));
+    }
+
+    public static void applyToCursor(final DataTreeModificationCursor cursor, final DataTreeCandidate candidate) {
+        DataTreeCandidateNodes.applyToCursor(cursor, candidate.getRootNode());
+    }
+
+    public static void applyToModification(final DataTreeModification modification,
+            final DataTreeCandidate candidate) {
+        if (modification instanceof CursorAwareDataTreeModification) {
+            applyToCursorAwareModification((CursorAwareDataTreeModification) modification, candidate);
+            return;
+        }
+
+        final DataTreeCandidateNode node = candidate.getRootNode();
+        final YangInstanceIdentifier path = candidate.getRootPath();
+        switch (node.getModificationType()) {
+            case DELETE:
+                modification.delete(path);
+                LOG.debug("Modification {} deleted path {}", modification, path);
+                break;
+            case SUBTREE_MODIFIED:
+                LOG.debug("Modification {} modified path {}", modification, path);
+
+                NodeIterator iterator = new NodeIterator(null, path, node.getChildNodes().iterator());
+                do {
+                    iterator = iterator.next(modification);
+                } while (iterator != null);
+                break;
+            case UNMODIFIED:
+                LOG.debug("Modification {} unmodified path {}", modification, path);
+                // No-op
+                break;
+            case WRITE:
+                modification.write(path, node.getDataAfter().get());
+                LOG.debug("Modification {} written path {}", modification, path);
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
+        }
+    }
+
+    private static void applyToCursorAwareModification(final CursorAwareDataTreeModification modification,
+            final DataTreeCandidate candidate) {
+        final YangInstanceIdentifier candidatePath = candidate.getRootPath();
+        if (candidatePath.isEmpty()) {
+            try (DataTreeModificationCursor cursor = modification.openCursor()) {
+                DataTreeCandidateNodes.applyRootToCursor(cursor, candidate.getRootNode());
+            }
+        } else {
+            try (DataTreeModificationCursor cursor = modification.openCursor(candidatePath.getParent()).get()) {
+                DataTreeCandidateNodes.applyRootedNodeToCursor(cursor, candidatePath, candidate.getRootNode());
+            }
+        }
+    }
+
+    private static final class NodeIterator {
+        private final Iterator<DataTreeCandidateNode> iterator;
+        private final YangInstanceIdentifier path;
+        private final NodeIterator parent;
+
+        NodeIterator(final @Nullable NodeIterator parent, final YangInstanceIdentifier path,
+                final Iterator<DataTreeCandidateNode> iterator) {
+            this.iterator = requireNonNull(iterator);
+            this.path = requireNonNull(path);
+            this.parent = parent;
+        }
+
+        NodeIterator next(final DataTreeModification modification) {
+            while (iterator.hasNext()) {
+                final DataTreeCandidateNode node = iterator.next();
+                final YangInstanceIdentifier child = path.node(node.getIdentifier());
+
+                switch (node.getModificationType()) {
+                    case DELETE:
+                        modification.delete(child);
+                        LOG.debug("Modification {} deleted path {}", modification, child);
+                        break;
+                    case APPEARED:
+                    case DISAPPEARED:
+                    case SUBTREE_MODIFIED:
+                        LOG.debug("Modification {} modified path {}", modification, child);
+                        return new NodeIterator(this, child, node.getChildNodes().iterator());
+                    case UNMODIFIED:
+                        LOG.debug("Modification {} unmodified path {}", modification, child);
+                        // No-op
+                        break;
+                    case WRITE:
+                        modification.write(child, node.getDataAfter().get());
+                        LOG.debug("Modification {} written path {}", modification, child);
+                        break;
+                    default:
+                        throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
+                }
+            }
+
+            return parent;
+        }
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeConfiguration.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeConfiguration.java
new file mode 100644 (file)
index 0000000..cfb2928
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * DataTree configuration class.
+ *
+ * <p>
+ * TreeConfig supports currently the following options:
+ * <ul>
+ * <li>treeType</li>
+ * <li>enable/disable unique indexes and unique constraint validation</li>
+ * <li>enable/disable mandatory nodes validation</li>
+ * </ul>
+ *
+ * <p>
+ * TreeConfig can be easily extended in order to support further data tree
+ * configuration options, like following:
+ * <ul>
+ * <li>enable/disable case exclusion validation</li>
+ * <li>enable/disable other indexes</li>
+ * <li>other schema aware validation options</li>
+ * </ul>
+ *
+ * <p>
+ * This can be useful when strict validation is not required or useful for some
+ * reasons.
+ */
+@Beta
+public class DataTreeConfiguration implements Immutable {
+    public static final DataTreeConfiguration DEFAULT_CONFIGURATION = new Builder(TreeType.CONFIGURATION)
+            .setMandatoryNodesValidation(true).build();
+    public static final DataTreeConfiguration DEFAULT_OPERATIONAL = new Builder(TreeType.OPERATIONAL)
+            .setMandatoryNodesValidation(true).build();
+
+    private final @NonNull TreeType treeType;
+    private final @NonNull YangInstanceIdentifier rootPath;
+    private final boolean uniqueIndexes;
+    private final boolean mandatoryNodesValidation;
+
+    DataTreeConfiguration(final TreeType treeType, final YangInstanceIdentifier rootPath, final boolean uniqueIndexes,
+            final boolean mandatoryNodesValidation) {
+        this.treeType = requireNonNull(treeType);
+        this.rootPath = requireNonNull(rootPath);
+        this.uniqueIndexes = uniqueIndexes;
+        this.mandatoryNodesValidation = mandatoryNodesValidation;
+    }
+
+    public @NonNull YangInstanceIdentifier getRootPath() {
+        return rootPath;
+    }
+
+    public @NonNull TreeType getTreeType() {
+        return treeType;
+    }
+
+    public boolean isUniqueIndexEnabled() {
+        return uniqueIndexes;
+    }
+
+    public boolean isMandatoryNodesValidationEnabled() {
+        return mandatoryNodesValidation;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("type", treeType).add("root", rootPath)
+                .add("mandatory", mandatoryNodesValidation)
+                .add("unique", uniqueIndexes).toString();
+    }
+
+    public static DataTreeConfiguration getDefault(final TreeType treeType) {
+        switch (requireNonNull(treeType)) {
+            case CONFIGURATION:
+                return DEFAULT_CONFIGURATION;
+            case OPERATIONAL:
+                return DEFAULT_OPERATIONAL;
+            default:
+                return new DataTreeConfiguration(treeType, YangInstanceIdentifier.empty(), false, true);
+        }
+    }
+
+    public static Builder builder(final TreeType treeType) {
+        return new Builder(treeType);
+    }
+
+    public Builder copyBuilder() {
+        return new Builder(treeType)
+                .setMandatoryNodesValidation(isMandatoryNodesValidationEnabled())
+                .setUniqueIndexes(isUniqueIndexEnabled())
+                .setRootPath(getRootPath());
+    }
+
+    public static class Builder implements org.opendaylight.yangtools.concepts.Builder<DataTreeConfiguration> {
+        private final TreeType treeType;
+        private YangInstanceIdentifier rootPath;
+        private boolean uniqueIndexes;
+        private boolean mandatoryNodesValidation;
+
+        public Builder(final TreeType treeType) {
+            this.treeType = requireNonNull(treeType);
+            this.rootPath = YangInstanceIdentifier.empty();
+        }
+
+        public Builder setUniqueIndexes(final boolean uniqueIndexes) {
+            this.uniqueIndexes = uniqueIndexes;
+            return this;
+        }
+
+        public Builder setMandatoryNodesValidation(final boolean mandatoryNodesValidation) {
+            this.mandatoryNodesValidation = mandatoryNodesValidation;
+            return this;
+        }
+
+        public Builder setRootPath(final YangInstanceIdentifier rootPath) {
+            this.rootPath = rootPath.toOptimized();
+            return this;
+        }
+
+        @Override
+        public DataTreeConfiguration build() {
+            return new DataTreeConfiguration(treeType, rootPath, uniqueIndexes, mandatoryNodesValidation);
+        }
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeFactory.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeFactory.java
new file mode 100644 (file)
index 0000000..f7becfe
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Factory interface for creating data trees.
+ */
+public interface DataTreeFactory {
+    /**
+     * Create a new data tree based on specified configuration, with a best-guess root. Use this method only if you
+     * do not have a corresponding SchemaContext handy. Mandatory nodes whose enforcement point is the root node will
+     * not be enforced even if some are present in the SchemaContext and validation is requested in configuration.
+     *
+     * <p>
+     * Correctness note: this method may not accurately initialize the root node in certain non-root scenarios due to
+     * the impossibility to accurately derive root type from plain YangInstanceIdentifier. Using
+     * {@link #create(DataTreeConfiguration, SchemaContext)} is recommended, as it does not suffer from this
+     * shortcoming.
+     *
+     * @param treeConfig
+     *          Tree configuration.
+     * @return A data tree instance.
+     * @throws NullPointerException if treeConfig is null
+     */
+    DataTree create(DataTreeConfiguration treeConfig);
+
+    /**
+     * Create a new data tree based on specified configuration, with a root node derived from the schema context lookup
+     * of the configuration. Mandatory nodes whose enforcement point is the root node will not be enforced even if some
+     * are present in the SchemaContext and validation is requested in configuration.
+     *
+     * @param treeConfig
+     *          Tree configuration.
+     * @return A data tree instance.
+     * @throws NullPointerException if any of the arguments are null
+     * @throws IllegalArgumentException if tree configuration does not match the SchemaContext, for example by root path
+     *                                  referring to a node which does not exist in the SchemaContext
+     */
+    DataTree create(DataTreeConfiguration treeConfig, SchemaContext initialSchemaContext);
+
+    /**
+     * Create a new data tree based on specified configuration, with the specified node.
+     *
+     * @param treeConfig
+     *          Tree configuration.
+     * @return A data tree instance.
+     * @throws DataValidationFailedException if initial root is not valid according to the schema context
+     * @throws NullPointerException if any of the arguments are null
+     * @throws IllegalArgumentException if a mismatch between the arguments is detected
+     */
+    DataTree create(DataTreeConfiguration treeConfig, SchemaContext initialSchemaContext,
+            NormalizedNodeContainer<?, ?, ?> initialRoot) throws DataValidationFailedException;
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeModification.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeModification.java
new file mode 100644 (file)
index 0000000..685755b
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Class encapsulation of set of modifications to a base tree. This tree is backed
+ * by a read-only snapshot and tracks modifications on top of that. The modification
+ * has the ability to rebase itself to a new snapshot.
+ */
+public interface DataTreeModification extends DataTreeSnapshot {
+    /**
+     * Delete the node at specified path.
+     *
+     * @param path Node path
+     */
+    void delete(YangInstanceIdentifier path);
+
+    /**
+     * Merge the specified data with the currently-present data
+     * at specified path.
+     *
+     * @param path Node path
+     * @param data Data to be merged
+     */
+    void merge(YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+    /**
+     * Replace the data at specified path with supplied data.
+     *
+     * @param path Node path
+     * @param data New node data
+     */
+    void write(YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+    /**
+     * Finish creation of a modification, making it ready for application
+     * to the data tree. Any calls to this object's methods except
+     * {@link #applyToCursor(DataTreeModificationCursor)} will result
+     * in undefined behavior, possibly with an
+     * {@link IllegalStateException} being thrown.
+     */
+    void ready();
+
+    /**
+     * Apply the contents of this modification to a cursor. This can be used
+     * to replicate this modification onto another one. The cursor's position
+     * must match the root of this modification, otherwise performing this
+     * operation will result in undefined behavior.
+     *
+     * @param cursor cursor to which this modification
+     */
+    void applyToCursor(@NonNull DataTreeModificationCursor cursor);
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeModificationCursor.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeModificationCursor.java
new file mode 100644 (file)
index 0000000..964ec83
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Extension to the {@link DataTreeSnapshotCursor} which allows modifying the data tree.
+ * An instance of this interface can be obtained from {@link CursorAwareDataTreeModification}
+ * and modifications made through this interface are staged in the parent modification.
+ */
+@Beta
+public interface DataTreeModificationCursor extends DataTreeSnapshotCursor {
+    /**
+     * Delete the specified child.
+     *
+     * @param child Child identifier
+     * @throws BackendFailedException when implementation-specific errors occurs
+     *                                while servicing the request.
+     */
+    void delete(PathArgument child);
+
+    /**
+     * Merge the specified data with the currently-present data
+     * at specified path.
+     *
+     * @param child Child identifier
+     * @param data Data to be merged
+     * @throws BackendFailedException when implementation-specific errors occurs
+     *                                while servicing the request.
+     */
+    void merge(PathArgument child, NormalizedNode<?, ?> data);
+
+    /**
+     * Replace the data at specified path with supplied data.
+     *
+     * @param child Child identifier
+     * @param data New node data
+     * @throws BackendFailedException when implementation-specific errors occurs
+     *                                while servicing the request.
+     */
+    void write(PathArgument child, NormalizedNode<?, ?> data);
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeSnapshot.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeSnapshot.java
new file mode 100644 (file)
index 0000000..e8c3141
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+/**
+ * Read-only snapshot of a {@link DataTree}. The snapshot is stable and isolated, e.g. data tree changes occurring after
+ * the snapshot has been taken are not visible through the snapshot.
+ */
+public interface DataTreeSnapshot extends SchemaContextProvider {
+    /**
+     * Read a particular node from the snapshot.
+     *
+     * @param path Path of the node
+     * @return Optional result encapsulating the presence and value of the node
+     */
+    Optional<NormalizedNode<?, ?>> readNode(YangInstanceIdentifier path);
+
+    /**
+     * Create a new data tree modification based on this snapshot, using the
+     * specified data application strategy.
+     *
+     * @return A new data tree modification
+     */
+    DataTreeModification newModification();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeSnapshotCursor.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeSnapshotCursor.java
new file mode 100644 (file)
index 0000000..83f9b21
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+/**
+ * A cursor holding a logical position within a {@link DataTreeSnapshot}. It allows operations relative to that
+ * position, as well as moving the position up or down the tree. Implementations are expected to be NOT thread-safe.
+ */
+@Beta
+public interface DataTreeSnapshotCursor extends AutoCloseable {
+    /**
+     * Move the cursor to the specified child of the current position.
+     *
+     * @param child Child identifier
+     * @throws BackendFailedException when an implementation-specific error occurs
+     *                                while servicing the request.
+     * @throws IllegalArgumentException when specified identifier does not identify
+     *                                  a valid child, or if that child is not an
+     *                                  instance of {@link NormalizedNodeContainer}.
+     */
+    void enter(@NonNull PathArgument child);
+
+    /**
+     * Move the cursor to the specified child of the current position. This is
+     * the equivalent of multiple invocations of {@link #enter(PathArgument)},
+     * except the operation is performed all at once.
+     *
+     * @param path Nested child identifier
+     * @throws BackendFailedException when an implementation-specific error occurs
+     *                                while servicing the request.
+     * @throws IllegalArgumentException when specified path does not identify
+     *                                  a valid child, or if that child is not an
+     *                                  instance of {@link NormalizedNodeContainer}.
+     */
+    void enter(@NonNull PathArgument... path);
+
+    /**
+     * Move the cursor to the specified child of the current position. This is
+     * equivalent to {@link #enter(PathArgument...)}, except it takes an {@link Iterable}
+     * argument.
+     *
+     * @param path Nested child identifier
+     * @throws BackendFailedException when an implementation-specific error occurs
+     *                                while servicing the request.
+     * @throws IllegalArgumentException when specified path does not identify
+     *                                  a valid child, or if that child is not an
+     *                                  instance of {@link NormalizedNodeContainer}.
+     */
+    void enter(@NonNull Iterable<PathArgument> path);
+
+    /**
+     * Move the cursor up to the parent of current position. This is equivalent of
+     * invoking <code>exit(1)</code>.
+     *
+     * @throws IllegalStateException when exiting would violate containment, typically
+     *                               by attempting to exit more levels than previously
+     *                               entered.
+     */
+    void exit();
+
+    /**
+     * Move the cursor up by specified amounts of steps from the current position.
+     * This is equivalent of invoking {@link #exit()} multiple times, except the
+     * operation is performed atomically.
+     *
+     * @param depth number of steps to exit
+     * @throws IllegalArgumentException when depth is negative.
+     * @throws IllegalStateException when exiting would violate containment, typically
+     *                               by attempting to exit more levels than previously
+     *                               entered.
+     */
+    void exit(int depth);
+
+    /**
+     * Read a particular node from the snapshot.
+     *
+     * @param child Child identifier
+     * @return Optional result encapsulating the presence and value of the node
+     * @throws BackendFailedException when implementation-specific error occurs while
+     *                                servicing the request.
+     * @throws IllegalArgumentException when specified path does not identify a valid child.
+     */
+    Optional<NormalizedNode<?, ?>> readNode(@NonNull PathArgument child);
+
+    /**
+     * Close this cursor. Attempting any further operations on the cursor will lead
+     * to undefined behavior.
+     */
+    @Override
+    void close();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeTip.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeTip.java
new file mode 100644 (file)
index 0000000..a46563e
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Tip of a data tree instance. It acts as a point to which modifications can be applied.
+ */
+@Beta
+@NonNullByDefault
+public interface DataTreeTip {
+    /**
+     * Validate whether a particular modification can be applied to the data tree.
+     *
+     * @param modification Data tree modification.
+     * @throws DataValidationFailedException If modification data is not valid.
+     * @throws NullPointerException if modification is null
+     * @throws IllegalArgumentException if modification is unrecognized
+     * @throws DataValidationFailedException if modification would result in inconsistent data tree
+     */
+    void validate(DataTreeModification modification) throws DataValidationFailedException;
+
+    /**
+     * Prepare a modification for commit.
+     *
+     * @param modification Data tree modification.
+     * @return candidate data tree
+     * @throws NullPointerException if modification is null
+     * @throws IllegalArgumentException if modification is unrecognized
+     * @throws DataValidationFailedException if modification would result in inconsistent data tree
+     */
+    DataTreeCandidateTip prepare(DataTreeModification modification) throws DataValidationFailedException;
+
+    /**
+     * {@inheritDoc}
+     *
+     * {@link DataTreeTip} implementations must not override the default identity hashCode method.
+     */
+    @Override
+    int hashCode();
+
+    /**
+     * {@inheritDoc}
+     *
+     * {@link DataTreeTip} implementations must not override the default identity hashCode method, meaning their
+     * equals implementation must result in identity comparison.
+     */
+    @Override
+    boolean equals(@Nullable Object obj);
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataValidationFailedException.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataValidationFailedException.java
new file mode 100644 (file)
index 0000000..9d931e2
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Exception thrown when a proposed change fails validation before being applied into the datastore. This can have
+ * multiple reasons, for example the datastore has been concurrently modified such that a conflicting node is present,
+ * or the modification is structurally incorrect.
+ */
+public class DataValidationFailedException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    private final YangInstanceIdentifier path;
+
+    /**
+     * Create a new instance.
+     *
+     * @param path Object path which caused this exception
+     * @param message Specific message describing the failure
+     */
+    public DataValidationFailedException(final YangInstanceIdentifier path, final String message) {
+        this(path, message, null);
+    }
+
+    /**
+     * Create a new instance, initializing the cause.
+     *
+     * @param path Object path which caused this exception
+     * @param message Specific message describing the failure
+     * @param cause Exception which triggered this failure, may be null
+     */
+    public DataValidationFailedException(final YangInstanceIdentifier path, final String message,
+            final Throwable cause) {
+        super(message, cause);
+        this.path = requireNonNull(path);
+    }
+
+    /**
+     * Returns the offending object path.
+     *
+     * @return Path of the offending object
+     */
+    public YangInstanceIdentifier getPath() {
+        return path;
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DefaultDataTreeCandidate.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DefaultDataTreeCandidate.java
new file mode 100644 (file)
index 0000000..90c3a1b
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Default utility implementation of the {@link DataTreeCandidate} contract.
+ */
+final class DefaultDataTreeCandidate implements DataTreeCandidate {
+    private final YangInstanceIdentifier rootPath;
+    private final DataTreeCandidateNode rootNode;
+
+    DefaultDataTreeCandidate(final YangInstanceIdentifier rootPath, final DataTreeCandidateNode rootNode) {
+        this.rootPath = requireNonNull(rootPath);
+        this.rootNode = requireNonNull(rootNode);
+    }
+
+    @Override
+    public DataTreeCandidateNode getRootNode() {
+        return rootNode;
+    }
+
+    @Override
+    public YangInstanceIdentifier getRootPath() {
+        return rootPath;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("rootPath", getRootPath()).add("rootNode", getRootNode())
+            .toString();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DeleteLeafCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DeleteLeafCandidateNode.java
new file mode 100644 (file)
index 0000000..73179c0
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+final class DeleteLeafCandidateNode extends AbstractLeafCandidateNode {
+    DeleteLeafCandidateNode(final NormalizedNode<?, ?> data) {
+        super(data);
+    }
+
+    @Override
+    public ModificationType getModificationType() {
+        return ModificationType.DELETE;
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataAfter() {
+        return Optional.empty();
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataBefore() {
+        return dataOptional();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/EmptyDataTreeCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/EmptyDataTreeCandidateNode.java
new file mode 100644 (file)
index 0000000..a582ecb
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+final class EmptyDataTreeCandidateNode implements DataTreeCandidateNode {
+    private final PathArgument identifier;
+
+    EmptyDataTreeCandidateNode(final PathArgument identifier) {
+        this.identifier = requireNonNull(identifier, "Identifier should not be null");
+    }
+
+    @Override
+    public PathArgument getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public Collection<DataTreeCandidateNode> getChildNodes() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public Optional<DataTreeCandidateNode> getModifiedChild(final PathArgument childIdentifier) {
+        return Optional.empty();
+    }
+
+    @Override
+    public ModificationType getModificationType() {
+        return ModificationType.UNMODIFIED;
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataAfter() {
+        return Optional.empty();
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataBefore() {
+        return Optional.empty();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/IncorrectDataStructureException.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/IncorrectDataStructureException.java
new file mode 100644 (file)
index 0000000..479f632
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Exception thrown when a proposed change fails validation before being applied into the datastore
+ * because of incorrect structure of user supplied data.
+ */
+public class IncorrectDataStructureException extends DataValidationFailedException {
+    private static final long serialVersionUID = 1L;
+
+    public IncorrectDataStructureException(final YangInstanceIdentifier path, final String message,
+            final Throwable cause) {
+        super(path, message, cause);
+    }
+
+    public IncorrectDataStructureException(final YangInstanceIdentifier path, final String message) {
+        super(path, message);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ModificationType.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ModificationType.java
new file mode 100644 (file)
index 0000000..de4be25
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+/**
+ * Enumeration of all possible node modification states. These are used in data tree modification context to quickly
+ * assess what sort of modification the node is undergoing.
+ */
+public enum ModificationType {
+    /**
+     * Node is currently unmodified.
+     */
+    UNMODIFIED,
+
+    /**
+     * A child node, either direct or indirect, has been modified. This means
+     * that the data representation of this node has potentially changed.
+     */
+    SUBTREE_MODIFIED,
+
+    /**
+     * This node has been placed into the tree, potentially completely replacing
+     * pre-existing contents.
+     */
+    WRITE,
+
+    /**
+     * This node has been deleted along with any of its child nodes.
+     */
+    DELETE,
+
+    /**
+     * This node has appeared because it is implied by one of its children. This type is usually produced when a
+     * structural container is created to host some leaf entries. It does not have an associated before-image.
+     *
+     * <p>
+     * Its semantics is a combination of SUBTREE_MODIFIED and WRITE, depending on which context it is being interpreted.
+     *
+     * <p>
+     * Users who track the value of the node can treat it as a WRITE. Users transforming a {@link DataTreeCandidate} to
+     * operations on a {@link DataTreeModification} should interpret it as a SUBTREE_MODIFIED and examine its children.
+     * This is needed to correctly deal with concurrent operations on the nodes children, as issuing a write on the
+     * DataTreeModification could end up removing any leaves which have not been present at the DataTree which emitted
+     * this event.
+     */
+    APPEARED,
+
+    /**
+     * This node has disappeared because it is no longer implied by any children. This type is usually produced when a
+     * structural container is removed because it has become empty. It does not have an associated after-image.
+     *
+     * <p>
+     * Its semantics is a combination of SUBTREE_MODIFIED and DELETE, depending on which context it is being
+     * interpreted. Users who track the value of the node can treat it as a DELETE, as the container has disappeared.
+     * Users transforming a {@link DataTreeCandidate} to operations on a {@link DataTreeModification} should interpret
+     * it as a SUBTREE_MODIFIED and examine its children.
+     *
+     * <p>
+     * This is needed to correctly deal with concurrent operations on the nodes children, as issuing a delete on the
+     * DataTreeModification would end up removing any leaves which have not been present at the DataTree which emitted
+     * this event.
+     */
+    DISAPPEARED,
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ModifiedNodeDoesNotExistException.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ModifiedNodeDoesNotExistException.java
new file mode 100644 (file)
index 0000000..951463e
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Exception thrown when a proposed change fails validation before being
+ * applied into the Data Tree because tree node which child nodes are
+ * modified or written did not exist when transaction started
+ * and still does not exists when transaction is processed.
+ *
+ * <p>
+ * Note if node existed in first place and was removed by other transaction,
+ * thrown exception should be {@link ConflictingModificationAppliedException}.
+ */
+public class ModifiedNodeDoesNotExistException extends DataValidationFailedException {
+    private static final long serialVersionUID = 1L;
+
+    public ModifiedNodeDoesNotExistException(final YangInstanceIdentifier path, final String message,
+            final Throwable cause) {
+        super(path, message, cause);
+    }
+
+    public ModifiedNodeDoesNotExistException(final YangInstanceIdentifier path, final String message) {
+        super(path, message);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/NormalizedNodeDataTreeCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/NormalizedNodeDataTreeCandidateNode.java
new file mode 100644 (file)
index 0000000..40fb660
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+/**
+ * Utility implementation of {@link DataTreeCandidateNode} which acts as if
+ * the {@link NormalizedNode} passed to it at creation time were freshly written.
+ */
+final class NormalizedNodeDataTreeCandidateNode implements DataTreeCandidateNode {
+    private final NormalizedNode<?, ?> data;
+
+    /**
+     * Create a new instance backed by supplied data.
+     *
+     * @param data Backing {@link NormalizedNode} data.
+     */
+    NormalizedNodeDataTreeCandidateNode(final @NonNull NormalizedNode<?, ?> data) {
+        this.data = requireNonNull(data);
+    }
+
+    @Override
+    public PathArgument getIdentifier() {
+        return data.getIdentifier();
+    }
+
+    @Override
+    public Collection<DataTreeCandidateNode> getChildNodes() {
+        if (data instanceof NormalizedNodeContainer) {
+            return Collections2.transform(((NormalizedNodeContainer<?, ?, ?>) data).getValue(),
+                input -> input == null ? null : new NormalizedNodeDataTreeCandidateNode(input));
+        }
+        return ImmutableList.of();
+    }
+
+    @Override
+    public Optional<DataTreeCandidateNode> getModifiedChild(final PathArgument childIdentifier) {
+        if (data instanceof NormalizedNodeContainer) {
+            @SuppressWarnings({ "rawtypes", "unchecked" })
+            final Optional<? extends NormalizedNode<?, ?>> child =
+                ((NormalizedNodeContainer)data).getChild(childIdentifier);
+            return child.map(NormalizedNodeDataTreeCandidateNode::new);
+        }
+        return Optional.empty();
+    }
+
+    @Override
+    public ModificationType getModificationType() {
+        return ModificationType.WRITE;
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataAfter() {
+        return Optional.of(data);
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataBefore() {
+        return Optional.empty();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ReadOnlyDataTree.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ReadOnlyDataTree.java
new file mode 100644 (file)
index 0000000..afe560b
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * A read-only view of a {@link DataTree}. This provides access to MVCC access methods, but unlike {@link DataTree},
+ * it does not expose methods to affect internal state.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public interface ReadOnlyDataTree {
+    /**
+     * Get the root path of this data tree.
+     *
+     * @return The tree's root path.
+     */
+    YangInstanceIdentifier getRootPath();
+
+    /**
+     * Take a read-only point-in-time snapshot of the tree.
+     *
+     * @return Data tree snapshot.
+     */
+    DataTreeSnapshot takeSnapshot();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RecursiveDeleteCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RecursiveDeleteCandidateNode.java
new file mode 100644 (file)
index 0000000..a36ef91
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+final class RecursiveDeleteCandidateNode extends AbstractRecursiveCandidateNode {
+    RecursiveDeleteCandidateNode(final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data) {
+        super(data);
+    }
+
+    @Override
+    public ModificationType getModificationType() {
+        return ModificationType.DELETE;
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataAfter() {
+        return Optional.empty();
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataBefore() {
+        return dataOptional();
+    }
+
+    @Override
+    DataTreeCandidateNode createContainer(
+            final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> childData) {
+        return new RecursiveDeleteCandidateNode(childData);
+    }
+
+    @Override
+    DataTreeCandidateNode createLeaf(final NormalizedNode<?, ?> childData) {
+        return new DeleteLeafCandidateNode(childData);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RecursiveReplaceCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RecursiveReplaceCandidateNode.java
new file mode 100644 (file)
index 0000000..4330893
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+final class RecursiveReplaceCandidateNode extends AbstractDataTreeCandidateNode {
+    private final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> oldData;
+
+    RecursiveReplaceCandidateNode(final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> oldData,
+            final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> newData) {
+        super(newData);
+        this.oldData = requireNonNull(oldData);
+    }
+
+    @Override
+    public ModificationType getModificationType() {
+        return ModificationType.WRITE;
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataAfter() {
+        return dataOptional();
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataBefore() {
+        return Optional.of(oldData);
+    }
+
+    @Override
+    public Optional<DataTreeCandidateNode> getModifiedChild(final PathArgument identifier) {
+        return DataTreeCandidateNodes.containerDelta(oldData, data(), identifier);
+    }
+
+    @Override
+    public Collection<DataTreeCandidateNode> getChildNodes() {
+        return DataTreeCandidateNodes.containerDelta(oldData, data());
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RecursiveUnmodifiedCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RecursiveUnmodifiedCandidateNode.java
new file mode 100644 (file)
index 0000000..d3f5bfe
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+final class RecursiveUnmodifiedCandidateNode extends AbstractRecursiveCandidateNode {
+    RecursiveUnmodifiedCandidateNode(final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data) {
+        super(data);
+    }
+
+    @Override
+    public ModificationType getModificationType() {
+        return ModificationType.UNMODIFIED;
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataAfter() {
+        return dataOptional();
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataBefore() {
+        return dataOptional();
+    }
+
+    @Override
+    DataTreeCandidateNode createContainer(
+            final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> childData) {
+        return new RecursiveUnmodifiedCandidateNode(childData);
+    }
+
+    @Override
+    DataTreeCandidateNode createLeaf(final NormalizedNode<?, ?> childData) {
+        return new UnmodifiedLeafCandidateNode(childData);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RecursiveWriteCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RecursiveWriteCandidateNode.java
new file mode 100644 (file)
index 0000000..0cd155c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+final class RecursiveWriteCandidateNode extends AbstractRecursiveCandidateNode {
+    RecursiveWriteCandidateNode(final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data) {
+        super(data);
+    }
+
+    @Override
+    public ModificationType getModificationType() {
+        return ModificationType.WRITE;
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataAfter() {
+        return dataOptional();
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataBefore() {
+        return Optional.empty();
+    }
+
+    @Override
+    DataTreeCandidateNode createContainer(
+            final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> childData) {
+        return new RecursiveWriteCandidateNode(childData);
+    }
+
+    @Override
+    DataTreeCandidateNode createLeaf(final NormalizedNode<?, ?> childData) {
+        return new WriteLeafCandidateNode(childData);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ReplaceLeafCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/ReplaceLeafCandidateNode.java
new file mode 100644 (file)
index 0000000..6804264
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+final class ReplaceLeafCandidateNode extends AbstractWriteCandidate {
+    private final NormalizedNode<?, ?> oldData;
+
+    ReplaceLeafCandidateNode(final NormalizedNode<?, ?> oldData, final NormalizedNode<?, ?> newData) {
+        super(newData);
+        this.oldData = requireNonNull(oldData);
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataBefore() {
+        return Optional.of(oldData);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RequiredElementCountException.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/RequiredElementCountException.java
new file mode 100644 (file)
index 0000000..252aef6
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+import java.util.OptionalInt;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Exception thrown when {@code min-elements} or {@code max-element} statement restrictions are violated.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public final class RequiredElementCountException extends DataValidationFailedException {
+    private static final long serialVersionUID = 1L;
+
+    private final int actualCount;
+    private final int minimumCount;
+    private final int maximumCount;
+
+    public RequiredElementCountException(final YangInstanceIdentifier path, final int actualCount,
+            final int minimumCount, final int maximumCount, final String message) {
+        super(path, message);
+        this.minimumCount = minimumCount;
+        this.maximumCount = maximumCount;
+        this.actualCount = actualCount;
+    }
+
+    public RequiredElementCountException(final YangInstanceIdentifier path, final int actualCount,
+            final int minimumCount, final int maximumCount, final String format, final Object... args) {
+        this(path, actualCount, minimumCount, maximumCount, String.format(format, args));
+    }
+
+    public OptionalInt getMinimumCount() {
+        return minimumCount == 0 ? OptionalInt.empty() : OptionalInt.of(minimumCount);
+    }
+
+    public OptionalInt getMaximumCount() {
+        return maximumCount == Integer.MAX_VALUE ? OptionalInt.empty() : OptionalInt.of(maximumCount);
+    }
+
+    public int getActualCount() {
+        return actualCount;
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/StoreTreeNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/StoreTreeNode.java
new file mode 100644 (file)
index 0000000..b163c83
--- /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.data.api.schema.tree;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * A tree node which has references to its child leaves. These are typically internal non-data leaves, such as
+ * containers, lists, etc.
+ *
+ * @param <C> Final node type
+ */
+public interface StoreTreeNode<C extends StoreTreeNode<C>> {
+    /**
+     * Returns a direct child of the node.
+     *
+     * @param child Identifier of child
+     * @return Optional with node if the child is existing, {@link Optional#empty()} otherwise.
+     * @throws NullPointerException when {@code child} is null
+     */
+    @NonNull Optional<? extends C> getChild(PathArgument child);
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/StoreTreeNodes.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/StoreTreeNodes.java
new file mode 100644 (file)
index 0000000..bf0437e
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static com.google.common.base.Verify.verify;
+
+import com.google.common.annotations.Beta;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.function.Predicate;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * A set of utility methods for interacting with {@link StoreTreeNode} objects.
+ */
+@Beta
+public final class StoreTreeNodes {
+    private StoreTreeNodes() {
+        throw new UnsupportedOperationException("Utility class should not be instantiated");
+    }
+
+    /**
+     * Finds a node in tree.
+     *
+     * @param <T>
+     *          Store tree node type.
+     * @param tree Data Tree
+     * @param path Path to the node
+     * @return Optional with node if the node is present in tree, {@link Optional#empty()} otherwise.
+     */
+    public static <T extends StoreTreeNode<T>> Optional<? extends T> findNode(final T tree,
+            final YangInstanceIdentifier path) {
+        Optional<? extends T> current = Optional.of(tree);
+        Iterator<PathArgument> pathIter = path.getPathArguments().iterator();
+        while (current.isPresent() && pathIter.hasNext()) {
+            current = current.get().getChild(pathIter.next());
+        }
+        return current;
+    }
+
+    public static <T extends StoreTreeNode<T>> T findNodeChecked(final T tree, final YangInstanceIdentifier path) {
+        T current = tree;
+
+        int depth = 1;
+        for (PathArgument pathArg : path.getPathArguments()) {
+            Optional<? extends T> potential = current.getChild(pathArg);
+            if (potential.isEmpty()) {
+                throw new IllegalArgumentException(String.format("Child %s is not present in tree.",
+                        path.getAncestor(depth)));
+            }
+            current = potential.get();
+            ++depth;
+        }
+        return current;
+    }
+
+    /**
+     * Finds a node or closest parent in the tree.
+     *
+     * @param <T>
+     *          Store tree node type.
+     * @param tree Data Tree
+     * @param path Path to the node
+     * @return Map.Entry Entry with key which is path to closest parent and value is parent node.
+     */
+    public static <T extends StoreTreeNode<T>> Entry<YangInstanceIdentifier, T> findClosest(final T tree,
+            final YangInstanceIdentifier path) {
+        return findClosestsOrFirstMatch(tree, path, input -> false);
+    }
+
+    public static <T extends StoreTreeNode<T>> Entry<YangInstanceIdentifier, T> findClosestsOrFirstMatch(final T tree,
+            final YangInstanceIdentifier path, final Predicate<T> predicate) {
+        Optional<? extends T> parent = Optional.of(tree);
+        Optional<? extends T> current = Optional.of(tree);
+
+        int nesting = 0;
+        Iterator<PathArgument> pathIter = path.getPathArguments().iterator();
+        while (current.isPresent() && pathIter.hasNext() && !predicate.test(current.get())) {
+            parent = current;
+            current = current.get().getChild(pathIter.next());
+            nesting++;
+        }
+        if (current.isPresent()) {
+            final YangInstanceIdentifier currentPath = path.getAncestor(nesting);
+            return new SimpleImmutableEntry<>(currentPath, current.get());
+        }
+
+        /*
+         * Subtracting 1 from nesting level at this point is safe, because we
+         * cannot reach here with nesting == 0: that would mean the above check
+         * for current.isPresent() failed, which it cannot, as current is always
+         * present. At any rate we verify state just to be on the safe side.
+         */
+        verify(nesting > 0);
+        return new SimpleImmutableEntry<>(path.getAncestor(nesting - 1), parent.get());
+    }
+
+    public static <T extends StoreTreeNode<T>> Optional<? extends T> getChild(final Optional<T> parent,
+            final PathArgument child) {
+        if (parent.isPresent()) {
+            return parent.get().getChild(child);
+        }
+        return Optional.empty();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/SynchronizedDataTreeModification.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/SynchronizedDataTreeModification.java
new file mode 100644 (file)
index 0000000..a676074
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A {@link DataTreeModification} implementation which delegates all calls to
+ * another instance, making sure no method is being invoked from multiple threads
+ * concurrently.
+ */
+public final class SynchronizedDataTreeModification implements DataTreeModification {
+    private final DataTreeModification delegate;
+
+    private SynchronizedDataTreeModification(final DataTreeModification delegate) {
+        this.delegate = requireNonNull(delegate);
+    }
+
+    public static DataTreeModification create(final DataTreeModification delegate) {
+        return new SynchronizedDataTreeModification(delegate);
+    }
+
+    @Override
+    public synchronized Optional<NormalizedNode<?, ?>> readNode(final YangInstanceIdentifier path) {
+        return delegate.readNode(path);
+    }
+
+    @Override
+    public synchronized DataTreeModification newModification() {
+        return delegate.newModification();
+    }
+
+    @Override
+    public synchronized void delete(final YangInstanceIdentifier path) {
+        delegate.delete(path);
+    }
+
+    @Override
+    public synchronized void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        delegate.merge(path, data);
+    }
+
+    @Override
+    public synchronized void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        delegate.write(path, data);
+    }
+
+    @Override
+    public synchronized void ready() {
+        delegate.ready();
+    }
+
+    @Override
+    public synchronized void applyToCursor(final DataTreeModificationCursor cursor) {
+        delegate.applyToCursor(cursor);
+    }
+
+    @Override
+    public synchronized SchemaContext getSchemaContext() {
+        return delegate.getSchemaContext();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/TreeType.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/TreeType.java
new file mode 100644 (file)
index 0000000..28ddb19
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * {@link DataTree} type, specifying which YANG modeled content is valid with a data tree. This reflects
+ * <a href="https://tools.ietf.org/html/rfc6020#section-7.21.1">RFC6020</a>/
+ * <a href="https://tools.ietf.org/html/rfc6020#section-7.19.1">RFC7950</a> data combinations based on {@code config}
+ * statement.
+ */
+// FIXME: 5.0.0: Consider defining a 'config false'-only type
+// FIXME: 5.0.0: Consider renaming this enum
+@Beta
+public enum TreeType {
+    /**
+     * Only {@code config true} nodes are allowed. This corresponds, but is not limited, to @{code candidate},
+     * {@code startup} and {@code running} data stores defined in
+     * <a href="https://tools.ietf.org/html/rfc8342#section-4.1">RFC8342 section 4.1</a> as well as {@code intended}
+     * data store defined in <a href="https://tools.ietf.org/html/rfc8342#section-5">RFC8342 section 4.1</a>.
+     */
+    CONFIGURATION,
+    /**
+     * Only {@code config true} and {@code config false} nodes are allowed. This corresponds, but is not limited, to
+     * {@code operational state} defined in
+     * <a href="https://tools.ietf.org/html/rfc8342#section-4.1">RFC8342 section 4.1</a> as well as {@code operational}
+     * data store defined in <a href="https://tools.ietf.org/html/rfc8342#section-5">RFC8342 section 4.1</a>.
+     */
+    OPERATIONAL,
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/UnmodifiedLeafCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/UnmodifiedLeafCandidateNode.java
new file mode 100644 (file)
index 0000000..40ed176
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+final class UnmodifiedLeafCandidateNode extends AbstractAvailableLeafCandidateNode {
+    UnmodifiedLeafCandidateNode(final NormalizedNode<?, ?> data) {
+        super(data);
+    }
+
+    @Override
+    public ModificationType getModificationType() {
+        return ModificationType.UNMODIFIED;
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataBefore() {
+        return dataOptional();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/WriteLeafCandidateNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/WriteLeafCandidateNode.java
new file mode 100644 (file)
index 0000000..7f5e020
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+final class WriteLeafCandidateNode extends AbstractWriteCandidate {
+    WriteLeafCandidateNode(final NormalizedNode<?, ?> data) {
+        super(data);
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> getDataBefore() {
+        return Optional.empty();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/AbstractContainerNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/AbstractContainerNode.java
new file mode 100644 (file)
index 0000000..74e5746
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+/**
+ * A TreeNode capable of holding child nodes. The fact that any of the children
+ * changed is tracked by the subtree version.
+ */
+abstract class AbstractContainerNode extends AbstractTreeNode {
+    protected AbstractContainerNode(final NormalizedNode<?, ?> data, final Version version) {
+        super(data, version);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> castData() {
+        return (NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>>) getData();
+    }
+
+    protected final Optional<TreeNode> getChildFromData(final PathArgument childId) {
+        // We do not cache the instantiated node as it is dirt cheap
+        return Optional.ofNullable(getChildFromData(castData(), childId, getVersion()));
+    }
+
+    static TreeNode getChildFromData(final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data,
+            final PathArgument childId, final Version version) {
+        final Optional<NormalizedNode<?, ?>> child = data.getChild(childId);
+        return child.isPresent() ? TreeNodeFactory.createTreeNode(child.get(), version) : null;
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/AbstractModifiedContainerNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/AbstractModifiedContainerNode.java
new file mode 100644 (file)
index 0000000..df4d058
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.Map;
+import org.opendaylight.yangtools.util.MapAdaptor;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A container node which has been modified. It tracks the subtree version and all modified children.
+ */
+abstract class AbstractModifiedContainerNode extends AbstractContainerNode {
+    private final Map<PathArgument, TreeNode> children;
+    private final Version subtreeVersion;
+
+    protected AbstractModifiedContainerNode(final NormalizedNode<?, ?> data, final Version version,
+            final Map<PathArgument, TreeNode> children, final Version subtreeVersion) {
+        super(data, version);
+        this.subtreeVersion = requireNonNull(subtreeVersion);
+        this.children = requireNonNull(children);
+    }
+
+    protected final TreeNode getModifiedChild(final PathArgument childId) {
+        return children.get(childId);
+    }
+
+    protected final Map<PathArgument, TreeNode> snapshotChildren() {
+        return MapAdaptor.getDefaultInstance().takeSnapshot(children);
+    }
+
+    @Override
+    public final Version getSubtreeVersion() {
+        return subtreeVersion;
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("subtreeVersion", subtreeVersion).add("children", children);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/AbstractMutableContainerNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/AbstractMutableContainerNode.java
new file mode 100644 (file)
index 0000000..3106e36
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import java.util.Map;
+import org.opendaylight.yangtools.util.MapAdaptor;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+/**
+ * Abstract base for container-based {@link MutableTreeNode}s. It tracks modified nodes in a map and deals with
+ * correctly implementing {@link #seal()}.
+ */
+abstract class AbstractMutableContainerNode implements MutableTreeNode {
+    private final Version version;
+    private Map<PathArgument, TreeNode> children;
+    private NormalizedNode<?, ?> data;
+    private Version subtreeVersion;
+
+    protected AbstractMutableContainerNode(final AbstractContainerNode parent,
+            final Map<PathArgument, TreeNode> children) {
+        this.data = parent.getData();
+        this.version = parent.getVersion();
+        this.subtreeVersion = parent.getSubtreeVersion();
+        this.children = requireNonNull(children);
+    }
+
+    protected final Version getVersion() {
+        return version;
+    }
+
+    protected final TreeNode getModifiedChild(final PathArgument child) {
+        return children.get(child);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> getData() {
+        return (NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>>) data;
+    }
+
+    @Override
+    public final void setSubtreeVersion(final Version subtreeVersion) {
+        this.subtreeVersion = requireNonNull(subtreeVersion);
+    }
+
+    @Override
+    public final void addChild(final TreeNode child) {
+        children.put(child.getIdentifier(), child);
+    }
+
+    @Override
+    public final void removeChild(final PathArgument id) {
+        children.remove(requireNonNull(id));
+    }
+
+    @Override
+    public final void setData(final NormalizedNode<?, ?> data) {
+        this.data = requireNonNull(data);
+    }
+
+    @Override
+    public final TreeNode seal() {
+        final TreeNode ret;
+
+        /*
+         * Decide which implementation:
+         *
+         * => version equals subtree version, this node has not been updated since its creation
+         * => children.size() equals data child size, this node has been completely materialized and further lookups
+         *    into data will not happen,
+         * => more materialization can happen
+         */
+        if (!version.equals(subtreeVersion)) {
+            final Map<PathArgument, TreeNode> newChildren = MapAdaptor.getDefaultInstance().optimize(children);
+            final int dataSize = getData().getValue().size();
+            if (dataSize != newChildren.size()) {
+                verify(dataSize > newChildren.size(), "Detected %s modified children, data has only %s",
+                    newChildren.size(), dataSize);
+                ret = new LazyContainerNode(data, version, newChildren, subtreeVersion);
+            } else {
+                ret = new MaterializedContainerNode(data, version, newChildren, subtreeVersion);
+            }
+        } else {
+            ret = new SimpleContainerNode(data, version);
+        }
+
+        // This forces a NPE if this class is accessed again. Better than corruption.
+        children = null;
+        return ret;
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/AbstractTreeNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/AbstractTreeNode.java
new file mode 100644 (file)
index 0000000..4d463c5
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A very basic data tree node. Contains some versioned data.
+ */
+@NonNullByDefault
+abstract class AbstractTreeNode implements TreeNode {
+    private final NormalizedNode<?, ?> data;
+    private final Version version;
+
+    protected AbstractTreeNode(final NormalizedNode<?, ?> data, final Version version) {
+        this.data = requireNonNull(data);
+        this.version = requireNonNull(version);
+    }
+
+    @Override
+    public final PathArgument getIdentifier() {
+        return data.getIdentifier();
+    }
+
+    @Override
+    public final Version getVersion() {
+        return version;
+    }
+
+    @Override
+    public final NormalizedNode<?, ?> getData() {
+        return data;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).add("version", version)).toString();
+    }
+
+    protected abstract ToStringHelper addToStringAttributes(ToStringHelper helper);
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/LazyContainerNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/LazyContainerNode.java
new file mode 100644 (file)
index 0000000..c576df5
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.Collections2;
+import java.util.Map;
+import java.util.Optional;
+import org.opendaylight.yangtools.util.MapAdaptor;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Lazily-materialized container node. Any new/modified children are tracked in a map. This map is consulted before
+ * instantiating a child node from data node. Resulting node is not cached.
+ */
+final class LazyContainerNode extends AbstractModifiedContainerNode {
+    LazyContainerNode(final NormalizedNode<?, ?> data, final Version version, final Version subtreeVersion) {
+        this(data, version, MapAdaptor.getDefaultInstance().initialSnapshot(1), subtreeVersion);
+    }
+
+    LazyContainerNode(final NormalizedNode<?, ?> data, final Version version,
+            final Map<PathArgument, TreeNode> children, final Version subtreeVersion) {
+        super(data, version, children, subtreeVersion);
+    }
+
+    @Override
+    public MutableTreeNode mutable() {
+        final Map<PathArgument, TreeNode> snapshot = snapshotChildren();
+        if (snapshot.size() == castData().getValue().size()) {
+            return new MaterializedMutableContainerNode(this, snapshot);
+        }
+
+        return new LazyMutableContainerNode(this, snapshot);
+    }
+
+    @Override
+    public Optional<TreeNode> getChild(final PathArgument childId) {
+        final TreeNode modified = getModifiedChild(childId);
+        return modified == null ? getChildFromData(childId) : Optional.of(modified);
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        // Modified children add added by superclass. Here we filter the other children.
+        return super.addToStringAttributes(helper).add("untouched", Collections2.filter(castData().getValue(),
+            input -> getModifiedChild(input.getIdentifier()) == null));
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/LazyMutableContainerNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/LazyMutableContainerNode.java
new file mode 100644 (file)
index 0000000..437edee
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import java.util.Map;
+import java.util.Optional;
+import org.opendaylight.yangtools.util.MapAdaptor;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * A container node which may need further materialization. Materialized nodes are tracked in a map, which is consulted
+ * before creating a new child node from backing data. Since the child is immutable, we do not have to track it.
+ */
+final class LazyMutableContainerNode extends AbstractMutableContainerNode {
+    LazyMutableContainerNode(final AbstractContainerNode parent) {
+        this(parent, MapAdaptor.getDefaultInstance().initialSnapshot(1));
+    }
+
+    LazyMutableContainerNode(final AbstractContainerNode parent, final Map<PathArgument, TreeNode> children) {
+        super(parent, children);
+    }
+
+    @Override
+    public Optional<TreeNode> getChild(final PathArgument childId) {
+        final TreeNode modified = getModifiedChild(childId);
+        if (modified != null) {
+            return Optional.of(modified);
+        }
+
+        return Optional.ofNullable(AbstractContainerNode.getChildFromData(getData(), childId, getVersion()));
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/MaterializedContainerNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/MaterializedContainerNode.java
new file mode 100644 (file)
index 0000000..7ce74db
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import java.util.Map;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A fully-modified node -- we know we have all children, so it performs lookups only.
+ */
+final class MaterializedContainerNode extends AbstractModifiedContainerNode {
+    protected MaterializedContainerNode(final NormalizedNode<?, ?> data, final Version version,
+            final Map<PathArgument, TreeNode> children, final Version subtreeVersion) {
+        super(data, version, children, subtreeVersion);
+    }
+
+    @Override
+    public Optional<TreeNode> getChild(final PathArgument childId) {
+        return Optional.ofNullable(getModifiedChild(childId));
+    }
+
+    @Override
+    public MutableTreeNode mutable() {
+        return new MaterializedMutableContainerNode(this, snapshotChildren());
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/MaterializedMutableContainerNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/MaterializedMutableContainerNode.java
new file mode 100644 (file)
index 0000000..c787e47
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import java.util.Map;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+final class MaterializedMutableContainerNode extends AbstractMutableContainerNode {
+    MaterializedMutableContainerNode(final AbstractContainerNode parent, final Map<PathArgument, TreeNode> children) {
+        super(parent, children);
+    }
+
+    @Override
+    public Optional<TreeNode> getChild(final PathArgument child) {
+        return Optional.ofNullable(getModifiedChild(child));
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/MutableTreeNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/MutableTreeNode.java
new file mode 100644 (file)
index 0000000..c613d1d
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode;
+
+/**
+ * A mutable tree node. This is a transient view materialized from a pre-existing node. Modifications are isolated. Once
+ * this object is {@link #seal()}ed, any interactions with it will result in undefined behavior.
+ */
+// FIXME: 5.0.0: Use @NonNullByDefault
+public interface MutableTreeNode extends StoreTreeNode<TreeNode> {
+    /**
+     * Set the data component of the node.
+     *
+     * @param data New data component, may not be null.
+     * @throws NullPointerException if {@code data} is null
+     */
+    void setData(NormalizedNode<?, ?> data);
+
+    /**
+     * Set the new subtree version. This is typically invoked when the user
+     * has modified some of this node's children.
+     *
+     * @param subtreeVersion New subtree version.
+     * @throws NullPointerException if {@code subtreeVersion} is null
+     */
+    void setSubtreeVersion(Version subtreeVersion);
+
+    /**
+     * Add a new child node. This acts as add-or-replace operation, e.g. it
+     * succeeds even if a conflicting child is already present.
+     *
+     * @param child New child node.
+     * @throws NullPointerException if {@code child} is null
+     */
+    void addChild(TreeNode child);
+
+    /**
+     * Remove a child node. This acts as delete-or-nothing operation, e.g. it
+     * succeeds even if the corresponding child is not present.
+     *
+     * @param id Child identifier.
+     * @throws NullPointerException if {@code id} is null
+     */
+    void removeChild(PathArgument id);
+
+    /**
+     * Finish node modification and return a read-only view of this node. After
+     * this method is invoked, any further calls to this object's method result
+     * in undefined behavior.
+     *
+     * @return Read-only view of this node.
+     */
+    @NonNull TreeNode seal();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/SimpleContainerNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/SimpleContainerNode.java
new file mode 100644 (file)
index 0000000..ba152bf
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A container node which has not seen a modification. All nodes underneath it share the same subtree version.
+ */
+final class SimpleContainerNode extends AbstractContainerNode {
+    protected SimpleContainerNode(final NormalizedNode<?, ?> data, final Version version) {
+        super(data, version);
+    }
+
+    @Override
+    public Version getSubtreeVersion() {
+        return getVersion();
+    }
+
+    @Override
+    public Optional<TreeNode> getChild(final PathArgument child) {
+        return getChildFromData(child);
+    }
+
+    @Override
+    public MutableTreeNode mutable() {
+        return new LazyMutableContainerNode(this);
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("data", getData());
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNode.java
new file mode 100644 (file)
index 0000000..41711e5
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode;
+
+/**
+ * A very basic data tree node. It has a version (when it was last modified), a subtree version (when any of its
+ * children were modified) and some read-only data.
+ *
+ * <p>
+ * Semantic difference between these two is important when dealing with modifications involving parent/child
+ * relationships and what operations can be execute concurrently without creating a data dependency conflict.
+ *
+ * <p>
+ * A replace/delete operation cannot be applied to this node if the subtree version does not match. This mismatch
+ * still allows modifications to its descendants.
+ *
+ * <p>
+ * A mismatch in node version indicates a replacement, preventing a modification of descendants or itself.
+ */
+// FIXME: BUG-2399: clarify that versioning rules are not enforced for non-presence containers, as they are not
+//                  considered to be data nodes.
+@NonNullByDefault
+public interface TreeNode extends Identifiable<PathArgument>, StoreTreeNode<TreeNode> {
+    /**
+     * Get the data node version. This version is updated whenever the data representation of this particular node
+     * changes as a result of a direct write to this node or to its parent nodes -- thus indicating that this node
+     * was logically replaced.
+     *
+     * @return Current data node version.
+     */
+    Version getVersion();
+
+    /**
+     * Get the subtree version. This version is updated whenever the data representation of this particular node
+     * changes as the result of a direct or indirect child node being created, replaced or removed.
+     *
+     * @return Current subtree version.
+     */
+    Version getSubtreeVersion();
+
+    /**
+     * Get a read-only view of the underlying data.
+     *
+     * @return Unmodifiable view of the underlying data.
+     */
+    NormalizedNode<?, ?> getData();
+
+    /**
+     * Get a mutable, isolated copy of the node.
+     *
+     * @return Mutable copy
+     */
+    MutableTreeNode mutable();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNodeFactory.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNodeFactory.java
new file mode 100644 (file)
index 0000000..7bae55c
--- /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.api.schema.tree.spi;
+
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedNodeContainer;
+
+/**
+ * Public entrypoint for other packages. Allows instantiating a tree node
+ * with specified version.
+ */
+public final class TreeNodeFactory {
+    private TreeNodeFactory() {
+        throw new UnsupportedOperationException("Utility class should not be instantiated");
+    }
+
+    /**
+     * Create a new AbstractTreeNode from a data node.
+     *
+     * @param data data node
+     * @param version data node version
+     * @return new AbstractTreeNode instance, covering the data tree provided
+     */
+    public static TreeNode createTreeNode(final NormalizedNode<?, ?> data, final Version version) {
+        if (data instanceof NormalizedNodeContainer<?, ?, ?>) {
+            @SuppressWarnings("unchecked")
+            final NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> container =
+                    (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) data;
+            return new SimpleContainerNode(container, version);
+        }
+        if (data instanceof OrderedNodeContainer<?>) {
+            @SuppressWarnings("unchecked")
+            final OrderedNodeContainer<NormalizedNode<?, ?>> container =
+                    (OrderedNodeContainer<NormalizedNode<?, ?>>) data;
+            return new SimpleContainerNode(container, version);
+        }
+        return new ValueNode(data, version);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/ValueNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/ValueNode.java
new file mode 100644 (file)
index 0000000..a2b750d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Concretization of AbstractTreeNode for leaf nodes which only contain data.
+ * Instances of this class report all children as absent, subtree version
+ * equal to this node's version and do not support mutable view.
+ */
+final class ValueNode extends AbstractTreeNode {
+    private static final Logger LOG = LoggerFactory.getLogger(ValueNode.class);
+
+    protected ValueNode(final NormalizedNode<?, ?> data, final Version version) {
+        super(data, version);
+    }
+
+    @Override
+    public Optional<TreeNode> getChild(final PathArgument childId) {
+        LOG.warn("Attempted to access child {} of value-node {}", childId, this);
+        return Optional.empty();
+    }
+
+    @Override
+    public Version getSubtreeVersion() {
+        return getVersion();
+    }
+
+    @Override
+    public MutableTreeNode mutable() {
+        /**
+         * Value nodes can only we read/written/delete, which does a straight
+         * replace. That means they don't haver need to be made mutable.
+         */
+        throw new UnsupportedOperationException(String.format("Attempted to mutate value-node %s", this));
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("value", getData());
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/Version.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/Version.java
new file mode 100644 (file)
index 0000000..759c72c
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+/**
+ * The concept of a version, either node version, or a subtree version. The
+ * only interface contract this class has is that no two versions are the
+ * same.
+ */
+public final class Version {
+    private Version() {
+
+    }
+
+    /**
+     * Create a new version, distinct from any other version.
+     *
+     * @return a new version.
+     */
+    @SuppressWarnings("static-method")
+    public Version next() {
+        return new Version();
+    }
+
+    /**
+     * Create an initial version.
+     *
+     * @return a new version.
+     */
+    public static Version initial() {
+        return new Version();
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/LazyXPathExpression.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/LazyXPathExpression.java
new file mode 100644 (file)
index 0000000..dea2f59
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Optional;
+import java.util.concurrent.Future;
+import javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Asynchronous interface to evaluation. It is functionally the same as an XPathExpression, but allows for asynchronous
+ * execution of evaluation of the expression.
+ *
+ * <p>
+ * FIXME: Whether or not the resulting XPathResult can perform blocking calls is up for grabs, but implementations are
+ *        definitely allowed to perform things like on-demand data transformation from foreign object and data models.
+ *
+ * @deprecated PREVIEW API. DO NOT IMPLEMENT YET AS THIS NEEDS TO BE VALIDATED FOR USE IN CLIENT APPLICATIONS.
+ *             APPLICATIONS WILLING TO USE THIS API PLEASE CONTACT
+ *             <a href="mailto:yangtools-dev@lists.opendaylight.org">yangtools-dev</a>.
+ */
+@Deprecated
+@Beta
+public interface LazyXPathExpression {
+    /**
+     * Evaluate this expression at the specified path in a document. If evaluation succeeds, it will return an
+     * {@link XPathResult} at some point it the future. If it fails to match anything, it {@link Future#get()} will
+     * return {@link Optional#empty()}.
+     *
+     * <p>
+     * FIXME: The amount of overhead an implementation can incur on the user as data from the resulting
+     *        {@link XPathResult} is being accessed is left UNDEFINED.
+     *        Specifically, the user is expected to store each result returned directly or indirectly in a local
+     *        variable instead of repeated calls to the result's methods, as these may incur CPU processing overhead.
+     *        Furthermore all method invocations can throw {@link LazyXPathExpressionException}, which the users are
+     *        expected to handle gracefully. RESILIENT USERS ARE EXPECTED TO CATCH {@link LazyXPathExpressionException}
+     *        AND RECOVER IN THE SAME MANNER THEY WOULD IF AN {@link XPathExpressionException} WOULD HAVE BEEN THROWN.
+     *        [ FIXME: would it be appropriate to allow implementations to SneakyThrow {@link XPathExpressionException}
+     *                 and not introduce a RuntimeExpcetion ? ]
+     *
+     * @param document {@link XPathDocument} on which evaluation should take place
+     * @param path Path to the node on which to evaluate the expression
+     * @return An optional {@link XPathResult}
+     * @throws NullPointerException if any of the arguments are null
+     * @throws IllegalArgumentException if the path does not match the path at which this expression was compiled
+     */
+    ListenableFuture<Optional<? extends XPathResult<?>>> evaluateLazily(@NonNull XPathDocument document,
+            @NonNull YangInstanceIdentifier path);
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/LazyXPathExpressionException.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/LazyXPathExpressionException.java
new file mode 100644 (file)
index 0000000..5cd2f6f
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import com.google.common.annotations.Beta;
+import javax.xml.xpath.XPathExpressionException;
+
+/**
+ * The runtime counterpart of {@link XPathExpressionException}. Can occur only when the user is accessing any state
+ * created from the user's invocation to the {@link LazyXPathExpression} API.
+ *
+ * @deprecated PREVIEW API. DO NOT IMPLEMENT YET AS THIS NEEDS TO BE VALIDATED FOR USE IN CLIENT APPLICATIONS.
+ *             APPLICATIONS WILLING TO USE THIS API PLEASE CONTACT
+ *             <a href="mailto:yangtools-dev@lists.opendaylight.org">yangtools-dev</a>.
+ */
+@Beta
+@Deprecated
+public class LazyXPathExpressionException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    public LazyXPathExpressionException(final String message) {
+        super(message);
+    }
+
+    public LazyXPathExpressionException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/OptimizableXPathExpression.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/OptimizableXPathExpression.java
new file mode 100644 (file)
index 0000000..0248168
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Interface implemented by {@link XPathExpression}s which can be further optimized for execution efficiency at the
+ * expense of additional processing being performed on them. The decision to optimize a particular expression is left
+ * to the user's discretion.
+ *
+ * <p>
+ * Implementations supporting profile-driven and similar optimizations which depend on data being gathered during
+ * evaluation should not implement this interface, but rather perform these optimizations transparently behind the
+ * scenes. That implies the users can expect those optimizations not interfering with the user's ability to evaluate
+ * the expression.
+ */
+@Beta
+@Deprecated
+public interface OptimizableXPathExpression extends XPathExpression {
+    /**
+     * Perform optimization of this expression. If an implementation supports different levels of optimization, it
+     * should return an {@link OptimizableXPathExpression} as a result of progressing optimizations for as long as
+     * it determines further processing can result in execution benefits. Note this expression is expected to remain
+     * unchanged.
+     *
+     * @return An optimized version of this expression.
+     */
+    @NonNull XPathExpression optimizeExpression();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/PrefixConverters.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/PrefixConverters.java
new file mode 100644 (file)
index 0000000..a551cdc
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Converter;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableBiMap.Builder;
+import com.google.common.collect.Maps;
+import java.util.Optional;
+import javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A set of utility functions for dealing with common types of namespace mappings.
+ */
+@Beta
+@Deprecated
+public final class PrefixConverters {
+    private PrefixConverters() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Create a prefix {@link Converter} for {@link XPathExpressionException} defined in a particular YANG
+     * {@link Module} .Instantiation requires establishing how a module's imports are mapped to actual modules
+     * and their namespaces. This information is cached and used for improved lookups.
+     *
+     * @param ctx A SchemaContext
+     * @param module Module in which the XPath is defined
+     * @return A new Converter
+     */
+    public static @NonNull Converter<String, QNameModule> create(final SchemaContext ctx, final Module module) {
+        // Always check for null ctx
+        requireNonNull(ctx, "Schema context may not be null");
+
+        // Use immutable map builder for detection of duplicates (which should never occur)
+        final Builder<String, QNameModule> b = ImmutableBiMap.builder();
+        b.put(module.getPrefix(), module.getQNameModule());
+
+        for (ModuleImport i : module.getImports()) {
+            final Optional<Module> mod = ctx.findModule(i.getModuleName(), i.getRevision());
+            checkArgument(mod.isPresent(), "Unsatisfied import of %s by module %s", i, module);
+
+            b.put(i.getPrefix(), mod.get().getQNameModule());
+        }
+
+        return Maps.asConverter(b.build());
+    }
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/RelocatableXPathExpression.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/RelocatableXPathExpression.java
new file mode 100644 (file)
index 0000000..e618025
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Interface implemented by {@link XPathExpression}s which can be recompiled to execute more efficiently at a
+ * at a different {@link SchemaPath} than they were originally compiled at. This can result in the expression being
+ * moved either up or down in a SchemaPath tree, usually closer to their {@link #getApexPath()}.
+ */
+@Beta
+@Deprecated
+public interface RelocatableXPathExpression extends XPathExpression {
+    /**
+     * Return a new XPathExpression relocated to a SchemaPath of the implementation's choosing. Note that
+     * {@link #getApexPath()} must not change during this operation.
+     *
+     * @return A new XPathExpression instance.
+     */
+    @NonNull XPathExpression relocateExpression();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathBooleanResult.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathBooleanResult.java
new file mode 100644 (file)
index 0000000..4b8c5f3
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * An {@link XPathResult} containing a Boolean.
+ */
+@Beta
+@Deprecated
+public interface XPathBooleanResult extends XPathResult<Boolean> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathDocument.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathDocument.java
new file mode 100644 (file)
index 0000000..23f1a7f
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * The notion of a document, modeled as a {@link NormalizedNode}.
+ */
+@Beta
+@Deprecated
+public interface XPathDocument {
+    /**
+     * Return the root node of this document.
+     *
+     * @return This document's root node.
+     */
+    @NonNull NormalizedNode<?, ?> getRootNode();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathExpression.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathExpression.java
new file mode 100644 (file)
index 0000000..4d129ff
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * A compiled XPath expression. Each instance is bound to a particular {@link XPathSchemaContext} and may not be
+ * evaluated on {@link XPathDocument}s from other context.
+ */
+@Beta
+@Deprecated
+public interface XPathExpression {
+    /**
+     * Evaluate this expression at the specified path in a document. If evaluation succeeds, it will return an
+     * {@link XPathResult}. If it fails to match anything, it will return {@link Optional#empty()}. Implementations
+     * of this method are expected to perform complete evaluation such that accessing data via the resulting
+     * {@link XPathResult} will not incur large overhead.
+     *
+     * @param document {@link XPathDocument} on which evaluation should take place
+     * @param path Path to the node on which to evaluate the expression
+     * @return An optional {@link XPathResult}
+     * @throws NullPointerException if any of the arguments are null
+     * @throws XPathExpressionException if the expression cannot be evaluated
+     * @throws IllegalArgumentException if the path does not match the path at which this expression was compiled
+     */
+    Optional<? extends XPathResult<?>> evaluate(@NonNull XPathDocument document, @NonNull YangInstanceIdentifier path)
+            throws XPathExpressionException;
+
+    /**
+     * Return the evaluation context SchemaPath of this expression. This is corresponds to the SchemaPath at which this
+     * expression was compiled at, or relocated to via {@link RelocatableXPathExpression#relocateExpression()}.
+     *
+     * @return The evaluation {@link SchemaPath}
+     */
+    @NonNull SchemaPath getEvaluationPath();
+
+    /**
+     * Return the SchemaPath of the topmost node which affects the result of evaluation of this expression. This
+     * information is useful for large evolving documents (such as
+     * {@link org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree} implementations) to minimize the frequency
+     * of evaluation. The apex can be either logically higher or lower in the SchemaPath tree than
+     * {@link #getEvaluationPath()}.
+     *
+     * @return The apex node evaluation of this expression can reference, or {@link SchemaPath#ROOT} if it cannot
+     *         cannot be conclusively determined.
+     */
+    @NonNull SchemaPath getApexPath();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathNodesetResult.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathNodesetResult.java
new file mode 100644 (file)
index 0000000..8d11f8f
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import java.util.Map.Entry;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * An {@link XPathResult} containing a set of nodes. Resulting nodes are identified by their
+ * {@link YangInstanceIdentifier}.
+ */
+@Beta
+@Deprecated
+public interface XPathNodesetResult
+    extends XPathResult<Collection<Entry<YangInstanceIdentifier, NormalizedNode<?, ?>>>> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathNumberResult.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathNumberResult.java
new file mode 100644 (file)
index 0000000..7564a27
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * An {@link XPathResult} containing a Number.
+ */
+@Beta
+@Deprecated
+public interface XPathNumberResult extends XPathResult<Number> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathResult.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathResult.java
new file mode 100644 (file)
index 0000000..c50d4cf
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Base interface for various things an XPath evaluation can return.
+ *
+ * @param <T> type of returned value
+ */
+@Beta
+@Deprecated
+// FIXME: do we want to support all the modes of
+//        <a href="http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResultType">DOM XPath</a> ?
+//        The default DataTree (yang-data-impl) implementation can support ORDERED_NODE_SNAPSHOT_TYPE. The clustered
+//        datastore may want to implement ORDERED_NODE_ITERATOR_TYPE (via iterators).
+public interface XPathResult<T> {
+    /**
+     * Get the value contained in this result.
+     *
+     * @return Result value
+     */
+    @NonNull T getValue();
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathSchemaContext.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathSchemaContext.java
new file mode 100644 (file)
index 0000000..f0e1b5a
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Converter;
+import javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * A schema-informed XPath context. It supports creation of {@link XPathDocument}s, which are bound to
+ * a particular root node.
+ */
+@Beta
+@Deprecated
+public interface XPathSchemaContext {
+    /**
+     * Compile an XPath expression for execution on {@link XPathDocument}s produced by this context.
+     *
+     * <p>
+     * The user must provide a prefix-to-mapping {@link Converter}, which will be used to convert any prefixes found
+     * in the XPath expression being compiled in the resulting context.
+     *
+     * @param schemaPath Schema path of the node at which this expression is expected to be evaluated
+     * @param prefixes Prefix-to-namespace converter
+     * @param xpath XPath expression to compile
+     * @return A compiled XPath expression
+     * @throws XPathExpressionException if the provided expression is invalid, either syntactically or by referencing
+     *         namespaces unknown to this schema context.
+     */
+    @NonNull XPathExpression compileExpression(@NonNull SchemaPath schemaPath,
+            @NonNull Converter<String, QNameModule> prefixes, @NonNull String xpath) throws XPathExpressionException;
+
+    /**
+     * Create a new document context.
+     *
+     * @param documentRoot Root node of the document
+     * @return A new {@link XPathDocument} on which queries may be executed.
+     * @throws IllegalArgumentException if the document root is not known to this schema context.
+     */
+    @NonNull XPathDocument createDocument(@NonNull NormalizedNode<?, ?> documentRoot);
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathSchemaContextFactory.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathSchemaContextFactory.java
new file mode 100644 (file)
index 0000000..60ba0c9
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A factory for obtaining {@link XPathSchemaContext}s. This is the primary entry point to an XPath evaluation
+ * implementation. Users are expected to resolve these via their service resolution framework, be it
+ * {@link java.util.ServiceLoader}, OSGi or similar.
+ *
+ * <p>
+ * Implementations are required to support {@link java.util.ServiceLoader}.
+ */
+@Deprecated
+@NonNullByDefault
+public interface XPathSchemaContextFactory {
+    /**
+     * Create an {@link XPathSchemaContext} based on a {@link SchemaContext}. This effectively binds the namespaces
+     * the user expects to map to YANG schema. The {@link XPathExpression} compilation, relocation and optimization
+     * processes can take advantage of the YANG schema provided.
+     *
+     * @param context SchemaContext associated with the resulting {@link XPathSchemaContext}
+     * @return An {@link XPathSchemaContext} instance
+     */
+    XPathSchemaContext createContext(SchemaContext context);
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathStringResult.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/XPathStringResult.java
new file mode 100644 (file)
index 0000000..55cb1ec
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.xpath;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * An {@link XPathResult} containing a String.
+ */
+@Beta
+@Deprecated
+public interface XPathStringResult extends XPathResult<String> {
+
+}
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/package-info.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/xpath/package-info.java
new file mode 100644 (file)
index 0000000..442093a
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+ */
+/**
+ * Deprecated APIs for evaluating XPath expressions on
+ * {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode} trees.
+ *
+ * <p>
+ * This API is deprecated and will be removed in a future version. For replacement refer to
+ * {@code org.opendaylight.yang.data.api.xpath} package.
+ */
+@java.lang.Deprecated
+package org.opendaylight.yangtools.yang.data.api.schema.xpath;
\ No newline at end of file
diff --git a/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/PathArgumentListTest.java b/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/PathArgumentListTest.java
new file mode 100644 (file)
index 0000000..8b3fb3c
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2015 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.UnmodifiableIterator;
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+public class PathArgumentListTest {
+    private static final class TestClass extends PathArgumentList {
+        @Override
+        public UnmodifiableIterator<PathArgument> iterator() {
+            return new UnmodifiableIterator<PathArgument>() {
+                @Override
+                public boolean hasNext() {
+                    return false;
+                }
+
+                @Override
+                public PathArgument next() {
+                    throw new NoSuchElementException();
+                }
+            };
+        }
+
+        @Override
+        public PathArgument get(final int index) {
+            return null;
+        }
+
+        @Override
+        public int size() {
+            return 0;
+        }
+    }
+
+    @Test
+    public void testIsEmpty() {
+        assertFalse(new TestClass().isEmpty());
+    }
+
+    @Test
+    public void testProtections() {
+        final PathArgumentList l = new TestClass();
+
+        try {
+            l.remove(null);
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // Expected
+        }
+
+        try {
+            l.addAll(Collections.emptyList());
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // Expected
+        }
+
+        try {
+            l.removeAll(Collections.emptyList());
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // Expected
+        }
+
+        try {
+            l.retainAll(Collections.emptyList());
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // Expected
+        }
+
+        try {
+            l.clear();
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // Expected
+        }
+
+        try {
+            l.addAll(0, null);
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void testPathArgument() {
+        final QNameModule qNameModule = QNameModule.create(URI.create("urn:opendaylight.test2"),
+            Revision.of("2015-08-08"));
+        final QName qNameRoot = QName.create(qNameModule, "root");
+        final QName qNameList = QName.create(qNameModule, "list");
+        final QName qNameLeaf = QName.create(qNameModule, "leaf-a");
+        final Map<QName, Object> entryLeaf = new HashMap<>();
+        entryLeaf.put(qNameList, "leaf");
+        final NodeIdentifierWithPredicates nodeIdentifierWithPredicates = NodeIdentifierWithPredicates.of(qNameList,
+            entryLeaf);
+        final YangInstanceIdentifier yangInstanceIdentifier = YangInstanceIdentifier.of(qNameRoot).node(qNameList)
+                .node(nodeIdentifierWithPredicates).node(qNameLeaf);
+        final PathArgument pathArgumentToRoot = yangInstanceIdentifier.getAncestor(1).getPathArguments().iterator()
+                .next();
+        final StackedPathArguments stackedPathArguments = (StackedPathArguments)yangInstanceIdentifier
+            .getPathArguments();
+        assertTrue(yangInstanceIdentifier.pathArgumentsEqual(yangInstanceIdentifier));
+        assertEquals(pathArgumentToRoot, stackedPathArguments.get(0));
+        assertEquals(4, stackedPathArguments.size());
+        assertTrue(stackedPathArguments.contains(pathArgumentToRoot));
+        assertEquals(0, stackedPathArguments.indexOf(pathArgumentToRoot));
+        assertEquals(0, stackedPathArguments.lastIndexOf(pathArgumentToRoot));
+
+        final StackedReversePathArguments stackedReversePathArguments =
+            (StackedReversePathArguments)yangInstanceIdentifier.getReversePathArguments();
+        final QName rootQname = pathArgumentToRoot.getNodeType();
+        final QName leafQname = stackedReversePathArguments.get(0).getNodeType();
+        assertEquals(qNameRoot, rootQname);
+        assertEquals(qNameLeaf, leafQname);
+        assertEquals(4, stackedReversePathArguments.size());
+        assertTrue(stackedReversePathArguments.contains(pathArgumentToRoot));
+        assertEquals(3, stackedReversePathArguments.indexOf(pathArgumentToRoot));
+        assertEquals(3, stackedReversePathArguments.lastIndexOf(pathArgumentToRoot));
+
+        final StackedYangInstanceIdentifier stackedYangInstanceIdentifier = (StackedYangInstanceIdentifier)
+                yangInstanceIdentifier;
+        final StackedYangInstanceIdentifier stackedYangInstanceIdentifierClone = stackedYangInstanceIdentifier.clone();
+        final YangInstanceIdentifier yangInstanceIdentifier1 = stackedYangInstanceIdentifier.getAncestor(4);
+        assertEquals(stackedYangInstanceIdentifier, stackedYangInstanceIdentifierClone);
+        assertEquals(stackedReversePathArguments, yangInstanceIdentifier1.getReversePathArguments());
+
+        try {
+            stackedYangInstanceIdentifier.getAncestor(12);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+    }
+}
diff --git a/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifierTest.java b/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifierTest.java
new file mode 100644 (file)
index 0000000..d79ada9
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * 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.yang.data.api;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Unit tests for InstanceIdentifier.
+ *
+ * @author Thomas Pantelis
+ */
+public class YangInstanceIdentifierTest {
+
+    private static final QName NODENAME1 = QName.create("test", "2014-05-28", "node1");
+    private static final QName NODENAME2 = QName.create("test", "2014-05-28", "node2");
+    private static final QName NODENAME3 = QName.create("test", "2014-05-28", "node3");
+    private static final QName NODENAME4 = QName.create("test", "2014-05-28", "node4");
+    private static final QName KEY1 = QName.create("test", "2014-05-28", "key1");
+    private static final QName KEY2 = QName.create("test", "2014-05-28", "key2");
+    private static final QName KEY3 = QName.create("test", "2014-05-28", "key3");
+
+    @Test
+    public void testGetLastPathArgument() {
+        YangInstanceIdentifier id1 = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+                new NodeIdentifier(NODENAME2));
+        assertEquals("getLastPathArgument", new NodeIdentifier(NODENAME2), id1.getLastPathArgument());
+        YangInstanceIdentifier id2 = YangInstanceIdentifier.create();
+        assertNull(id2.getLastPathArgument());
+    }
+
+    @Test
+    public void testHashCodeEquals() {
+        YangInstanceIdentifier id1 = YangInstanceIdentifier.create(NodeIdentifier.create(NODENAME1),
+                new NodeIdentifier(NODENAME2));
+        YangInstanceIdentifier id2 = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+                NodeIdentifier.create(NODENAME2));
+
+        assertEquals("hashCode", id1.hashCode(), id2.hashCode());
+    }
+
+    @Test
+    public void testEquals() {
+        final YangInstanceIdentifier id1 = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+                new NodeIdentifier(NODENAME2));
+        final YangInstanceIdentifier id2 = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+                new NodeIdentifier(NODENAME2));
+        final YangInstanceIdentifier id3 = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME2),
+                new NodeIdentifier(NODENAME1));
+        final YangInstanceIdentifier id4 = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1));
+
+        assertFalse("equals", id1.equals(null));
+        assertTrue("equals", id1.equals(id1));
+        assertTrue("equals", id1.equals(id2));
+        assertFalse("equals", id1.equals(id3));
+        assertFalse("equals", id1.equals(id4));
+        assertFalse("equals", id1.equals(new Object()));
+    }
+
+    @Test
+    public void testToString() {
+        YangInstanceIdentifier id = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+                new NodeIdentifier(NODENAME2));
+
+        assertNotNull(id.toString());
+    }
+
+    @Test
+    public void testNode() {
+        final YangInstanceIdentifier id = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+                new NodeIdentifier(NODENAME2));
+
+        YangInstanceIdentifier newID = id.node(NODENAME3);
+
+        assertNotNull("InstanceIdentifier is null", newID);
+        assertEquals("Path size", 3, newID.getPathArguments().size());
+
+        Iterator<PathArgument> it = newID.getPathArguments().iterator();
+        assertEquals("PathArg 1 node type", NODENAME1, it.next().getNodeType());
+        assertEquals("PathArg 2 node type", NODENAME2, it.next().getNodeType());
+        assertEquals("PathArg 3 node type", NODENAME3, it.next().getNodeType());
+
+        newID = id.node(new NodeIdentifier(NODENAME3));
+
+        assertNotNull("InstanceIdentifier is null", newID);
+        assertEquals("Path size", 3, newID.getPathArguments().size());
+
+        it = newID.getPathArguments().iterator();
+        assertEquals("PathArg 1 node type", NODENAME1, it.next().getNodeType());
+        assertEquals("PathArg 2 node type", NODENAME2, it.next().getNodeType());
+        assertEquals("PathArg 3 node type", NODENAME3, it.next().getNodeType());
+    }
+
+    @Test
+    public void testRelativeTo() {
+        final YangInstanceIdentifier id1 = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+                new NodeIdentifier(NODENAME2), new NodeIdentifier(NODENAME3), new NodeIdentifier(NODENAME4));
+        final YangInstanceIdentifier id2 = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+                new NodeIdentifier(NODENAME2));
+        final YangInstanceIdentifier id3 = YangInstanceIdentifier.create(Arrays.asList(
+                    new NodeIdentifier(NODENAME1), new NodeIdentifier(NODENAME2)));
+
+        Optional<YangInstanceIdentifier> relative = id1.relativeTo(id2);
+        assertTrue("isPresent", relative.isPresent());
+
+        List<PathArgument> path = relative.get().getPathArguments();
+        assertEquals("Path size", 2, path.size());
+        assertEquals("PathArg 1 node type", NODENAME3, path.get(0).getNodeType());
+        assertEquals("PathArg 2 node type", NODENAME4, path.get(1).getNodeType());
+
+        relative = id2.relativeTo(id3);
+        assertTrue("isPresent", relative.isPresent());
+        assertEquals("Path size", 0, relative.get().getPathArguments().size());
+
+        relative = id2.relativeTo(id1);
+        assertFalse("isPresent", relative.isPresent());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testContainsNull() {
+        final YangInstanceIdentifier id = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1));
+
+        id.contains(null);
+    }
+
+    @Test
+    public void testContains() {
+        final YangInstanceIdentifier id1 = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+                new NodeIdentifier(NODENAME2), new NodeIdentifier(NODENAME3), new NodeIdentifier(NODENAME4));
+        final YangInstanceIdentifier id2 = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+                new NodeIdentifier(NODENAME2));
+        final YangInstanceIdentifier id3 = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+                new NodeIdentifier(NODENAME2));
+        final YangInstanceIdentifier id4 = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+                new NodeIdentifier(NODENAME3));
+
+        assertTrue("contains", id2.contains(id1));
+        assertTrue("contains", id2.contains(id3));
+        assertFalse("contains", id1.contains(id2));
+        assertFalse("contains", id2.contains(id4));
+    }
+
+    @Test
+    public void testOf() {
+        YangInstanceIdentifier newID = YangInstanceIdentifier.of(NODENAME1);
+
+        assertNotNull("InstanceIdentifier is null", newID);
+        assertEquals("Path size", 1, newID.getPathArguments().size());
+        assertEquals("PathArg 1 node type", NODENAME1, newID.getPathArguments().get(0).getNodeType());
+
+        // for code coverage
+        assertNotNull(newID.toString());
+    }
+
+    @Test
+    public void testBuilder() {
+        YangInstanceIdentifier newID = YangInstanceIdentifier.builder()
+                .node(NODENAME1)
+                .nodeWithKey(NODENAME2, Collections.singletonMap(KEY1, "foo"))
+                .nodeWithKey(NODENAME3, KEY2, "bar").build();
+
+        assertNotNull("InstanceIdentifier is null", newID);
+        assertEquals("Path size", 3, newID.getPathArguments().size());
+
+        Iterator<PathArgument> it = newID.getPathArguments().iterator();
+        assertEquals("PathArg 1 node type", NODENAME1, it.next().getNodeType());
+        verifyNodeIdentifierWithPredicates("PathArg 2", it.next(), NODENAME2, KEY1, "foo");
+        verifyNodeIdentifierWithPredicates("PathArg 3", it.next(), NODENAME3, KEY2, "bar");
+
+        newID = YangInstanceIdentifier.builder(newID).node(NODENAME4).build();
+
+        assertNotNull("InstanceIdentifier is null", newID);
+        assertEquals("Path size", 4, newID.getPathArguments().size());
+
+        it = newID.getPathArguments().iterator();
+        assertEquals("PathArg 1 node type", NODENAME1, it.next().getNodeType());
+        assertEquals("PathArg 2 node type", NODENAME2, it.next().getNodeType());
+        assertEquals("PathArg 3 node type", NODENAME3, it.next().getNodeType());
+        assertEquals("PathArg 4 node type", NODENAME4, it.next().getNodeType());
+
+        newID = YangInstanceIdentifier.builder().node(NODENAME1).build();
+
+        assertNotNull("InstanceIdentifier is null", newID);
+        assertEquals("Path size", 1, newID.getPathArguments().size());
+        assertEquals("PathArg 1 node type", NODENAME1, newID.getPathArguments().get(0).getNodeType());
+    }
+
+    private static void verifyNodeIdentifierWithPredicates(final String prefix,
+            final PathArgument arg, final QName nodeName, final QName key, final Object value) {
+
+        assertNotNull(prefix + " is null", arg);
+        assertThat(arg, instanceOf(NodeIdentifierWithPredicates.class));
+        NodeIdentifierWithPredicates node = (NodeIdentifierWithPredicates)arg;
+        assertEquals(prefix + " node type", nodeName, node.getNodeType());
+        assertEquals(prefix + " key values map size", 1, node.size());
+        Entry<QName, Object> entry = node.entrySet().iterator().next();
+        assertEquals(prefix + " key values map entry key", key, entry.getKey());
+        assertEquals(prefix + " key values map entry value", value, entry.getValue());
+    }
+
+    @Test
+    public void testNodeIdentifierWithPredicates() {
+
+        NodeIdentifierWithPredicates node1 = NodeIdentifierWithPredicates.of(NODENAME1, KEY1, "foo");
+        verifyNodeIdentifierWithPredicates("NodeIdentifierWithPredicates", node1, NODENAME1, KEY1, "foo");
+
+        NodeIdentifierWithPredicates node2 = NodeIdentifierWithPredicates.of(NODENAME1, KEY1, "foo");
+
+        assertEquals("hashCode", node1.hashCode(), node2.hashCode());
+        assertTrue("equals", node1.equals(node2));
+
+        assertFalse("equals", node1.equals(NodeIdentifierWithPredicates.of(NODENAME2, KEY1, "foo")));
+        assertFalse("equals", node1.equals(NodeIdentifierWithPredicates.of(NODENAME1, KEY2, "foo")));
+        assertFalse("equals", node1.equals(NodeIdentifierWithPredicates.of(NODENAME1, KEY1, "bar")));
+        assertFalse("equals", node1.equals(new Object()));
+
+        // for code coverage
+        assertNotNull(node1.toString());
+        assertNotNull(node1.toRelativeString(node2));
+
+        NodeIdentifierWithPredicates node3 = NodeIdentifierWithPredicates.of(NODENAME1,
+                ImmutableMap.of(KEY1, 10, KEY2, 20));
+
+        NodeIdentifierWithPredicates node4 = NodeIdentifierWithPredicates.of(NODENAME1,
+                ImmutableMap.of(KEY1, 10, KEY2, 20));
+
+        assertEquals("hashCode", node3.hashCode(), node4.hashCode());
+        assertTrue("equals", node3.equals(node4));
+
+        assertFalse("equals", node3.equals(node1));
+        assertFalse("equals", node1.equals(NodeIdentifierWithPredicates.of(NODENAME1,
+            ImmutableMap.of(KEY1, 10, KEY3, 20))));
+
+        node1 = NodeIdentifierWithPredicates.of(NODENAME1, KEY1, new byte[]{ 1, 2 });
+        node2 = NodeIdentifierWithPredicates.of(NODENAME1, KEY1, new byte[]{ 1, 2 });
+
+        assertEquals("hashCode", node1.hashCode(), node2.hashCode());
+        assertTrue("equals", node1.equals(node2));
+
+        assertFalse("equals", node1.equals(NodeIdentifierWithPredicates.of(NODENAME1, KEY1, new byte[]{ 1, 3})));
+        assertFalse("equals", node1.equals(NodeIdentifierWithPredicates.of(NODENAME1, KEY1, new byte[]{ 1 })));
+        assertFalse("equals", node1.equals(NodeIdentifierWithPredicates.of(NODENAME1, KEY1, new byte[]{ 1, 2, 3 })));
+    }
+
+    @Test
+    public void testNodeWithValue() {
+
+        NodeWithValue<?> node1 = new NodeWithValue<>(NODENAME1, "foo");
+        assertEquals("getNodeType", NODENAME1, node1.getNodeType());
+        assertEquals("getValue", "foo", node1.getValue());
+
+        NodeWithValue<?> node2 = new NodeWithValue<>(NODENAME1, "foo");
+
+        assertEquals("hashCode", node1.hashCode(), node2.hashCode());
+        assertTrue("equals", node1.equals(node2));
+
+        assertFalse("equals", node1.equals(new NodeWithValue<>(NODENAME1, "bar")));
+        assertFalse("equals", node1.equals(new NodeWithValue<>(NODENAME2, "foo")));
+        assertFalse("equals", node1.equals(new Object()));
+
+        // for code coverage
+        assertNotNull(node1.toString());
+        assertNotNull(node1.toRelativeString(node2));
+
+        NodeWithValue<?> node3 = new NodeWithValue<>(NODENAME1, new byte[]{1,2});
+        NodeWithValue<?> node4 = new NodeWithValue<>(NODENAME1, new byte[]{1,2});
+
+        assertEquals("hashCode", node3.hashCode(), node4.hashCode());
+        assertTrue("equals", node3.equals(node4));
+
+        assertFalse("equals", node3.equals(new NodeWithValue<>(NODENAME1, new byte[]{1,3})));
+        assertFalse("equals", node3.equals(node1));
+    }
+
+    @Test
+    public void testNodeIdentifier() {
+
+        final NodeIdentifier node1 = new NodeIdentifier(NODENAME1);
+        assertEquals("getNodeType", NODENAME1, node1.getNodeType());
+        final NodeIdentifier node2 = new NodeIdentifier(NODENAME1);
+        final AugmentationIdentifier node3 = new AugmentationIdentifier(ImmutableSet.of(NODENAME1, NODENAME2));
+
+        assertEquals("hashCode", node1.hashCode(), node2.hashCode());
+        assertEquals("compareTo", 0, node1.compareTo(node2));
+        assertTrue("compareTo", node1.compareTo(new NodeIdentifier(NODENAME3)) != 0);
+
+        assertFalse("equals", node1.equals(null));
+        assertFalse("equals", node1.equals(node3));
+        assertTrue("equals", node1.equals(node1));
+        assertTrue("equals", node1.equals(node2));
+        assertFalse("equals", node1.equals(new NodeIdentifier(NODENAME3)));
+        assertFalse("equals", node1.equals(new Object()));
+
+        // for code coverage
+        assertNotNull(node1.toString());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testAugmentationIdentifierNodeType() {
+        AugmentationIdentifier node1 = new AugmentationIdentifier(ImmutableSet.of(NODENAME1, NODENAME2));
+        node1.getNodeType();
+    }
+
+    @Test
+    public void testAugmentationIdentifier() {
+
+        final AugmentationIdentifier node1 = new AugmentationIdentifier(ImmutableSet.of(NODENAME1, NODENAME2));
+        assertEquals("getPossibleChildNames", ImmutableSet.of(NODENAME1, NODENAME2), node1.getPossibleChildNames());
+        final AugmentationIdentifier node2 = new AugmentationIdentifier(ImmutableSet.of(NODENAME2, NODENAME1));
+        final AugmentationIdentifier node3 = new AugmentationIdentifier(ImmutableSet.of(NODENAME1, NODENAME3));
+        final AugmentationIdentifier node4 = new AugmentationIdentifier(ImmutableSet.of(NODENAME1, NODENAME2,
+                    NODENAME3));
+        final NodeIdentifier node5 = new NodeIdentifier(NODENAME3);
+
+        assertEquals("hashCode", node1.hashCode(), node2.hashCode());
+
+        assertTrue("equals", node1.equals(node1));
+        assertTrue("equals", node1.equals(node2));
+        assertFalse("equals", node1.equals(node3));
+        assertFalse("equals", node1.equals(new AugmentationIdentifier(ImmutableSet.of(NODENAME1))));
+        assertFalse("equals", node1.equals(new Object()));
+
+        assertEquals("compareTo", -1, node1.compareTo(node5));
+        assertEquals("compareTo", 0, node1.compareTo(node2));
+        assertEquals("compareTo", 0, node1.compareTo(node2));
+        assertEquals("compareTo", 1, node1.compareTo(node4));
+        assertEquals("compareTo", -1, node4.compareTo(node1));
+
+        // for code coverage
+        assertNotNull(node1.toString());
+        assertNotNull(node1.toRelativeString(node5));
+    }
+
+    private static YangInstanceIdentifier serdes(final YangInstanceIdentifier id) throws IOException,
+            ClassNotFoundException {
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+            oos.writeObject(id);
+        }
+
+        final byte[] bytes = bos.toByteArray();
+        final ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+        try (ObjectInputStream ois = new ObjectInputStream(bis)) {
+            final YangInstanceIdentifier ret = (YangInstanceIdentifier) ois.readObject();
+            assertEquals(0, ois.available());
+            return ret;
+        }
+    }
+
+    @Test
+    public void testSerialization() throws IOException, ClassNotFoundException {
+        final YangInstanceIdentifier fixed = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+            new NodeIdentifier(NODENAME2));
+        assertEquals(fixed, serdes(fixed));
+
+        final YangInstanceIdentifier stacked = YangInstanceIdentifier.empty().node(new NodeIdentifier(NODENAME1));
+        assertEquals(stacked, serdes(stacked));
+
+        final YangInstanceIdentifier empty = serdes(YangInstanceIdentifier.empty());
+        assertSame(YangInstanceIdentifier.empty(), empty);
+    }
+
+    @Test
+    public void testToOptimized() {
+        final YangInstanceIdentifier fixed = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1),
+            new NodeIdentifier(NODENAME2));
+        final YangInstanceIdentifier stacked = YangInstanceIdentifier.empty().node(NodeIdentifier.create(NODENAME1))
+                .node(NodeIdentifier.create(NODENAME2));
+
+        assertSame(fixed, fixed.toOptimized());
+
+        final YangInstanceIdentifier opt = stacked.toOptimized();
+        assertTrue(opt instanceof FixedYangInstanceIdentifier);
+        assertEquals(fixed, stacked.toOptimized());
+    }
+
+    @Test
+    public void testGetParent() {
+        final YangInstanceIdentifier fixed = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1));
+        final YangInstanceIdentifier stacked = YangInstanceIdentifier.empty().node(new NodeIdentifier(NODENAME1));
+        final YangInstanceIdentifier twoStacked = stacked.node(new NodeIdentifier(NODENAME2));
+
+        assertNull(YangInstanceIdentifier.empty().getParent());
+        assertSame(YangInstanceIdentifier.empty(), fixed.getParent());
+        assertSame(YangInstanceIdentifier.empty(), stacked.getParent());
+        assertSame(stacked, twoStacked.getParent());
+    }
+
+    @Test
+    public void testIsEmpty() {
+        final YangInstanceIdentifier fixed = YangInstanceIdentifier.create(new NodeIdentifier(NODENAME1));
+        final YangInstanceIdentifier stacked = YangInstanceIdentifier.empty().node(new NodeIdentifier(NODENAME1));
+
+        assertTrue(YangInstanceIdentifier.empty().isEmpty());
+        assertFalse(fixed.isEmpty());
+        assertFalse(stacked.isEmpty());
+    }
+}
diff --git a/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedNodesTest.java b/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/NormalizedNodesTest.java
new file mode 100644 (file)
index 0000000..422fbac
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+public class NormalizedNodesTest {
+
+    @Test
+    public void testGetDirectChild() {
+        final PathArgument mockedPathArgument = mock(PathArgument.class);
+
+        final LeafNode<?> mockedLeafNode = mock(LeafNode.class);
+        assertEquals(Optional.empty(), NormalizedNodes.getDirectChild(mockedLeafNode, mockedPathArgument));
+
+        final LeafSetEntryNode<?> mockedLeafSetEntryNode = mock(LeafSetEntryNode.class);
+        assertEquals(Optional.empty(), NormalizedNodes.getDirectChild(mockedLeafSetEntryNode, mockedPathArgument));
+
+        final DataContainerNode<?> mockedDataContainerNode = mock(DataContainerNode.class);
+        final ContainerNode mockedContainerNode = mock(ContainerNode.class);
+        doReturn(Optional.of(mockedContainerNode)).when(mockedDataContainerNode).getChild(any(PathArgument.class));
+        assertEquals(mockedContainerNode, NormalizedNodes.getDirectChild(mockedDataContainerNode, mockedPathArgument)
+                .get());
+
+        final MapNode mockedMapNode = mock(MapNode.class);
+        final QName listQName = QName.create("test-ns", "test-list");
+        final QName listKeyQName = QName.create("test-ns", "test-list-key");
+        final NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
+                NodeIdentifierWithPredicates.of(listQName, listKeyQName, "str-value");
+        final MapEntryNode mockedMapEntryNode = mock(MapEntryNode.class);
+        doReturn(Optional.of(mockedMapEntryNode)).when(mockedMapNode).getChild(any(NodeIdentifierWithPredicates.class));
+        assertEquals(mockedMapEntryNode, NormalizedNodes.getDirectChild(mockedMapNode, nodeIdentifierWithPredicates)
+                .get());
+        assertEquals(Optional.empty(), NormalizedNodes.getDirectChild(mockedMapNode, mockedPathArgument));
+
+        final LeafSetNode<?> mockedLeafSetNode = mock(LeafSetNode.class);
+        final QName leafListQName = QName.create("test-ns", "test-leaf-list");
+        final NodeWithValue<?> nodeWithValue = new NodeWithValue<>(leafListQName, "str-value");
+        doReturn(Optional.of(mockedLeafSetEntryNode)).when(mockedLeafSetNode).getChild(any(NodeWithValue.class));
+        assertEquals(mockedLeafSetEntryNode, NormalizedNodes.getDirectChild(mockedLeafSetNode, nodeWithValue).get());
+    }
+
+    @Test
+    public void testFindNode() {
+        final DataContainerNode<?> mockedDataContainerNode = mock(DataContainerNode.class);
+        final ContainerNode mockedContainerNode = mock(ContainerNode.class);
+        final LeafNode<?> mockedLeafNode = mock(LeafNode.class);
+        doReturn(Optional.of(mockedContainerNode)).when(mockedDataContainerNode).getChild(any(PathArgument.class));
+        doReturn(Optional.of(mockedLeafNode)).when(mockedContainerNode).getChild(any(PathArgument.class));
+
+        final QName node1QName = QName.create("test-ns", "2016-09-16", "node1");
+        final QName node2Qname = QName.create("test-ns", "2016-09-16", "node2");
+        final QName node3QName = QName.create("test-ns", "2016-09-16", "node3");
+        final QName node4Qname = QName.create("test-ns", "2016-09-16", "node4");
+
+        final YangInstanceIdentifier rootPath = YangInstanceIdentifier.create(new NodeIdentifier(node1QName),
+                new NodeIdentifier(node2Qname));
+        final YangInstanceIdentifier childPath = YangInstanceIdentifier.create(new NodeIdentifier(node1QName),
+                new NodeIdentifier(node2Qname), new NodeIdentifier(node3QName), new NodeIdentifier(node4Qname));
+
+        assertEquals(mockedLeafNode, NormalizedNodes.findNode(rootPath, mockedDataContainerNode, childPath).get());
+        assertEquals(Optional.empty(), NormalizedNodes.findNode(childPath, mockedDataContainerNode, rootPath));
+
+        final Optional<YangInstanceIdentifier> relativePath = childPath.relativeTo(rootPath);
+        final PathArgument[] pathArguments = relativePath.get().getPathArguments().toArray(new PathArgument[2]);
+
+        assertEquals(mockedLeafNode, NormalizedNodes.findNode(Optional.of(mockedDataContainerNode),
+                pathArguments).get());
+
+        assertEquals(mockedLeafNode, NormalizedNodes.findNode(mockedDataContainerNode, pathArguments).get());
+    }
+
+    @Test
+    public void testToStringTree() {
+        final LeafNode<?> mockedLeafNode = mock(LeafNode.class);
+        final QName leafNodeQName = QName.create("test-ns", "2016-09-16", "leaf-node");
+        final NodeIdentifier leafNodeId = new NodeIdentifier(leafNodeQName);
+        doReturn(leafNodeId).when(mockedLeafNode).getIdentifier();
+        doReturn("str-value-1").when(mockedLeafNode).getValue();
+
+        String stringTree = NormalizedNodes.toStringTree(mockedLeafNode);
+        assertNotNull(stringTree);
+        assertEquals("leaf-node str-value-1\n", stringTree);
+
+        final AugmentationNode mockedAugmentationNode = mock(AugmentationNode.class);
+        final QName listQName = QName.create("test-ns", "2016-09-16", "list-node");
+        final AugmentationIdentifier augNodeId = new AugmentationIdentifier(ImmutableSet.of(listQName));
+        doReturn(augNodeId).when(mockedAugmentationNode).getIdentifier();
+
+        final MapNode mockedMapNode = mock(MapNode.class);
+        final NodeIdentifier listNodeId = new NodeIdentifier(listQName);
+        doReturn(listNodeId).when(mockedMapNode).getIdentifier();
+        doReturn(Collections.singletonList(mockedMapNode)).when(mockedAugmentationNode).getValue();
+
+        final MapEntryNode mockedMapEntryNode = mock(MapEntryNode.class);
+        final NodeIdentifierWithPredicates listEntryNodeId = NodeIdentifierWithPredicates.of(listQName,
+                leafNodeQName, "key-leaf-value");
+        doReturn(listEntryNodeId).when(mockedMapEntryNode).getIdentifier();
+        doReturn(Collections.singletonList(mockedMapEntryNode)).when(mockedMapNode).getValue();
+
+        doReturn(Collections.singletonList(mockedLeafNode)).when(mockedMapEntryNode).getValue();
+
+        stringTree = NormalizedNodes.toStringTree(mockedAugmentationNode);
+        assertNotNull(stringTree);
+        assertEquals("augmentation {\n    list-node {\n        list-node[key-leaf-value] {\n            leaf-node "
+                + "str-value-1\n        }\n    }\n}\n", stringTree);
+    }
+}
diff --git a/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriterTest.java b/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriterTest.java
new file mode 100644 (file)
index 0000000..1fd932b
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2016 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Optional;
+import java.util.Set;
+import javax.xml.transform.dom.DOMSource;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode;
+
+public class NormalizedNodeWriterTest {
+
+    private QNameModule bazModule;
+
+    private QName myKeyedList;
+    private QName myKeyLeaf;
+    private QName myLeafList;
+
+    @Before
+    public void setUp() {
+        bazModule = QNameModule.create(URI.create("baz-namespace"), Revision.of("1970-01-01"));
+        myKeyedList = QName.create(bazModule, "my-keyed-list");
+        myKeyLeaf = QName.create(bazModule, "my-key-leaf");
+        myLeafList = QName.create(bazModule, "my-leaf-list");
+    }
+
+    @Test
+    public void testNormalizedNodeWriter() throws IOException {
+        final NormalizedNodeStreamWriter loggingNormalizedNodeStreamWriter = new LoggingNormalizedNodeStreamWriter();
+        final NormalizedNodeWriter orderedNormalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(
+                loggingNormalizedNodeStreamWriter);
+
+        assertEquals(loggingNormalizedNodeStreamWriter, orderedNormalizedNodeWriter.getWriter());
+
+        final NormalizedNode<?, ?> mockedNormalizedNode = mock(NormalizedNode.class);
+        try {
+            orderedNormalizedNodeWriter.write(mockedNormalizedNode);
+            fail("An IllegalStateException should have been thrown!");
+        } catch (IllegalStateException ex) {
+            assertTrue(ex.getMessage().startsWith("It wasn't possible to serialize node"));
+        }
+
+        final NormalizedNode<?, ?> mockedLeafSetEntryNode = mock(LeafSetEntryNode.class);
+        doReturn(new NodeWithValue<>(myLeafList, "leaflist-value-1")).when(mockedLeafSetEntryNode).getIdentifier();
+        doReturn("leaflist-value-1").when(mockedLeafSetEntryNode).getValue();
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedLeafSetEntryNode));
+
+        final NormalizedNode<?, ?> mockedLeafNode = mock(LeafNode.class);
+        doReturn("leaf-value-1").when(mockedLeafNode).getValue();
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedLeafNode));
+
+        final DOMSourceAnyxmlNode mockedAnyXmlNode = mock(DOMSourceAnyxmlNode.class);
+        doCallRealMethod().when(mockedAnyXmlNode).getValueObjectModel();
+        doReturn(new DOMSource()).when(mockedAnyXmlNode).getValue();
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedAnyXmlNode));
+
+        final NormalizedNode<?, ?> mockedContainerNode = mock(ContainerNode.class);
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedContainerNode));
+
+        final NormalizedNode<?, ?> mockedYangModeledAnyXmlNode = mock(YangModeledAnyXmlNode.class);
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedYangModeledAnyXmlNode));
+
+        final MapEntryNode mockedMapEntryNode = mock(MapEntryNode.class);
+        doReturn(NodeIdentifierWithPredicates.of(myKeyedList, myKeyLeaf, "list-key-value-1"))
+                .when(mockedMapEntryNode).getIdentifier();
+        doReturn(Optional.empty()).when(mockedMapEntryNode).getChild(any(NodeIdentifier.class));
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedMapEntryNode));
+
+        final UnkeyedListEntryNode mockedUnkeyedListEntryNode = mock(UnkeyedListEntryNode.class);
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedUnkeyedListEntryNode));
+
+        final ChoiceNode mockedChoiceNode = mock(ChoiceNode.class);
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedChoiceNode));
+
+        final AugmentationNode mockedAugmentationNode = mock(AugmentationNode.class);
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedAugmentationNode));
+
+        final UnkeyedListNode mockedUnkeyedListNode = mock(UnkeyedListNode.class);
+        final Set<?> value = ImmutableSet.builder().add(mockedUnkeyedListEntryNode).build();
+        doReturn(value).when(mockedUnkeyedListNode).getValue();
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedUnkeyedListNode));
+
+        final OrderedMapNode mockedOrderedMapNode = mock(OrderedMapNode.class);
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedOrderedMapNode));
+
+        final MapNode mockedMapNode = mock(MapNode.class);
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedMapNode));
+
+        final OrderedLeafSetNode<?> mockedOrderedLeafSetNode = mock(OrderedLeafSetNode.class);
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedOrderedLeafSetNode));
+
+        final LeafSetNode<?> mockedLeafSetNode = mock(LeafSetNode.class);
+        assertNotNull(orderedNormalizedNodeWriter.write(mockedLeafSetNode));
+
+        orderedNormalizedNodeWriter.flush();
+        orderedNormalizedNodeWriter.close();
+
+        final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(
+                loggingNormalizedNodeStreamWriter, false);
+
+        assertNotNull(normalizedNodeWriter.write(mockedMapEntryNode));
+
+        normalizedNodeWriter.flush();
+        normalizedNodeWriter.close();
+    }
+}
diff --git a/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateNodesTest.java b/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateNodesTest.java
new file mode 100644 (file)
index 0000000..056bb95
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class DataTreeCandidateNodesTest {
+
+    @Test
+    public void testFromNormalizedNode() {
+        final NormalizedNode<?, ?> mockedNormalizedNode = mock(NormalizedNode.class);
+        final DataTreeCandidateNode dataTreeCandidateNode = DataTreeCandidateNodes.written(mockedNormalizedNode);
+        assertNotNull(dataTreeCandidateNode);
+    }
+
+    @Test
+    public void testApplyToCursorWithWriteModificationType() {
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+
+        doReturn(ModificationType.WRITE).when(mockedDataTreeCandidateNode).getModificationType();
+        final NormalizedNode<?, ?> mockedNormalizedNode = mock(NormalizedNode.class);
+        doReturn(Optional.of(mockedNormalizedNode)).when(mockedDataTreeCandidateNode).getDataAfter();
+        DataTreeCandidateNodes.applyToCursor(mockedCursor, mockedDataTreeCandidateNode);
+        verify(mockedCursor, times(1)).write(isNull(), any(NormalizedNode.class));
+    }
+
+    @Test
+    public void testApplyToCursorWithDeleteModificationType() {
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+
+        doReturn(ModificationType.DELETE).when(mockedDataTreeCandidateNode).getModificationType();
+        DataTreeCandidateNodes.applyToCursor(mockedCursor, mockedDataTreeCandidateNode);
+        verify(mockedCursor, times(1)).delete(isNull());
+    }
+
+    @Test
+    public void testApplyToCursorWithSubtreeModifiedModificationType() {
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+
+        doReturn(ModificationType.SUBTREE_MODIFIED).when(mockedDataTreeCandidateNode).getModificationType();
+
+        final DataTreeCandidateNode mockedChildNode1 = mock(DataTreeCandidateNode.class);
+        doReturn(ModificationType.DELETE).when(mockedChildNode1).getModificationType();
+
+        final DataTreeCandidateNode mockedChildNode2 = mock(DataTreeCandidateNode.class);
+        doReturn(ModificationType.WRITE).when(mockedChildNode2).getModificationType();
+        final NormalizedNode<?, ?> mockedNormalizedNode = mock(NormalizedNode.class);
+        doReturn(Optional.of(mockedNormalizedNode)).when(mockedChildNode2).getDataAfter();
+
+        final DataTreeCandidateNode mockedChildNode3 = mock(DataTreeCandidateNode.class);
+        doReturn(ModificationType.SUBTREE_MODIFIED).when(mockedChildNode3).getModificationType();
+        final DataTreeCandidateNode mockedChildNode3ChildNode = mock(DataTreeCandidateNode.class);
+        doReturn(ModificationType.DELETE).when(mockedChildNode3ChildNode).getModificationType();
+        doReturn(Collections.singletonList(mockedChildNode3ChildNode)).when(mockedChildNode3).getChildNodes();
+
+        final Collection<DataTreeCandidateNode> childNodes = ImmutableList.of(mockedChildNode1, mockedChildNode2,
+                mockedChildNode3);
+        doReturn(childNodes).when(mockedDataTreeCandidateNode).getChildNodes();
+
+        DataTreeCandidateNodes.applyToCursor(mockedCursor, mockedDataTreeCandidateNode);
+        verify(mockedCursor, times(2)).enter((PathArgument) isNull());
+        verify(mockedCursor, times(2)).delete(isNull());
+        verify(mockedCursor, times(1)).write(isNull(), any(NormalizedNode.class));
+    }
+
+    @Test
+    public void testApplyToCursorWithUnsupportedModificationType() {
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+
+        doReturn(ModificationType.APPEARED).when(mockedDataTreeCandidateNode).getModificationType();
+        try {
+            DataTreeCandidateNodes.applyToCursor(mockedCursor, mockedDataTreeCandidateNode);
+            fail("An IllegalArgumentException should have been thrown!");
+        } catch (IllegalArgumentException ex) {
+            assertTrue(ex.getMessage().contains("Unsupported modification"));
+        }
+    }
+
+    @Test
+    public void testApplyRootedNodeToCursorWithWriteModificationType() {
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        final YangInstanceIdentifier mockedRootPath = mock(YangInstanceIdentifier.class);
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+
+        doReturn(ModificationType.WRITE).when(mockedDataTreeCandidateNode).getModificationType();
+        final NormalizedNode<?, ?> mockedNormalizedNode = mock(NormalizedNode.class);
+        doReturn(Optional.of(mockedNormalizedNode)).when(mockedDataTreeCandidateNode).getDataAfter();
+        DataTreeCandidateNodes.applyRootedNodeToCursor(mockedCursor, mockedRootPath, mockedDataTreeCandidateNode);
+        verify(mockedCursor, times(1)).write(isNull(), any(NormalizedNode.class));
+    }
+
+    @Test
+    public void testApplyRootedNodeToCursorWithDeleteModificationType() {
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        final YangInstanceIdentifier mockedRootPath = mock(YangInstanceIdentifier.class);
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+
+        doReturn(ModificationType.DELETE).when(mockedDataTreeCandidateNode).getModificationType();
+        DataTreeCandidateNodes.applyRootedNodeToCursor(mockedCursor, mockedRootPath, mockedDataTreeCandidateNode);
+        verify(mockedCursor, times(1)).delete(isNull());
+    }
+
+    @Test
+    public void testApplyRootedNodeToCursorWithSubtreeModifiedModificationType() {
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        final YangInstanceIdentifier mockedRootPath = mock(YangInstanceIdentifier.class);
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+
+        doReturn(ModificationType.SUBTREE_MODIFIED).when(mockedDataTreeCandidateNode).getModificationType();
+
+        final DataTreeCandidateNode mockedChildNode1 = mock(DataTreeCandidateNode.class);
+        doReturn(ModificationType.DELETE).when(mockedChildNode1).getModificationType();
+        doReturn(Collections.singletonList(mockedChildNode1)).when(mockedDataTreeCandidateNode).getChildNodes();
+
+        DataTreeCandidateNodes.applyRootedNodeToCursor(mockedCursor, mockedRootPath, mockedDataTreeCandidateNode);
+        verify(mockedCursor, times(1)).enter((PathArgument) isNull());
+        verify(mockedCursor, times(1)).delete(isNull());
+    }
+
+    @Test
+    public void testApplyRootedNodeToCursorWithUnsupportedModificationType() {
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        final YangInstanceIdentifier mockedRootPath = mock(YangInstanceIdentifier.class);
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+
+        doReturn(ModificationType.APPEARED).when(mockedDataTreeCandidateNode).getModificationType();
+        try {
+            DataTreeCandidateNodes.applyRootedNodeToCursor(mockedCursor, mockedRootPath, mockedDataTreeCandidateNode);
+            fail("An IllegalArgumentException should have been thrown!");
+        } catch (IllegalArgumentException ex) {
+            assertTrue(ex.getMessage().contains("Unsupported modification"));
+        }
+    }
+
+    @Test
+    public void testApplyRootToCursorWithSubtreeModifiedModificationType() {
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+
+        doReturn(ModificationType.SUBTREE_MODIFIED).when(mockedDataTreeCandidateNode).getModificationType();
+
+        final DataTreeCandidateNode mockedChildNode1 = mock(DataTreeCandidateNode.class);
+        doReturn(ModificationType.DELETE).when(mockedChildNode1).getModificationType();
+        doReturn(Collections.singletonList(mockedChildNode1)).when(mockedDataTreeCandidateNode).getChildNodes();
+
+        DataTreeCandidateNodes.applyRootToCursor(mockedCursor, mockedDataTreeCandidateNode);
+        verify(mockedCursor, times(1)).delete(isNull());
+    }
+
+    @Test
+    public void testApplyRootToCursorWithDeleteModificationType() {
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+
+        doReturn(ModificationType.DELETE).when(mockedDataTreeCandidateNode).getModificationType();
+        try {
+            DataTreeCandidateNodes.applyRootToCursor(mockedCursor, mockedDataTreeCandidateNode);
+            fail("An IllegalArgumentException should have been thrown!");
+        } catch (IllegalArgumentException ex) {
+            assertTrue(ex.getMessage().contains("Can not delete root"));
+        }
+    }
+
+    @Test
+    public void testApplyRootToCursorWithUnsupportedModificationType() {
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+
+        doReturn(ModificationType.APPEARED).when(mockedDataTreeCandidateNode).getModificationType();
+        try {
+            DataTreeCandidateNodes.applyRootToCursor(mockedCursor, mockedDataTreeCandidateNode);
+            fail("An IllegalArgumentException should have been thrown!");
+        } catch (IllegalArgumentException ex) {
+            assertTrue(ex.getMessage().contains("Unsupported modification"));
+        }
+    }
+}
diff --git a/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidatesTest.java b/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidatesTest.java
new file mode 100644 (file)
index 0000000..c5944c2
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class DataTreeCandidatesTest {
+
+    @Test
+    public void testNewDataTreeCandidate() {
+        final YangInstanceIdentifier mockedRootPath = mock(YangInstanceIdentifier.class);
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        final DataTreeCandidate dataTreeCandidate = DataTreeCandidates.newDataTreeCandidate(mockedRootPath,
+                mockedDataTreeCandidateNode);
+
+        assertNotNull(dataTreeCandidate);
+        assertTrue(dataTreeCandidate instanceof DefaultDataTreeCandidate);
+        assertEquals(mockedRootPath, dataTreeCandidate.getRootPath());
+        assertEquals(mockedDataTreeCandidateNode, dataTreeCandidate.getRootNode());
+        assertTrue(dataTreeCandidate.toString().contains(
+                "DefaultDataTreeCandidate{rootPath=/, rootNode=Mock for DataTreeCandidateNode, hashCode: "));
+    }
+
+    @Test
+    public void testFromNormalizedNode() {
+        final YangInstanceIdentifier mockedRootPath = mock(YangInstanceIdentifier.class);
+        final NormalizedNode<?, ?> mockedNormalizedNode = mock(NormalizedNode.class);
+        final DataTreeCandidate dataTreeCandidate = DataTreeCandidates.fromNormalizedNode(mockedRootPath,
+                mockedNormalizedNode);
+
+        assertNotNull(dataTreeCandidate);
+        assertTrue(dataTreeCandidate instanceof DefaultDataTreeCandidate);
+        assertEquals(mockedRootPath, dataTreeCandidate.getRootPath());
+        assertTrue(dataTreeCandidate.getRootNode() instanceof NormalizedNodeDataTreeCandidateNode);
+    }
+
+    @Test
+    public void testApplyToCursor() {
+        final DataTreeCandidate mockedDataTreeCandidate = mock(DataTreeCandidate.class);
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        doReturn(ModificationType.DELETE).when(mockedDataTreeCandidateNode).getModificationType();
+        doReturn(mockedDataTreeCandidateNode).when(mockedDataTreeCandidate).getRootNode();
+        DataTreeCandidates.applyToCursor(mockedCursor, mockedDataTreeCandidate);
+        verify(mockedCursor, times(1)).delete(isNull());
+    }
+
+    @Test
+    public void testApplyToCursorAwareModification() {
+        final DataTreeCandidate mockedDataTreeCandidate = mock(DataTreeCandidate.class);
+        final CursorAwareDataTreeModification mockedModification = mock(CursorAwareDataTreeModification.class);
+
+        final YangInstanceIdentifier mockedRootPath = mock(YangInstanceIdentifier.class);
+        doReturn(mockedRootPath).when(mockedDataTreeCandidate).getRootPath();
+        final DataTreeModificationCursor mockedCursor = mock(DataTreeModificationCursor.class);
+        doReturn(Optional.of(mockedCursor)).when(mockedModification).openCursor(isNull());
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        doReturn(mockedDataTreeCandidateNode).when(mockedDataTreeCandidate).getRootNode();
+
+        doReturn(ModificationType.DELETE).when(mockedDataTreeCandidateNode).getModificationType();
+
+        DataTreeCandidates.applyToModification(mockedModification, mockedDataTreeCandidate);
+        verify(mockedModification, times(1)).openCursor(isNull());
+        verify(mockedCursor, times(1)).delete(isNull());
+
+        doReturn(Boolean.TRUE).when(mockedRootPath).isEmpty();
+        try {
+            DataTreeCandidates.applyToModification(mockedModification, mockedDataTreeCandidate);
+            fail("An IllegalArgumentException should have been thrown!");
+        } catch (IllegalArgumentException ex) {
+            assertTrue(ex.getMessage().contains("Can not delete root"));
+        }
+    }
+
+    @Test
+    public void testApplyToModificationWithDeleteModificationType() {
+        final DataTreeCandidate mockedDataTreeCandidate = mock(DataTreeCandidate.class);
+        final DataTreeModification mockedModification = mock(DataTreeModification.class);
+
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        doReturn(mockedDataTreeCandidateNode).when(mockedDataTreeCandidate).getRootNode();
+        final YangInstanceIdentifier mockedRootPath = mock(YangInstanceIdentifier.class);
+        doReturn(mockedRootPath).when(mockedDataTreeCandidate).getRootPath();
+
+        doReturn(ModificationType.DELETE).when(mockedDataTreeCandidateNode).getModificationType();
+
+        DataTreeCandidates.applyToModification(mockedModification, mockedDataTreeCandidate);
+        verify(mockedModification, times(1)).delete(any(YangInstanceIdentifier.class));
+    }
+
+    @Test
+    public void testApplyToModificationWithWriteModificationType() {
+        final DataTreeCandidate mockedDataTreeCandidate = mock(DataTreeCandidate.class);
+        final DataTreeModification mockedModification = mock(DataTreeModification.class);
+
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        doReturn(mockedDataTreeCandidateNode).when(mockedDataTreeCandidate).getRootNode();
+        final YangInstanceIdentifier mockedRootPath = mock(YangInstanceIdentifier.class);
+        doReturn(mockedRootPath).when(mockedDataTreeCandidate).getRootPath();
+        final NormalizedNode<?, ?> mockedNormalizedNode = mock(NormalizedNode.class);
+        doReturn(Optional.of(mockedNormalizedNode)).when(mockedDataTreeCandidateNode).getDataAfter();
+
+        doReturn(ModificationType.WRITE).when(mockedDataTreeCandidateNode).getModificationType();
+
+        DataTreeCandidates.applyToModification(mockedModification, mockedDataTreeCandidate);
+        verify(mockedModification, times(1)).write(any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+    }
+
+    @Test
+    public void testApplyToModificationWithSubtreeModifiedModificationType() {
+        final DataTreeCandidate mockedDataTreeCandidate = mock(DataTreeCandidate.class);
+        final DataTreeModification mockedModification = mock(DataTreeModification.class);
+
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        doReturn(mockedDataTreeCandidateNode).when(mockedDataTreeCandidate).getRootNode();
+        final YangInstanceIdentifier mockedRootPath = mock(YangInstanceIdentifier.class);
+        doReturn(mockedRootPath).when(mockedDataTreeCandidate).getRootPath();
+
+        doReturn(ModificationType.SUBTREE_MODIFIED).when(mockedDataTreeCandidateNode).getModificationType();
+
+        final DataTreeCandidateNode mockedChildNode1 = mock(DataTreeCandidateNode.class);
+        doReturn(ModificationType.DELETE).when(mockedChildNode1).getModificationType();
+        final PathArgument mockedPathArgument1 = mock(PathArgument.class);
+        doReturn(mockedPathArgument1).when(mockedChildNode1).getIdentifier();
+
+        final DataTreeCandidateNode mockedChildNode2 = mock(DataTreeCandidateNode.class);
+        doReturn(ModificationType.WRITE).when(mockedChildNode2).getModificationType();
+        final NormalizedNode<?, ?> mockedNormalizedNode = mock(NormalizedNode.class);
+        doReturn(Optional.of(mockedNormalizedNode)).when(mockedChildNode2).getDataAfter();
+        final PathArgument mockedPathArgument2 = mock(PathArgument.class);
+        doReturn(mockedPathArgument2).when(mockedChildNode2).getIdentifier();
+
+        final DataTreeCandidateNode mockedChildNode3 = mock(DataTreeCandidateNode.class);
+        doReturn(ModificationType.SUBTREE_MODIFIED).when(mockedChildNode3).getModificationType();
+        final PathArgument mockedPathArgument3 = mock(PathArgument.class);
+        doReturn(mockedPathArgument3).when(mockedChildNode3).getIdentifier();
+        final DataTreeCandidateNode mockedChildNode3ChildNode = mock(DataTreeCandidateNode.class);
+        doReturn(ModificationType.DELETE).when(mockedChildNode3ChildNode).getModificationType();
+        final PathArgument mockedPathArgument31 = mock(PathArgument.class);
+        doReturn(mockedPathArgument3).when(mockedChildNode3).getIdentifier();
+        doReturn(mockedPathArgument31).when(mockedChildNode3ChildNode).getIdentifier();
+        doReturn(Collections.singletonList(mockedChildNode3ChildNode)).when(mockedChildNode3).getChildNodes();
+
+        final Collection<DataTreeCandidateNode> childNodes = ImmutableList.of(mockedChildNode1, mockedChildNode2,
+                mockedChildNode3);
+        doReturn(childNodes).when(mockedDataTreeCandidateNode).getChildNodes();
+
+        DataTreeCandidates.applyToModification(mockedModification, mockedDataTreeCandidate);
+        verify(mockedModification, times(2)).delete(any(YangInstanceIdentifier.class));
+        verify(mockedModification, times(1)).write(any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+    }
+
+    @Test
+    public void testApplyToModificationWithUnsupportedModificationType() {
+        final DataTreeCandidate mockedDataTreeCandidate = mock(DataTreeCandidate.class);
+        final DataTreeModification mockedModification = mock(DataTreeModification.class);
+
+        final DataTreeCandidateNode mockedDataTreeCandidateNode = mock(DataTreeCandidateNode.class);
+        doReturn(mockedDataTreeCandidateNode).when(mockedDataTreeCandidate).getRootNode();
+        final YangInstanceIdentifier mockedRootPath = mock(YangInstanceIdentifier.class);
+        doReturn(mockedRootPath).when(mockedDataTreeCandidate).getRootPath();
+
+        doReturn(ModificationType.APPEARED).when(mockedDataTreeCandidateNode).getModificationType();
+
+        try {
+            DataTreeCandidates.applyToModification(mockedModification, mockedDataTreeCandidate);
+            fail("An IllegalArgumentException should have been thrown!");
+        } catch (IllegalArgumentException ex) {
+            assertTrue(ex.getMessage().contains("Unsupported modification"));
+        }
+    }
+}
diff --git a/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeConfigurationTest.java b/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeConfigurationTest.java
new file mode 100644 (file)
index 0000000..b3688ba
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class DataTreeConfigurationTest {
+
+    @Test
+    public void testDataTreeConfiguration() {
+        DataTreeConfiguration.Builder builder = new DataTreeConfiguration.Builder(TreeType.CONFIGURATION);
+        builder.setUniqueIndexes(true);
+        builder.setMandatoryNodesValidation(true);
+
+        DataTreeConfiguration dataTreeConfiguration = builder.build();
+        assertEquals(TreeType.CONFIGURATION, dataTreeConfiguration.getTreeType());
+        assertTrue(dataTreeConfiguration.isUniqueIndexEnabled());
+        assertTrue(dataTreeConfiguration.isMandatoryNodesValidationEnabled());
+
+        builder = new DataTreeConfiguration.Builder(TreeType.OPERATIONAL);
+        builder.setUniqueIndexes(false);
+        builder.setMandatoryNodesValidation(false);
+
+        dataTreeConfiguration = builder.build();
+        assertEquals(TreeType.OPERATIONAL, dataTreeConfiguration.getTreeType());
+        assertFalse(dataTreeConfiguration.isUniqueIndexEnabled());
+        assertFalse(dataTreeConfiguration.isMandatoryNodesValidationEnabled());
+
+        dataTreeConfiguration = DataTreeConfiguration.getDefault(TreeType.CONFIGURATION);
+        assertEquals(TreeType.CONFIGURATION, dataTreeConfiguration.getTreeType());
+        assertFalse(dataTreeConfiguration.isUniqueIndexEnabled());
+        assertTrue(dataTreeConfiguration.isMandatoryNodesValidationEnabled());
+
+        dataTreeConfiguration = DataTreeConfiguration.getDefault(TreeType.OPERATIONAL);
+        assertEquals(TreeType.OPERATIONAL, dataTreeConfiguration.getTreeType());
+        assertFalse(dataTreeConfiguration.isUniqueIndexEnabled());
+        assertTrue(dataTreeConfiguration.isMandatoryNodesValidationEnabled());
+    }
+}
diff --git a/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/NormalizedNodeDataTreeCandidateNodeTest.java b/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/NormalizedNodeDataTreeCandidateNodeTest.java
new file mode 100644 (file)
index 0000000..d7463ae
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+
+public class NormalizedNodeDataTreeCandidateNodeTest {
+
+    @Test
+    public void testNormalizedNodeDataTreeCandidateNode() {
+        final NormalizedNode<?, ?> mockedNormalizedNode = mock(NormalizedNode.class);
+        final NormalizedNodeDataTreeCandidateNode normalizedNodeDataTreeCandidateNode = new
+                NormalizedNodeDataTreeCandidateNode(mockedNormalizedNode);
+
+        final PathArgument mockedPathArgument = mock(PathArgument.class);
+        doReturn(mockedPathArgument).when(mockedNormalizedNode).getIdentifier();
+        assertEquals(mockedPathArgument, normalizedNodeDataTreeCandidateNode.getIdentifier());
+
+        final Collection<DataTreeCandidateNode> childNodes = normalizedNodeDataTreeCandidateNode.getChildNodes();
+        assertTrue(childNodes instanceof List);
+        assertTrue(childNodes.isEmpty());
+
+        assertEquals(Optional.empty(), normalizedNodeDataTreeCandidateNode.getModifiedChild(mockedPathArgument));
+
+        assertEquals(ModificationType.WRITE, normalizedNodeDataTreeCandidateNode.getModificationType());
+        assertEquals(Optional.of(mockedNormalizedNode), normalizedNodeDataTreeCandidateNode.getDataAfter());
+        assertEquals(Optional.empty(), normalizedNodeDataTreeCandidateNode.getDataBefore());
+
+        final NormalizedNodeContainer mockedNormalizedNodeContainer = mock(NormalizedNodeContainer.class);
+        final NormalizedNodeDataTreeCandidateNode normalizedNodeDataTreeCandidateNode2 = new
+                NormalizedNodeDataTreeCandidateNode(mockedNormalizedNodeContainer);
+        final NormalizedNode<?, ?> mockedChildNormNode1 = mock(NormalizedNode.class);
+        final NormalizedNode<?, ?> mockedChildNormNode2 = mock(NormalizedNode.class);
+        final Collection<NormalizedNode<?, ?>> mockedChildNodes = Arrays.asList(mockedChildNormNode1,
+                mockedChildNormNode2, null);
+        doReturn(mockedChildNodes).when(mockedNormalizedNodeContainer).getValue();
+        final Collection<DataTreeCandidateNode> childNodes2 = normalizedNodeDataTreeCandidateNode2.getChildNodes();
+        assertEquals(3, childNodes2.size());
+
+        doReturn(Optional.empty()).when(mockedNormalizedNodeContainer).getChild(any(PathArgument.class));
+        assertEquals(Optional.empty(), normalizedNodeDataTreeCandidateNode2.getModifiedChild(mockedPathArgument));
+
+        doReturn(Optional.of(mockedChildNormNode1)).when(mockedNormalizedNodeContainer).getChild(
+                any(PathArgument.class));
+        assertTrue(normalizedNodeDataTreeCandidateNode2.getModifiedChild(mockedPathArgument).isPresent());
+    }
+}
diff --git a/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/DuplicityTest.java b/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/DuplicityTest.java
new file mode 100644 (file)
index 0000000..c9fe7cd
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import java.util.Map;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DuplicateEntry;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+
+public class DuplicityTest {
+
+    @Test
+    public void testDuplicate() {
+        final LeafNode<?> leafNode = mock(LeafNode.class);
+        final ContainerNode containerNode = mock(ContainerNode.class);
+        final Map<NormalizedNode<?, ?>, DuplicateEntry> normalizedNodeDuplicateEntryMapNode = NormalizedNodes
+                .findDuplicates(leafNode);
+        final Map<NormalizedNode<?, ?>, DuplicateEntry> normalizedNodeDuplicateEntryMapContainer = NormalizedNodes
+                .findDuplicates(containerNode);
+        assertEquals(0, normalizedNodeDuplicateEntryMapNode.size());
+        assertEquals(0, normalizedNodeDuplicateEntryMapContainer.size());
+    }
+}
diff --git a/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNodeFactoryTest.java b/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNodeFactoryTest.java
new file mode 100644 (file)
index 0000000..a26e381
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+
+public class TreeNodeFactoryTest {
+
+    private static void checkTreeNode(final TreeNode node, final NormalizedNode<?, ?> data, final Version version) {
+        assertSame(data, node.getData());
+        assertSame(version, node.getSubtreeVersion());
+        assertSame(version, node.getVersion());
+    }
+
+    @Test
+    public void testNormalizedNodeContainer() {
+        final ContainerNode data = Mockito.mock(ContainerNode.class);
+        final Version version = Version.initial();
+        final TreeNode node = TreeNodeFactory.createTreeNode(data, version);
+
+        assertTrue(node instanceof SimpleContainerNode);
+        checkTreeNode(node, data, version);
+    }
+
+    @Test
+    public void testOrderedNodeContainer() {
+        final OrderedMapNode data = Mockito.mock(OrderedMapNode.class);
+        final Version version = Version.initial();
+        final TreeNode node = TreeNodeFactory.createTreeNode(data, version);
+
+        assertTrue(node instanceof SimpleContainerNode);
+        checkTreeNode(node, data, version);
+    }
+
+    @Test
+    public void testLeaf() {
+        final LeafNode<?> data = Mockito.mock(LeafNode.class);
+        final Version version = Version.initial();
+        final TreeNode node = TreeNodeFactory.createTreeNode(data, version);
+
+        assertTrue(node instanceof ValueNode);
+        checkTreeNode(node, data, version);
+    }
+}
diff --git a/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/VersionTest.java b/yang/yang-data-api/src/test/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/VersionTest.java
new file mode 100644 (file)
index 0000000..f405345
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
+
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+public class VersionTest {
+
+    @Test
+    public void testInitial() {
+        final Version v1 = Version.initial();
+        final Version v2 = Version.initial();
+
+        assertFalse(v1.equals(v2));
+        assertFalse(v2.equals(v1));
+    }
+
+    @Test
+    public void testNext() {
+        final Version v1 = Version.initial();
+        final Version v2 = v1.next();
+        final Version v3 = v2.next();
+        final Version v4 = v1.next();
+
+        assertFalse(v3.equals(v4));
+        assertFalse(v4.equals(v3));
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/pom.xml b/yang/yang-data-codec-binfmt/pom.xml
new file mode 100644 (file)
index 0000000..1807833
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-data-codec-binfmt</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <!-- FIXME: add RFC7952 (metadata) support
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-data-api</artifactId>
+        </dependency-->
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-test-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.data.codec.binfmt</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
+
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractLithiumDataInput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractLithiumDataInput.java
new file mode 100644 (file)
index 0000000..c052dbf
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 2014, 2015 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.binfmt;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.Sets;
+import java.io.DataInput;
+import java.io.IOException;
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.yangtools.util.ImmutableOffsetMapTemplate;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * NormalizedNodeInputStreamReader reads the byte stream and constructs the normalized node including its children
+ * nodes. This process goes in recursive manner, where each NodeTypes object signifies the start of the object, except
+ * END_NODE. If a node can have children, then that node's end is calculated based on appearance of END_NODE.
+ */
+abstract class AbstractLithiumDataInput extends AbstractNormalizedNodeDataInput {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractLithiumDataInput.class);
+
+    private final List<String> codedStringMap = new ArrayList<>();
+
+    private QName lastLeafSetQName;
+
+    AbstractLithiumDataInput(final DataInput input) {
+        super(input);
+    }
+
+    @Override
+    public final void streamNormalizedNode(final NormalizedNodeStreamWriter writer) throws IOException {
+        streamNormalizedNode(requireNonNull(writer), input.readByte());
+    }
+
+    private void streamNormalizedNode(final NormalizedNodeStreamWriter writer, final byte nodeType) throws IOException {
+        switch (nodeType) {
+            case LithiumNode.ANY_XML_NODE:
+                streamAnyxml(writer);
+                break;
+            case LithiumNode.AUGMENTATION_NODE:
+                streamAugmentation(writer);
+                break;
+            case LithiumNode.CHOICE_NODE:
+                streamChoice(writer);
+                break;
+            case LithiumNode.CONTAINER_NODE:
+                streamContainer(writer);
+                break;
+            case LithiumNode.LEAF_NODE:
+                streamLeaf(writer);
+                break;
+            case LithiumNode.LEAF_SET:
+                streamLeafSet(writer);
+                break;
+            case LithiumNode.ORDERED_LEAF_SET:
+                streamOrderedLeafSet(writer);
+                break;
+            case LithiumNode.LEAF_SET_ENTRY_NODE:
+                streamLeafSetEntry(writer);
+                break;
+            case LithiumNode.MAP_ENTRY_NODE:
+                streamMapEntry(writer);
+                break;
+            case LithiumNode.MAP_NODE:
+                streamMap(writer);
+                break;
+            case LithiumNode.ORDERED_MAP_NODE:
+                streamOrderedMap(writer);
+                break;
+            case LithiumNode.UNKEYED_LIST:
+                streamUnkeyedList(writer);
+                break;
+            case LithiumNode.UNKEYED_LIST_ITEM:
+                streamUnkeyedListItem(writer);
+                break;
+            default:
+                throw new InvalidNormalizedNodeStreamException("Unexpected node " + nodeType);
+        }
+    }
+
+    private void streamAnyxml(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming anyxml node {}", identifier);
+
+        final DOMSource value = readDOMSource();
+        if (writer.startAnyxmlNode(identifier, DOMSource.class)) {
+            writer.domSourceValue(value);
+            writer.endNode();
+        }
+    }
+
+    private void streamAugmentation(final NormalizedNodeStreamWriter writer) throws IOException {
+        final AugmentationIdentifier augIdentifier = readAugmentationIdentifier();
+        LOG.trace("Streaming augmentation node {}", augIdentifier);
+        writer.startAugmentationNode(augIdentifier);
+        commonStreamContainer(writer);
+    }
+
+    private void streamChoice(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming choice node {}", identifier);
+        writer.startChoiceNode(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamContainer(writer);
+    }
+
+    private void streamContainer(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming container node {}", identifier);
+        writer.startContainerNode(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamContainer(writer);
+    }
+
+    private void streamLeaf(final NormalizedNodeStreamWriter writer) throws IOException {
+        startLeaf(writer);
+        endLeaf(writer, readObject());
+    }
+
+    // Leaf inside a MapEntryNode, it can potentially be a key leaf, in which case we want to de-duplicate values.
+    private void streamLeaf(final NormalizedNodeStreamWriter writer, final NodeIdentifierWithPredicates entryId)
+            throws IOException {
+        final NodeIdentifier identifier = startLeaf(writer);
+        final Object value = readObject();
+        final Object entryValue = entryId.getValue(identifier.getNodeType());
+        endLeaf(writer, entryValue == null ? value : entryValue);
+    }
+
+    private NodeIdentifier startLeaf(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming leaf node {}", identifier);
+        writer.startLeafNode(identifier);
+        return identifier;
+    }
+
+    private static void endLeaf(final NormalizedNodeStreamWriter writer, final Object value) throws IOException {
+        writer.scalarValue(value);
+        writer.endNode();
+    }
+
+    private void streamLeafSet(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming leaf set node {}", identifier);
+        writer.startLeafSet(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamLeafSet(writer, identifier);
+    }
+
+    private void streamOrderedLeafSet(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming ordered leaf set node {}", identifier);
+        writer.startOrderedLeafSet(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamLeafSet(writer, identifier);
+    }
+
+    private void commonStreamLeafSet(final NormalizedNodeStreamWriter writer, final NodeIdentifier identifier)
+            throws IOException {
+        lastLeafSetQName = identifier.getNodeType();
+        try {
+            commonStreamContainer(writer);
+        } finally {
+            // Make sure we never leak this
+            lastLeafSetQName = null;
+        }
+    }
+
+    private void streamLeafSetEntry(final NormalizedNodeStreamWriter writer) throws IOException {
+        final QName name = lastLeafSetQName != null ? lastLeafSetQName : readQName();
+        final Object value = readObject();
+        final NodeWithValue<Object> leafIdentifier = new NodeWithValue<>(name, value);
+        LOG.trace("Streaming leaf set entry node {}, value {}", leafIdentifier, value);
+        writer.startLeafSetEntryNode(leafIdentifier);
+        writer.scalarValue(value);
+        writer.endNode();
+    }
+
+    private void streamMap(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming map node {}", identifier);
+        writer.startMapNode(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamContainer(writer);
+    }
+
+    private void streamOrderedMap(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming ordered map node {}", identifier);
+        writer.startOrderedMapNode(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamContainer(writer);
+    }
+
+    private void streamMapEntry(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifierWithPredicates entryIdentifier = readNormalizedNodeWithPredicates();
+        LOG.trace("Streaming map entry node {}", entryIdentifier);
+        writer.startMapEntryNode(entryIdentifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+
+        // Same loop as commonStreamContainer(), but ...
+        for (byte nodeType = input.readByte(); nodeType != LithiumNode.END_NODE; nodeType = input.readByte()) {
+            if (nodeType == LithiumNode.LEAF_NODE) {
+                // ... leaf nodes may need de-duplication
+                streamLeaf(writer, entryIdentifier);
+            } else {
+                streamNormalizedNode(writer, nodeType);
+            }
+        }
+        writer.endNode();
+    }
+
+    private void streamUnkeyedList(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming unkeyed list node {}", identifier);
+        writer.startUnkeyedList(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamContainer(writer);
+    }
+
+    private void streamUnkeyedListItem(final NormalizedNodeStreamWriter writer) throws IOException {
+        final NodeIdentifier identifier = readNodeIdentifier();
+        LOG.trace("Streaming unkeyed list item node {}", identifier);
+        writer.startUnkeyedListItem(identifier, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+        commonStreamContainer(writer);
+    }
+
+    private void commonStreamContainer(final NormalizedNodeStreamWriter writer) throws IOException {
+        for (byte nodeType = input.readByte(); nodeType != LithiumNode.END_NODE; nodeType = input.readByte()) {
+            streamNormalizedNode(writer, nodeType);
+        }
+        writer.endNode();
+    }
+
+    private DOMSource readDOMSource() throws IOException {
+        String xml = readObject().toString();
+        try {
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+            factory.setNamespaceAware(true);
+            Element node = factory.newDocumentBuilder().parse(
+                    new InputSource(new StringReader(xml))).getDocumentElement();
+            return new DOMSource(node);
+        } catch (SAXException | ParserConfigurationException e) {
+            throw new IOException("Error parsing XML: " + xml, e);
+        }
+    }
+
+    final QName defaultReadQName() throws IOException {
+        // Read in the same sequence of writing
+        String localName = readCodedString();
+        String namespace = readCodedString();
+        String revision = Strings.emptyToNull(readCodedString());
+
+        return QNameFactory.create(localName, namespace, revision);
+    }
+
+    final String readCodedString() throws IOException {
+        final byte valueType = input.readByte();
+        switch (valueType) {
+            case LithiumTokens.IS_NULL_VALUE:
+                return null;
+            case LithiumTokens.IS_CODE_VALUE:
+                final int code = input.readInt();
+                try {
+                    return codedStringMap.get(code);
+                } catch (IndexOutOfBoundsException e) {
+                    throw new IOException("String code " + code + " was not found", e);
+                }
+            case LithiumTokens.IS_STRING_VALUE:
+                final String value = input.readUTF().intern();
+                codedStringMap.add(value);
+                return value;
+            default:
+                throw new IOException("Unhandled string value type " + valueType);
+        }
+    }
+
+    private Set<QName> readQNameSet() throws IOException {
+        // Read the children count
+        final int count = input.readInt();
+        final Set<QName> children = Sets.newHashSetWithExpectedSize(count);
+        for (int i = 0; i < count; i++) {
+            children.add(readQName());
+        }
+        return children;
+    }
+
+    abstract AugmentationIdentifier readAugmentationIdentifier() throws IOException;
+
+    abstract NodeIdentifier readNodeIdentifier() throws IOException;
+
+    final AugmentationIdentifier defaultReadAugmentationIdentifier() throws IOException {
+        return AugmentationIdentifier.create(readQNameSet());
+    }
+
+    private NodeIdentifierWithPredicates readNormalizedNodeWithPredicates() throws IOException {
+        final QName qname = readQName();
+        final int count = input.readInt();
+        switch (count) {
+            case 0:
+                return NodeIdentifierWithPredicates.of(qname);
+            case 1:
+                return NodeIdentifierWithPredicates.of(qname, readQName(), readObject());
+            default:
+                // ImmutableList is used by ImmutableOffsetMapTemplate for lookups, hence we use that.
+                final Builder<QName> keys = ImmutableList.builderWithExpectedSize(count);
+                final Object[] values = new Object[count];
+                for (int i = 0; i < count; i++) {
+                    keys.add(readQName());
+                    values[i] = readObject();
+                }
+
+                return NodeIdentifierWithPredicates.of(qname, ImmutableOffsetMapTemplate.ordered(keys.build())
+                    .instantiateWithValues(values));
+        }
+    }
+
+    private Object readObject() throws IOException {
+        byte objectType = input.readByte();
+        switch (objectType) {
+            case LithiumValue.BITS_TYPE:
+                return readObjSet();
+
+            case LithiumValue.BOOL_TYPE:
+                return input.readBoolean();
+
+            case LithiumValue.BYTE_TYPE:
+                return input.readByte();
+
+            case LithiumValue.INT_TYPE:
+                return input.readInt();
+
+            case LithiumValue.LONG_TYPE:
+                return input.readLong();
+
+            case LithiumValue.QNAME_TYPE:
+                return readQName();
+
+            case LithiumValue.SHORT_TYPE:
+                return input.readShort();
+
+            case LithiumValue.STRING_TYPE:
+                return input.readUTF();
+
+            case LithiumValue.STRING_BYTES_TYPE:
+                return readStringBytes();
+
+            case LithiumValue.BIG_DECIMAL_TYPE:
+                return new BigDecimal(input.readUTF());
+
+            case LithiumValue.BIG_INTEGER_TYPE:
+                return new BigInteger(input.readUTF());
+
+            case LithiumValue.BINARY_TYPE:
+                byte[] bytes = new byte[input.readInt()];
+                input.readFully(bytes);
+                return bytes;
+
+            case LithiumValue.YANG_IDENTIFIER_TYPE:
+                return readYangInstanceIdentifierInternal();
+
+            case LithiumValue.EMPTY_TYPE:
+            // Leaf nodes no longer allow null values and thus we no longer emit null values. Previously, the "empty"
+            // yang type was represented as null so we translate an incoming null value to Empty. It was possible for
+            // a BI user to set a string leaf to null and we're rolling the dice here but the chances for that are
+            // very low. We'd have to know the yang type but, even if we did, we can't let a null value pass upstream
+            // so we'd have to drop the leaf which might cause other issues.
+            case LithiumValue.NULL_TYPE:
+                return Empty.getInstance();
+
+            default:
+                return null;
+        }
+    }
+
+    private String readStringBytes() throws IOException {
+        byte[] bytes = new byte[input.readInt()];
+        input.readFully(bytes);
+        return new String(bytes, StandardCharsets.UTF_8);
+    }
+
+    @Override
+    public final YangInstanceIdentifier readYangInstanceIdentifier() throws IOException {
+        return readYangInstanceIdentifierInternal();
+    }
+
+    private YangInstanceIdentifier readYangInstanceIdentifierInternal() throws IOException {
+        int size = input.readInt();
+        final Builder<PathArgument> pathArguments = ImmutableList.builderWithExpectedSize(size);
+        for (int i = 0; i < size; i++) {
+            pathArguments.add(readPathArgument());
+        }
+        return YangInstanceIdentifier.create(pathArguments.build());
+    }
+
+    private Set<String> readObjSet() throws IOException {
+        int count = input.readInt();
+        Set<String> children = new HashSet<>(count);
+        for (int i = 0; i < count; i++) {
+            children.add(readCodedString());
+        }
+        return children;
+    }
+
+    @Override
+    public final PathArgument readPathArgument() throws IOException {
+        // read Type
+        int type = input.readByte();
+
+        switch (type) {
+            case LithiumPathArgument.AUGMENTATION_IDENTIFIER:
+                return readAugmentationIdentifier();
+            case LithiumPathArgument.NODE_IDENTIFIER:
+                return readNodeIdentifier();
+            case LithiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES:
+                return readNormalizedNodeWithPredicates();
+            case LithiumPathArgument.NODE_IDENTIFIER_WITH_VALUE:
+                return new NodeWithValue<>(readQName(), readObject());
+            default:
+                // FIXME: throw hard error
+                return null;
+        }
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractLithiumDataOutput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractLithiumDataOutput.java
new file mode 100644 (file)
index 0000000..3f41ffd
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.binfmt;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * "original" type mapping. Baseline is Lithium but it really was introduced in Oxygen, where {@code type empty} was
+ * remapped from null.
+ *
+ * <p>
+ * {@code uint8}, {@code uint16}, {@code uint32} use java.lang types with widening, hence their value types overlap with
+ * mapping of {@code int16}, {@code int32} and {@code int64}, making that difference indiscernible without YANG schema
+ * knowledge.
+ */
+abstract class AbstractLithiumDataOutput extends AbstractNormalizedNodeDataOutput {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractLithiumDataOutput.class);
+    private static final TransformerFactory TF = TransformerFactory.newInstance();
+    private static final ImmutableMap<Class<?>, Byte> KNOWN_TYPES = ImmutableMap.<Class<?>, Byte>builder()
+            .put(String.class, LithiumValue.STRING_TYPE)
+            .put(Byte.class, LithiumValue.BYTE_TYPE)
+            .put(Integer.class, LithiumValue.INT_TYPE)
+            .put(Long.class, LithiumValue.LONG_TYPE)
+            .put(Boolean.class, LithiumValue.BOOL_TYPE)
+            .put(QName.class, LithiumValue.QNAME_TYPE)
+            .put(Short.class, LithiumValue.SHORT_TYPE)
+            .put(BigInteger.class, LithiumValue.BIG_INTEGER_TYPE)
+            .put(BigDecimal.class, LithiumValue.BIG_DECIMAL_TYPE)
+            .put(byte[].class, LithiumValue.BINARY_TYPE)
+            .put(Empty.class, LithiumValue.EMPTY_TYPE)
+            .build();
+
+    private final Map<String, Integer> stringCodeMap = new HashMap<>();
+
+    private QName lastLeafSetQName;
+    private boolean inSimple;
+
+    AbstractLithiumDataOutput(final DataOutput output) {
+        super(output);
+    }
+
+    @Override
+    public final void startLeafNode(final NodeIdentifier name) throws IOException {
+        LOG.trace("Starting a new leaf node");
+        startNode(name, LithiumNode.LEAF_NODE);
+        inSimple = true;
+    }
+
+    @Override
+    public final void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        LOG.trace("Starting a new leaf set");
+        commonStartLeafSet(name, LithiumNode.LEAF_SET);
+    }
+
+    @Override
+    public final void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        LOG.trace("Starting a new ordered leaf set");
+        commonStartLeafSet(name, LithiumNode.ORDERED_LEAF_SET);
+    }
+
+    private void commonStartLeafSet(final NodeIdentifier name, final byte nodeType) throws IOException {
+        startNode(name, nodeType);
+        lastLeafSetQName = name.getNodeType();
+    }
+
+    @Override
+    public final void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
+        LOG.trace("Starting a new leaf set entry node");
+
+        output.writeByte(LithiumNode.LEAF_SET_ENTRY_NODE);
+
+        // lastLeafSetQName is set if the parent LeafSetNode was previously written. Otherwise this is a
+        // stand alone LeafSetEntryNode so write out it's name here.
+        if (lastLeafSetQName == null) {
+            writeQNameInternal(name.getNodeType());
+        }
+        inSimple = true;
+    }
+
+    @Override
+    public final void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        LOG.trace("Starting a new container node");
+        startNode(name, LithiumNode.CONTAINER_NODE);
+    }
+
+    @Override
+    public final void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint)
+            throws IOException {
+        LOG.trace("Starting a new yang modeled anyXml node");
+        startNode(name, LithiumNode.YANG_MODELED_ANY_XML_NODE);
+    }
+
+    @Override
+    public final void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        LOG.trace("Starting a new unkeyed list");
+        startNode(name, LithiumNode.UNKEYED_LIST);
+    }
+
+    @Override
+    public final void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        LOG.trace("Starting a new unkeyed list item");
+        startNode(name, LithiumNode.UNKEYED_LIST_ITEM);
+    }
+
+    @Override
+    public final void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        LOG.trace("Starting a new map node");
+        startNode(name, LithiumNode.MAP_NODE);
+    }
+
+    @Override
+    public final void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+            throws IOException {
+        LOG.trace("Starting a new map entry node");
+        startNode(identifier, LithiumNode.MAP_ENTRY_NODE);
+        writeKeyValueMap(identifier.entrySet());
+    }
+
+    @Override
+    public final void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        LOG.trace("Starting a new ordered map node");
+        startNode(name, LithiumNode.ORDERED_MAP_NODE);
+    }
+
+    @Override
+    public final void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        LOG.trace("Starting a new choice node");
+        startNode(name, LithiumNode.CHOICE_NODE);
+    }
+
+    @Override
+    public final void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
+        requireNonNull(identifier, "Node identifier should not be null");
+        LOG.trace("Starting a new augmentation node");
+
+        output.writeByte(LithiumNode.AUGMENTATION_NODE);
+        writeAugmentationIdentifier(identifier);
+    }
+
+    @Override
+    public final boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        if (DOMSource.class.isAssignableFrom(objectModel)) {
+            LOG.trace("Starting anyxml node");
+            startNode(name, LithiumNode.ANY_XML_NODE);
+            inSimple = true;
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public final void scalarValue(final Object value) throws IOException {
+        writeObject(value);
+    }
+
+    @Override
+    public final void domSourceValue(final DOMSource value) throws IOException {
+        final StringWriter writer = new StringWriter();
+        try {
+            TF.newTransformer().transform(value, new StreamResult(writer));
+        } catch (TransformerException e) {
+            throw new IOException("Error writing anyXml", e);
+        }
+        writeObject(writer.toString());
+    }
+
+    @Override
+    public final void endNode() throws IOException {
+        LOG.trace("Ending the node");
+        if (!inSimple) {
+            lastLeafSetQName = null;
+            output.writeByte(LithiumNode.END_NODE);
+        }
+        inSimple = false;
+    }
+
+    @Override
+    @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST",
+            justification = "The casts in the switch clauses are indirectly confirmed via the determination of 'type'.")
+    final void writePathArgumentInternal(final PathArgument pathArgument) throws IOException {
+        final byte type = LithiumPathArgument.getSerializablePathArgumentType(pathArgument);
+        output.writeByte(type);
+
+        switch (type) {
+            case LithiumPathArgument.NODE_IDENTIFIER:
+                NodeIdentifier nodeIdentifier = (NodeIdentifier) pathArgument;
+                writeQNameInternal(nodeIdentifier.getNodeType());
+                break;
+            case LithiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES:
+                NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
+                    (NodeIdentifierWithPredicates) pathArgument;
+                writeQNameInternal(nodeIdentifierWithPredicates.getNodeType());
+                writeKeyValueMap(nodeIdentifierWithPredicates.entrySet());
+                break;
+            case LithiumPathArgument.NODE_IDENTIFIER_WITH_VALUE:
+                NodeWithValue<?> nodeWithValue = (NodeWithValue<?>) pathArgument;
+                writeQNameInternal(nodeWithValue.getNodeType());
+                writeObject(nodeWithValue.getValue());
+                break;
+            case LithiumPathArgument.AUGMENTATION_IDENTIFIER:
+                // No Qname in augmentation identifier
+                writeAugmentationIdentifier((AugmentationIdentifier) pathArgument);
+                break;
+            default:
+                throw new IllegalStateException("Unknown node identifier type is found : "
+                        + pathArgument.getClass().toString());
+        }
+    }
+
+    @Override
+    final void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException {
+        List<PathArgument> pathArguments = identifier.getPathArguments();
+        output.writeInt(pathArguments.size());
+
+        for (PathArgument pathArgument : pathArguments) {
+            writePathArgumentInternal(pathArgument);
+        }
+    }
+
+    final void defaultWriteAugmentationIdentifier(final @NonNull AugmentationIdentifier aid) throws IOException {
+        final Set<QName> qnames = aid.getPossibleChildNames();
+        // Write each child's qname separately, if list is empty send count as 0
+        if (!qnames.isEmpty()) {
+            output.writeInt(qnames.size());
+            for (QName qname : qnames) {
+                writeQNameInternal(qname);
+            }
+        } else {
+            LOG.debug("augmentation node does not have any child");
+            output.writeInt(0);
+        }
+    }
+
+    final void defaultWriteQName(final QName qname) throws IOException {
+        writeString(qname.getLocalName());
+        writeModule(qname.getModule());
+    }
+
+    final void defaultWriteModule(final QNameModule module) throws IOException {
+        writeString(module.getNamespace().toString());
+        final Optional<Revision> revision = module.getRevision();
+        if (revision.isPresent()) {
+            writeString(revision.get().toString());
+        } else {
+            writeByte(LithiumTokens.IS_NULL_VALUE);
+        }
+    }
+
+    abstract void writeModule(QNameModule module) throws IOException;
+
+    abstract void writeAugmentationIdentifier(@NonNull AugmentationIdentifier aid) throws IOException;
+
+    private void startNode(final PathArgument arg, final byte nodeType) throws IOException {
+        requireNonNull(arg, "Node identifier should not be null");
+        checkState(!inSimple, "Attempted to start a child in a simple node");
+
+        // First write the type of node
+        output.writeByte(nodeType);
+        // Write Start Tag
+        writeQNameInternal(arg.getNodeType());
+    }
+
+    private void writeObjSet(final Set<?> set) throws IOException {
+        output.writeInt(set.size());
+        for (Object o : set) {
+            checkArgument(o instanceof String, "Expected value type to be String but was %s (%s)", o.getClass(), o);
+            writeString((String) o);
+        }
+    }
+
+    private void writeObject(final Object value) throws IOException {
+        byte type = getSerializableType(value);
+        // Write object type first
+        output.writeByte(type);
+
+        switch (type) {
+            case LithiumValue.BOOL_TYPE:
+                output.writeBoolean((Boolean) value);
+                break;
+            case LithiumValue.QNAME_TYPE:
+                writeQNameInternal((QName) value);
+                break;
+            case LithiumValue.INT_TYPE:
+                output.writeInt((Integer) value);
+                break;
+            case LithiumValue.BYTE_TYPE:
+                output.writeByte((Byte) value);
+                break;
+            case LithiumValue.LONG_TYPE:
+                output.writeLong((Long) value);
+                break;
+            case LithiumValue.SHORT_TYPE:
+                output.writeShort((Short) value);
+                break;
+            case LithiumValue.BITS_TYPE:
+                writeObjSet((Set<?>) value);
+                break;
+            case LithiumValue.BINARY_TYPE:
+                byte[] bytes = (byte[]) value;
+                output.writeInt(bytes.length);
+                output.write(bytes);
+                break;
+            case LithiumValue.YANG_IDENTIFIER_TYPE:
+                writeYangInstanceIdentifierInternal((YangInstanceIdentifier) value);
+                break;
+            case LithiumValue.EMPTY_TYPE:
+                break;
+            case LithiumValue.STRING_BYTES_TYPE:
+                final byte[] valueBytes = value.toString().getBytes(StandardCharsets.UTF_8);
+                output.writeInt(valueBytes.length);
+                output.write(valueBytes);
+                break;
+            default:
+                output.writeUTF(value.toString());
+                break;
+        }
+    }
+
+    private void writeKeyValueMap(final Set<Entry<QName, Object>> entrySet) throws IOException {
+        if (!entrySet.isEmpty()) {
+            output.writeInt(entrySet.size());
+            for (Entry<QName, Object> entry : entrySet) {
+                writeQNameInternal(entry.getKey());
+                writeObject(entry.getValue());
+            }
+        } else {
+            output.writeInt(0);
+        }
+    }
+
+    private void writeString(final @NonNull String string) throws IOException {
+        final Integer value = stringCodeMap.get(verifyNotNull(string));
+        if (value == null) {
+            stringCodeMap.put(string, stringCodeMap.size());
+            writeByte(LithiumTokens.IS_STRING_VALUE);
+            writeUTF(string);
+        } else {
+            writeByte(LithiumTokens.IS_CODE_VALUE);
+            writeInt(value);
+        }
+    }
+
+    @VisibleForTesting
+    static final byte getSerializableType(final Object node) {
+        final Byte type = KNOWN_TYPES.get(requireNonNull(node).getClass());
+        if (type != null) {
+            if (type == LithiumValue.STRING_TYPE
+                    && ((String) node).length() >= LithiumValue.STRING_BYTES_LENGTH_THRESHOLD) {
+                return LithiumValue.STRING_BYTES_TYPE;
+            }
+            return type;
+        }
+
+        if (node instanceof Set) {
+            return LithiumValue.BITS_TYPE;
+        }
+
+        if (node instanceof YangInstanceIdentifier) {
+            return LithiumValue.YANG_IDENTIFIER_TYPE;
+        }
+
+        throw new IllegalArgumentException("Unknown value type " + node.getClass().getSimpleName());
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractMagnesiumDataInput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractMagnesiumDataInput.java
new file mode 100644 (file)
index 0000000..21b1432
--- /dev/null
@@ -0,0 +1,849 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+import java.io.DataInput;
+import java.io.IOException;
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * Abstract base class for NormalizedNodeDataInput based on {@link MagnesiumNode}, {@link MagnesiumPathArgument} and
+ * {@link MagnesiumValue}.
+ */
+abstract class AbstractMagnesiumDataInput extends AbstractNormalizedNodeDataInput {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractMagnesiumDataInput.class);
+
+    // Known singleton objects
+    private static final @NonNull Byte INT8_0 = 0;
+    private static final @NonNull Short INT16_0 = 0;
+    private static final @NonNull Integer INT32_0 = 0;
+    private static final @NonNull Long INT64_0 = 0L;
+    private static final byte @NonNull[] BINARY_0 = new byte[0];
+    private static final @NonNull AugmentationIdentifier EMPTY_AID = AugmentationIdentifier.create(ImmutableSet.of());
+
+    private final List<AugmentationIdentifier> codedAugments = new ArrayList<>();
+    private final List<NodeIdentifier> codedNodeIdentifiers = new ArrayList<>();
+    private final List<QNameModule> codedModules = new ArrayList<>();
+    private final List<String> codedStrings = new ArrayList<>();
+
+    AbstractMagnesiumDataInput(final DataInput input) {
+        super(input);
+    }
+
+    @Override
+    public final void streamNormalizedNode(final NormalizedNodeStreamWriter writer) throws IOException {
+        streamNormalizedNode(requireNonNull(writer), null, input.readByte());
+    }
+
+    private void streamNormalizedNode(final NormalizedNodeStreamWriter writer, final PathArgument parent,
+            final byte nodeHeader) throws IOException {
+        switch (nodeHeader & MagnesiumNode.TYPE_MASK) {
+            case MagnesiumNode.NODE_LEAF:
+                streamLeaf(writer, parent, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_CONTAINER:
+                streamContainer(writer, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_LIST:
+                streamList(writer, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_MAP:
+                streamMap(writer, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_MAP_ORDERED:
+                streamMapOrdered(writer, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_LEAFSET:
+                streamLeafset(writer, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_LEAFSET_ORDERED:
+                streamLeafsetOrdered(writer, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_CHOICE:
+                streamChoice(writer, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_AUGMENTATION:
+                streamAugmentation(writer, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_ANYXML:
+                streamAnyxml(writer, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_ANYXML_MODELED:
+                streamAnyxmlModeled(writer, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_LIST_ENTRY:
+                streamListEntry(writer, parent, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_LEAFSET_ENTRY:
+                streamLeafsetEntry(writer, parent, nodeHeader);
+                break;
+            case MagnesiumNode.NODE_MAP_ENTRY:
+                streamMapEntry(writer, parent, nodeHeader);
+                break;
+            default:
+                throw new InvalidNormalizedNodeStreamException("Unexpected node header " + nodeHeader);
+        }
+    }
+
+    private void streamAnyxml(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
+        final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
+        LOG.trace("Streaming anyxml node {}", identifier);
+
+        final DOMSource value = readDOMSource();
+        if (writer.startAnyxmlNode(identifier, DOMSource.class)) {
+            writer.domSourceValue(value);
+            writer.endNode();
+        }
+    }
+
+    private void streamAnyxmlModeled(final NormalizedNodeStreamWriter writer, final byte nodeHeader)
+            throws IOException {
+        // TODO: decide how to deal with these
+        throw new UnsupportedOperationException("Reading YANG-modeled anyxml was never supported");
+    }
+
+    private void streamAugmentation(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
+        final AugmentationIdentifier augIdentifier = decodeAugmentationIdentifier(nodeHeader);
+        LOG.trace("Streaming augmentation node {}", augIdentifier);
+        writer.startAugmentationNode(augIdentifier);
+        commonStreamContainer(writer, augIdentifier);
+    }
+
+    private void streamChoice(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
+        final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
+        LOG.trace("Streaming choice node {}", identifier);
+        writer.startChoiceNode(identifier, UNKNOWN_SIZE);
+        commonStreamContainer(writer, identifier);
+    }
+
+    private void streamContainer(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
+        final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
+        LOG.trace("Streaming container node {}", identifier);
+        writer.startContainerNode(identifier, UNKNOWN_SIZE);
+        commonStreamContainer(writer, identifier);
+    }
+
+    private void streamLeaf(final NormalizedNodeStreamWriter writer, final PathArgument parent, final byte nodeHeader)
+            throws IOException {
+        final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
+        LOG.trace("Streaming leaf node {}", identifier);
+        writer.startLeafNode(identifier);
+
+        final Object value;
+        if ((nodeHeader & MagnesiumNode.PREDICATE_ONE) == MagnesiumNode.PREDICATE_ONE) {
+            if (!(parent instanceof NodeIdentifierWithPredicates)) {
+                throw new InvalidNormalizedNodeStreamException("Invalid predicate leaf " + identifier + " in parent "
+                        + parent);
+            }
+
+            value = ((NodeIdentifierWithPredicates) parent).getValue(identifier.getNodeType());
+            if (value == null) {
+                throw new InvalidNormalizedNodeStreamException("Failed to find predicate leaf " + identifier
+                    + " in parent " + parent);
+            }
+        } else {
+            value = readLeafValue();
+        }
+
+        writer.scalarValue(value);
+        writer.endNode();
+    }
+
+    private void streamLeafset(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
+        final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
+        LOG.trace("Streaming leaf set node {}", identifier);
+        writer.startLeafSet(identifier, UNKNOWN_SIZE);
+        commonStreamContainer(writer, identifier);
+    }
+
+    private void streamLeafsetOrdered(final NormalizedNodeStreamWriter writer, final byte nodeHeader)
+            throws IOException {
+        final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
+        LOG.trace("Streaming ordered leaf set node {}", identifier);
+        writer.startOrderedLeafSet(identifier, UNKNOWN_SIZE);
+
+        commonStreamContainer(writer, identifier);
+    }
+
+    private void streamLeafsetEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
+            final byte nodeHeader) throws IOException {
+        final NodeIdentifier nodeId = decodeNodeIdentifier(nodeHeader, parent);
+        final Object value = readLeafValue();
+        final NodeWithValue<Object> leafIdentifier = new NodeWithValue<>(nodeId.getNodeType(), value);
+        LOG.trace("Streaming leaf set entry node {}", leafIdentifier);
+        writer.startLeafSetEntryNode(leafIdentifier);
+        writer.scalarValue(value);
+        writer.endNode();
+    }
+
+    private void streamList(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
+        final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
+        writer.startUnkeyedList(identifier, UNKNOWN_SIZE);
+        commonStreamContainer(writer, identifier);
+    }
+
+    private void streamListEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
+            final byte nodeHeader) throws IOException {
+        final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader, parent);
+        LOG.trace("Streaming unkeyed list item node {}", identifier);
+        writer.startUnkeyedListItem(identifier, UNKNOWN_SIZE);
+        commonStreamContainer(writer, identifier);
+    }
+
+    private void streamMap(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
+        final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
+        LOG.trace("Streaming map node {}", identifier);
+        writer.startMapNode(identifier, UNKNOWN_SIZE);
+        commonStreamContainer(writer, identifier);
+    }
+
+    private void streamMapOrdered(final NormalizedNodeStreamWriter writer, final byte nodeHeader) throws IOException {
+        final NodeIdentifier identifier = decodeNodeIdentifier(nodeHeader);
+        LOG.trace("Streaming ordered map node {}", identifier);
+        writer.startOrderedMapNode(identifier, UNKNOWN_SIZE);
+        commonStreamContainer(writer, identifier);
+    }
+
+    private void streamMapEntry(final NormalizedNodeStreamWriter writer, final PathArgument parent,
+            final byte nodeHeader) throws IOException {
+        final NodeIdentifier nodeId = decodeNodeIdentifier(nodeHeader, parent);
+
+        final int size;
+        switch (mask(nodeHeader, MagnesiumNode.PREDICATE_MASK)) {
+            case MagnesiumNode.PREDICATE_ZERO:
+                size = 0;
+                break;
+            case MagnesiumNode.PREDICATE_ONE:
+                size = 1;
+                break;
+            case MagnesiumNode.PREDICATE_1B:
+                size = input.readUnsignedByte();
+                break;
+            case MagnesiumNode.PREDICATE_4B:
+                size = input.readInt();
+                break;
+            default:
+                // ISE on purpose: this should never ever happen
+                throw new IllegalStateException("Failed to decode NodeIdentifierWithPredicates size from header "
+                        + nodeHeader);
+        }
+
+        final NodeIdentifierWithPredicates identifier = readNodeIdentifierWithPredicates(nodeId.getNodeType(), size);
+        LOG.trace("Streaming map entry node {}", identifier);
+        writer.startMapEntryNode(identifier, UNKNOWN_SIZE);
+        commonStreamContainer(writer, identifier);
+    }
+
+    private void commonStreamContainer(final NormalizedNodeStreamWriter writer, final PathArgument parent)
+            throws IOException {
+        for (byte nodeType = input.readByte(); nodeType != MagnesiumNode.NODE_END; nodeType = input.readByte()) {
+            streamNormalizedNode(writer, parent, nodeType);
+        }
+        writer.endNode();
+    }
+
+    private @NonNull NodeIdentifier decodeNodeIdentifier() throws IOException {
+        final QNameModule module = decodeQNameModule();
+        final String localName = readRefString();
+        final NodeIdentifier nodeId;
+        try {
+            nodeId = QNameFactory.getNodeIdentifier(module, localName);
+        } catch (ExecutionException e) {
+            throw new InvalidNormalizedNodeStreamException("Illegal QName module=" + module + " localName="
+                    + localName, e);
+        }
+
+        codedNodeIdentifiers.add(nodeId);
+        return nodeId;
+    }
+
+    private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader) throws IOException {
+        return decodeNodeIdentifier(nodeHeader, null);
+    }
+
+    private NodeIdentifier decodeNodeIdentifier(final byte nodeHeader, final PathArgument parent) throws IOException {
+        final int index;
+        switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
+            case MagnesiumNode.ADDR_DEFINE:
+                return readNodeIdentifier();
+            case MagnesiumNode.ADDR_LOOKUP_1B:
+                index = input.readUnsignedByte();
+                break;
+            case MagnesiumNode.ADDR_LOOKUP_4B:
+                index = input.readInt();
+                break;
+            case MagnesiumNode.ADDR_PARENT:
+                if (parent instanceof NodeIdentifier) {
+                    return (NodeIdentifier) parent;
+                }
+                throw new InvalidNormalizedNodeStreamException("Invalid node identifier reference to parent " + parent);
+            default:
+                throw new InvalidNormalizedNodeStreamException("Unexpected node identifier addressing in header "
+                        + nodeHeader);
+        }
+
+        try {
+            return codedNodeIdentifiers.get(index);
+        } catch (IndexOutOfBoundsException e) {
+            throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
+        }
+    }
+
+    private AugmentationIdentifier decodeAugmentationIdentifier(final byte nodeHeader) throws IOException {
+        final int index;
+        switch (nodeHeader & MagnesiumNode.ADDR_MASK) {
+            case MagnesiumNode.ADDR_DEFINE:
+                return readAugmentationIdentifier();
+            case MagnesiumNode.ADDR_LOOKUP_1B:
+                index = input.readUnsignedByte();
+                break;
+            case MagnesiumNode.ADDR_LOOKUP_4B:
+                index = input.readInt();
+                break;
+            default:
+                throw new InvalidNormalizedNodeStreamException(
+                    "Unexpected augmentation identifier addressing in header " + nodeHeader);
+        }
+
+        try {
+            return codedAugments.get(index);
+        } catch (IndexOutOfBoundsException e) {
+            throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier reference " + index, e);
+        }
+    }
+
+    @Override
+    public final YangInstanceIdentifier readYangInstanceIdentifier() throws IOException {
+        final byte type = input.readByte();
+        if (type == MagnesiumValue.YIID) {
+            return readYangInstanceIdentifier(input.readInt());
+        } else if (type >= MagnesiumValue.YIID_0) {
+            // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
+            return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
+        } else {
+            throw new InvalidNormalizedNodeStreamException("Unexpected YangInstanceIdentifier type " + type);
+        }
+    }
+
+    private @NonNull YangInstanceIdentifier readYangInstanceIdentifier(final int size) throws IOException {
+        if (size > 0) {
+            final Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(size);
+            for (int i = 0; i < size; ++i) {
+                builder.add(readPathArgument());
+            }
+            return YangInstanceIdentifier.create(builder.build());
+        } else if (size == 0) {
+            return YangInstanceIdentifier.empty();
+        } else {
+            throw new InvalidNormalizedNodeStreamException("Invalid YangInstanceIdentifier size " + size);
+        }
+    }
+
+    @Override
+    public final QName readQName() throws IOException {
+        final byte type = input.readByte();
+        switch (type) {
+            case MagnesiumValue.QNAME:
+                return decodeQName();
+            case MagnesiumValue.QNAME_REF_1B:
+                return decodeQNameRef1();
+            case MagnesiumValue.QNAME_REF_2B:
+                return decodeQNameRef2();
+            case MagnesiumValue.QNAME_REF_4B:
+                return decodeQNameRef4();
+            default:
+                throw new InvalidNormalizedNodeStreamException("Unexpected QName type " + type);
+        }
+    }
+
+    @Override
+    public final PathArgument readPathArgument() throws IOException {
+        final byte header = input.readByte();
+        switch (header & MagnesiumPathArgument.TYPE_MASK) {
+            case MagnesiumPathArgument.AUGMENTATION_IDENTIFIER:
+                return readAugmentationIdentifier(header);
+            case MagnesiumPathArgument.NODE_IDENTIFIER:
+                verifyPathIdentifierOnly(header);
+                return readNodeIdentifier(header);
+            case MagnesiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES:
+                return readNodeIdentifierWithPredicates(header);
+            case MagnesiumPathArgument.NODE_WITH_VALUE:
+                verifyPathIdentifierOnly(header);
+                return readNodeWithValue(header);
+            case MagnesiumPathArgument.MOUNTPOINT_IDENTIFIER:
+                verifyPathIdentifierOnly(header);
+                return MountPointIdentifier.create(readNodeIdentifier(header).getNodeType());
+            default:
+                throw new InvalidNormalizedNodeStreamException("Unexpected PathArgument header " + header);
+        }
+    }
+
+    private AugmentationIdentifier readAugmentationIdentifier() throws IOException {
+        final AugmentationIdentifier result = readAugmentationIdentifier(input.readInt());
+        codedAugments.add(result);
+        return result;
+    }
+
+    private AugmentationIdentifier readAugmentationIdentifier(final byte header) throws IOException {
+        final byte count = mask(header, MagnesiumPathArgument.AID_COUNT_MASK);
+        switch (count) {
+            case MagnesiumPathArgument.AID_COUNT_1B:
+                return readAugmentationIdentifier(input.readUnsignedByte());
+            case MagnesiumPathArgument.AID_COUNT_2B:
+                return readAugmentationIdentifier(input.readUnsignedShort());
+            case MagnesiumPathArgument.AID_COUNT_4B:
+                return readAugmentationIdentifier(input.readInt());
+            default:
+                return readAugmentationIdentifier(rshift(count, MagnesiumPathArgument.AID_COUNT_SHIFT));
+        }
+    }
+
+    private AugmentationIdentifier readAugmentationIdentifier(final int size) throws IOException {
+        if (size > 0) {
+            final List<QName> qnames = new ArrayList<>(size);
+            for (int i = 0; i < size; ++i) {
+                qnames.add(readQName());
+            }
+            return AugmentationIdentifier.create(ImmutableSet.copyOf(qnames));
+        } else if (size == 0) {
+            return EMPTY_AID;
+        } else {
+            throw new InvalidNormalizedNodeStreamException("Invalid augmentation identifier size " + size);
+        }
+    }
+
+    private NodeIdentifier readNodeIdentifier() throws IOException {
+        return decodeNodeIdentifier();
+    }
+
+    private NodeIdentifier readNodeIdentifier(final byte header) throws IOException {
+        switch (header & MagnesiumPathArgument.QNAME_MASK) {
+            case MagnesiumPathArgument.QNAME_DEF:
+                return decodeNodeIdentifier();
+            case MagnesiumPathArgument.QNAME_REF_1B:
+                return decodeNodeIdentifierRef1();
+            case MagnesiumPathArgument.QNAME_REF_2B:
+                return decodeNodeIdentifierRef2();
+            case MagnesiumPathArgument.QNAME_REF_4B:
+                return decodeNodeIdentifierRef4();
+            default:
+                throw new InvalidNormalizedNodeStreamException("Invalid QName coding in " + header);
+        }
+    }
+
+    private NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final byte header) throws IOException {
+        final QName qname = readNodeIdentifier(header).getNodeType();
+        switch (mask(header, MagnesiumPathArgument.SIZE_MASK)) {
+            case MagnesiumPathArgument.SIZE_1B:
+                return readNodeIdentifierWithPredicates(qname, input.readUnsignedByte());
+            case MagnesiumPathArgument.SIZE_2B:
+                return readNodeIdentifierWithPredicates(qname, input.readUnsignedShort());
+            case MagnesiumPathArgument.SIZE_4B:
+                return readNodeIdentifierWithPredicates(qname, input.readInt());
+            default:
+                return readNodeIdentifierWithPredicates(qname, rshift(header, MagnesiumPathArgument.SIZE_SHIFT));
+        }
+    }
+
+    private NodeIdentifierWithPredicates readNodeIdentifierWithPredicates(final QName qname, final int size)
+            throws IOException {
+        if (size == 1) {
+            return NodeIdentifierWithPredicates.of(qname, readQName(), readLeafValue());
+        } else if (size > 1) {
+            final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builderWithExpectedSize(size);
+            for (int i = 0; i < size; ++i) {
+                builder.put(readQName(), readLeafValue());
+            }
+            return NodeIdentifierWithPredicates.of(qname, builder.build());
+        } else if (size == 0) {
+            return NodeIdentifierWithPredicates.of(qname);
+        } else {
+            throw new InvalidNormalizedNodeStreamException("Invalid predicate count " + size);
+        }
+    }
+
+    private NodeWithValue<?> readNodeWithValue(final byte header) throws IOException {
+        final QName qname = readNodeIdentifier(header).getNodeType();
+        return new NodeWithValue<>(qname, readLeafValue());
+    }
+
+    private static void verifyPathIdentifierOnly(final byte header) throws InvalidNormalizedNodeStreamException {
+        if (mask(header, MagnesiumPathArgument.SIZE_MASK) != 0) {
+            throw new InvalidNormalizedNodeStreamException("Invalid path argument header " + header);
+        }
+    }
+
+    private @NonNull NodeIdentifier decodeNodeIdentifierRef1() throws IOException {
+        return lookupNodeIdentifier(input.readUnsignedByte());
+    }
+
+    private @NonNull NodeIdentifier decodeNodeIdentifierRef2() throws IOException {
+        return lookupNodeIdentifier(input.readUnsignedShort() + 256);
+    }
+
+    private @NonNull NodeIdentifier decodeNodeIdentifierRef4() throws IOException {
+        return lookupNodeIdentifier(input.readInt());
+    }
+
+    private @NonNull QName decodeQName() throws IOException {
+        return decodeNodeIdentifier().getNodeType();
+    }
+
+    private @NonNull QName decodeQNameRef1() throws IOException {
+        return lookupQName(input.readUnsignedByte());
+    }
+
+    private @NonNull QName decodeQNameRef2() throws IOException {
+        return lookupQName(input.readUnsignedShort() + 256);
+    }
+
+    private @NonNull QName decodeQNameRef4() throws IOException {
+        return lookupQName(input.readInt());
+    }
+
+    private @NonNull QNameModule decodeQNameModule() throws IOException {
+        final byte type = input.readByte();
+        final int index;
+        switch (type) {
+            case MagnesiumValue.MODREF_1B:
+                index = input.readUnsignedByte();
+                break;
+            case MagnesiumValue.MODREF_2B:
+                index = input.readUnsignedShort() + 256;
+                break;
+            case MagnesiumValue.MODREF_4B:
+                index = input.readInt();
+                break;
+            default:
+                return decodeQNameModuleDef(type);
+        }
+
+        try {
+            return codedModules.get(index);
+        } catch (IndexOutOfBoundsException e) {
+            throw new InvalidNormalizedNodeStreamException("Invalid QNameModule reference " + index, e);
+        }
+    }
+
+    // QNameModule definition, i.e. two encoded strings
+    private @NonNull QNameModule decodeQNameModuleDef(final byte type) throws IOException {
+        final String namespace = readRefString(type);
+
+        final byte refType = input.readByte();
+        final String revision = refType == MagnesiumValue.STRING_EMPTY ? null : readRefString(refType);
+        final QNameModule module;
+        try {
+            module = QNameFactory.createModule(namespace, revision);
+        } catch (UncheckedExecutionException e) {
+            throw new InvalidNormalizedNodeStreamException("Illegal QNameModule ns=" + namespace + " rev=" + revision,
+                e);
+        }
+
+        codedModules.add(module);
+        return module;
+    }
+
+    private @NonNull String readRefString() throws IOException {
+        return readRefString(input.readByte());
+    }
+
+    private @NonNull String readRefString(final byte type) throws IOException {
+        final String str;
+        switch (type) {
+            case MagnesiumValue.STRING_REF_1B:
+                return lookupString(input.readUnsignedByte());
+            case MagnesiumValue.STRING_REF_2B:
+                return lookupString(input.readUnsignedShort() + 256);
+            case MagnesiumValue.STRING_REF_4B:
+                return lookupString(input.readInt());
+            case MagnesiumValue.STRING_EMPTY:
+                return "";
+            case MagnesiumValue.STRING_2B:
+                str = readString2();
+                break;
+            case MagnesiumValue.STRING_4B:
+                str = readString4();
+                break;
+            case MagnesiumValue.STRING_CHARS:
+                str = readCharsString();
+                break;
+            case MagnesiumValue.STRING_UTF:
+                str = input.readUTF();
+                break;
+            default:
+                throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
+        }
+
+        // TODO: consider interning Strings -- that would help with bits, but otherwise it's probably not worth it
+        codedStrings.add(verifyNotNull(str));
+        return str;
+    }
+
+    private @NonNull String readString() throws IOException {
+        final byte type = input.readByte();
+        switch (type) {
+            case MagnesiumValue.STRING_EMPTY:
+                return "";
+            case MagnesiumValue.STRING_UTF:
+                return input.readUTF();
+            case MagnesiumValue.STRING_2B:
+                return readString2();
+            case MagnesiumValue.STRING_4B:
+                return readString4();
+            case MagnesiumValue.STRING_CHARS:
+                return readCharsString();
+            default:
+                throw new InvalidNormalizedNodeStreamException("Unexpected String type " + type);
+        }
+    }
+
+    private @NonNull String readString2() throws IOException {
+        return readByteString(input.readUnsignedShort());
+    }
+
+    private @NonNull String readString4() throws IOException {
+        return readByteString(input.readInt());
+    }
+
+    private @NonNull String readByteString(final int size) throws IOException {
+        if (size > 0) {
+            final byte[] bytes = new byte[size];
+            input.readFully(bytes);
+            return new String(bytes, StandardCharsets.UTF_8);
+        } else if (size == 0) {
+            return "";
+        } else {
+            throw new InvalidNormalizedNodeStreamException("Invalid String bytes length " + size);
+        }
+    }
+
+    private @NonNull String readCharsString() throws IOException {
+        final int size = input.readInt();
+        if (size > 0) {
+            final char[] chars = new char[size];
+            for (int i = 0; i < size; ++i) {
+                chars[i] = input.readChar();
+            }
+            return String.valueOf(chars);
+        } else if (size == 0) {
+            return "";
+        } else {
+            throw new InvalidNormalizedNodeStreamException("Invalid String chars length " + size);
+        }
+    }
+
+    private @NonNull NodeIdentifier lookupNodeIdentifier(final int index) throws InvalidNormalizedNodeStreamException {
+        try {
+            return codedNodeIdentifiers.get(index);
+        } catch (IndexOutOfBoundsException e) {
+            throw new InvalidNormalizedNodeStreamException("Invalid QName reference " + index, e);
+        }
+    }
+
+    private @NonNull QName lookupQName(final int index) throws InvalidNormalizedNodeStreamException {
+        return lookupNodeIdentifier(index).getNodeType();
+    }
+
+    private @NonNull String lookupString(final int index) throws InvalidNormalizedNodeStreamException {
+        try {
+            return codedStrings.get(index);
+        } catch (IndexOutOfBoundsException e) {
+            throw new InvalidNormalizedNodeStreamException("Invalid String reference " + index, e);
+        }
+    }
+
+    private @NonNull DOMSource readDOMSource() throws IOException {
+        final String str = readString();
+        try {
+            return new DOMSource(UntrustedXML.newDocumentBuilder().parse(new InputSource(new StringReader(str)))
+                .getDocumentElement());
+        } catch (SAXException e) {
+            throw new IOException("Error parsing XML: " + str, e);
+        }
+    }
+
+    private @NonNull Object readLeafValue() throws IOException {
+        final byte type = input.readByte();
+        switch (type) {
+            case MagnesiumValue.BOOLEAN_FALSE:
+                return Boolean.FALSE;
+            case MagnesiumValue.BOOLEAN_TRUE:
+                return Boolean.TRUE;
+            case MagnesiumValue.EMPTY:
+                return Empty.getInstance();
+            case MagnesiumValue.INT8:
+                return input.readByte();
+            case MagnesiumValue.INT8_0:
+                return INT8_0;
+            case MagnesiumValue.INT16:
+                return input.readShort();
+            case MagnesiumValue.INT16_0:
+                return INT16_0;
+            case MagnesiumValue.INT32:
+                return input.readInt();
+            case MagnesiumValue.INT32_0:
+                return INT32_0;
+            case MagnesiumValue.INT32_2B:
+                return input.readShort() & 0xFFFF;
+            case MagnesiumValue.INT64:
+                return input.readLong();
+            case MagnesiumValue.INT64_0:
+                return INT64_0;
+            case MagnesiumValue.INT64_4B:
+                return input.readInt() & 0xFFFFFFFFL;
+            case MagnesiumValue.UINT8:
+                return Uint8.fromByteBits(input.readByte());
+            case MagnesiumValue.UINT8_0:
+                return Uint8.ZERO;
+            case MagnesiumValue.UINT16:
+                return Uint16.fromShortBits(input.readShort());
+            case MagnesiumValue.UINT16_0:
+                return Uint16.ZERO;
+            case MagnesiumValue.UINT32:
+                return Uint32.fromIntBits(input.readInt());
+            case MagnesiumValue.UINT32_0:
+                return Uint32.ZERO;
+            case MagnesiumValue.UINT32_2B:
+                return Uint32.fromIntBits(input.readShort() & 0xFFFF);
+            case MagnesiumValue.UINT64:
+                return Uint64.fromLongBits(input.readLong());
+            case MagnesiumValue.UINT64_0:
+                return Uint64.ZERO;
+            case MagnesiumValue.UINT64_4B:
+                return Uint64.fromLongBits(input.readInt() & 0xFFFFFFFFL);
+            case MagnesiumValue.BIGDECIMAL:
+                // FIXME: use string -> BigDecimal cache
+                return new BigDecimal(input.readUTF());
+            case MagnesiumValue.BIGINTEGER:
+                return readBigInteger();
+            case MagnesiumValue.STRING_EMPTY:
+                return "";
+            case MagnesiumValue.STRING_UTF:
+                return input.readUTF();
+            case MagnesiumValue.STRING_2B:
+                return readString2();
+            case MagnesiumValue.STRING_4B:
+                return readString4();
+            case MagnesiumValue.STRING_CHARS:
+                return readCharsString();
+            case MagnesiumValue.BINARY_0:
+                return BINARY_0;
+            case MagnesiumValue.BINARY_1B:
+                return readBinary(128 + input.readUnsignedByte());
+            case MagnesiumValue.BINARY_2B:
+                return readBinary(384 + input.readUnsignedShort());
+            case MagnesiumValue.BINARY_4B:
+                return readBinary(input.readInt());
+            case MagnesiumValue.YIID_0:
+                return YangInstanceIdentifier.empty();
+            case MagnesiumValue.YIID:
+                return readYangInstanceIdentifier(input.readInt());
+            case MagnesiumValue.QNAME:
+                return decodeQName();
+            case MagnesiumValue.QNAME_REF_1B:
+                return decodeQNameRef1();
+            case MagnesiumValue.QNAME_REF_2B:
+                return decodeQNameRef2();
+            case MagnesiumValue.QNAME_REF_4B:
+                return decodeQNameRef4();
+            case MagnesiumValue.BITS_0:
+                return ImmutableSet.of();
+            case MagnesiumValue.BITS_1B:
+                return readBits(input.readUnsignedByte() + 29);
+            case MagnesiumValue.BITS_2B:
+                return readBits(input.readUnsignedShort() + 285);
+            case MagnesiumValue.BITS_4B:
+                return readBits(input.readInt());
+
+            default:
+                if (type > MagnesiumValue.BINARY_0 && type <= MagnesiumValue.BINARY_127) {
+                    return readBinary(type - MagnesiumValue.BINARY_0);
+                } else if (type > MagnesiumValue.BITS_0 && type < MagnesiumValue.BITS_1B) {
+                    return readBits(type - MagnesiumValue.BITS_0);
+                } else if (type > MagnesiumValue.YIID_0) {
+                    // Note 'byte' is range limited, so it is always '&& type <= MagnesiumValue.YIID_31'
+                    return readYangInstanceIdentifier(type - MagnesiumValue.YIID_0);
+                } else {
+                    throw new InvalidNormalizedNodeStreamException("Invalid value type " + type);
+                }
+        }
+    }
+
+    abstract @NonNull BigInteger readBigInteger() throws IOException;
+
+    private byte @NonNull [] readBinary(final int size) throws IOException {
+        if (size > 0) {
+            final byte[] ret = new byte[size];
+            input.readFully(ret);
+            return ret;
+        } else if (size == 0) {
+            return BINARY_0;
+        } else {
+            throw new InvalidNormalizedNodeStreamException("Invalid binary length " + size);
+        }
+    }
+
+    private @NonNull ImmutableSet<String> readBits(final int size) throws IOException {
+        if (size > 0) {
+            final ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+            for (int i = 0; i < size; ++i) {
+                builder.add(readRefString());
+            }
+            return builder.build();
+        } else if (size == 0) {
+            return ImmutableSet.of();
+        } else {
+            throw new InvalidNormalizedNodeStreamException("Invalid bits length " + size);
+        }
+    }
+
+    private static byte mask(final byte header, final byte mask) {
+        return (byte) (header & mask);
+    }
+
+    private static int rshift(final byte header, final byte shift) {
+        return (header & 0xFF) >>> shift;
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractMagnesiumDataOutput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractMagnesiumDataOutput.java
new file mode 100644 (file)
index 0000000..79c640a
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract base class for NormalizedNodeDataOutput based on {@link MagnesiumNode}, {@link MagnesiumPathArgument} and
+ * {@link MagnesiumValue}.
+ */
+abstract class AbstractMagnesiumDataOutput extends AbstractNormalizedNodeDataOutput {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractMagnesiumDataOutput.class);
+
+    // Marker for encoding state when we have entered startLeafNode() within a startMapEntry() and that leaf corresponds
+    // to a key carried within NodeIdentifierWithPredicates.
+    private static final Object KEY_LEAF_STATE = new Object();
+    // Marker for nodes which have simple content and do not use END_NODE marker to terminate
+    private static final Object NO_ENDNODE_STATE = new Object();
+
+    private static final TransformerFactory TF = TransformerFactory.newInstance();
+
+    /**
+     * Stack tracking encoding state. In general we track the node identifier of the currently-open element, but there
+     * are a few other circumstances where we push other objects. See {@link #KEY_LEAF_STATE} and
+     * {@link #NO_ENDNODE_STATE}.
+     */
+    private final Deque<Object> stack = new ArrayDeque<>();
+
+    // Coding maps
+    private final Map<AugmentationIdentifier, Integer> aidCodeMap = new HashMap<>();
+    private final Map<QNameModule, Integer> moduleCodeMap = new HashMap<>();
+    private final Map<String, Integer> stringCodeMap = new HashMap<>();
+    private final Map<QName, Integer> qnameCodeMap = new HashMap<>();
+
+    AbstractMagnesiumDataOutput(final DataOutput output) {
+        super(output);
+    }
+
+    @Override
+    public final void startLeafNode(final NodeIdentifier name) throws IOException {
+        final Object current = stack.peek();
+        if (current instanceof NodeIdentifierWithPredicates) {
+            final QName qname = name.getNodeType();
+            if (((NodeIdentifierWithPredicates) current).containsKey(qname)) {
+                writeQNameNode(MagnesiumNode.NODE_LEAF | MagnesiumNode.PREDICATE_ONE, qname);
+                stack.push(KEY_LEAF_STATE);
+                return;
+            }
+        }
+
+        startSimpleNode(MagnesiumNode.NODE_LEAF, name);
+    }
+
+    @Override
+    public final void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        startQNameNode(MagnesiumNode.NODE_LEAFSET, name);
+    }
+
+    @Override
+    public final void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        startQNameNode(MagnesiumNode.NODE_LEAFSET_ORDERED, name);
+    }
+
+    @Override
+    public final void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
+        if (matchesParentQName(name.getNodeType())) {
+            output.writeByte(MagnesiumNode.NODE_LEAFSET_ENTRY);
+            stack.push(NO_ENDNODE_STATE);
+        } else {
+            startSimpleNode(MagnesiumNode.NODE_LEAFSET_ENTRY, name);
+        }
+    }
+
+    @Override
+    public final void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        startQNameNode(MagnesiumNode.NODE_CONTAINER, name);
+    }
+
+    @Override
+    public final void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        startQNameNode(MagnesiumNode.NODE_LIST, name);
+    }
+
+    @Override
+    public final void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        startInheritedNode(MagnesiumNode.NODE_LIST_ENTRY, name);
+    }
+
+    @Override
+    public final void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        startQNameNode(MagnesiumNode.NODE_MAP, name);
+    }
+
+    @Override
+    public final void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+            throws IOException {
+        final int size = identifier.size();
+        if (size == 1) {
+            startInheritedNode((byte) (MagnesiumNode.NODE_MAP_ENTRY | MagnesiumNode.PREDICATE_ONE), identifier);
+        } else if (size == 0) {
+            startInheritedNode((byte) (MagnesiumNode.NODE_MAP_ENTRY | MagnesiumNode.PREDICATE_ZERO), identifier);
+        } else if (size < 256) {
+            startInheritedNode((byte) (MagnesiumNode.NODE_MAP_ENTRY | MagnesiumNode.PREDICATE_1B), identifier);
+            output.writeByte(size);
+        } else {
+            startInheritedNode((byte) (MagnesiumNode.NODE_MAP_ENTRY | MagnesiumNode.PREDICATE_4B), identifier);
+            output.writeInt(size);
+        }
+
+        writePredicates(identifier);
+    }
+
+    @Override
+    public final void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        startQNameNode(MagnesiumNode.NODE_MAP_ORDERED, name);
+    }
+
+    @Override
+    public final void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        startQNameNode(MagnesiumNode.NODE_CHOICE, name);
+    }
+
+    @Override
+    public final void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
+        final Integer code = aidCodeMap.get(identifier);
+        if (code == null) {
+            aidCodeMap.put(identifier, aidCodeMap.size());
+            output.writeByte(MagnesiumNode.NODE_AUGMENTATION | MagnesiumNode.ADDR_DEFINE);
+            final Set<QName> qnames = identifier.getPossibleChildNames();
+            output.writeInt(qnames.size());
+            for (QName qname : qnames) {
+                writeQNameInternal(qname);
+            }
+        } else {
+            writeNodeType(MagnesiumNode.NODE_AUGMENTATION, code);
+        }
+        stack.push(identifier);
+    }
+
+    @Override
+    public final boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        if (DOMSource.class.isAssignableFrom(objectModel)) {
+            startSimpleNode(MagnesiumNode.NODE_ANYXML, name);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public final void domSourceValue(final DOMSource value) throws IOException {
+        final StringWriter writer = new StringWriter();
+        try {
+            TF.newTransformer().transform(value, new StreamResult(writer));
+        } catch (TransformerException e) {
+            throw new IOException("Error writing anyXml", e);
+        }
+        writeValue(writer.toString());
+    }
+
+    @Override
+    public final void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint)
+            throws IOException {
+        // FIXME: implement this
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final void endNode() throws IOException {
+        if (stack.pop() instanceof PathArgument) {
+            output.writeByte(MagnesiumNode.NODE_END);
+        }
+    }
+
+    @Override
+    public final void scalarValue(final Object value) throws IOException {
+        if (KEY_LEAF_STATE.equals(stack.peek())) {
+            LOG.trace("Inside a map entry key leaf, not emitting value {}", value);
+        } else {
+            writeObject(value);
+        }
+    }
+
+    @Override
+    final void writeQNameInternal(final QName qname) throws IOException {
+        final Integer code = qnameCodeMap.get(qname);
+        if (code == null) {
+            output.writeByte(MagnesiumValue.QNAME);
+            encodeQName(qname);
+        } else {
+            writeQNameRef(code);
+        }
+    }
+
+    @Override
+    final void writePathArgumentInternal(final PathArgument pathArgument) throws IOException {
+        if (pathArgument instanceof NodeIdentifier) {
+            writeNodeIdentifier((NodeIdentifier) pathArgument);
+        } else if (pathArgument instanceof NodeIdentifierWithPredicates) {
+            writeNodeIdentifierWithPredicates((NodeIdentifierWithPredicates) pathArgument);
+        } else if (pathArgument instanceof AugmentationIdentifier) {
+            writeAugmentationIdentifier((AugmentationIdentifier) pathArgument);
+        } else if (pathArgument instanceof NodeWithValue) {
+            writeNodeWithValue((NodeWithValue<?>) pathArgument);
+        } else if (pathArgument instanceof MountPointIdentifier) {
+            writeMountPointIdentifier((MountPointIdentifier) pathArgument);
+        } else {
+            throw new IOException("Unhandled PathArgument " + pathArgument);
+        }
+    }
+
+    private void writeAugmentationIdentifier(final AugmentationIdentifier identifier) throws IOException {
+        final Set<QName> qnames = identifier.getPossibleChildNames();
+        final int size = qnames.size();
+        if (size < 29) {
+            output.writeByte(MagnesiumPathArgument.AUGMENTATION_IDENTIFIER
+                | size << MagnesiumPathArgument.AID_COUNT_SHIFT);
+        } else if (size < 256) {
+            output.writeByte(MagnesiumPathArgument.AUGMENTATION_IDENTIFIER | MagnesiumPathArgument.AID_COUNT_1B);
+            output.writeByte(size);
+        } else if (size < 65536) {
+            output.writeByte(MagnesiumPathArgument.AUGMENTATION_IDENTIFIER | MagnesiumPathArgument.AID_COUNT_2B);
+            output.writeShort(size);
+        } else {
+            output.writeByte(MagnesiumPathArgument.AUGMENTATION_IDENTIFIER | MagnesiumPathArgument.AID_COUNT_4B);
+            output.writeInt(size);
+        }
+
+        for (QName qname : qnames) {
+            writeQNameInternal(qname);
+        }
+    }
+
+    private void writeNodeIdentifier(final NodeIdentifier identifier) throws IOException {
+        writePathArgumentQName(identifier.getNodeType(), MagnesiumPathArgument.NODE_IDENTIFIER);
+    }
+
+    private void writeMountPointIdentifier(final MountPointIdentifier identifier) throws IOException {
+        writePathArgumentQName(identifier.getNodeType(), MagnesiumPathArgument.MOUNTPOINT_IDENTIFIER);
+    }
+
+    private void writeNodeIdentifierWithPredicates(final NodeIdentifierWithPredicates identifier) throws IOException {
+        final int size = identifier.size();
+        if (size < 5) {
+            writePathArgumentQName(identifier.getNodeType(),
+                (byte) (MagnesiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES
+                        | size << MagnesiumPathArgument.SIZE_SHIFT));
+        } else if (size < 256) {
+            writePathArgumentQName(identifier.getNodeType(),
+                (byte) (MagnesiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES | MagnesiumPathArgument.SIZE_1B));
+            output.writeByte(size);
+        } else if (size < 65536) {
+            writePathArgumentQName(identifier.getNodeType(),
+                (byte) (MagnesiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES | MagnesiumPathArgument.SIZE_2B));
+            output.writeShort(size);
+        } else {
+            writePathArgumentQName(identifier.getNodeType(),
+                (byte) (MagnesiumPathArgument.NODE_IDENTIFIER_WITH_PREDICATES | MagnesiumPathArgument.SIZE_4B));
+            output.writeInt(size);
+        }
+
+        writePredicates(identifier);
+    }
+
+    private void writePredicates(final NodeIdentifierWithPredicates identifier) throws IOException {
+        for (Entry<QName, Object> e : identifier.entrySet()) {
+            writeQNameInternal(e.getKey());
+            writeObject(e.getValue());
+        }
+    }
+
+    private void writeNodeWithValue(final NodeWithValue<?> identifier) throws IOException {
+        writePathArgumentQName(identifier.getNodeType(), MagnesiumPathArgument.NODE_WITH_VALUE);
+        writeObject(identifier.getValue());
+    }
+
+    private void writePathArgumentQName(final QName qname, final byte typeHeader) throws IOException {
+        final Integer code = qnameCodeMap.get(qname);
+        if (code != null) {
+            final int val = code;
+            if (val < 256) {
+                output.writeByte(typeHeader | MagnesiumPathArgument.QNAME_REF_1B);
+                output.writeByte(val);
+            } else if (val < 65792) {
+                output.writeByte(typeHeader | MagnesiumPathArgument.QNAME_REF_2B);
+                output.writeShort(val - 256);
+            } else {
+                output.writeByte(typeHeader | MagnesiumPathArgument.QNAME_REF_4B);
+                output.writeInt(val);
+            }
+        } else {
+            // implied '| MagnesiumPathArgument.QNAME_DEF'
+            output.writeByte(typeHeader);
+            encodeQName(qname);
+        }
+    }
+
+    @Override
+    final void writeYangInstanceIdentifierInternal(final YangInstanceIdentifier identifier) throws IOException {
+        writeValue(identifier);
+    }
+
+    private void writeObject(final @NonNull Object value) throws IOException {
+        if (value instanceof String) {
+            writeValue((String) value);
+        } else if (value instanceof Boolean) {
+            writeValue((Boolean) value);
+        } else if (value instanceof Byte) {
+            writeValue((Byte) value);
+        } else if (value instanceof Short) {
+            writeValue((Short) value);
+        } else if (value instanceof Integer) {
+            writeValue((Integer) value);
+        } else if (value instanceof Long) {
+            writeValue((Long) value);
+        } else if (value instanceof Uint8) {
+            writeValue((Uint8) value);
+        } else if (value instanceof Uint16) {
+            writeValue((Uint16) value);
+        } else if (value instanceof Uint32) {
+            writeValue((Uint32) value);
+        } else if (value instanceof Uint64) {
+            writeValue((Uint64) value);
+        } else if (value instanceof QName) {
+            writeQNameInternal((QName) value);
+        } else if (value instanceof YangInstanceIdentifier) {
+            writeValue((YangInstanceIdentifier) value);
+        } else if (value instanceof byte[]) {
+            writeValue((byte[]) value);
+        } else if (value instanceof Empty) {
+            output.writeByte(MagnesiumValue.EMPTY);
+        } else if (value instanceof Set) {
+            writeValue((Set<?>) value);
+        } else if (value instanceof BigDecimal) {
+            writeValue((BigDecimal) value);
+        } else if (value instanceof BigInteger) {
+            writeValue((BigInteger) value);
+        } else {
+            throw new IOException("Unhandled value type " + value.getClass());
+        }
+    }
+
+    private void writeValue(final boolean value) throws IOException {
+        output.writeByte(value ? MagnesiumValue.BOOLEAN_TRUE : MagnesiumValue.BOOLEAN_FALSE);
+    }
+
+    private void writeValue(final byte value) throws IOException {
+        if (value != 0) {
+            output.writeByte(MagnesiumValue.INT8);
+            output.writeByte(value);
+        } else {
+            output.writeByte(MagnesiumValue.INT8_0);
+        }
+    }
+
+    private void writeValue(final short value) throws IOException {
+        if (value != 0) {
+            output.writeByte(MagnesiumValue.INT16);
+            output.writeShort(value);
+        } else {
+            output.writeByte(MagnesiumValue.INT16_0);
+        }
+    }
+
+    private void writeValue(final int value) throws IOException {
+        if ((value & 0xFFFF0000) != 0) {
+            output.writeByte(MagnesiumValue.INT32);
+            output.writeInt(value);
+        } else if (value != 0) {
+            output.writeByte(MagnesiumValue.INT32_2B);
+            output.writeShort(value);
+        } else {
+            output.writeByte(MagnesiumValue.INT32_0);
+        }
+    }
+
+    private void writeValue(final long value) throws IOException {
+        if ((value & 0xFFFFFFFF00000000L) != 0) {
+            output.writeByte(MagnesiumValue.INT64);
+            output.writeLong(value);
+        } else if (value != 0) {
+            output.writeByte(MagnesiumValue.INT64_4B);
+            output.writeInt((int) value);
+        } else {
+            output.writeByte(MagnesiumValue.INT64_0);
+        }
+    }
+
+    private void writeValue(final Uint8 value) throws IOException {
+        final byte b = value.byteValue();
+        if (b != 0) {
+            output.writeByte(MagnesiumValue.UINT8);
+            output.writeByte(b);
+        } else {
+            output.writeByte(MagnesiumValue.UINT8_0);
+        }
+    }
+
+    private void writeValue(final Uint16 value) throws IOException {
+        final short s = value.shortValue();
+        if (s != 0) {
+            output.writeByte(MagnesiumValue.UINT16);
+            output.writeShort(s);
+        } else {
+            output.writeByte(MagnesiumValue.UINT16_0);
+        }
+    }
+
+    private void writeValue(final Uint32 value) throws IOException {
+        final int i = value.intValue();
+        if ((i & 0xFFFF0000) != 0) {
+            output.writeByte(MagnesiumValue.UINT32);
+            output.writeInt(i);
+        } else if (i != 0) {
+            output.writeByte(MagnesiumValue.UINT32_2B);
+            output.writeShort(i);
+        } else {
+            output.writeByte(MagnesiumValue.UINT32_0);
+        }
+    }
+
+    private void writeValue(final Uint64 value) throws IOException {
+        final long l = value.longValue();
+        if ((l & 0xFFFFFFFF00000000L) != 0) {
+            output.writeByte(MagnesiumValue.UINT64);
+            output.writeLong(l);
+        } else if (l != 0) {
+            output.writeByte(MagnesiumValue.UINT64_4B);
+            output.writeInt((int) l);
+        } else {
+            output.writeByte(MagnesiumValue.UINT64_0);
+        }
+    }
+
+    private void writeValue(final BigDecimal value) throws IOException {
+        output.writeByte(MagnesiumValue.BIGDECIMAL);
+        output.writeUTF(value.toString());
+    }
+
+    abstract void writeValue(BigInteger value) throws IOException;
+
+    private void writeValue(final String value) throws IOException {
+        if (value.isEmpty()) {
+            output.writeByte(MagnesiumValue.STRING_EMPTY);
+        } else if (value.length() <= Short.MAX_VALUE / 2) {
+            output.writeByte(MagnesiumValue.STRING_UTF);
+            output.writeUTF(value);
+        } else if (value.length() <= 1048576) {
+            final byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
+            if (bytes.length < 65536) {
+                output.writeByte(MagnesiumValue.STRING_2B);
+                output.writeShort(bytes.length);
+            } else {
+                output.writeByte(MagnesiumValue.STRING_4B);
+                output.writeInt(bytes.length);
+            }
+            output.write(bytes);
+        } else {
+            output.writeByte(MagnesiumValue.STRING_CHARS);
+            output.writeInt(value.length());
+            output.writeChars(value);
+        }
+    }
+
+    private void writeValue(final byte[] value) throws IOException {
+        if (value.length < 128) {
+            output.writeByte(MagnesiumValue.BINARY_0 + value.length);
+        } else if (value.length < 384) {
+            output.writeByte(MagnesiumValue.BINARY_1B);
+            output.writeByte(value.length - 128);
+        } else if (value.length < 65920) {
+            output.writeByte(MagnesiumValue.BINARY_2B);
+            output.writeShort(value.length - 384);
+        } else {
+            output.writeByte(MagnesiumValue.BINARY_4B);
+            output.writeInt(value.length);
+        }
+        output.write(value);
+    }
+
+    private void writeValue(final YangInstanceIdentifier value) throws IOException {
+        final List<PathArgument> args = value.getPathArguments();
+        final int size = args.size();
+        if (size > 31) {
+            output.writeByte(MagnesiumValue.YIID);
+            output.writeInt(size);
+        } else {
+            output.writeByte(MagnesiumValue.YIID_0 + size);
+        }
+        for (PathArgument arg : args) {
+            writePathArgumentInternal(arg);
+        }
+    }
+
+    private void writeValue(final Set<?> value) throws IOException {
+        final int size = value.size();
+        if (size < 29) {
+            output.writeByte(MagnesiumValue.BITS_0 + size);
+        } else if (size < 285) {
+            output.writeByte(MagnesiumValue.BITS_1B);
+            output.writeByte(size - 29);
+        } else if (size < 65821) {
+            output.writeByte(MagnesiumValue.BITS_2B);
+            output.writeShort(size - 285);
+        } else {
+            output.writeByte(MagnesiumValue.BITS_4B);
+            output.writeInt(size);
+        }
+
+        for (Object bit : value) {
+            checkArgument(bit instanceof String, "Expected value type to be String but was %s", bit);
+            encodeString((String) bit);
+        }
+    }
+
+    // Check if the proposed QName matches the parent. This is only effective if the parent is identified by
+    // NodeIdentifier -- which is typically true
+    private boolean matchesParentQName(final QName qname) {
+        final Object current = stack.peek();
+        return current instanceof NodeIdentifier && qname.equals(((NodeIdentifier) current).getNodeType());
+    }
+
+    // Start an END_NODE-terminated node, which typically has a QName matching the parent. If that is the case we emit
+    // a parent reference instead of an explicit QName reference -- saving at least one byte
+    private void startInheritedNode(final byte type, final PathArgument name) throws IOException {
+        final QName qname = name.getNodeType();
+        if (matchesParentQName(qname)) {
+            output.write(type);
+        } else {
+            writeQNameNode(type, qname);
+        }
+        stack.push(name);
+    }
+
+    // Start an END_NODE-terminated node, which needs its QName encoded
+    private void startQNameNode(final byte type, final PathArgument name) throws IOException {
+        writeQNameNode(type, name.getNodeType());
+        stack.push(name);
+    }
+
+    // Start a simple node, which is not terminated through END_NODE and encode its QName
+    private void startSimpleNode(final byte type, final PathArgument name) throws IOException {
+        writeQNameNode(type, name.getNodeType());
+        stack.push(NO_ENDNODE_STATE);
+    }
+
+    // Encode a QName-based (i.e. NodeIdentifier*) node with a particular QName. This will either result in a QName
+    // definition, or a reference, where this is encoded along with the node type.
+    private void writeQNameNode(final int type, final @NonNull QName qname) throws IOException {
+        final Integer code = qnameCodeMap.get(qname);
+        if (code == null) {
+            output.writeByte(type | MagnesiumNode.ADDR_DEFINE);
+            encodeQName(qname);
+        } else {
+            writeNodeType(type, code);
+        }
+    }
+
+    // Write a node type + lookup
+    private void writeNodeType(final int type, final int code) throws IOException {
+        if (code <= 255) {
+            output.writeByte(type | MagnesiumNode.ADDR_LOOKUP_1B);
+            output.writeByte(code);
+        } else {
+            output.writeByte(type | MagnesiumNode.ADDR_LOOKUP_4B);
+            output.writeInt(code);
+        }
+    }
+
+    // Encode a QName using lookup tables, resuling either in a reference to an existing entry, or emitting two
+    // String values.
+    private void encodeQName(final @NonNull QName qname) throws IOException {
+        final Integer prev = qnameCodeMap.put(qname, qnameCodeMap.size());
+        if (prev != null) {
+            throw new IOException("Internal coding error: attempted to re-encode " + qname + "%s already encoded as "
+                    + prev);
+        }
+
+        final QNameModule module = qname.getModule();
+        final Integer code = moduleCodeMap.get(module);
+        if (code == null) {
+            moduleCodeMap.put(module, moduleCodeMap.size());
+            encodeString(module.getNamespace().toString());
+            final Optional<Revision> rev = module.getRevision();
+            if (rev.isPresent()) {
+                encodeString(rev.get().toString());
+            } else {
+                output.writeByte(MagnesiumValue.STRING_EMPTY);
+            }
+        } else {
+            writeModuleRef(code);
+        }
+        encodeString(qname.getLocalName());
+    }
+
+    // Encode a String using lookup tables, resulting either in a reference to an existing entry, or emitting as
+    // a literal value
+    private void encodeString(final @NonNull String str) throws IOException {
+        final Integer code = stringCodeMap.get(str);
+        if (code != null) {
+            writeRef(code);
+        } else {
+            stringCodeMap.put(str, stringCodeMap.size());
+            writeValue(str);
+        }
+    }
+
+    // Write a QName with a lookup table reference. This is a combination of asserting the value is a QName plus
+    // the effects of writeRef()
+    private void writeQNameRef(final int code) throws IOException {
+        final int val = code;
+        if (val < 256) {
+            output.writeByte(MagnesiumValue.QNAME_REF_1B);
+            output.writeByte(val);
+        } else if (val < 65792) {
+            output.writeByte(MagnesiumValue.QNAME_REF_2B);
+            output.writeShort(val - 256);
+        } else {
+            output.writeByte(MagnesiumValue.QNAME_REF_4B);
+            output.writeInt(val);
+        }
+    }
+
+    // Write a lookup table reference, which table is being referenced is implied by the caller
+    private void writeRef(final int code) throws IOException {
+        final int val = code;
+        if (val < 256) {
+            output.writeByte(MagnesiumValue.STRING_REF_1B);
+            output.writeByte(val);
+        } else if (val < 65792) {
+            output.writeByte(MagnesiumValue.STRING_REF_2B);
+            output.writeShort(val - 256);
+        } else {
+            output.writeByte(MagnesiumValue.STRING_REF_4B);
+            output.writeInt(val);
+        }
+    }
+
+    // Write a lookup module table reference, which table is being referenced is implied by the caller
+    private void writeModuleRef(final int code) throws IOException {
+        final int val = code;
+        if (val < 256) {
+            output.writeByte(MagnesiumValue.MODREF_1B);
+            output.writeByte(val);
+        } else if (val < 65792) {
+            output.writeByte(MagnesiumValue.MODREF_2B);
+            output.writeShort(val - 256);
+        } else {
+            output.writeByte(MagnesiumValue.MODREF_4B);
+            output.writeInt(val);
+        }
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractNormalizedNodeDataInput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractNormalizedNodeDataInput.java
new file mode 100644 (file)
index 0000000..43c066e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import java.io.DataInput;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+abstract class AbstractNormalizedNodeDataInput extends ForwardingDataInput implements NormalizedNodeDataInput {
+    // Visible for subclasses
+    final @NonNull DataInput input;
+
+    AbstractNormalizedNodeDataInput(final DataInput input) {
+        this.input = requireNonNull(input);
+    }
+
+    @Override
+    final DataInput delegate() {
+        return input;
+    }
+
+    @Override
+    public final SchemaPath readSchemaPath() throws IOException {
+        final boolean absolute = input.readBoolean();
+        final int size = input.readInt();
+
+        final Builder<QName> qnames = ImmutableList.builderWithExpectedSize(size);
+        for (int i = 0; i < size; ++i) {
+            qnames.add(readQName());
+        }
+        return SchemaPath.create(qnames.build(), absolute);
+    }
+
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractNormalizedNodeDataOutput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/AbstractNormalizedNodeDataOutput.java
new file mode 100755 (executable)
index 0000000..551e425
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2015 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.binfmt;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+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.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Abstract base class for implementing {@link NormalizedNodeDataOutput} contract. This class uses
+ * {@link NormalizedNodeStreamWriter} as an internal interface for performing the actual NormalizedNode writeout,
+ * i.e. it will defer to a {@link NormalizedNodeWriter} instance.
+ *
+ * <p>
+ * As such, this is an implementation detail not exposed from this package, hence implementations can rely on the
+ * stream being initialized with a header and version.
+ */
+abstract class AbstractNormalizedNodeDataOutput implements NormalizedNodeDataOutput, NormalizedNodeStreamWriter {
+    // Visible for subclasses
+    final DataOutput output;
+
+    private NormalizedNodeWriter normalizedNodeWriter;
+    private boolean headerWritten;
+
+    AbstractNormalizedNodeDataOutput(final DataOutput output) {
+        this.output = requireNonNull(output);
+    }
+
+
+    private void ensureHeaderWritten() throws IOException {
+        if (!headerWritten) {
+            output.writeByte(TokenTypes.SIGNATURE_MARKER);
+            output.writeShort(streamVersion());
+            headerWritten = true;
+        }
+    }
+
+    @Override
+    public final void write(final int value) throws IOException {
+        ensureHeaderWritten();
+        output.write(value);
+    }
+
+    @Override
+    public final void write(final byte[] bytes) throws IOException {
+        ensureHeaderWritten();
+        output.write(bytes);
+    }
+
+    @Override
+    public final void write(final byte[] bytes, final int off, final int len) throws IOException {
+        ensureHeaderWritten();
+        output.write(bytes, off, len);
+    }
+
+    @Override
+    public final void writeBoolean(final boolean value) throws IOException {
+        ensureHeaderWritten();
+        output.writeBoolean(value);
+    }
+
+    @Override
+    public final void writeByte(final int value) throws IOException {
+        ensureHeaderWritten();
+        output.writeByte(value);
+    }
+
+    @Override
+    public final void writeShort(final int value) throws IOException {
+        ensureHeaderWritten();
+        output.writeShort(value);
+    }
+
+    @Override
+    public final void writeChar(final int value) throws IOException {
+        ensureHeaderWritten();
+        output.writeChar(value);
+    }
+
+    @Override
+    public final void writeInt(final int value) throws IOException {
+        ensureHeaderWritten();
+        output.writeInt(value);
+    }
+
+    @Override
+    public final void writeLong(final long value) throws IOException {
+        ensureHeaderWritten();
+        output.writeLong(value);
+    }
+
+    @Override
+    public final void writeFloat(final float value) throws IOException {
+        ensureHeaderWritten();
+        output.writeFloat(value);
+    }
+
+    @Override
+    public final void writeDouble(final double value) throws IOException {
+        ensureHeaderWritten();
+        output.writeDouble(value);
+    }
+
+    @Override
+    public final void writeBytes(final String str) throws IOException {
+        ensureHeaderWritten();
+        output.writeBytes(str);
+    }
+
+    @Override
+    public final void writeChars(final String str) throws IOException {
+        ensureHeaderWritten();
+        output.writeChars(str);
+    }
+
+    @Override
+    public final void writeUTF(final String str) throws IOException {
+        ensureHeaderWritten();
+        output.writeUTF(str);
+    }
+
+    @Override
+    public final void writeQName(final QName qname) throws IOException {
+        ensureHeaderWritten();
+        writeQNameInternal(qname);
+    }
+
+    @Override
+    public final void writeNormalizedNode(final NormalizedNode<?, ?> node) throws IOException {
+        ensureHeaderWritten();
+        if (normalizedNodeWriter == null) {
+            normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(this);
+        }
+        normalizedNodeWriter.write(node);
+    }
+
+    @Override
+    public final void writePathArgument(final PathArgument pathArgument) throws IOException {
+        ensureHeaderWritten();
+        writePathArgumentInternal(pathArgument);
+    }
+
+    @Override
+    public final void writeYangInstanceIdentifier(final YangInstanceIdentifier identifier) throws IOException {
+        ensureHeaderWritten();
+        writeYangInstanceIdentifierInternal(identifier);
+    }
+
+    @Override
+    public final void writeSchemaPath(final SchemaPath path) throws IOException {
+        ensureHeaderWritten();
+
+        output.writeBoolean(path.isAbsolute());
+        final List<QName> qnames = path.getPath();
+        output.writeInt(qnames.size());
+        for (QName qname : qnames) {
+            writeQNameInternal(qname);
+        }
+    }
+
+    @Override
+    public final void close() throws IOException {
+        flush();
+    }
+
+    @Override
+    public void flush() throws IOException {
+        if (output instanceof OutputStream) {
+            ((OutputStream)output).flush();
+        }
+    }
+
+    @Override
+    public final boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        // FIXME: We do not support anydata nodes of any kind, yet
+        return false;
+    }
+
+    abstract short streamVersion();
+
+    abstract void writeQNameInternal(@NonNull QName qname) throws IOException;
+
+    abstract void writePathArgumentInternal(PathArgument pathArgument) throws IOException;
+
+    abstract void writeYangInstanceIdentifierInternal(YangInstanceIdentifier identifier) throws IOException;
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/ForwardingDataInput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/ForwardingDataInput.java
new file mode 100644 (file)
index 0000000..e2994ef
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import java.io.DataInput;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNull;
+
+// Not a ForwardingObject because delegate() can legally throw and we do not want redirect toString()
+abstract class ForwardingDataInput implements DataInput {
+
+    abstract @NonNull DataInput delegate() throws IOException;
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final void readFully(final byte[] b) throws IOException {
+        delegate().readFully(b);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final void readFully(final byte[] b, final int off, final int len) throws IOException {
+        delegate().readFully(b, off, len);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final int skipBytes(final int n) throws IOException {
+        return delegate().skipBytes(n);
+    }
+
+    @Override
+    public final boolean readBoolean() throws IOException {
+        return delegate().readBoolean();
+    }
+
+    @Override
+    public final byte readByte() throws IOException {
+        return delegate().readByte();
+    }
+
+    @Override
+    public final int readUnsignedByte() throws IOException {
+        return delegate().readUnsignedByte();
+    }
+
+    @Override
+    public final short readShort() throws IOException {
+        return delegate().readShort();
+    }
+
+    @Override
+    public final int readUnsignedShort() throws IOException {
+        return delegate().readUnsignedShort();
+    }
+
+    @Override
+    public final char readChar() throws IOException {
+        return delegate().readChar();
+    }
+
+    @Override
+    public final int readInt() throws IOException {
+        return delegate().readInt();
+    }
+
+    @Override
+    public final long readLong() throws IOException {
+        return delegate().readLong();
+    }
+
+    @Override
+    public final float readFloat() throws IOException {
+        return delegate().readFloat();
+    }
+
+    @Override
+    public final double readDouble() throws IOException {
+        return delegate().readDouble();
+    }
+
+    @Override
+    public final String readLine() throws IOException {
+        return delegate().readLine();
+    }
+
+    @Override
+    public final String readUTF() throws IOException {
+        return delegate().readUTF();
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/ForwardingNormalizedNodeDataInput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/ForwardingNormalizedNodeDataInput.java
new file mode 100644 (file)
index 0000000..a915487
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.ReusableStreamReceiver;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+abstract class ForwardingNormalizedNodeDataInput extends ForwardingDataInput implements NormalizedNodeDataInput {
+
+    @Override
+    abstract @NonNull NormalizedNodeDataInput delegate() throws IOException;
+
+    @Override
+    public final void streamNormalizedNode(final NormalizedNodeStreamWriter writer) throws IOException {
+        delegate().streamNormalizedNode(writer);
+    }
+
+    @Override
+    public final NormalizedNode<?, ?> readNormalizedNode() throws IOException {
+        return delegate().readNormalizedNode();
+    }
+
+    @Override
+    public final NormalizedNode<?, ?> readNormalizedNode(final ReusableStreamReceiver receiver) throws IOException {
+        return delegate().readNormalizedNode(receiver);
+    }
+
+    @Override
+    public final YangInstanceIdentifier readYangInstanceIdentifier() throws IOException {
+        return delegate().readYangInstanceIdentifier();
+    }
+
+    @Override
+    public final QName readQName() throws IOException {
+        return delegate().readQName();
+    }
+
+    @Override
+    public final PathArgument readPathArgument() throws IOException {
+        return delegate().readPathArgument();
+    }
+
+    @Override
+    public final SchemaPath readSchemaPath() throws IOException {
+        return delegate().readSchemaPath();
+    }
+
+    @Override
+    public final NormalizedNodeStreamVersion getVersion() throws IOException {
+        return delegate().getVersion();
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/InvalidNormalizedNodeStreamException.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/InvalidNormalizedNodeStreamException.java
new file mode 100644 (file)
index 0000000..6bd9b96
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 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.yang.data.codec.binfmt;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown from NormalizedNodeInputStreamReader when the input stream does not contain
+ * valid serialized data.
+ *
+ * @author Thomas Pantelis
+ */
+public class InvalidNormalizedNodeStreamException extends IOException {
+    private static final long serialVersionUID = 1L;
+
+    public InvalidNormalizedNodeStreamException(final String message) {
+        super(message);
+    }
+
+    public InvalidNormalizedNodeStreamException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumNode.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumNode.java
new file mode 100644 (file)
index 0000000..bb0ee6c
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, 2015 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.binfmt;
+
+/**
+ * Stream constants identifying individual node types.
+ */
+final class LithiumNode {
+    static final byte LEAF_NODE = 1;
+    static final byte LEAF_SET = 2;
+    static final byte LEAF_SET_ENTRY_NODE = 3;
+    static final byte CONTAINER_NODE = 4;
+    static final byte UNKEYED_LIST = 5;
+    static final byte UNKEYED_LIST_ITEM = 6;
+    static final byte MAP_NODE = 7;
+    static final byte MAP_ENTRY_NODE = 8;
+    static final byte ORDERED_MAP_NODE = 9;
+    static final byte CHOICE_NODE = 10;
+    static final byte AUGMENTATION_NODE = 11;
+    static final byte ANY_XML_NODE = 12;
+    static final byte END_NODE = 13;
+    static final byte ORDERED_LEAF_SET = 14;
+    static final byte YANG_MODELED_ANY_XML_NODE = 15;
+
+    private LithiumNode() {
+        throw new UnsupportedOperationException("utility class");
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumNormalizedNodeInputStreamReader.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumNormalizedNodeInputStreamReader.java
new file mode 100755 (executable)
index 0000000..61f5030
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014, 2015 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.binfmt;
+
+import com.google.common.base.Strings;
+import java.io.DataInput;
+import java.io.IOException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+
+/**
+ * Lithium (or Oxygen really) specialization of AbstractLithiumDataInput.
+ */
+final class LithiumNormalizedNodeInputStreamReader extends AbstractLithiumDataInput {
+    LithiumNormalizedNodeInputStreamReader(final DataInput input) {
+        super(input);
+    }
+
+    @Override
+    public NormalizedNodeStreamVersion getVersion() {
+        return NormalizedNodeStreamVersion.LITHIUM;
+    }
+
+    @Override
+    public QName readQName() throws IOException {
+        // Read in the same sequence of writing
+        String localName = readCodedString();
+        String namespace = readCodedString();
+        String revision = Strings.emptyToNull(readCodedString());
+
+        return QNameFactory.create(localName, namespace, revision);
+    }
+
+    @Override
+    AugmentationIdentifier readAugmentationIdentifier() throws IOException {
+        return defaultReadAugmentationIdentifier();
+    }
+
+    @Override
+    NodeIdentifier readNodeIdentifier() throws IOException {
+        return new NodeIdentifier(readQName());
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumNormalizedNodeOutputStreamWriter.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumNormalizedNodeOutputStreamWriter.java
new file mode 100644 (file)
index 0000000..29b812c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014, 2015 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.binfmt;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+
+/**
+ * NormalizedNodeOutputStreamWriter will be used by distributed datastore to send normalized node in
+ * a stream.
+ * A stream writer wrapper around this class will write node objects to stream in recursive manner.
+ * for example - If you have a ContainerNode which has a two LeafNode as children, then
+ * you will first call
+ * {@link #startContainerNode(org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier, int)},
+ * then will call
+ * {@link #leafNode(org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier, Object)} twice
+ * and then, {@link #endNode()} to end container node.
+ *
+ * <p>Based on the each node, the node type is also written to the stream, that helps in reconstructing the object,
+ * while reading.
+ */
+final class LithiumNormalizedNodeOutputStreamWriter extends AbstractLithiumDataOutput {
+    LithiumNormalizedNodeOutputStreamWriter(final DataOutput output) {
+        super(output);
+    }
+
+    @Override
+    short streamVersion() {
+        return TokenTypes.LITHIUM_VERSION;
+    }
+
+    @Override
+    void writeQNameInternal(final QName qname) throws IOException {
+        defaultWriteQName(qname);
+    }
+
+    @Override
+    void writeModule(final QNameModule module) throws IOException {
+        defaultWriteModule(module);
+    }
+
+    @Override
+    void writeAugmentationIdentifier(final AugmentationIdentifier aid) throws IOException {
+        defaultWriteAugmentationIdentifier(aid);
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumPathArgument.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumPathArgument.java
new file mode 100644 (file)
index 0000000..67c8284
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.binfmt;
+
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+final class LithiumPathArgument {
+    static final byte AUGMENTATION_IDENTIFIER = 1;
+    static final byte NODE_IDENTIFIER = 2;
+    static final byte NODE_IDENTIFIER_WITH_VALUE = 3;
+    static final byte NODE_IDENTIFIER_WITH_PREDICATES = 4;
+
+    private LithiumPathArgument() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    static byte getSerializablePathArgumentType(final PathArgument pathArgument) {
+        if (pathArgument instanceof NodeIdentifier) {
+            return NODE_IDENTIFIER;
+        } else if (pathArgument instanceof NodeIdentifierWithPredicates) {
+            return NODE_IDENTIFIER_WITH_PREDICATES;
+        } else if (pathArgument instanceof AugmentationIdentifier) {
+            return AUGMENTATION_IDENTIFIER;
+        } else if (pathArgument instanceof NodeWithValue) {
+            return NODE_IDENTIFIER_WITH_VALUE;
+        } else {
+            throw new IllegalArgumentException("Unknown type of PathArgument = " + pathArgument);
+        }
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumTokens.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumTokens.java
new file mode 100644 (file)
index 0000000..25bcfbe
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 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.binfmt;
+
+/**
+ * Tokens related to Lithium/NeonSR2 encoding.
+ */
+final class LithiumTokens {
+    /**
+     * The value is a reference to a previously-defined entity, typically through {@link #IS_STRING_VALUE}.
+     */
+    static final byte IS_CODE_VALUE = 1;
+    /**
+     * The value is a String, which needs to be kept memoized for the purposes for being referenced by
+     * {@link #IS_CODE_VALUE}.
+     */
+    static final byte IS_STRING_VALUE = 2;
+    /**
+     * The value is an explicit null.
+     */
+    static final byte IS_NULL_VALUE = 3;
+
+    private LithiumTokens() {
+
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumValue.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/LithiumValue.java
new file mode 100644 (file)
index 0000000..1487209
--- /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.data.codec.binfmt;
+
+final class LithiumValue {
+    // The String length threshold beyond which a String should be encoded as bytes
+    static final int STRING_BYTES_LENGTH_THRESHOLD = Short.MAX_VALUE / 4;
+
+    static final byte SHORT_TYPE = 1;
+    static final byte BYTE_TYPE = 2;
+    static final byte INT_TYPE = 3;
+    static final byte LONG_TYPE = 4;
+    static final byte BOOL_TYPE = 5;
+    static final byte QNAME_TYPE = 6;
+    static final byte BITS_TYPE = 7;
+    static final byte YANG_IDENTIFIER_TYPE = 8;
+    static final byte STRING_TYPE = 9;
+    static final byte BIG_INTEGER_TYPE = 10;
+    static final byte BIG_DECIMAL_TYPE = 11;
+    static final byte BINARY_TYPE = 12;
+    // Leaf nodes no longer allow null values. The "empty" type is now represented as
+    // org.opendaylight.yangtools.yang.common.Empty. This is kept for backwards compatibility.
+    @Deprecated
+    static final byte NULL_TYPE = 13;
+    static final byte STRING_BYTES_TYPE = 14;
+    static final byte EMPTY_TYPE = 15;
+
+    private LithiumValue() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumDataInput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumDataInput.java
new file mode 100644 (file)
index 0000000..08adc46
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.math.BigInteger;
+
+final class MagnesiumDataInput extends AbstractMagnesiumDataInput {
+    MagnesiumDataInput(final DataInput input) {
+        super(input);
+    }
+
+    @Override
+    public NormalizedNodeStreamVersion getVersion() {
+        return NormalizedNodeStreamVersion.MAGNESIUM;
+    }
+
+    @Override
+    BigInteger readBigInteger() throws IOException {
+        throw new InvalidNormalizedNodeStreamException("BigInteger coding is not supported");
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumDataOutput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumDataOutput.java
new file mode 100644 (file)
index 0000000..051e82c
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.math.BigInteger;
+
+final class MagnesiumDataOutput extends AbstractMagnesiumDataOutput {
+    MagnesiumDataOutput(final DataOutput output) {
+        super(output);
+    }
+
+    @Override
+    short streamVersion() {
+        return TokenTypes.MAGNESIUM_VERSION;
+    }
+
+    @Override
+    void writeValue(final BigInteger value) throws IOException {
+        throw new IOException("BigInteger values are not supported");
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumNode.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumNode.java
new file mode 100644 (file)
index 0000000..ab8cc0b
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+/**
+ * Magnesium encoding Node types. Encoded as a single byte, split as follows:
+ * <pre>
+ *   7 6 5 4 3 2 1 0
+ *  +-+-+-+-+-+-+-+-+
+ *  | P | A |  Type |
+ *  +-+-+-+-+-+-+-+-+
+ * </pre>
+ * The fields being:
+ * <ul>
+ *   <li>Bits 7 and 6 (most significant): predicate presence. Only valid for NODE_MAP_ENTRY and NODE_LEAF</li>
+ *   <li>Bits 5 and 4: addressing mode</li>
+ *   <li>Bits 3-0 (least significant) node type</li>
+ * </ul>
+ */
+// TODO: restructure this into some concrete examples
+//- a leaf referencing a previously-encoded NodeIdentifier would take
+//6 bytes:
+//  (byte)    NodeTypes.LEAF_NODE
+//  (byte)    TokenTypes.IS_QNAME_CODE
+//  (int)     code value
+//where as new tokens can do that in as few as 2 bytes:
+//  (byte)    NodeType.(NODE_LEAF | ADDR_LOOKUP_1B)
+//  (byte)    code value
+//with worst-case being 5 bytes:
+//  (byte)    NodeType.(NODE_LEAF | ADDR_LOOKUP_4B)
+//  (int)     code value
+//- a map entry node referencing previously-encoded QNames and a single
+//predicate would take a base of 15 bytes (not counting value object):
+//  (byte)    NodeTypes.MAP_ENTRY_NODE
+//  (byte)    TokenTypes.IS_QNAME_CODE
+//  (int)     code value
+//  (int)     size of predicates
+//  (byte)    TokenTypes.IS_QNAME_CODE
+//  (int)     code value
+//whereas new tokens can do that in as few as 3 bytes:
+//  (byte)    NodeType.(NODE_MAP_ENTRY | ADDR_LOOKUP_1B | PREDICATE_ONE)
+//  (byte)    code value
+//  (byte)    code value
+//this ability is maintained for up to 255 predicates with:
+//  (byte)    NodeType.(NODE_MAP_ENTRY | ADDR_LOOKUP_1B | PREDICATE_1B)
+//  (byte)    code value
+//  (byte)    size of predicates
+//  (byte)    code value [0-255]
+//- a leaf representing a key inside a map entry has the ability to skip
+//value encoding by being as simple as:
+//  (byte)    NodeTYpe.(NODE_LEAF | ADDR_LOOKUP_1B | PREDICATE_ONE)
+//  (byte)    code value
+//
+final class MagnesiumNode {
+    /**
+     * End of node marker. Does not support addressing modes.
+     */
+    static final byte NODE_END             = 0x00; // N/A
+    /**
+     * A leaf node. Encoding can specify {@link #PREDICATE_ONE}, which indicates the value is skipped as the encoder
+     * has emitted a parent MapNode, whose identifier contains the value.
+     */
+    static final byte NODE_LEAF            = 0x01;
+    static final byte NODE_CONTAINER       = 0x02;
+    static final byte NODE_LIST            = 0x03;
+    static final byte NODE_MAP             = 0x04;
+    static final byte NODE_MAP_ORDERED     = 0x05;
+    static final byte NODE_LEAFSET         = 0x06;
+    static final byte NODE_LEAFSET_ORDERED = 0x07;
+    static final byte NODE_CHOICE          = 0x08;
+    static final byte NODE_AUGMENTATION    = 0x09;
+    static final byte NODE_ANYXML          = 0x0A;
+    static final byte NODE_LIST_ENTRY      = 0x0B;
+    static final byte NODE_LEAFSET_ENTRY   = 0x0C;
+    static final byte NODE_MAP_ENTRY       = 0x0D;
+
+    // TODO: either implement or remove this coding. While Lithium has emit code, it lacks the code do read such nodes,
+    //       which most probably means we do not need to bother ...
+    static final byte NODE_ANYXML_MODELED  = 0x0E;
+    // 0x0F reserved for anydata
+    static final byte TYPE_MASK            = 0x0F;
+
+
+    /**
+     * Inherit identifier from parent. This addressing mode is applicable in:
+     * <ul>
+     *   <li>{@link #NODE_END}, where an identifier is not applicable
+     *   <li>{@link #NODE_LIST_ENTRY}, where the NodeIdentifier is inherited from parent {@link #NODE_LIST}</li>
+     *   <li>{@link #NODE_MAP_ENTRY}, where the NodeIdentifier is inherited from parent {@link #NODE_MAP} or
+     *       {@link #NODE_MAP_ORDERED}</li>
+     *   <li>{@link #NODE_LEAFSET_ENTRY}, where the QName inherited from parent and the value is inferred from the
+     *       next {@link MagnesiumValue} encoded</li>
+     * </ul>
+     */
+    static final byte ADDR_PARENT     = 0x00;
+    /**
+     * Define a new QName-based identifier constant. For {@link #NODE_AUGMENTATION} this is a set of QNames. Assign
+     * a new linear key to this constant.
+     */
+    static final byte ADDR_DEFINE     = 0x10;
+    /**
+     * Reference a previously {@link #ADDR_DEFINE}d identifier constant. This node byte is followed by an unsigned
+     * byte, which holds the linear key previously defined (i.e. 0-255).
+     */
+    static final byte ADDR_LOOKUP_1B  = 0x20;
+    /**
+     * Reference a previously {@link #ADDR_DEFINE}d identifier constant. This node byte is followed by a signed int,
+     * which holds the linear key previously defined.
+     */
+    static final byte ADDR_LOOKUP_4B  = 0x30;
+    static final byte ADDR_MASK       = ADDR_LOOKUP_4B;
+
+    /**
+     * Predicate encoding: no predicates are present in a {@link #NODE_MAP_ENTRY}.
+     */
+    static final byte PREDICATE_ZERO = 0x00;
+
+    /**
+     * Predicate encoding: a single predicate is present in a {@link #NODE_MAP_ENTRY}. In case of {@link #NODE_LEAF}
+     * encoded as part of a {@link #NODE_MAP_ENTRY} this bit indicates the <strong>value</strong> is not encoded and
+     * should be looked up from the map entry's predicates.
+     *
+     * <p>
+     * The predicate is encoded as a {@link #ADDR_DEFINE} or {@link #ADDR_LOOKUP_1B}/{@link #ADDR_LOOKUP_4B},
+     * followed by an encoded {@link MagnesiumValue}.
+     */
+    static final byte PREDICATE_ONE   = 0x40;
+
+    /**
+     * Predicate encoding: 0-255 predicates are present, as specified by the following {@code unsigned byte}. This
+     * encoding is expected to be exceedingly rare. This should not be used to encode 0 or 1 predicate, those cases
+     * should be encoded as:
+     * <ul>
+     *   <li>no PREDICATE_* set when there are no predicates (probably not valid anyway)</li>
+     *   <li><{@link #PREDICATE_ONE} if there is only one predicate</li>
+     * </ul>
+     */
+    static final byte PREDICATE_1B    = (byte) 0x80;
+
+    /**
+     * Predicate encoding 0 - {@link Integer#MAX_VALUE} predicates are present, as specified by the following
+     * {@code int}. This should not be used where 0-255 predicates are present.
+     */
+    static final byte PREDICATE_4B    = (byte) (PREDICATE_ONE | PREDICATE_1B);
+    static final byte PREDICATE_MASK  = PREDICATE_4B;
+
+    private MagnesiumNode() {
+
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumPathArgument.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumPathArgument.java
new file mode 100644 (file)
index 0000000..c835ae6
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+/**
+ * Path Argument types used in Magnesium encoding. These are encoded as a single byte, three bits of which are reserved
+ * for PathArgument type itself:
+ * <pre>
+ *   7 6 5 4 3 2 1 0
+ *  +-+-+-+-+-+-+-+-+
+ *  |         | Type|
+ *  +-+-+-+-+-+-+-+-+
+ * </pre>
+ * There are five type defined:
+ * <ul>
+ *   <li>{@link #AUGMENTATION_IDENTIFIER}, which additionally holds the number of QName elements encoded:
+ *     <pre>
+ *        7 6 5 4 3 2 1 0
+ *       +-+-+-+-+-+-+-+-+
+ *       |  Count  |0 0 0|
+ *       +-+-+-+-+-+-+-+-+
+ *     </pre>
+ *     Where count is coded as an unsigned integer, with {@link #AID_COUNT_1B} and {@link #AID_COUNT_2B} and
+ *     {@link #AID_COUNT_4B} indicating extended coding with up to 4 additional bytes. This byte is followed by
+ *     {@code count} {@link MagnesiumValue} QNames.
+ *     <pre>
+ *       7 6 5 4 3 2 1 0
+ *      +-+-+-+-+-+-+-+-+
+ *      |0 0 0| Q |0 0 1|
+ *      +-+-+-+-+-+-+-+-+
+ *     </pre>
+ *     Where QName coding is achieved via {@link #QNAME_DEF}, {@link #QNAME_REF_1B}, {@link #QNAME_REF_2B} and
+ *     {@link #QNAME_REF_4B}.
+ *   </li>
+ *   <li>{@link #NODE_IDENTIFIER_WITH_PREDICATES}, which encodes a QName same way NodeIdentifier does:
+ *     <pre>
+ *       7 6 5 4 3 2 1 0
+ *      +-+-+-+-+-+-+-+-+
+ *      | Size| Q |0 1 0|
+ *      +-+-+-+-+-+-+-+-+
+ *      </pre>
+ *      but additionally encodes number of predicates contained using {@link #SIZE_0} through {@link #SIZE_4}. If that
+ *      number cannot be expressed, {@link #SIZE_1B}, {@value #SIZE_2B} and {@link #SIZE_4B} indicate number and format
+ *      of additional bytes that hold number of predicates.
+ *
+ *      <p>
+ *      This is then followed by the specified number of QName/Object key/value pairs based on {@link MagnesiumValue}
+ *      encoding.
+ *   </li>
+ *   <li>{@link #NODE_WITH_VALUE}, which encodes a QName same way NodeIdentifier does:
+ *     <pre>
+ *       7 6 5 4 3 2 1 0
+ *      +-+-+-+-+-+-+-+-+
+ *      |0 0 0| Q |0 1 1|
+ *      +-+-+-+-+-+-+-+-+
+ *     </pre>
+ *     but is additionally followed by a single encoded value, as per {@link MagnesiumValue}.
+ *   </li>
+ *   <li>{@link #MOUNTPOINT_IDENTIFIER}, which encodes a QName same way NodeIdentifier does:
+ *     <pre>
+ *       7 6 5 4 3 2 1 0
+ *      +-+-+-+-+-+-+-+-+
+ *      |0 0 0| Q |1 0 0|
+ *      +-+-+-+-+-+-+-+-+
+ *     </pre>
+ *   </li>
+ * </ul>
+ */
+final class MagnesiumPathArgument {
+    // 3 bits reserved for type...
+    static final byte AUGMENTATION_IDENTIFIER         = 0x00;
+    static final byte NODE_IDENTIFIER                 = 0x01;
+    static final byte NODE_IDENTIFIER_WITH_PREDICATES = 0x02;
+    static final byte NODE_WITH_VALUE                 = 0x03;
+    static final byte MOUNTPOINT_IDENTIFIER           = 0x04;
+
+    // ... leaving three values currently unused
+    // 0x05 reserved
+    // 0x06 reserved
+    // 0x07 reserved
+
+    static final byte TYPE_MASK                       = 0x07;
+
+    // In case of AUGMENTATION_IDENTIFIER, top 5 bits are used to encode the number of path arguments, except last three
+    // values. This means that up to AugmentationIdentifiers with up to 28 components have this length encoded inline,
+    // otherwise we encode them in following 1 (unsigned), 2 (unsigned) or 4 (signed) bytes
+    static final byte AID_COUNT_1B                    = (byte) 0xE8;
+    static final byte AID_COUNT_2B                    = (byte) 0xF0;
+    static final byte AID_COUNT_4B                    = (byte) 0xF8;
+    static final byte AID_COUNT_MASK                  = AID_COUNT_4B;
+    static final byte AID_COUNT_SHIFT                 = 3;
+
+    // For normal path path arguments we can either define a QName reference or follow a 1-4 byte reference.
+    static final byte QNAME_DEF                       = 0x00;
+    static final byte QNAME_REF_1B                    = 0x08; // Unsigned
+    static final byte QNAME_REF_2B                    = 0x10; // Unsigned
+    static final byte QNAME_REF_4B                    = 0x18; // Signed
+    static final byte QNAME_MASK                      = QNAME_REF_4B;
+
+    // For NodeIdentifierWithPredicates we also carry the number of subsequent path arguments. The case of 0-4 arguments
+    // is indicated directly, otherwise there is 1-4 bytes carrying the reference.
+    static final byte SIZE_0                          = 0x00;
+    static final byte SIZE_1                          = 0x20;
+    static final byte SIZE_2                          = 0x40;
+    static final byte SIZE_3                          = 0x60;
+    static final byte SIZE_4                          = (byte) 0x80;
+    static final byte SIZE_1B                         = (byte) 0xA0;
+    static final byte SIZE_2B                         = (byte) 0xC0;
+    static final byte SIZE_4B                         = (byte) 0xE0;
+    static final byte SIZE_MASK                       = SIZE_4B;
+    static final byte SIZE_SHIFT                      = 5;
+
+    private MagnesiumPathArgument() {
+
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumValue.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/MagnesiumValue.java
new file mode 100644 (file)
index 0000000..62fae59
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import java.io.DataOutput;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Magnesium encoding value types. Serialized as a single byte.
+ */
+/*
+ * Note these constants are organized by their absolute value, which is slightly counter-intuitive when trying to make
+ * sense of what is going on.
+ *
+ * TODO: create some sort of facility which would provide symbolic names for debugging and documentation purposes.
+ */
+final class MagnesiumValue {
+    /**
+     * {@link Boolean#FALSE} value.
+     */
+    static final byte BOOLEAN_FALSE  = 0x00;
+    /**
+     * {@link Boolean#TRUE} value.
+     */
+    static final byte BOOLEAN_TRUE   = 0x01;
+    /**
+     * An {@link Empty} value.
+     */
+    static final byte EMPTY          = 0x02;
+    /**
+     * A Byte, followed by a byte holding the value.
+     */
+    static final byte INT8           = 0x03;
+    /**
+     * A Short, followed by a {@code short} holding the value.
+     */
+    static final byte INT16          = 0x04;
+    /**
+     * An Integer, followed by an {@code int} holding the value.
+     */
+    static final byte INT32          = 0x05;
+    /**
+     * A Long, followed by an {@code long} holding the value.
+     */
+    static final byte INT64          = 0x06;
+    /**
+     * A Uint8, followed by an {@code unsigned byte} holding the value.
+     */
+    static final byte UINT8          = 0x07;
+    /**
+     * A Uint16, followed by a {@code unsigned short} holding the value.
+     */
+    static final byte UINT16         = 0x08;
+    /**
+     * A Uint32, followed by an {@code unsigned int} holding the value.
+     */
+    static final byte UINT32         = 0x09;
+    /**
+     * A Uint64, followed by an {@code unsigned long} holding the value.
+     */
+    static final byte UINT64         = 0x0A;
+    /**
+     * A {@link String}, encoded through {@link DataOutput#writeUTF(String)}. Note this is generally true of any
+     * string with less then 16384 characters.
+     */
+    static final byte STRING_UTF     = 0x0B;
+    /**
+     * A {@link String}, encoded as an {@code unsigned short} followed by that many UTF8-encoded bytes.
+     */
+    static final byte STRING_2B      = 0x0C;
+    /**
+     * A {@link String}, encoded as an {@code int >= 0} followed by that many UTF8-encoded bytes.
+     */
+    static final byte STRING_4B      = 0x0D;
+    /**
+     * A {@link String}, encoded as an {@code int >= 0} followed by that many UTF16 characters, i.e. as produced by
+     * {@link DataOutput#writeChars(String)}.
+     */
+    static final byte STRING_CHARS   = 0x0E;
+    /**
+     * Utility 'reference coding' codepoint with {@code unsigned byte} offset. This is not a value type, but is used in
+     * context of various schema-related encodings like constant strings, QNameModule and similar.
+     */
+    static final byte STRING_REF_1B  = 0x0F;
+    /**
+     * Utility 'reference coding' codepoint with {@code unsigned short} offset. This is not a value type, but is used in
+     * context of various schema-related encodings like constant strings, QNameModule and similar.
+     */
+    static final byte STRING_REF_2B  = 0x10;
+    /**
+     * Utility 'reference coding' codepoint with {@code int} offset. This is not a value type, but is used in context of
+     * various schema-related encodings like constant strings, QNameModule and similar.
+     */
+    static final byte STRING_REF_4B  = 0x11;
+    /**
+     * A {@code byte[])}, encoded as a single {@code unsigned byte} followed by 128-383 bytes. Note that smaller
+     * arrays are encoded via {@link #BINARY_0} - {@link #BINARY_127} range.
+     */
+    static final byte BINARY_1B      = 0x12;
+    /**
+     * A {@code byte[])}, encoded as a single {@code unsigned short} followed by 384-65919 bytes. See also
+     * {@link #BINARY_1B}.
+     */
+    static final byte BINARY_2B      = 0x13;
+    /**
+     * A {@code byte[])}, encoded as a single {@code int} followed by that many bytes bytes. See also
+     * {@link #BINARY_2B}.
+     */
+    static final byte BINARY_4B      = 0x14;
+    /**
+     * A {@link YangInstanceIdentifier}, encoded as a single {@code int}, followed by that many components. See
+     * also {@link #YIID_0}, which offers optimized encoding for up to 31 components. Components are encoded using
+     * {@link MagnesiumPathArgument} coding.
+     */
+    static final byte YIID           = 0x15;
+    /**
+     * A QName literal. Encoded as QNameModule + String. This literal is expected to be memoized on receiver side, which
+     * assigns the next linear integer identifier. The sender will memoize it too and further references to this QName
+     * will be made via {@link #QNAME_REF_1B}, {@link #QNAME_REF_2B} or {@link #QNAME_REF_4B}.
+     *
+     * <p>
+     * Note that QNameModule (and String in this context) encoding works similarly -- it can only occur as part of a
+     * QName (coming from here or {@link MagnesiumPathArgument}) and is subject to the same memoization.
+     *
+     * <p>
+     * For example, given two QNames {@code foo = QName.create("foo", "abc")} and
+     * {@code bar = QName.create("foo", "def")}, if they are written in order {@code foo, bar, foo}, then the following
+     * events are emitted:
+     * <pre>
+     *   QNAME                (define QName, assign shorthand Q0)
+     *   STRING_UTF   "foo"   ("foo", assign shorthand S0, implies define QNameModule, assign shorthand M0)
+     *   STRING_EMPTY         (foo's non-existent revision)
+     *   STRING_UTF   "abc"   ("abc", assign shorthand S1)
+     *   QNAME                (define QName, assign shorthand Q1)
+     *   MODREF_1B    (byte)0 (reference M0)
+     *   STRING_UTF   "def"   ("def", assign shorthand S2)
+     *   QNAME_REF_1B (byte)0 (reference Q0)
+     * </pre>
+     */
+    // Design note: STRING_EMPTY is required to *NOT* establish a shortcut, as that is less efficient (and hence does
+    //              not make sense from the sender, the receiver or the serialization protocol itself.
+    static final byte QNAME          = 0x16;
+    /**
+     * Reference a QName previously defined via {@link #QNAME}. Reference number is encoded as {@code unsigned byte}.
+     */
+    static final byte QNAME_REF_1B   = 0x17;
+    /**
+     * Reference a QName previously defined via {@link #QNAME}. Reference number is encoded as {@code unsigned short}.
+     */
+    static final byte QNAME_REF_2B   = 0x18;
+    /**
+     * Reference a QName previously defined via {@link #QNAME}. Reference number is encoded as {@code int}.
+     */
+    static final byte QNAME_REF_4B   = 0x19;
+    /**
+     * Reference a previously defined QNameModule. Reference number is encoded as {@code unsigned byte}.
+     */
+    static final byte MODREF_1B      = 0x1A;
+    /**
+     * Reference a previously defined QNameModule. Reference number is encoded as {@code unsigned short}.
+     */
+    static final byte MODREF_2B      = 0x1B;
+    /**
+     * Reference a previously defined QNameModule. Reference number is encoded as {@code int}.
+     */
+    static final byte MODREF_4B      = 0x1C;
+
+    /**
+     * A {@link BigDecimal}, encoded through {@link DataOutput#writeUTF(String)}.
+     */
+    // This is legacy compatibility. At some point we will remove support for writing these.
+    static final byte BIGDECIMAL     = 0x1D;
+    /**
+     * A {@link BigInteger}, encoded through {@link DataOutput#writeUTF(String)}.
+     */
+    // This is legacy compatibility. At some point we will remove support for writing these.
+    static final byte BIGINTEGER     = 0x1E;
+
+    // 0x1F reserved
+
+    /**
+     * Byte value {@code 0}.
+     */
+    static final byte INT8_0         = 0x20;
+    /**
+     * Short value {@code 0}.
+     */
+    static final byte INT16_0        = 0x21;
+    /**
+     * Integer value {@code 0}.
+     */
+    static final byte INT32_0        = 0x22;
+    /**
+     * Long value {@code 0}.
+     */
+    static final byte INT64_0        = 0x23;
+    /**
+     * {@link Uint8#ZERO} value.
+     */
+    static final byte UINT8_0        = 0x24;
+    /**
+     * {@link Uint16#ZERO} value.
+     */
+    static final byte UINT16_0       = 0x25;
+    /**
+     * {@link Uint32#ZERO} value.
+     */
+    static final byte UINT32_0       = 0x26;
+    /**
+     * {@link Uint64#ZERO} value.
+     */
+    static final byte UINT64_0       = 0x27;
+    /**
+     * Empty String value ({@code ""}).
+     */
+    static final byte STRING_EMPTY   = 0x28;
+    /**
+     * {@link #INT32} with a 2-byte operand.
+     */
+    static final byte INT32_2B       = 0x29;
+    /**
+     * {@link #UINT32} with a 2-byte operand.
+     */
+    static final byte UINT32_2B      = 0x2A;
+    /**
+     * {@link #INT64} with a 4-byte operand.
+     */
+    static final byte INT64_4B       = 0x2B;
+    /**
+     * {@link #UINT64} with a 4-byte operand.
+     */
+    static final byte UINT64_4B      = 0x2C;
+
+    // 0x2D - 0x39 reserved
+
+    /**
+     * Empty bits value. This code point starts the range, where the number of bits can be extracted as
+     * {@code code & 0x1F)}. Last three values of this range are used to encode more than 28 entries.
+     */
+    static final byte BITS_0         = 0x40;
+    /**
+     * A bits value of up to 255 entries. Number of values is encoded as the following {@code unsigned byte}.
+     */
+    static final byte BITS_1B        = 0x5D;
+    /**
+     * A bits value of up to 65535 entries. Number of values is encoded as the following {@code unsigned short}.
+     */
+    static final byte BITS_2B        = 0x5E;
+    /**
+     * A bits value. Number of values is encoded as the following {@code int}.
+     */
+    static final byte BITS_4B        = 0x5F;
+
+    /**
+     * {@link YangInstanceIdentifier} with zero components. This code point starts the range ending with
+     * {@link #YIID_31}, where the number of components can be extracted as {@code code & 0x1F}. Identifiers with
+     * more than 31 components are encoded using {@link #YIID}.
+     */
+    static final byte YIID_0         = 0x60;
+    /**
+     * {@link YangInstanceIdentifier} with 31 components. See {@link #YIID_0}.
+     */
+    static final byte YIID_31        = 0x7F;
+
+    /**
+     * A {@code byte[]} with 0 bytes. This code point starts the range ending with {@link #BINARY_127}, where
+     * the number of bytes can be extracted as {@code code & 0x7F}. Arrays longer than 127 bytes are encoded using
+     * {@link #BINARY_1B}, {@link #BINARY_2B} and {@link #BINARY_4B} as needed.
+     */
+    static final byte BINARY_0       = (byte) 0x80;
+    /**
+     * A {@code byte[]} with 127 bytes. See {@link #BINARY_0}.
+     */
+    static final byte BINARY_127     = (byte) 0xFF;
+
+    private MagnesiumValue() {
+
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NeonSR2NormalizedNodeInputStreamReader.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NeonSR2NormalizedNodeInputStreamReader.java
new file mode 100644 (file)
index 0000000..5403cbf
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import static com.google.common.base.Verify.verify;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+
+/**
+ * Neon SR2 specialization of AbstractLithiumDataInput. Unlike its Lithium counterpart, this format uses coding for
+ * QNameModules, QNames, NodeIdentifiers and AugmentationIdentifiers, thus reducing stream duplication.
+ */
+final class NeonSR2NormalizedNodeInputStreamReader extends AbstractLithiumDataInput {
+    private final ArrayList<NodeIdentifier> codedNodeIdentifiers = new ArrayList<>();
+    private final List<AugmentationIdentifier> codedAugments = new ArrayList<>();
+    private final List<QNameModule> codedModules = new ArrayList<>();
+    private final List<QName> codedQNames = new ArrayList<>();
+
+    NeonSR2NormalizedNodeInputStreamReader(final DataInput input) {
+        super(input);
+    }
+
+    @Override
+    public NormalizedNodeStreamVersion getVersion() {
+        return NormalizedNodeStreamVersion.NEON_SR2;
+    }
+
+    @Override
+    public QName readQName() throws IOException {
+        final byte valueType = readByte();
+        switch (valueType) {
+            case NeonSR2Tokens.IS_QNAME_CODE:
+                return codedQName(readInt());
+            case NeonSR2Tokens.IS_QNAME_VALUE:
+                return rawQName();
+            default:
+                throw new IOException("Unhandled QName value type " + valueType);
+        }
+    }
+
+    @Override
+    AugmentationIdentifier readAugmentationIdentifier() throws IOException {
+        final byte valueType = readByte();
+        switch (valueType) {
+            case NeonSR2Tokens.IS_AUGMENT_CODE:
+                return codedAugmentId(readInt());
+            case NeonSR2Tokens.IS_AUGMENT_VALUE:
+                return rawAugmentId();
+            default:
+                throw new IOException("Unhandled QName value type " + valueType);
+        }
+    }
+
+    @Override
+    NodeIdentifier readNodeIdentifier() throws IOException {
+        // NodeIdentifier rides on top of QName, with this method really saying 'interpret next QName as NodeIdentifier'
+        // to do that we inter-mingle with readQName()
+        final byte valueType = readByte();
+        switch (valueType) {
+            case NeonSR2Tokens.IS_QNAME_CODE:
+                return codedNodeIdentifier(readInt());
+            case NeonSR2Tokens.IS_QNAME_VALUE:
+                return rawNodeIdentifier();
+            default:
+                throw new IOException("Unhandled QName value type " + valueType);
+        }
+    }
+
+    private QNameModule readModule() throws IOException {
+        final byte valueType = readByte();
+        switch (valueType) {
+            case NeonSR2Tokens.IS_MODULE_CODE:
+                return codedModule(readInt());
+            case NeonSR2Tokens.IS_MODULE_VALUE:
+                return rawModule();
+            default:
+                throw new IOException("Unhandled QName value type " + valueType);
+        }
+    }
+
+    private NodeIdentifier codedNodeIdentifier(final int code) throws IOException {
+        final NodeIdentifier existing = codedNodeIdentifiers.size() > code ? codedNodeIdentifiers.get(code) : null;
+        return existing != null ? existing : storeNodeIdentifier(code, codedQName(code));
+    }
+
+    private NodeIdentifier rawNodeIdentifier() throws IOException {
+        // Capture size before it incremented
+        final int code = codedQNames.size();
+        return storeNodeIdentifier(code, rawQName());
+    }
+
+    private NodeIdentifier storeNodeIdentifier(final int code, final QName qname) {
+        final NodeIdentifier ret = NodeIdentifier.create(qname);
+        final int size = codedNodeIdentifiers.size();
+
+        if (code >= size) {
+            // Null-fill others
+            codedNodeIdentifiers.ensureCapacity(code + 1);
+            for (int i = size; i < code; ++i) {
+                codedNodeIdentifiers.add(null);
+            }
+
+            codedNodeIdentifiers.add(ret);
+        } else {
+            final NodeIdentifier check = codedNodeIdentifiers.set(code, ret);
+            verify(check == null);
+        }
+
+        return ret;
+    }
+
+    private QName codedQName(final int code) throws IOException {
+        try {
+            return codedQNames.get(code);
+        } catch (IndexOutOfBoundsException e) {
+            throw new IOException("QName code " + code + " was not found", e);
+        }
+    }
+
+    private QName rawQName() throws IOException {
+        final String localName = readCodedString();
+        final QNameModule module = readModule();
+        final QName qname = QNameFactory.create(module, localName);
+        codedQNames.add(qname);
+        return qname;
+    }
+
+    private AugmentationIdentifier codedAugmentId(final int code) throws IOException {
+        try {
+            return codedAugments.get(code);
+        } catch (IndexOutOfBoundsException e) {
+            throw new IOException("QName set code " + code + " was not found", e);
+        }
+    }
+
+    private AugmentationIdentifier rawAugmentId() throws IOException {
+        final AugmentationIdentifier aid = defaultReadAugmentationIdentifier();
+        codedAugments.add(aid);
+        return aid;
+    }
+
+    private QNameModule codedModule(final int code) throws IOException {
+        try {
+            return codedModules.get(code);
+        } catch (IndexOutOfBoundsException e) {
+            throw new IOException("Module code " + code + " was not found", e);
+        }
+    }
+
+    private QNameModule rawModule() throws IOException {
+        final String namespace = readCodedString();
+        final String revision = readCodedString();
+        final QNameModule mod = QNameFactory.createModule(namespace, revision);
+        codedModules.add(mod);
+        return mod;
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NeonSR2NormalizedNodeOutputStreamWriter.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NeonSR2NormalizedNodeOutputStreamWriter.java
new file mode 100644 (file)
index 0000000..e9b3236
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+
+/**
+ * NormalizedNodeOutputStreamWriter will be used by distributed datastore to send normalized node in
+ * a stream.
+ * A stream writer wrapper around this class will write node objects to stream in recursive manner.
+ * for example - If you have a ContainerNode which has a two LeafNode as children, then
+ * you will first call
+ * {@link #startContainerNode(org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier, int)},
+ * then will call
+ * {@link #leafNode(org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier, Object)} twice
+ * and then, {@link #endNode()} to end container node.
+ *
+ * <p>Based on the each node, the node type is also written to the stream, that helps in reconstructing the object,
+ * while reading.
+ */
+final class NeonSR2NormalizedNodeOutputStreamWriter extends AbstractLithiumDataOutput {
+    private final Map<AugmentationIdentifier, Integer> aidCodeMap = new HashMap<>();
+    private final Map<QNameModule, Integer> moduleCodeMap = new HashMap<>();
+    private final Map<QName, Integer> qnameCodeMap = new HashMap<>();
+
+    NeonSR2NormalizedNodeOutputStreamWriter(final DataOutput output) {
+        super(output);
+    }
+
+    @Override
+    short streamVersion() {
+        return TokenTypes.NEON_SR2_VERSION;
+    }
+
+    @Override
+    void writeQNameInternal(final QName qname) throws IOException {
+        final Integer value = qnameCodeMap.get(qname);
+        if (value == null) {
+            // Fresh QName, remember it and emit as three strings
+            qnameCodeMap.put(qname, qnameCodeMap.size());
+            writeByte(NeonSR2Tokens.IS_QNAME_VALUE);
+            defaultWriteQName(qname);
+        } else {
+            // We have already seen this QName: write its code
+            writeByte(NeonSR2Tokens.IS_QNAME_CODE);
+            writeInt(value);
+        }
+    }
+
+    @Override
+    void writeAugmentationIdentifier(final AugmentationIdentifier aid) throws IOException {
+        final Integer value = aidCodeMap.get(aid);
+        if (value == null) {
+            // Fresh AugmentationIdentifier, remember it and emit as three strings
+            aidCodeMap.put(aid, aidCodeMap.size());
+            writeByte(NeonSR2Tokens.IS_AUGMENT_VALUE);
+            defaultWriteAugmentationIdentifier(aid);
+        } else {
+            // We have already seen this AugmentationIdentifier: write its code
+            writeByte(NeonSR2Tokens.IS_AUGMENT_CODE);
+            writeInt(value);
+        }
+    }
+
+    @Override
+    void writeModule(final QNameModule module) throws IOException {
+        final Integer value = moduleCodeMap.get(module);
+        if (value == null) {
+            // Fresh QNameModule, remember it and emit as three strings
+            moduleCodeMap.put(module, moduleCodeMap.size());
+            writeByte(NeonSR2Tokens.IS_MODULE_VALUE);
+            defaultWriteModule(module);
+        } else {
+            // We have already seen this QNameModule: write its code
+            writeByte(NeonSR2Tokens.IS_MODULE_CODE);
+            writeInt(value);
+        }
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NeonSR2Tokens.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NeonSR2Tokens.java
new file mode 100644 (file)
index 0000000..a75f1a5
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+/**
+ * Tokens used in Neon SR2 encoding. Note that Neon SR2 builds on top of Lithium, hence the token values must never
+ * overlap.
+ */
+final class NeonSR2Tokens {
+    static final byte IS_QNAME_CODE = 4;
+    static final byte IS_QNAME_VALUE = 5;
+    static final byte IS_AUGMENT_CODE = 6;
+    static final byte IS_AUGMENT_VALUE = 7;
+    static final byte IS_MODULE_CODE = 8;
+    static final byte IS_MODULE_VALUE = 9;
+
+    private NeonSR2Tokens() {
+
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NormalizedNodeDataInput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NormalizedNodeDataInput.java
new file mode 100644 (file)
index 0000000..c15948b
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2014, 2015 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.binfmt;
+
+import com.google.common.annotations.Beta;
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.ReusableStreamReceiver;
+import org.opendaylight.yangtools.yang.data.impl.schema.ReusableImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Interface for reading {@link NormalizedNode}s, {@link YangInstanceIdentifier}s, {@link PathArgument}s
+ * and {@link SchemaPath}s.
+ */
+@Beta
+public interface NormalizedNodeDataInput extends DataInput {
+    /**
+     * Interpret current stream position as a NormalizedNode, stream its events into a NormalizedNodeStreamWriter.
+     *
+     * @param writer Writer to emit events to
+     * @throws IOException if an error occurs
+     * @throws IllegalStateException if the dictionary has been detached
+     * @throws NullPointerException if {@code writer} is null
+     */
+    void streamNormalizedNode(NormalizedNodeStreamWriter writer) throws IOException;
+
+    /**
+     * Read a normalized node from the reader.
+     *
+     * @return Next node from the stream, or null if end of stream has been reached.
+     * @throws IOException if an error occurs
+     * @throws IllegalStateException if the dictionary has been detached
+     */
+    default NormalizedNode<?, ?> readNormalizedNode() throws IOException {
+        return readNormalizedNode(ReusableImmutableNormalizedNodeStreamWriter.create());
+    }
+
+    /**
+     * Read a normalized node from the reader, using specified writer to construct the result.
+     *
+     * @param receiver Reusable receiver to, expected to be reset
+     * @return Next node from the stream, or null if end of stream has been reached.
+     * @throws IOException if an error occurs
+     * @throws IllegalStateException if the dictionary has been detached
+     */
+    default NormalizedNode<?, ?> readNormalizedNode(final ReusableStreamReceiver receiver) throws IOException {
+        try {
+            streamNormalizedNode(receiver);
+            return receiver.getResult();
+        } finally {
+            receiver.reset();
+        }
+    }
+
+    YangInstanceIdentifier readYangInstanceIdentifier() throws IOException;
+
+    @NonNull QName readQName() throws IOException;
+
+    PathArgument readPathArgument() throws IOException;
+
+    SchemaPath readSchemaPath() throws IOException;
+
+    /**
+     * Return the version of the underlying input stream.
+     *
+     * @return Stream version
+     * @throws IOException if the version cannot be ascertained
+     */
+    NormalizedNodeStreamVersion getVersion() throws IOException;
+
+    default Optional<NormalizedNode<?, ?>> readOptionalNormalizedNode() throws IOException {
+        return readBoolean() ? Optional.of(readNormalizedNode()) : Optional.empty();
+    }
+
+    /**
+     * Creates a new {@link NormalizedNodeDataInput} instance that reads from the given input. This method first reads
+     * and validates that the input contains a valid NormalizedNode stream.
+     *
+     * @param input the DataInput to read from
+     * @return a new {@link NormalizedNodeDataInput} instance
+     * @throws InvalidNormalizedNodeStreamException if the stream version is not supported
+     * @throws IOException if an error occurs reading from the input
+     */
+    static @NonNull NormalizedNodeDataInput newDataInput(final @NonNull DataInput input) throws IOException {
+        return new VersionedNormalizedNodeDataInput(input).delegate();
+    }
+
+    /**
+     * Creates a new {@link NormalizedNodeDataInput} instance that reads from the given input. This method does not
+     * perform any initial validation of the input stream.
+     *
+     * @param input the DataInput to read from
+     * @return a new {@link NormalizedNodeDataInput} instance
+     * @deprecated Use {@link #newDataInput(DataInput)} instead.
+     */
+    // FIXME: 5.0.0: deprecate for removal
+    @Deprecated
+    static @NonNull NormalizedNodeDataInput newDataInputWithoutValidation(final @NonNull DataInput input) {
+        return new VersionedNormalizedNodeDataInput(input);
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NormalizedNodeDataOutput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NormalizedNodeDataOutput.java
new file mode 100644 (file)
index 0000000..a8cb9b1
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014, 2015 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.binfmt;
+
+import com.google.common.annotations.Beta;
+import java.io.DataOutput;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Interface for emitting {@link NormalizedNode}s, {@link YangInstanceIdentifier}s, {@link PathArgument}s
+ * and {@link SchemaPath}s.
+ */
+@Beta
+@NonNullByDefault
+public interface NormalizedNodeDataOutput extends AutoCloseable, DataOutput {
+    void writeQName(QName qname) throws IOException;
+
+    void writeNormalizedNode(NormalizedNode<?, ?> normalizedNode) throws IOException;
+
+    void writePathArgument(PathArgument pathArgument) throws IOException;
+
+    void writeYangInstanceIdentifier(YangInstanceIdentifier identifier) throws IOException;
+
+    void writeSchemaPath(SchemaPath path) throws IOException;
+
+    @Override
+    void close() throws IOException;
+
+    default void writeOptionalNormalizedNode(final @Nullable NormalizedNode<?, ?> normalizedNode) throws IOException {
+        if (normalizedNode != null) {
+            writeBoolean(true);
+            writeNormalizedNode(normalizedNode);
+        } else {
+            writeBoolean(false);
+        }
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NormalizedNodeStreamVersion.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/NormalizedNodeStreamVersion.java
new file mode 100644 (file)
index 0000000..3bf680b
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import com.google.common.annotations.Beta;
+import java.io.DataOutput;
+import java.math.BigInteger;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.data.api.schema.ValueNode;
+
+/**
+ * Enumeration of all stream versions this implementation supports on both input and output.
+ */
+@Beta
+@NonNullByDefault
+public enum NormalizedNodeStreamVersion {
+    /**
+     * Original stream version, as shipped in OpenDaylight Lithium simultaneous release. The caveat here is that this
+     * version has augmented in OpenDaylight Oxygen to retrofit a non-null representation of the empty type.
+     */
+    // FIXME: 5.0.0: consider deprecating this version
+    LITHIUM {
+        @Override
+        public NormalizedNodeDataOutput newDataOutput(DataOutput output) {
+            return new LithiumNormalizedNodeOutputStreamWriter(output);
+        }
+    },
+    /**
+     * Updated stream version, as shipped in OpenDaylight Neon SR2 release. Improves identifier encoding over
+     * {@link #LITHIUM}, so that QName caching is more effective.
+     */
+    NEON_SR2 {
+        @Override
+        public NormalizedNodeDataOutput newDataOutput(DataOutput output) {
+            return new NeonSR2NormalizedNodeOutputStreamWriter(output);
+        }
+    },
+    /**
+     * First shipping in Sodium SR1. Improved stream coding to eliminate redundancies present in {@link #NEON_SR2}.
+     * Supports {code Uint8} et al. as well as {@link BigInteger}.
+     */
+    SODIUM_SR1 {
+        @Override
+        public NormalizedNodeDataOutput newDataOutput(DataOutput output) {
+            return new SodiumSR1DataOutput(output);
+        }
+    },
+    /**
+     * First shipping is Magnesium. Does not support {@link BigInteger} mirroring it being superseded by {@link Uint64}
+     * in {@link ValueNode#getValue()}.
+     */
+    MAGNESIUM {
+        @Override
+        public NormalizedNodeDataOutput newDataOutput(DataOutput output) {
+            return new MagnesiumDataOutput(output);
+        }
+    };
+
+    /**
+     * Return the current runtime version. Guaranteed to not throw {@link UnsupportedOperationException} from
+     * {@link #newDataOutput(DataOutput)}.
+     *
+     * @return Current runtime version.
+     */
+    public static NormalizedNodeStreamVersion current() {
+        return MAGNESIUM;
+    }
+
+    /**
+     * Creates a new {@link NormalizedNodeDataOutput} instance that writes to the given output.
+     *
+     * @param output the DataOutput to write to
+     * @return a new {@link NormalizedNodeDataOutput} instance
+     * @throws NullPointerException if {@code output} is null
+     * @throws UnsupportedOperationException if this version cannot be created in this runtime
+     */
+    public abstract NormalizedNodeDataOutput newDataOutput(DataOutput output);
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/QNameFactory.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/QNameFactory.java
new file mode 100644 (file)
index 0000000..a8065a2
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2014, 2015 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.binfmt;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.net.URI;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+
+final class QNameFactory {
+    private static final class StringQName implements Immutable {
+        private final @NonNull String localName;
+        private final @NonNull String namespace;
+        private final @Nullable String revision;
+
+        StringQName(final String localName, final String namespace, final String revision) {
+            this.localName = requireNonNull(localName);
+            this.namespace = requireNonNull(namespace);
+            this.revision = revision;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(localName, namespace, revision);
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof StringQName)) {
+                return false;
+            }
+            final StringQName other = (StringQName) obj;
+            return localName.equals(other.localName) && namespace.equals(other.namespace)
+                    && Objects.equals(revision, other.revision);
+        }
+
+        QName toQName() {
+            return revision != null ? QName.create(namespace, revision, localName) : QName.create(namespace, localName);
+        }
+    }
+
+    private static final class ModuleQName implements Immutable {
+        private final @NonNull QNameModule module;
+        private final @NonNull String localName;
+
+        ModuleQName(final QNameModule module, final String localName) {
+            this.module = requireNonNull(module);
+            this.localName = requireNonNull(localName);
+        }
+
+        @Override
+        public int hashCode() {
+            return 31 * module.hashCode() + localName.hashCode();
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof ModuleQName)) {
+                return false;
+            }
+            final ModuleQName other = (ModuleQName) obj;
+            return localName.equals(other.localName) && module.equals(other.module);
+        }
+
+        QName toQName() {
+            return QName.create(module, localName);
+        }
+    }
+
+    private static final class StringModule implements Immutable {
+        private final @NonNull String namespace;
+        private final @Nullable String revision;
+
+        StringModule(final String namespace, final String revision) {
+            this.namespace = requireNonNull(namespace);
+            this.revision = revision;
+        }
+
+        @Override
+        public int hashCode() {
+            return 31 * namespace.hashCode() + Objects.hashCode(revision);
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof StringModule)) {
+                return false;
+            }
+            final StringModule other = (StringModule) obj;
+            return namespace.equals(other.namespace) && Objects.equals(revision, other.revision);
+        }
+
+        QNameModule toQNameModule() {
+            return QNameModule.create(URI.create(namespace), Revision.ofNullable(revision));
+        }
+    }
+
+    private static final int MAX_QNAME_CACHE_SIZE = Integer.getInteger(
+        "org.opendaylight.controller.cluster.datastore.node.utils.qname-cache.max-size", 10000);
+    private static final int MAX_MODULE_CACHE_SIZE = Integer.getInteger(
+        "org.opendaylight.controller.cluster.datastore.node.utils.module-cache.max-size", 2000);
+
+    private static final LoadingCache<StringQName, QName> STRING_CACHE = CacheBuilder.newBuilder()
+            .maximumSize(MAX_QNAME_CACHE_SIZE).weakValues().build(new CacheLoader<StringQName, QName>() {
+                @Override
+                public QName load(final StringQName key) {
+                    return key.toQName().intern();
+                }
+            });
+    private static final LoadingCache<ModuleQName, QName> QNAME_CACHE = CacheBuilder.newBuilder()
+            .maximumSize(MAX_QNAME_CACHE_SIZE).weakValues().build(new CacheLoader<ModuleQName, QName>() {
+                @Override
+                public QName load(final ModuleQName key) {
+                    return key.toQName().intern();
+                }
+            });
+    private static final LoadingCache<StringModule, QNameModule> MODULE_CACHE = CacheBuilder.newBuilder()
+            .maximumSize(MAX_MODULE_CACHE_SIZE).weakValues().build(new CacheLoader<StringModule, QNameModule>() {
+                @Override
+                public QNameModule load(final StringModule key) {
+                    return key.toQNameModule().intern();
+                }
+            });
+    private static final LoadingCache<ModuleQName, NodeIdentifier> NODEID_CACHE = CacheBuilder.newBuilder()
+            .maximumSize(MAX_QNAME_CACHE_SIZE).weakValues().build(new CacheLoader<ModuleQName, NodeIdentifier>() {
+                @Override
+                public NodeIdentifier load(final ModuleQName key) throws ExecutionException {
+                    return NodeIdentifier.create(QNAME_CACHE.get(key));
+                }
+            });
+
+    private QNameFactory() {
+
+    }
+
+    static QName create(final String localName, final String namespace, final @Nullable String revision) {
+        return STRING_CACHE.getUnchecked(new StringQName(localName, namespace, revision));
+    }
+
+    static QName create(final QNameModule module, final String localName) {
+        return QNAME_CACHE.getUnchecked(new ModuleQName(module, localName));
+    }
+
+    static QNameModule createModule(final String namespace, final @Nullable String revision) {
+        return MODULE_CACHE.getUnchecked(new StringModule(namespace, revision));
+    }
+
+    static @NonNull NodeIdentifier getNodeIdentifier(final QNameModule module, final String localName)
+            throws ExecutionException {
+        return NODEID_CACHE.get(new ModuleQName(module, localName));
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/SodiumSR1DataInput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/SodiumSR1DataInput.java
new file mode 100644 (file)
index 0000000..7f738ea
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.math.BigInteger;
+
+final class SodiumSR1DataInput extends AbstractMagnesiumDataInput {
+    SodiumSR1DataInput(final DataInput input) {
+        super(input);
+    }
+
+    @Override
+    public NormalizedNodeStreamVersion getVersion() {
+        return NormalizedNodeStreamVersion.SODIUM_SR1;
+    }
+
+    @Override
+    BigInteger readBigInteger() throws IOException {
+        // FIXME: use string -> BigInteger cache
+        return new BigInteger(input.readUTF());
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/SodiumSR1DataOutput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/SodiumSR1DataOutput.java
new file mode 100644 (file)
index 0000000..f902fcd
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.math.BigInteger;
+
+final class SodiumSR1DataOutput extends AbstractMagnesiumDataOutput {
+    SodiumSR1DataOutput(final DataOutput output) {
+        super(output);
+    }
+
+    @Override
+    short streamVersion() {
+        return TokenTypes.SODIUM_SR1_VERSION;
+    }
+
+    @Override
+    void writeValue(final BigInteger value) throws IOException {
+        output.writeByte(MagnesiumValue.BIGINTEGER);
+        output.writeUTF(value.toString());
+    }
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/TokenTypes.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/TokenTypes.java
new file mode 100644 (file)
index 0000000..bad4b42
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 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.binfmt;
+
+final class TokenTypes {
+    private TokenTypes() {
+        throw new UnsupportedOperationException();
+    }
+
+    static final byte SIGNATURE_MARKER = (byte) 0xab;
+
+    /**
+     * Original stream version. Uses a per-stream dictionary for strings. QNames are serialized as three strings.
+     */
+    static final short LITHIUM_VERSION = 1;
+    /**
+     * Revised stream version. Unlike {@link #LITHIUM_VERSION}, QNames and QNameModules are using a per-stream
+     * dictionary, too.
+     */
+    static final short NEON_SR2_VERSION = 2;
+    /**
+     * From-scratch designed version shipping in Sodium SR1.
+     */
+    static final short SODIUM_SR1_VERSION = 3;
+    /**
+     * Magnesium version. Structurally matches {@link #SODIUM_SR1_VERSION}, but does not allow BigIntegers to be
+     * present.
+     */
+    static final short MAGNESIUM_VERSION = 4;
+}
diff --git a/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/VersionedNormalizedNodeDataInput.java b/yang/yang-data-codec-binfmt/src/main/java/org/opendaylight/yangtools/yang/data/codec/binfmt/VersionedNormalizedNodeDataInput.java
new file mode 100644 (file)
index 0000000..099f56d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.binfmt;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+final class VersionedNormalizedNodeDataInput extends ForwardingNormalizedNodeDataInput {
+    private DataInput input;
+    private NormalizedNodeDataInput delegate;
+
+    VersionedNormalizedNodeDataInput(final DataInput input) {
+        this.input = requireNonNull(input);
+    }
+
+    @Override
+    NormalizedNodeDataInput delegate() throws IOException {
+        if (delegate != null) {
+            return delegate;
+        }
+
+        final byte marker = input.readByte();
+        if (marker != TokenTypes.SIGNATURE_MARKER) {
+            throw defunct("Invalid signature marker: %d", marker);
+        }
+
+        final short version = input.readShort();
+        final NormalizedNodeDataInput ret;
+        switch (version) {
+            case TokenTypes.LITHIUM_VERSION:
+                ret = new LithiumNormalizedNodeInputStreamReader(input);
+                break;
+            case TokenTypes.NEON_SR2_VERSION:
+                ret = new NeonSR2NormalizedNodeInputStreamReader(input);
+                break;
+            case TokenTypes.SODIUM_SR1_VERSION:
+                ret = new SodiumSR1DataInput(input);
+                break;
+            case TokenTypes.MAGNESIUM_VERSION:
+                ret = new MagnesiumDataInput(input);
+                break;
+            default:
+                throw defunct("Unhandled stream version %s", version);
+        }
+
+        setDelegate(ret);
+        return ret;
+    }
+
+    private InvalidNormalizedNodeStreamException defunct(final String format, final Object... args) {
+        final InvalidNormalizedNodeStreamException ret = new InvalidNormalizedNodeStreamException(
+            String.format(format, args));
+        // Make sure the stream is not touched
+        setDelegate(new ForwardingNormalizedNodeDataInput() {
+            @Override
+            NormalizedNodeDataInput delegate() throws IOException {
+                throw new InvalidNormalizedNodeStreamException("Stream is not usable", ret);
+            }
+        });
+        return ret;
+    }
+
+    private void setDelegate(final NormalizedNodeDataInput delegate) {
+        this.delegate = requireNonNull(delegate);
+        input = null;
+    }
+}
diff --git a/yang/yang-data-codec-gson/pom.xml b/yang/yang-data-codec-gson/pom.xml
new file mode 100644 (file)
index 0000000..38070f3
--- /dev/null
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-data-codec-gson</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-util</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-codec-xml</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-test-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>mockito-configuration</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.data.codec.gson</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractJSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractJSONCodec.java
new file mode 100644 (file)
index 0000000..e7da7f5
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.yangtools.yang.data.impl.codec.DataStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+
+/**
+ * Abstract base implementation of {@link JSONCodec}, which wraps a {@link TypeDefinitionAwareCodec}.
+ *
+ * @param <T> Deserialized object type
+ */
+abstract class AbstractJSONCodec<T> implements JSONCodec<T> {
+    private final DataStringCodec<T> codec;
+
+    AbstractJSONCodec(final DataStringCodec<T> codec) {
+        this.codec = requireNonNull(codec);
+    }
+
+    @Override
+    public final Class<T> getDataType() {
+        return codec.getInputClass();
+    }
+
+    @Override
+    public final T parseValue(final Object ctx, final String str) {
+        return codec.deserialize(str);
+    }
+
+    final String serialize(final T input) {
+        return codec.serialize(input);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/BooleanJSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/BooleanJSONCodec.java
new file mode 100644 (file)
index 0000000..13182f0
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import org.opendaylight.yangtools.yang.data.impl.codec.DataStringCodec;
+
+/**
+ * A {@link JSONCodec} which does not need double quotes in output representation.
+ */
+final class BooleanJSONCodec extends AbstractJSONCodec<Boolean> {
+    BooleanJSONCodec(final DataStringCodec<Boolean> codec) {
+        super(codec);
+    }
+
+    @Override
+    public void writeValue(final JsonWriter ctx, final Boolean value) throws IOException {
+        ctx.value(value);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/EmptyJSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/EmptyJSONCodec.java
new file mode 100644 (file)
index 0000000..e5f3371
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import org.opendaylight.yangtools.yang.common.Empty;
+
+final class EmptyJSONCodec implements JSONCodec<Empty> {
+
+    static final EmptyJSONCodec INSTANCE = new EmptyJSONCodec();
+
+    private EmptyJSONCodec() {
+
+    }
+
+    @Override
+    public Class<Empty> getDataType() {
+        return Empty.class;
+    }
+
+    @Override
+    public Empty parseValue(final Object ctx, final String input) {
+        return Empty.getInstance();
+    }
+
+    @Override
+    public void writeValue(final JsonWriter ctx, final Empty value) throws IOException {
+        ctx.beginArray();
+        ctx.value((String) null);
+        ctx.endArray();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/IdentityrefJSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/IdentityrefJSONCodec.java
new file mode 100644 (file)
index 0000000..6042a46
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.util.codec.IdentityCodecUtil;
+import org.opendaylight.yangtools.yang.data.util.codec.QNameCodecUtil;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+final class IdentityrefJSONCodec implements JSONCodec<QName> {
+    private final SchemaContext schemaContext;
+    private final QNameModule parentModule;
+
+    IdentityrefJSONCodec(final SchemaContext context, final QNameModule parentModule) {
+        this.schemaContext = requireNonNull(context);
+        this.parentModule = requireNonNull(parentModule);
+    }
+
+    @Override
+    public Class<QName> getDataType() {
+        return QName.class;
+    }
+
+    @Override
+    public QName parseValue(final Object ctx, final String value) {
+        return IdentityCodecUtil.parseIdentity(value, schemaContext, prefix -> {
+            if (prefix.isEmpty()) {
+                return parentModule;
+            }
+
+            final Iterator<Module> modules = schemaContext.findModules(prefix).iterator();
+            checkArgument(modules.hasNext(), "Could not find module %s", prefix);
+            return modules.next().getQNameModule();
+        }).getQName();
+    }
+
+    /**
+     * Serialize QName with specified JsonWriter.
+     *
+     * @param writer JsonWriter
+     * @param value QName
+     */
+    @Override
+    public void writeValue(final JsonWriter writer, final QName value) throws IOException {
+        final String str = QNameCodecUtil.encodeQName(value, uri -> {
+            final Optional<String> optName = schemaContext.findModule(uri).map(Module::getName);
+            checkArgument(optName.isPresent(), "Cannot find module for %s", uri);
+            return optName.get();
+        });
+        writer.value(str);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodec.java
new file mode 100644 (file)
index 0000000..e84cff4
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import org.opendaylight.yangtools.yang.data.util.codec.TypeAwareCodec;
+
+interface JSONCodec<T> extends TypeAwareCodec<T, Object, JsonWriter> {
+    /**
+     * {@inheritDoc}.
+     *
+     * @throws IOException if the write fails
+     */
+    @Override
+    void writeValue(JsonWriter ctx, T value) throws IOException;
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java
new file mode 100644 (file)
index 0000000..87ee320
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.annotations.Beta;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.impl.codec.AbstractIntegerStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.BinaryStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.BitsStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.BooleanStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.DecimalStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.EnumStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.StringStringCodec;
+import org.opendaylight.yangtools.yang.data.util.codec.AbstractCodecFactory;
+import org.opendaylight.yangtools.yang.data.util.codec.CodecCache;
+import org.opendaylight.yangtools.yang.data.util.codec.LazyCodecCache;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnknownTypeDefinition;
+
+/**
+ * Factory for creating JSON equivalents of codecs. Each instance of this object is bound to
+ * a particular {@link SchemaContext}, but can be reused by multiple {@link JSONNormalizedNodeStreamWriter}s.
+ */
+@Beta
+public abstract class JSONCodecFactory extends AbstractCodecFactory<JSONCodec<?>> {
+    JSONCodecFactory(final @NonNull SchemaContext context, final @NonNull CodecCache<JSONCodec<?>> cache) {
+        super(context, cache);
+    }
+
+    @Override
+    protected final JSONCodec<?> binaryCodec(final BinaryTypeDefinition type) {
+        return new QuotedJSONCodec<>(BinaryStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> booleanCodec(final BooleanTypeDefinition type) {
+        return new BooleanJSONCodec(BooleanStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> bitsCodec(final BitsTypeDefinition type) {
+        return new QuotedJSONCodec<>(BitsStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> decimalCodec(final DecimalTypeDefinition type) {
+        return wrapDecimalCodec(DecimalStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> emptyCodec(final EmptyTypeDefinition type) {
+        return EmptyJSONCodec.INSTANCE;
+    }
+
+    @Override
+    protected final JSONCodec<?> enumCodec(final EnumTypeDefinition type) {
+        return new QuotedJSONCodec<>(EnumStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> identityRefCodec(final IdentityrefTypeDefinition type, final QNameModule module) {
+        return new IdentityrefJSONCodec(getSchemaContext(), module);
+    }
+
+    @Override
+    protected final JSONCodec<?> int8Codec(final Int8TypeDefinition type) {
+        return new NumberJSONCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> int16Codec(final Int16TypeDefinition type) {
+        return new NumberJSONCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> int32Codec(final Int32TypeDefinition type) {
+        return new NumberJSONCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> int64Codec(final Int64TypeDefinition type) {
+        return wrapIntegerCodec(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> stringCodec(final StringTypeDefinition type) {
+        return new QuotedJSONCodec<>(StringStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> uint8Codec(final Uint8TypeDefinition type) {
+        return new NumberJSONCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> uint16Codec(final Uint16TypeDefinition type) {
+        return new NumberJSONCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> uint32Codec(final Uint32TypeDefinition type) {
+        return new NumberJSONCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> uint64Codec(final Uint64TypeDefinition type) {
+        return wrapIntegerCodec(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected final JSONCodec<?> unionCodec(final UnionTypeDefinition type, final List<JSONCodec<?>> codecs) {
+        return UnionJSONCodec.create(type, codecs);
+    }
+
+    @Override
+    protected final JSONCodec<?> unknownCodec(final UnknownTypeDefinition type) {
+        return NullJSONCodec.INSTANCE;
+    }
+
+    @Override
+    protected abstract JSONCodec<?> instanceIdentifierCodec(InstanceIdentifierTypeDefinition type);
+
+    // Returns a one-off factory for the purposes of normalizing an anydata tree.
+    //
+    // FIXME: 5.0.0: this is really ugly, as we should be able to tell if the new context is the same as ours and
+    //               whether our cache is thread-safe -- in which case we should just return this.
+    //               The supplier/cache/factory layout needs to be reworked so that this call ends up being equivalent
+    //               to JSONCodecFactorySupplier.getShared() in case this factory is not thread safe.
+    //
+    //               The above is not currently possible, as we cannot reference JSONCodecFactorySupplier from the
+    //               factory due to that potentially creating a circular reference.
+    final JSONCodecFactory rebaseTo(final SchemaContext newSchemaContext) {
+        return rebaseTo(newSchemaContext, new LazyCodecCache<>());
+    }
+
+    abstract JSONCodecFactory rebaseTo(SchemaContext newSchemaContext, CodecCache<JSONCodec<?>> newCache);
+
+    abstract JSONCodec<?> wrapDecimalCodec(DecimalStringCodec decimalCodec);
+
+    abstract JSONCodec<?> wrapIntegerCodec(AbstractIntegerStringCodec<?, ?> integerCodec);
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactorySupplier.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactorySupplier.java
new file mode 100644 (file)
index 0000000..4be9b89
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Stopwatch;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.util.codec.CodecCache;
+import org.opendaylight.yangtools.yang.data.util.codec.LazyCodecCache;
+import org.opendaylight.yangtools.yang.data.util.codec.NoopCodecCache;
+import org.opendaylight.yangtools.yang.data.util.codec.PrecomputedCodecCache;
+import org.opendaylight.yangtools.yang.data.util.codec.SharedCodecCache;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * API entry point for acquiring {@link JSONCodecFactory} instances.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public enum JSONCodecFactorySupplier {
+    /**
+     * Source of {@link JSONCodecFactory} instances compliant with RFC7951.
+     */
+    RFC7951() {
+        @Override
+        JSONCodecFactory createFactory(final SchemaContext context, final CodecCache<JSONCodec<?>> cache) {
+            return new RFC7951JSONCodecFactory(context, cache);
+        }
+    },
+    /**
+     * Source of {@link JSONCodecFactory} instances compliant with draft-lhotka-netmod-yang-json-02.
+     */
+    DRAFT_LHOTKA_NETMOD_YANG_JSON_02() {
+        @Override
+        JSONCodecFactory createFactory(final SchemaContext context, final CodecCache<JSONCodec<?>> cache) {
+            return new Lhotka02JSONCodecFactory(context, cache);
+        }
+    };
+
+    private static final Logger LOG = LoggerFactory.getLogger(JSONCodecFactorySupplier.class);
+
+    private static final class EagerCacheLoader extends CacheLoader<SchemaContext, JSONCodecFactory> {
+        private final BiFunction<SchemaContext, CodecCache<JSONCodec<?>>, JSONCodecFactory> factorySupplier;
+
+        EagerCacheLoader(final BiFunction<SchemaContext, CodecCache<JSONCodec<?>>, JSONCodecFactory> factorySupplier) {
+            this.factorySupplier = requireNonNull(factorySupplier);
+        }
+
+        @Override
+        public JSONCodecFactory load(final SchemaContext key) {
+            final Stopwatch sw = Stopwatch.createStarted();
+            final LazyCodecCache<JSONCodec<?>> lazyCache = new LazyCodecCache<>();
+            final JSONCodecFactory lazy = factorySupplier.apply(key, lazyCache);
+            final int visitedLeaves = requestCodecsForChildren(lazy, key);
+            sw.stop();
+
+            final PrecomputedCodecCache<JSONCodec<?>> cache = lazyCache.toPrecomputed();
+            LOG.debug("{} leaf nodes resulted in {} simple and {} complex codecs in {}", visitedLeaves,
+                cache.simpleSize(), cache.complexSize(), sw);
+            return factorySupplier.apply(key, cache);
+        }
+
+        private static int requestCodecsForChildren(final JSONCodecFactory lazy, final DataNodeContainer parent) {
+            int ret = 0;
+            for (DataSchemaNode child : parent.getChildNodes()) {
+                if (child instanceof TypedDataSchemaNode) {
+                    lazy.codecFor((TypedDataSchemaNode) child);
+                    ++ret;
+                } else if (child instanceof DataNodeContainer) {
+                    ret += requestCodecsForChildren(lazy, (DataNodeContainer) child);
+                }
+            }
+
+            return ret;
+        }
+    }
+
+    // Weak keys to retire the entry when SchemaContext goes away
+    private final LoadingCache<SchemaContext, JSONCodecFactory> precomputed;
+
+    // Weak keys to retire the entry when SchemaContext goes away and to force identity-based lookup
+    private final LoadingCache<SchemaContext, JSONCodecFactory> shared;
+
+    JSONCodecFactorySupplier() {
+        precomputed = CacheBuilder.newBuilder().weakKeys().build(new EagerCacheLoader(this::createFactory));
+        shared = CacheBuilder.newBuilder().weakKeys().build(new CacheLoader<SchemaContext, JSONCodecFactory>() {
+            @Override
+            public JSONCodecFactory load(final SchemaContext key) {
+                return createFactory(key, new SharedCodecCache<>());
+            }
+        });
+    }
+
+    /**
+     * Get a thread-safe, eagerly-caching {@link JSONCodecFactory} for a SchemaContext. This method can, and will,
+     * return the same instance as long as the associated SchemaContext is present. Returned object can be safely
+     * used by multiple threads concurrently. If the SchemaContext instance does not have a cached instance
+     * of {@link JSONCodecFactory}, it will be completely precomputed before this method will return.
+     *
+     * <p>
+     * Choosing this implementation is appropriate when the memory overhead of keeping a full codec tree is not as
+     * great a concern as predictable performance. When compared to the implementation returned by
+     * {@link #getShared(SchemaContext)}, this implementation is expected to offer higher performance and have lower
+     * peak memory footprint when most of the SchemaContext is actually in use.
+     *
+     * <p>
+     * For call sites which do not want to pay the CPU cost of pre-computing this implementation, but still would like
+     * to use it if is available (by being populated by some other caller), you can use
+     * {@link #getPrecomputedIfAvailable(SchemaContext)}.
+     *
+     * @param context SchemaContext instance
+     * @return A sharable {@link JSONCodecFactory}
+     * @throws NullPointerException if context is null
+     */
+    public @NonNull JSONCodecFactory getPrecomputed(final @NonNull SchemaContext context) {
+        return verifyNotNull(precomputed.getUnchecked(context));
+    }
+
+    /**
+     * Get a thread-safe, eagerly-caching {@link JSONCodecFactory} for a SchemaContext, if it is available. This
+     * method is a non-blocking equivalent of {@link #getPrecomputed(SchemaContext)} for use in code paths where
+     * the potential of having to pre-compute the implementation is not acceptable. One such scenario is when the
+     * code base wants to opportunistically take advantage of pre-computed version, but is okay with a fallback to
+     * a different implementation.
+     *
+     * @param context SchemaContext instance
+     * @return A sharable {@link JSONCodecFactory}, or absent if such an implementation is not available.
+     * @throws NullPointerException if context is null
+     */
+    public @NonNull Optional<JSONCodecFactory> getPrecomputedIfAvailable(final @NonNull SchemaContext context) {
+        return Optional.ofNullable(precomputed.getIfPresent(context));
+    }
+
+    /**
+     * Get a thread-safe, lazily-caching {@link JSONCodecFactory} for a SchemaContext. This method can, and will,
+     * return the same instance as long as the associated SchemaContext is present or the factory is not invalidated
+     * by memory pressure. Returned object can be safely used by multiple threads concurrently.
+     *
+     * <p>
+     * Choosing this implementation is a safe default, as it will not incur prohibitive blocking, nor will it tie up
+     * memory in face of pressure.
+     *
+     * @param context SchemaContext instance
+     * @return A sharable {@link JSONCodecFactory}
+     * @throws NullPointerException if context is null
+     */
+    public @NonNull JSONCodecFactory getShared(final @NonNull SchemaContext context) {
+        return verifyNotNull(shared.getUnchecked(context));
+    }
+
+    /**
+     * Create a new thread-unsafe, lazily-caching {@link JSONCodecFactory} for a SchemaContext. This method will
+     * return distinct objects every time it is invoked. Returned object may not be used from multiple threads
+     * concurrently.
+     *
+     * <p>
+     * This implementation is appropriate for one-off serialization from a single thread. It will aggressively cache
+     * codecs for reuse and will tie them up in memory until the factory is freed.
+     *
+     * @param context SchemaContext instance
+     * @return A non-sharable {@link JSONCodecFactory}
+     * @throws NullPointerException if context is null
+     */
+    public @NonNull JSONCodecFactory createLazy(final @NonNull SchemaContext context) {
+        return createFactory(context, new LazyCodecCache<>());
+    }
+
+    /**
+     * Create a simplistic, thread-safe {@link JSONCodecFactory} for a {@link SchemaContext}. This method will return
+     * distinct objects every time it is invoked. Returned object may be use from multiple threads concurrently.
+     *
+     * <p>
+     * This implementation exists mostly for completeness only, as it does not perform any caching at all and each codec
+     * is computed every time it is requested. This may be useful in extremely constrained environments, where memory
+     * footprint is more critical than performance.
+     *
+     * @param context SchemaContext instance
+     * @return A non-sharable {@link JSONCodecFactory}
+     * @throws NullPointerException if context is null.
+     */
+    public @NonNull JSONCodecFactory createSimple(final @NonNull SchemaContext context) {
+        return createFactory(context, NoopCodecCache.getInstance());
+    }
+
+    abstract @NonNull JSONCodecFactory createFactory(SchemaContext context, CodecCache<JSONCodec<?>> cache);
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONInstanceIdentifierCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONInstanceIdentifierCodec.java
new file mode 100644 (file)
index 0000000..4d54a8f
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Iterator;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+abstract class JSONInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec
+        implements JSONCodec<YangInstanceIdentifier> {
+    private final DataSchemaContextTree dataContextTree;
+    private final JSONCodecFactory codecFactory;
+    private final SchemaContext context;
+
+    JSONInstanceIdentifierCodec(final SchemaContext context, final JSONCodecFactory jsonCodecFactory) {
+        this.context = requireNonNull(context);
+        this.dataContextTree = DataSchemaContextTree.from(context);
+        this.codecFactory = requireNonNull(jsonCodecFactory);
+    }
+
+    @Override
+    protected final Module moduleForPrefix(final String prefix) {
+        final Iterator<Module> modules = context.findModules(prefix).iterator();
+        return modules.hasNext() ? modules.next() : null;
+    }
+
+    @Override
+    protected final String prefixForNamespace(final URI namespace) {
+        final Iterator<Module> modules = context.findModules(namespace).iterator();
+        return modules.hasNext() ? modules.next().getName() : null;
+    }
+
+    @Override
+    protected final DataSchemaContextTree getDataContextTree() {
+        return dataContextTree;
+    }
+
+    @Override
+    protected final Object deserializeKeyValue(final DataSchemaNode schemaNode, final String value) {
+        requireNonNull(schemaNode, "schemaNode cannot be null");
+        checkArgument(schemaNode instanceof LeafSchemaNode, "schemaNode must be of type LeafSchemaNode");
+        final JSONCodec<?> objectJSONCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode);
+        return objectJSONCodec.parseValue(null, value);
+    }
+
+    @Override
+    public final Class<YangInstanceIdentifier> getDataType() {
+        return YangInstanceIdentifier.class;
+    }
+
+    @Override
+    public final YangInstanceIdentifier parseValue(final Object ctx, final String str) {
+        return deserialize(str);
+    }
+
+    @Override
+    public final void writeValue(final JsonWriter ctx, final YangInstanceIdentifier value) throws IOException {
+        ctx.value(serialize(value));
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..38c873f
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+import static org.w3c.dom.Node.ELEMENT_NODE;
+import static org.w3c.dom.Node.TEXT_NODE;
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.net.URI;
+import java.util.regex.Pattern;
+import javax.xml.transform.dom.DOMSource;
+import org.checkerframework.checker.regex.qual.Regex;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.rfc8528.data.api.StreamWriterMountPointExtension;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedAnydata;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriterExtension;
+import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker;
+import org.opendaylight.yangtools.yang.data.util.SingleChildDataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+/**
+ * This implementation will create JSON output as output stream.
+ *
+ * <p>
+ * Values of leaf and leaf-list are NOT translated according to codecs.
+ */
+public abstract class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter,
+        StreamWriterMountPointExtension {
+    private static final class Exclusive extends JSONNormalizedNodeStreamWriter {
+        Exclusive(final JSONCodecFactory codecFactory, final SchemaTracker tracker, final JsonWriter writer,
+                final JSONStreamWriterRootContext rootContext) {
+            super(codecFactory, tracker, writer, rootContext);
+        }
+
+        @Override
+        public void close() throws IOException {
+            flush();
+            closeWriter();
+        }
+    }
+
+    private static final class Nested extends JSONNormalizedNodeStreamWriter {
+        Nested(final JSONCodecFactory codecFactory, final SchemaTracker tracker, final JsonWriter writer,
+                final JSONStreamWriterRootContext rootContext) {
+            super(codecFactory, tracker, writer, rootContext);
+        }
+
+        @Override
+        public void close() throws IOException {
+            flush();
+            // The caller "owns" the writer, let them close it
+        }
+    }
+
+    /**
+     * RFC6020 deviation: we are not required to emit empty containers unless they
+     * are marked as 'presence'.
+     */
+    private static final boolean DEFAULT_EMIT_EMPTY_CONTAINERS = true;
+
+    @Regex
+    private static final String NUMBER_STRING = "-?\\d+(\\.\\d+)?";
+    private static final Pattern NUMBER_PATTERN = Pattern.compile(NUMBER_STRING);
+
+    @Regex
+    private static final String NOT_DECIMAL_NUMBER_STRING = "-?\\d+";
+    private static final Pattern NOT_DECIMAL_NUMBER_PATTERN = Pattern.compile(NOT_DECIMAL_NUMBER_STRING);
+
+    private final SchemaTracker tracker;
+    private final JSONCodecFactory codecs;
+    private final JsonWriter writer;
+    private JSONStreamWriterContext context;
+
+    JSONNormalizedNodeStreamWriter(final JSONCodecFactory codecFactory, final SchemaTracker tracker,
+            final JsonWriter writer, final JSONStreamWriterRootContext rootContext) {
+        this.writer = requireNonNull(writer);
+        this.codecs = requireNonNull(codecFactory);
+        this.tracker = requireNonNull(tracker);
+        this.context = requireNonNull(rootContext);
+    }
+
+    /**
+     * Create a new stream writer, which writes to the specified output stream.
+     *
+     * <p>
+     * The codec factory can be reused between multiple writers.
+     *
+     * <p>
+     * Returned writer is exclusive user of JsonWriter, which means it will start
+     * top-level JSON element and ends it.
+     *
+     * <p>
+     * This instance of writer can be used only to emit one top level element,
+     * otherwise it will produce incorrect JSON. Closing this instance will close
+     * the writer too.
+     *
+     * @param codecFactory JSON codec factory
+     * @param path Schema Path
+     * @param initialNs Initial namespace
+     * @param jsonWriter JsonWriter
+     * @return A stream writer instance
+     */
+    public static NormalizedNodeStreamWriter createExclusiveWriter(final JSONCodecFactory codecFactory,
+            final SchemaPath path, final URI initialNs, final JsonWriter jsonWriter) {
+        return new Exclusive(codecFactory, SchemaTracker.create(codecFactory.getSchemaContext(), path), jsonWriter,
+            new JSONStreamWriterExclusiveRootContext(initialNs));
+    }
+
+    /**
+     * Create a new stream writer, which writes to the specified output stream.
+     *
+     * <p>
+     * The codec factory can be reused between multiple writers.
+     *
+     * <p>
+     * Returned writer is exclusive user of JsonWriter, which means it will start
+     * top-level JSON element and ends it.
+     *
+     * <p>
+     * This instance of writer can be used only to emit one top level element,
+     * otherwise it will produce incorrect JSON. Closing this instance will close
+     * the writer too.
+     *
+     * @param codecFactory JSON codec factory
+     * @param rootNode Root node
+     * @param initialNs Initial namespace
+     * @param jsonWriter JsonWriter
+     * @return A stream writer instance
+     */
+    public static NormalizedNodeStreamWriter createExclusiveWriter(final JSONCodecFactory codecFactory,
+            final DataNodeContainer rootNode, final URI initialNs, final JsonWriter jsonWriter) {
+        return new Exclusive(codecFactory, SchemaTracker.create(rootNode), jsonWriter,
+            new JSONStreamWriterExclusiveRootContext(initialNs));
+    }
+
+    /**
+     * Create a new stream writer, which writes to the specified output stream.
+     *
+     * <p>
+     * The codec factory can be reused between multiple writers.
+     *
+     * <p>
+     * Returned writer can be used emit multiple top level element,
+     * but does not start / close parent JSON object, which must be done
+     * by user providing {@code jsonWriter} instance in order for
+     * JSON to be valid. Closing this instance <strong>will not</strong>
+     * close the wrapped writer; the caller must take care of that.
+     *
+     * @param codecFactory JSON codec factory
+     * @param path Schema Path
+     * @param initialNs Initial namespace
+     * @param jsonWriter JsonWriter
+     * @return A stream writer instance
+     */
+    public static NormalizedNodeStreamWriter createNestedWriter(final JSONCodecFactory codecFactory,
+            final SchemaPath path, final URI initialNs, final JsonWriter jsonWriter) {
+        return new Nested(codecFactory, SchemaTracker.create(codecFactory.getSchemaContext(), path), jsonWriter,
+            new JSONStreamWriterSharedRootContext(initialNs));
+    }
+
+    /**
+     * Create a new stream writer, which writes to the specified output stream.
+     *
+     * <p>
+     * The codec factory can be reused between multiple writers.
+     *
+     * <p>
+     * Returned writer can be used emit multiple top level element,
+     * but does not start / close parent JSON object, which must be done
+     * by user providing {@code jsonWriter} instance in order for
+     * JSON to be valid. Closing this instance <strong>will not</strong>
+     * close the wrapped writer; the caller must take care of that.
+     *
+     * @param codecFactory JSON codec factory
+     * @param rootNode Root node
+     * @param initialNs Initial namespace
+     * @param jsonWriter JsonWriter
+     * @return A stream writer instance
+     */
+    public static NormalizedNodeStreamWriter createNestedWriter(final JSONCodecFactory codecFactory,
+            final DataNodeContainer rootNode, final URI initialNs, final JsonWriter jsonWriter) {
+        return new Nested(codecFactory, SchemaTracker.create(rootNode), jsonWriter,
+            new JSONStreamWriterSharedRootContext(initialNs));
+    }
+
+    @Override
+    public ClassToInstanceMap<NormalizedNodeStreamWriterExtension> getExtensions() {
+        return ImmutableClassToInstanceMap.of(StreamWriterMountPointExtension.class, this);
+    }
+
+    @Override
+    public void startLeafNode(final NodeIdentifier name) throws IOException {
+        tracker.startLeafNode(name);
+        context.emittingChild(codecs.getSchemaContext(), writer);
+        context.writeChildJsonIdentifier(codecs.getSchemaContext(), writer, name.getNodeType());
+    }
+
+    @Override
+    public final void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        tracker.startLeafSet(name);
+        context = new JSONStreamWriterListContext(context, name);
+    }
+
+    @Override
+    public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
+        tracker.startLeafSetEntryNode(name);
+        context.emittingChild(codecs.getSchemaContext(), writer);
+    }
+
+    @Override
+    public final void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        tracker.startLeafSet(name);
+        context = new JSONStreamWriterListContext(context, name);
+    }
+
+    /*
+     * Warning suppressed due to static final constant which triggers a warning
+     * for the call to schema.isPresenceContainer().
+     */
+    @Override
+    public final void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        final SchemaNode schema = tracker.startContainerNode(name);
+        final boolean isPresence = schema instanceof ContainerSchemaNode
+            ? ((ContainerSchemaNode) schema).isPresenceContainer() : DEFAULT_EMIT_EMPTY_CONTAINERS;
+        context = new JSONStreamWriterNamedObjectContext(context, name, isPresence);
+    }
+
+    @Override
+    public final void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        tracker.startList(name);
+        context = new JSONStreamWriterListContext(context, name);
+    }
+
+    @Override
+    public final void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        tracker.startListItem(name);
+        context = new JSONStreamWriterObjectContext(context, name, DEFAULT_EMIT_EMPTY_CONTAINERS);
+    }
+
+    @Override
+    public final void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        tracker.startList(name);
+        context = new JSONStreamWriterListContext(context, name);
+    }
+
+    @Override
+    public final void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+            throws IOException {
+        tracker.startListItem(identifier);
+        context = new JSONStreamWriterObjectContext(context, identifier, DEFAULT_EMIT_EMPTY_CONTAINERS);
+    }
+
+    @Override
+    public final void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        tracker.startList(name);
+        context = new JSONStreamWriterListContext(context, name);
+    }
+
+    @Override
+    public final void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
+        tracker.startChoiceNode(name);
+        context = new JSONStreamWriterInvisibleContext(context);
+    }
+
+    @Override
+    public final void startAugmentationNode(final AugmentationIdentifier identifier) {
+        tracker.startAugmentationNode(identifier);
+        context = new JSONStreamWriterInvisibleContext(context);
+    }
+
+    @Override
+    public final boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        if (NormalizedAnydata.class.isAssignableFrom(objectModel)) {
+            tracker.startAnydataNode(name);
+            context.emittingChild(codecs.getSchemaContext(), writer);
+            context.writeChildJsonIdentifier(codecs.getSchemaContext(), writer, name.getNodeType());
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public final NormalizedNodeStreamWriter startMountPoint(final MountPointIdentifier mountId,
+            final MountPointContext mountCtx) throws IOException {
+        final SchemaContext ctx = mountCtx.getSchemaContext();
+        return new Nested(codecs.rebaseTo(ctx), SchemaTracker.create(ctx), writer,
+            new JSONStreamWriterSharedRootContext(context.getNamespace()));
+    }
+
+    @Override
+    public final boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        if (DOMSource.class.isAssignableFrom(objectModel)) {
+            tracker.startAnyxmlNode(name);
+            context.emittingChild(codecs.getSchemaContext(), writer);
+            context.writeChildJsonIdentifier(codecs.getSchemaContext(), writer, name.getNodeType());
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public final void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint)
+            throws IOException {
+        tracker.startYangModeledAnyXmlNode(name);
+        context = new JSONStreamWriterNamedObjectContext(context, name, true);
+    }
+
+    @Override
+    public final void endNode() throws IOException {
+        tracker.endNode();
+        context = context.endNode(codecs.getSchemaContext(), writer);
+    }
+
+    @Override
+    public final void flush() throws IOException {
+        writer.flush();
+    }
+
+    final void closeWriter() throws IOException {
+        if (!(context instanceof JSONStreamWriterRootContext)) {
+            throw new IOException("Unexpected root context " + context);
+        }
+
+        context.endNode(codecs.getSchemaContext(), writer);
+        writer.close();
+    }
+
+    @Override
+    public void scalarValue(final Object value) throws IOException {
+        final Object current = tracker.getParent();
+        if (current instanceof TypedDataSchemaNode) {
+            writeValue(value, codecs.codecFor((TypedDataSchemaNode) current));
+        } else if (current instanceof AnydataSchemaNode) {
+            writeAnydataValue(value);
+        } else {
+            throw new IllegalStateException(String.format("Cannot emit scalar %s for %s", value, current));
+        }
+    }
+
+    @Override
+    public void domSourceValue(final DOMSource value) throws IOException {
+        final Object current = tracker.getParent();
+        checkState(current instanceof AnyxmlSchemaNode, "Cannot emit DOMSource %s for %s", value, current);
+        // FIXME: should have a codec based on this :)
+        writeAnyXmlValue(value);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void writeValue(final Object value, final JSONCodec<?> codec) throws IOException {
+        ((JSONCodec<Object>) codec).writeValue(writer, value);
+    }
+
+    private void writeAnydataValue(final Object value) throws IOException {
+        if (value instanceof NormalizedAnydata) {
+            writeNormalizedAnydata((NormalizedAnydata) value);
+        } else {
+            throw new IllegalStateException("Unexpected anydata value " + value);
+        }
+    }
+
+    private void writeNormalizedAnydata(final NormalizedAnydata anydata) throws IOException {
+        anydata.writeTo(JSONNormalizedNodeStreamWriter.createNestedWriter(codecs.rebaseTo(anydata.getSchemaContext()),
+            new SingleChildDataNodeContainer(anydata.getContextNode()), context.getNamespace(), writer));
+    }
+
+    private void writeAnyXmlValue(final DOMSource anyXmlValue) throws IOException {
+        writeXmlNode(anyXmlValue.getNode());
+    }
+
+    private void writeXmlNode(final Node node) throws IOException {
+        if (isArrayElement(node)) {
+            writeArrayContent(node);
+            return;
+        }
+        final Element firstChildElement = getFirstChildElement(node);
+        if (firstChildElement == null) {
+            writeXmlValue(node);
+        } else {
+            writeObjectContent(firstChildElement);
+        }
+    }
+
+    private void writeArrayContent(final Node node) throws IOException {
+        writer.beginArray();
+        handleArray(node);
+        writer.endArray();
+    }
+
+    private void writeObjectContent(final Element firstChildElement) throws IOException {
+        writer.beginObject();
+        writeObject(firstChildElement);
+        writer.endObject();
+    }
+
+    private static boolean isArrayElement(final Node node) {
+        if (ELEMENT_NODE == node.getNodeType()) {
+            final String nodeName = node.getNodeName();
+            for (Node nextNode = node.getNextSibling(); nextNode != null; nextNode = nextNode.getNextSibling()) {
+                if (ELEMENT_NODE == nextNode.getNodeType() && nodeName.equals(nextNode.getNodeName())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void handleArray(final Node node) throws IOException {
+        final Element parentNode = (Element)node.getParentNode();
+        final NodeList elementsList = parentNode.getElementsByTagName(node.getNodeName());
+        for (int i = 0, length = elementsList.getLength(); i < length; i++) {
+            final Node arrayElement = elementsList.item(i);
+            final Element parent = (Element)arrayElement.getParentNode();
+            if (parentNode.isSameNode(parent)) {
+                final Element firstChildElement = getFirstChildElement(arrayElement);
+                if (firstChildElement != null) {
+                    writeObjectContent(firstChildElement);
+                } else {
+                    // It may be scalar
+                    writeXmlValue(arrayElement);
+                }
+            }
+        }
+    }
+
+    private void writeObject(Node node) throws IOException {
+        String previousNodeName = "";
+        while (node != null) {
+            if (ELEMENT_NODE == node.getNodeType()) {
+                if (!node.getNodeName().equals(previousNodeName)) {
+                    previousNodeName = node.getNodeName();
+                    writer.name(node.getNodeName());
+                    writeXmlNode(node);
+                }
+            }
+            node = node.getNextSibling();
+        }
+    }
+
+    private void writeXmlValue(final Node node) throws IOException {
+        Text firstChild = getFirstChildText(node);
+        String childNodeText = firstChild != null ? firstChild.getWholeText() : "";
+        childNodeText = childNodeText != null ? childNodeText.trim() : "";
+
+        if (NUMBER_PATTERN.matcher(childNodeText).matches()) {
+            writer.value(parseNumber(childNodeText));
+            return;
+        }
+        switch (childNodeText) {
+            case "null":
+                writer.nullValue();
+                break;
+            case "false":
+                writer.value(false);
+                break;
+            case "true":
+                writer.value(true);
+                break;
+            default:
+                writer.value(childNodeText);
+        }
+    }
+
+    private static Element getFirstChildElement(final Node node) {
+        final NodeList children = node.getChildNodes();
+        for (int i = 0, length = children.getLength(); i < length; i++) {
+            final Node childNode = children.item(i);
+            if (ELEMENT_NODE == childNode.getNodeType()) {
+                return (Element) childNode;
+            }
+        }
+        return null;
+    }
+
+    private static Text getFirstChildText(final Node node) {
+        final NodeList children = node.getChildNodes();
+        for (int i = 0, length = children.getLength(); i < length; i++) {
+            final Node childNode = children.item(i);
+            if (TEXT_NODE == childNode.getNodeType()) {
+                return (Text) childNode;
+            }
+        }
+        return null;
+    }
+
+    // json numbers are 64 bit wide floating point numbers - in java terms it is either long or double
+    private static Number parseNumber(final String numberText) {
+        if (NOT_DECIMAL_NUMBER_PATTERN.matcher(numberText).matches()) {
+            return Long.valueOf(numberText);
+        }
+
+        return Double.valueOf(numberText);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterContext.java
new file mode 100644 (file)
index 0000000..fa2a7b9
--- /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.yang.data.codec.gson;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Abstract base class for a single level of {@link JSONNormalizedNodeStreamWriter} recursion. Provides the base API
+ * towards the writer, which is then specialized by subclasses.
+ */
+abstract class JSONStreamWriterContext {
+    private final JSONStreamWriterContext parent;
+    private final boolean mandatory;
+    private final int depth;
+
+    private boolean emittedMyself;
+    private boolean inChild;
+
+    /**
+     * Construct a new context.
+     *
+     * @param parent Parent context, usually non-null.
+     * @param mandatory Mandatory flag. If set to true, the corresponding node
+     *                  will be emitted even if it has no children.
+     */
+    protected JSONStreamWriterContext(final JSONStreamWriterContext parent, final boolean mandatory) {
+        this.mandatory = mandatory;
+        this.parent = parent;
+
+        if (parent != null) {
+            depth = parent.depth + 1;
+        } else {
+            depth = 0;
+        }
+    }
+
+    /**
+     * Write a child JSON node identifier, optionally prefixing it with the module name corresponding to its namespace.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @param qname Namespace/name tuple
+     * @throws IOException when the writer reports it
+     */
+    final void writeChildJsonIdentifier(final SchemaContext schema, final JsonWriter writer, final QName qname)
+            throws IOException {
+
+        final StringBuilder sb = new StringBuilder();
+        // Prepend module name if namespaces do not match
+        final QNameModule module = qname.getModule();
+        if (!module.getNamespace().equals(getNamespace())) {
+            final Optional<String> modules = schema.findModule(module).map(Module::getName);
+            checkArgument(modules.isPresent(), "Could not find module for namespace {}", module);
+            sb.append(modules.get()).append(':');
+        }
+        sb.append(qname.getLocalName());
+
+        writer.name(sb.toString());
+    }
+
+    /**
+     * Write our JSON node identifier, optionally prefixing it with the module name corresponding to its namespace.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @param qname Namespace/name tuple
+     * @throws IOException when the writer reports it
+     */
+    protected final void writeMyJsonIdentifier(final SchemaContext schema, final JsonWriter writer, final QName qname)
+            throws IOException {
+        parent.writeChildJsonIdentifier(schema, writer, qname);
+    }
+
+    /**
+     * Return the namespace associated with current node.
+     *
+     * @return Namespace as URI
+     */
+    protected abstract @NonNull URI getNamespace();
+
+    /**
+     * Emit the start of an element.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @throws IOException when the writer reports it
+     */
+    protected abstract void emitStart(SchemaContext schema, JsonWriter writer) throws IOException;
+
+    /**
+     * Emit the end of an element.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @throws IOException when writer reports it
+     */
+    protected abstract void emitEnd(JsonWriter writer) throws IOException;
+
+    private void emitMyself(final SchemaContext schema, final JsonWriter writer) throws IOException {
+        if (!emittedMyself) {
+            if (parent != null) {
+                parent.emitMyself(schema, writer);
+            }
+
+            emitStart(schema, writer);
+            emittedMyself = true;
+        }
+    }
+
+    /**
+     * Invoked whenever a child node is being emitted. Checks whether this node has
+     * been emitted, and takes care of that if necessary. Also makes sure separator
+     * is emitted before a second and subsequent child.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @throws IOException when writer reports it
+     */
+    final void emittingChild(final SchemaContext schema, final JsonWriter writer) throws IOException {
+        checkState(!inChild, "Duplicate child encountered");
+        emitMyself(schema, writer);
+        inChild = true;
+    }
+
+    /**
+     * Invoked by the writer when it is leaving this node. Checks whether this node
+     * needs to be emitted and takes of that if necessary.
+     *
+     * @param schema Schema context
+     * @param writer Output writer
+     * @return Parent node context
+     * @throws IOException when writer reports it
+     * @throws IllegalArgumentException if this node cannot be ended (e.g. root)
+     */
+    final JSONStreamWriterContext endNode(final SchemaContext schema, final JsonWriter writer) throws IOException {
+        if (inChild) {
+            inChild = false;
+            return this;
+        }
+        if (!emittedMyself && mandatory) {
+            emitMyself(schema, writer);
+        }
+        if (emittedMyself) {
+            emitEnd(writer);
+        }
+        return parent;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper;
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterExclusiveRootContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterExclusiveRootContext.java
new file mode 100644 (file)
index 0000000..426d34f
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.net.URI;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+final class JSONStreamWriterExclusiveRootContext extends JSONStreamWriterRootContext {
+    JSONStreamWriterExclusiveRootContext(final URI namespace) {
+        super(namespace, true);
+    }
+
+    @Override
+    protected void emitStart(final SchemaContext schema, final JsonWriter writer) throws IOException {
+        writer.beginObject();
+    }
+
+    @Override
+    protected void emitEnd(final JsonWriter writer) throws IOException {
+        writer.endObject();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterInvisibleContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterInvisibleContext.java
new file mode 100644 (file)
index 0000000..f84dc93
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * A virtual recursion level in {@link JSONNormalizedNodeStreamWriter}, used for nodes
+ * which are not emitted in the JSON representation.
+ */
+final class JSONStreamWriterInvisibleContext extends JSONStreamWriterURIContext {
+    JSONStreamWriterInvisibleContext(final JSONStreamWriterContext parent) {
+        super(requireNonNull(parent), parent.getNamespace());
+    }
+
+    @Override
+    protected void emitEnd(final JsonWriter writer) {
+        // No-op
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterListContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterListContext.java
new file mode 100644 (file)
index 0000000..8d902ba
--- /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.data.codec.gson;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A single recursion level of {@link JSONNormalizedNodeStreamWriter} representing
+ * a list.
+ */
+final class JSONStreamWriterListContext extends JSONStreamWriterQNameContext {
+    protected JSONStreamWriterListContext(final JSONStreamWriterContext parent, final NodeIdentifier id) {
+        super(requireNonNull(parent), id.getNodeType(), false);
+    }
+
+    @Override
+    protected void emitStart(final SchemaContext schema, final JsonWriter writer) throws IOException {
+        writeMyJsonIdentifier(schema, writer, getQName());
+        writer.beginArray();
+    }
+
+    @Override
+    protected void emitEnd(final JsonWriter writer) throws IOException {
+        writer.endArray();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterNamedObjectContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterNamedObjectContext.java
new file mode 100644 (file)
index 0000000..cac9579
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A recursion level of {@link JSONNormalizedNodeStreamWriter}, which represents
+ * a JSON object which has to be prefixed with its identifier -- such as a
+ * container.
+ */
+final class JSONStreamWriterNamedObjectContext extends JSONStreamWriterObjectContext {
+    protected JSONStreamWriterNamedObjectContext(final JSONStreamWriterContext parent, final PathArgument arg,
+            final boolean mandatory) {
+        super(parent, arg, mandatory);
+    }
+
+    @Override
+    protected void emitStart(final SchemaContext schema, final JsonWriter writer) throws IOException {
+        writeMyJsonIdentifier(schema, writer, getQName());
+        super.emitStart(schema, writer);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterObjectContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterObjectContext.java
new file mode 100644 (file)
index 0000000..bfc81a4
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A recursion level of {@link JSONNormalizedNodeStreamWriter}, which represents
+ * a JSON object which does not have to be prefixed with its identifier -- such
+ * as when it is in a containing list.
+ */
+class JSONStreamWriterObjectContext extends JSONStreamWriterQNameContext {
+    protected JSONStreamWriterObjectContext(final JSONStreamWriterContext parent, final PathArgument arg,
+            final boolean mandatory) {
+        super(requireNonNull(parent), arg.getNodeType(), mandatory);
+    }
+
+    @Override
+    protected void emitStart(final SchemaContext schema, final JsonWriter writer) throws IOException {
+        writer.beginObject();
+    }
+
+    @Override
+    protected void emitEnd(final JsonWriter writer) throws IOException {
+        writer.endObject();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterQNameContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterQNameContext.java
new file mode 100644 (file)
index 0000000..3c00d02
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static java.util.Objects.requireNonNull;
+
+import java.net.URI;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Abstract base class for {@link JSONNormalizedNodeStreamWriter} recursion
+ * levels which emit a QName-identified node.
+ */
+abstract class JSONStreamWriterQNameContext extends JSONStreamWriterContext {
+    private final QName qname;
+
+    JSONStreamWriterQNameContext(final JSONStreamWriterContext parent, final QName qname, final boolean mandatory) {
+        super(parent, mandatory);
+        this.qname = requireNonNull(qname);
+    }
+
+    /**
+     * Returns the node's identifier as a QName.
+     *
+     * @return QName identifier
+     */
+    protected final QName getQName() {
+        return qname;
+    }
+
+    @Override
+    protected final URI getNamespace() {
+        return qname.getNamespace();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterRootContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterRootContext.java
new file mode 100644 (file)
index 0000000..0ac5aad
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import java.net.URI;
+
+/**
+ * The root node of a particular {@link JSONNormalizedNodeStreamWriter} instance.
+ * It holds the base namespace and can never be removed from the stack.
+ */
+abstract class JSONStreamWriterRootContext extends JSONStreamWriterURIContext {
+    JSONStreamWriterRootContext(final URI namespace, final boolean mandatory) {
+        super(null, namespace, mandatory);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterSharedRootContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterSharedRootContext.java
new file mode 100644 (file)
index 0000000..858637a
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.net.URI;
+
+final class JSONStreamWriterSharedRootContext extends JSONStreamWriterRootContext {
+    JSONStreamWriterSharedRootContext(final URI namespace) {
+        super(namespace, false);
+    }
+
+    @Override
+    protected void emitEnd(final JsonWriter writer) throws IOException {
+        // NOOP
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterURIContext.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStreamWriterURIContext.java
new file mode 100644 (file)
index 0000000..7caa4ad
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.net.URI;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Abstract class tracking a virtual level of {@link JSONNormalizedNodeStreamWriter}
+ * recursion. It only tracks the namespace associated with this node.
+ */
+abstract class JSONStreamWriterURIContext extends JSONStreamWriterContext {
+    private final URI namespace;
+
+    JSONStreamWriterURIContext(final JSONStreamWriterContext parent, final URI namespace) {
+        this(parent, namespace, false);
+    }
+
+    JSONStreamWriterURIContext(final JSONStreamWriterContext parent, final URI namespace, final boolean mandatory) {
+        super(parent, mandatory);
+        this.namespace = namespace;
+    }
+
+    @Override
+    protected final URI getNamespace() {
+        return namespace;
+    }
+
+    @Override
+    protected void emitStart(final SchemaContext schema, final JsonWriter writer) throws IOException {
+        // No-op
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java
new file mode 100644 (file)
index 0000000..9b7c48f
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.MalformedJsonException;
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.Flushable;
+import java.io.IOException;
+import java.net.URI;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Set;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.util.AbstractNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.AnyXmlNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.CompositeNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.LeafListEntryNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.LeafListNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.LeafNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.ListEntryNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.ListNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.OperationAsContainer;
+import org.opendaylight.yangtools.yang.data.util.ParserStreamUtils;
+import org.opendaylight.yangtools.yang.data.util.SimpleNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.OperationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+
+/**
+ * This class parses JSON elements from a GSON JsonReader. It disallows multiple elements of the same name unlike the
+ * default GSON JsonParser.
+ */
+@Beta
+public final class JsonParserStream implements Closeable, Flushable {
+    static final String ANYXML_ARRAY_ELEMENT_ID = "array-element";
+
+    private static final Logger LOG = LoggerFactory.getLogger(JsonParserStream.class);
+    private final Deque<URI> namespaces = new ArrayDeque<>();
+    private final NormalizedNodeStreamWriter writer;
+    private final JSONCodecFactory codecs;
+    private final DataSchemaNode parentNode;
+
+    // TODO: consider class specialization to remove this field
+    private final boolean lenient;
+
+    private JsonParserStream(final NormalizedNodeStreamWriter writer, final JSONCodecFactory codecs,
+            final DataSchemaNode parentNode, final boolean lenient) {
+        this.writer = requireNonNull(writer);
+        this.codecs = requireNonNull(codecs);
+        this.parentNode = parentNode;
+        this.lenient = lenient;
+    }
+
+    /**
+     * Create a new {@link JsonParserStream} backed by specified {@link NormalizedNodeStreamWriter}
+     * and {@link JSONCodecFactory}. The stream will be logically rooted at the top of the SchemaContext associated
+     * with the specified codec factory.
+     *
+     * @param writer NormalizedNodeStreamWriter to use for instantiation of normalized nodes
+     * @param codecFactory {@link JSONCodecFactory} to use for parsing leaves
+     * @return A new {@link JsonParserStream}
+     * @throws NullPointerException if any of the arguments are null
+     */
+    public static @NonNull JsonParserStream create(final @NonNull NormalizedNodeStreamWriter writer,
+            final @NonNull JSONCodecFactory codecFactory) {
+        return new JsonParserStream(writer, codecFactory, codecFactory.getSchemaContext(), false);
+    }
+
+    /**
+     * Create a new {@link JsonParserStream} backed by specified {@link NormalizedNodeStreamWriter}
+     * and {@link JSONCodecFactory}. The stream will be logically rooted at the specified parent node.
+     *
+     * @param writer NormalizedNodeStreamWriter to use for instantiation of normalized nodes
+     * @param codecFactory {@link JSONCodecFactory} to use for parsing leaves
+     * @param parentNode Logical root node
+     * @return A new {@link JsonParserStream}
+     * @throws NullPointerException if any of the arguments are null
+     */
+    public static @NonNull JsonParserStream create(final @NonNull NormalizedNodeStreamWriter writer,
+            final @NonNull JSONCodecFactory codecFactory, final @NonNull SchemaNode parentNode) {
+        return new JsonParserStream(writer, codecFactory, validateParent(parentNode), false);
+    }
+
+    /**
+     * Create a new {@link JsonParserStream} backed by specified {@link NormalizedNodeStreamWriter}
+     * and {@link JSONCodecFactory}. The stream will be logically rooted at the top of the SchemaContext associated
+     * with the specified codec factory.
+     *
+     * <p>
+     * Returned parser will treat incoming JSON data leniently:
+     * <ul>
+     *   <li>JSON elements referring to unknown constructs will be silently ignored</li>
+     * </ul>
+     *
+     * @param writer NormalizedNodeStreamWriter to use for instantiation of normalized nodes
+     * @param codecFactory {@link JSONCodecFactory} to use for parsing leaves
+     * @return A new {@link JsonParserStream}
+     * @throws NullPointerException if any of the arguments are null
+     */
+    public static @NonNull JsonParserStream createLenient(final @NonNull NormalizedNodeStreamWriter writer,
+            final @NonNull JSONCodecFactory codecFactory) {
+        return new JsonParserStream(writer, codecFactory, codecFactory.getSchemaContext(), true);
+    }
+
+    /**
+     * Create a new {@link JsonParserStream} backed by specified {@link NormalizedNodeStreamWriter}
+     * and {@link JSONCodecFactory}. The stream will be logically rooted at the specified parent node.
+     *
+     * <p>
+     * Returned parser will treat incoming JSON data leniently:
+     * <ul>
+     *   <li>JSON elements referring to unknown constructs will be silently ignored</li>
+     * </ul>
+     *
+     * @param writer NormalizedNodeStreamWriter to use for instantiation of normalized nodes
+     * @param codecFactory {@link JSONCodecFactory} to use for parsing leaves
+     * @param parentNode Logical root node
+     * @return A new {@link JsonParserStream}
+     * @throws NullPointerException if any of the arguments are null
+     */
+    public static @NonNull JsonParserStream createLenient(final @NonNull NormalizedNodeStreamWriter writer,
+            final @NonNull JSONCodecFactory codecFactory, final @NonNull SchemaNode parentNode) {
+        return new JsonParserStream(writer, codecFactory, validateParent(parentNode), true);
+    }
+
+    public JsonParserStream parse(final JsonReader reader) {
+        // code copied from gson's JsonParser and Stream classes
+
+        final boolean readerLenient = reader.isLenient();
+        reader.setLenient(true);
+        boolean isEmpty = true;
+        try {
+            reader.peek();
+            isEmpty = false;
+            final CompositeNodeDataWithSchema<?> compositeNodeDataWithSchema =
+                    new CompositeNodeDataWithSchema<>(parentNode);
+            read(reader, compositeNodeDataWithSchema);
+            compositeNodeDataWithSchema.write(writer);
+
+            return this;
+        } catch (final EOFException e) {
+            if (isEmpty) {
+                return this;
+            }
+            // The stream ended prematurely so it is likely a syntax error.
+            throw new JsonSyntaxException(e);
+        } catch (final MalformedJsonException | NumberFormatException e) {
+            throw new JsonSyntaxException(e);
+        } catch (final IOException e) {
+            throw new JsonIOException(e);
+        } catch (StackOverflowError | OutOfMemoryError e) {
+            throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e);
+        } finally {
+            reader.setLenient(readerLenient);
+        }
+    }
+
+    private void traverseAnyXmlValue(final JsonReader in, final Document doc, final Element parentElement)
+            throws IOException {
+        switch (in.peek()) {
+            case STRING:
+            case NUMBER:
+                Text textNode = doc.createTextNode(in.nextString());
+                parentElement.appendChild(textNode);
+                break;
+            case BOOLEAN:
+                textNode = doc.createTextNode(Boolean.toString(in.nextBoolean()));
+                parentElement.appendChild(textNode);
+                break;
+            case NULL:
+                in.nextNull();
+                textNode = doc.createTextNode("null");
+                parentElement.appendChild(textNode);
+                break;
+            case BEGIN_ARRAY:
+                in.beginArray();
+                while (in.hasNext()) {
+                    final Element childElement = doc.createElement(ANYXML_ARRAY_ELEMENT_ID);
+                    parentElement.appendChild(childElement);
+                    traverseAnyXmlValue(in, doc, childElement);
+                }
+                in.endArray();
+                break;
+            case BEGIN_OBJECT:
+                in.beginObject();
+                while (in.hasNext()) {
+                    final Element childElement = doc.createElement(in.nextName());
+                    parentElement.appendChild(childElement);
+                    traverseAnyXmlValue(in, doc, childElement);
+                }
+                in.endObject();
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void readAnyXmlValue(final JsonReader in, final AnyXmlNodeDataWithSchema parent,
+            final String anyXmlObjectName) throws IOException {
+        final String anyXmlObjectNS = getCurrentNamespace().toString();
+        final Document doc = UntrustedXML.newDocumentBuilder().newDocument();
+        final Element rootElement = doc.createElementNS(anyXmlObjectNS, anyXmlObjectName);
+        doc.appendChild(rootElement);
+        traverseAnyXmlValue(in, doc, rootElement);
+
+        final DOMSource domSource = new DOMSource(doc.getDocumentElement());
+        parent.setValue(domSource);
+    }
+
+    public void read(final JsonReader in, AbstractNodeDataWithSchema<?> parent) throws IOException {
+        switch (in.peek()) {
+            case STRING:
+            case NUMBER:
+                setValue(parent, in.nextString());
+                break;
+            case BOOLEAN:
+                setValue(parent, Boolean.toString(in.nextBoolean()));
+                break;
+            case NULL:
+                in.nextNull();
+                setValue(parent, null);
+                break;
+            case BEGIN_ARRAY:
+                in.beginArray();
+                while (in.hasNext()) {
+                    if (parent instanceof LeafNodeDataWithSchema) {
+                        read(in, parent);
+                    } else {
+                        final AbstractNodeDataWithSchema<?> newChild = newArrayEntry(parent);
+                        read(in, newChild);
+                    }
+                }
+                in.endArray();
+                return;
+            case BEGIN_OBJECT:
+                final Set<String> namesakes = new HashSet<>();
+                in.beginObject();
+                /*
+                 * This allows parsing of incorrectly /as showcased/
+                 * in testconf nesting of list items - eg.
+                 * lists with one value are sometimes serialized
+                 * without wrapping array.
+                 *
+                 */
+                if (isArray(parent)) {
+                    parent = newArrayEntry(parent);
+                }
+                while (in.hasNext()) {
+                    final String jsonElementName = in.nextName();
+                    DataSchemaNode parentSchema = parent.getSchema();
+                    if (parentSchema instanceof YangModeledAnyxmlSchemaNode) {
+                        parentSchema = ((YangModeledAnyxmlSchemaNode) parentSchema).getSchemaOfAnyXmlData();
+                    }
+                    final Entry<String, URI> namespaceAndName = resolveNamespace(jsonElementName, parentSchema);
+                    final String localName = namespaceAndName.getKey();
+                    final URI namespace = namespaceAndName.getValue();
+                    if (lenient && (localName == null || namespace == null)) {
+                        LOG.debug("Schema node with name {} was not found under {}", localName,
+                            parentSchema.getQName());
+                        in.skipValue();
+                        continue;
+                    }
+                    addNamespace(namespace);
+                    if (!namesakes.add(jsonElementName)) {
+                        throw new JsonSyntaxException("Duplicate name " + jsonElementName + " in JSON input.");
+                    }
+
+                    final Deque<DataSchemaNode> childDataSchemaNodes =
+                            ParserStreamUtils.findSchemaNodeByNameAndNamespace(parentSchema, localName,
+                                getCurrentNamespace());
+                    checkState(!childDataSchemaNodes.isEmpty(),
+                        "Schema for node with name %s and namespace %s does not exist at %s",
+                        localName, getCurrentNamespace(), parentSchema.getPath());
+
+
+                    final AbstractNodeDataWithSchema<?> newChild = ((CompositeNodeDataWithSchema<?>) parent)
+                            .addChild(childDataSchemaNodes);
+                    if (newChild instanceof AnyXmlNodeDataWithSchema) {
+                        readAnyXmlValue(in, (AnyXmlNodeDataWithSchema) newChild, jsonElementName);
+                    } else {
+                        read(in, newChild);
+                    }
+                    removeNamespace();
+                }
+                in.endObject();
+                return;
+            default:
+                break;
+        }
+    }
+
+    private static boolean isArray(final AbstractNodeDataWithSchema<?> parent) {
+        return parent instanceof ListNodeDataWithSchema || parent instanceof LeafListNodeDataWithSchema;
+    }
+
+    private static AbstractNodeDataWithSchema<?> newArrayEntry(final AbstractNodeDataWithSchema<?> parent) {
+        AbstractNodeDataWithSchema<?> newChild;
+        if (parent instanceof ListNodeDataWithSchema) {
+            newChild = ListEntryNodeDataWithSchema.forSchema(((ListNodeDataWithSchema) parent).getSchema());
+        } else if (parent instanceof LeafListNodeDataWithSchema) {
+            newChild = new LeafListEntryNodeDataWithSchema(((LeafListNodeDataWithSchema) parent).getSchema());
+        } else {
+            throw new IllegalStateException("Found an unexpected array nested under " + parent.getSchema().getQName());
+        }
+        ((CompositeNodeDataWithSchema<?>) parent).addChild(newChild);
+        return newChild;
+    }
+
+    private void setValue(final AbstractNodeDataWithSchema<?> parent, final String value) {
+        checkArgument(parent instanceof SimpleNodeDataWithSchema, "Node %s is not a simple type",
+                parent.getSchema().getQName());
+        final SimpleNodeDataWithSchema<?> parentSimpleNode = (SimpleNodeDataWithSchema<?>) parent;
+        checkArgument(parentSimpleNode.getValue() == null, "Node '%s' has already set its value to '%s'",
+                parentSimpleNode.getSchema().getQName(), parentSimpleNode.getValue());
+
+        final Object translatedValue = translateValueByType(value, parentSimpleNode.getSchema());
+        parentSimpleNode.setValue(translatedValue);
+    }
+
+    private Object translateValueByType(final String value, final DataSchemaNode node) {
+        checkArgument(node instanceof TypedDataSchemaNode);
+        return codecs.codecFor((TypedDataSchemaNode) node).parseValue(null, value);
+    }
+
+    private void removeNamespace() {
+        namespaces.pop();
+    }
+
+    private void addNamespace(final URI namespace) {
+        namespaces.push(namespace);
+    }
+
+    private Entry<String, URI> resolveNamespace(final String childName, final DataSchemaNode dataSchemaNode) {
+        final int lastIndexOfColon = childName.lastIndexOf(':');
+        String moduleNamePart = null;
+        String nodeNamePart = null;
+        URI namespace = null;
+        if (lastIndexOfColon != -1) {
+            moduleNamePart = childName.substring(0, lastIndexOfColon);
+            nodeNamePart = childName.substring(lastIndexOfColon + 1);
+
+            final Iterator<Module> m = codecs.getSchemaContext().findModules(moduleNamePart).iterator();
+            namespace = m.hasNext() ? m.next().getNamespace() : null;
+        } else {
+            nodeNamePart = childName;
+        }
+
+        if (namespace == null) {
+            final Set<URI> potentialUris = resolveAllPotentialNamespaces(nodeNamePart, dataSchemaNode);
+            if (potentialUris.contains(getCurrentNamespace())) {
+                namespace = getCurrentNamespace();
+            } else if (potentialUris.size() == 1) {
+                namespace = potentialUris.iterator().next();
+            } else if (potentialUris.size() > 1) {
+                throw new IllegalStateException("Choose suitable module name for element " + nodeNamePart + ":"
+                        + toModuleNames(potentialUris));
+            } else if (potentialUris.isEmpty() && !lenient) {
+                throw new IllegalStateException("Schema node with name " + nodeNamePart + " was not found under "
+                        + dataSchemaNode.getQName() + ".");
+            }
+        }
+
+        return new SimpleImmutableEntry<>(nodeNamePart, namespace);
+    }
+
+    private String toModuleNames(final Set<URI> potentialUris) {
+        final StringBuilder builder = new StringBuilder();
+        for (final URI potentialUri : potentialUris) {
+            builder.append('\n');
+            //FIXME how to get information about revision from JSON input? currently first available is used.
+            builder.append(codecs.getSchemaContext().findModules(potentialUri).iterator().next().getName());
+        }
+        return builder.toString();
+    }
+
+    private Set<URI> resolveAllPotentialNamespaces(final String elementName, final DataSchemaNode dataSchemaNode) {
+        final Set<URI> potentialUris = new HashSet<>();
+        final Set<ChoiceSchemaNode> choices = new HashSet<>();
+        if (dataSchemaNode instanceof DataNodeContainer) {
+            for (final DataSchemaNode childSchemaNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
+                if (childSchemaNode instanceof ChoiceSchemaNode) {
+                    choices.add((ChoiceSchemaNode)childSchemaNode);
+                } else if (childSchemaNode.getQName().getLocalName().equals(elementName)) {
+                    potentialUris.add(childSchemaNode.getQName().getNamespace());
+                }
+            }
+
+            for (final ChoiceSchemaNode choiceNode : choices) {
+                for (final CaseSchemaNode concreteCase : choiceNode.getCases().values()) {
+                    potentialUris.addAll(resolveAllPotentialNamespaces(elementName, concreteCase));
+                }
+            }
+        }
+        return potentialUris;
+    }
+
+    private URI getCurrentNamespace() {
+        return namespaces.peek();
+    }
+
+    private static DataSchemaNode validateParent(final SchemaNode parent) {
+        if (parent instanceof DataSchemaNode) {
+            return (DataSchemaNode) parent;
+        } else if (parent instanceof OperationDefinition) {
+            return OperationAsContainer.of((OperationDefinition) parent);
+        } else {
+            throw new IllegalArgumentException("Illegal parent node " + requireNonNull(parent));
+        }
+    }
+
+    @Override
+    public void flush() throws IOException {
+        writer.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        writer.flush();
+        writer.close();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonWriterFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonWriterFactory.java
new file mode 100644 (file)
index 0000000..807a694
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.annotations.Beta;
+import com.google.gson.stream.JsonWriter;
+import java.io.Writer;
+
+/**
+ * Factory Method class for JsonWriter creation.
+ */
+@Beta
+public final class JsonWriterFactory {
+    private JsonWriterFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Create a new JsonWriter, which writes to the specified output writer.
+     *
+     * @param writer Output writer
+     * @return A JsonWriter instance
+     */
+    public static JsonWriter createJsonWriter(final Writer writer) {
+        return new JsonWriter(writer);
+    }
+
+    /**
+     * Create a new JsonWriter, which writes to the specified output writer.
+     *
+     * @param writer Output writer
+     * @param indentSize size of the indent
+     * @return A JsonWriter instance
+     */
+    public static JsonWriter createJsonWriter(final Writer writer, final int indentSize) {
+        JsonWriter jsonWriter = new JsonWriter(writer);
+        final String indent = " ".repeat(indentSize);
+        jsonWriter.setIndent(indent);
+        return jsonWriter;
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/Lhotka02JSONCodecFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/Lhotka02JSONCodecFactory.java
new file mode 100644 (file)
index 0000000..56ce4a7
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import org.opendaylight.yangtools.yang.data.impl.codec.AbstractIntegerStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.DecimalStringCodec;
+import org.opendaylight.yangtools.yang.data.util.codec.CodecCache;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+
+final class Lhotka02JSONCodecFactory extends JSONCodecFactory {
+    private final JSONInstanceIdentifierCodec iidCodec;
+
+    Lhotka02JSONCodecFactory(final SchemaContext context, final CodecCache<JSONCodec<?>> cache) {
+        super(context, cache);
+        iidCodec = new Lhotka02JSONInstanceIdentifierCodec(context, this);
+    }
+
+    @Override
+    protected JSONCodec<?> instanceIdentifierCodec(final InstanceIdentifierTypeDefinition type) {
+        return iidCodec;
+    }
+
+    @Override
+    Lhotka02JSONCodecFactory rebaseTo(final SchemaContext newSchemaContext, final CodecCache<JSONCodec<?>> newCache) {
+        return new Lhotka02JSONCodecFactory(newSchemaContext, newCache);
+    }
+
+    @Override
+    JSONCodec<?> wrapDecimalCodec(final DecimalStringCodec decimalCodec) {
+        return new NumberJSONCodec<>(decimalCodec);
+    }
+
+    @Override
+    JSONCodec<?> wrapIntegerCodec(final AbstractIntegerStringCodec<?, ?> integerCodec) {
+        return new NumberJSONCodec<>(integerCodec);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/Lhotka02JSONInstanceIdentifierCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/Lhotka02JSONInstanceIdentifierCodec.java
new file mode 100644 (file)
index 0000000..c8ff720
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+final class Lhotka02JSONInstanceIdentifierCodec extends JSONInstanceIdentifierCodec {
+    Lhotka02JSONInstanceIdentifierCodec(final SchemaContext context, final JSONCodecFactory jsonCodecFactory) {
+        super(context, jsonCodecFactory);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/NullJSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/NullJSONCodec.java
new file mode 100644 (file)
index 0000000..79861b3
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class NullJSONCodec implements JSONCodec<Object> {
+    static final NullJSONCodec INSTANCE = new NullJSONCodec();
+    private static final Logger LOG = LoggerFactory.getLogger(NullJSONCodec.class);
+
+    private NullJSONCodec() {
+
+    }
+
+    @Override
+    public Class<Object> getDataType() {
+        return Object.class;
+    }
+
+    @Override
+    public Object parseValue(final Object ctx, final String str) {
+        LOG.warn("Call of the deserializeString method on null codec. No operation performed.");
+        return null;
+    }
+
+    @Override
+    public void writeValue(final JsonWriter ctx, final Object value) throws IOException {
+        // NOOP since codec is unknown.
+        LOG.warn("Call of the serializeToWriter method on null codec. No operation performed.");
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/NumberJSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/NumberJSONCodec.java
new file mode 100644 (file)
index 0000000..9800476
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import org.opendaylight.yangtools.yang.data.impl.codec.DataStringCodec;
+
+/**
+ * A {@link JSONCodec} which does not need double quotes in output representation.
+ *
+ * @param <T> Deserialized value type
+ */
+final class NumberJSONCodec<T extends Number> extends AbstractJSONCodec<T> {
+    NumberJSONCodec(final DataStringCodec<T> codec) {
+        super(codec);
+    }
+
+    @Override
+    public void writeValue(final JsonWriter ctx, final T value) throws IOException {
+        ctx.value(value);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/QuotedJSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/QuotedJSONCodec.java
new file mode 100644 (file)
index 0000000..6d3a107
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import org.opendaylight.yangtools.yang.data.impl.codec.DataStringCodec;
+
+/**
+ * A {@link JSONCodec} which needs double quotes in output representation.
+ *
+ * @param <T> Deserialized value type
+ */
+final class QuotedJSONCodec<T> extends AbstractJSONCodec<T> {
+    QuotedJSONCodec(final DataStringCodec<T> codec) {
+        super(codec);
+    }
+
+    @Override
+    public void writeValue(final JsonWriter ctx, final T value) throws IOException {
+        ctx.value(serialize(value));
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/RFC7951JSONCodecFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/RFC7951JSONCodecFactory.java
new file mode 100644 (file)
index 0000000..51e7a57
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import org.opendaylight.yangtools.yang.data.impl.codec.AbstractIntegerStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.DecimalStringCodec;
+import org.opendaylight.yangtools.yang.data.util.codec.CodecCache;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+
+final class RFC7951JSONCodecFactory extends JSONCodecFactory {
+    private final RFC7951JSONInstanceIdentifierCodec iidCodec;
+
+    RFC7951JSONCodecFactory(final SchemaContext context, final CodecCache<JSONCodec<?>> cache) {
+        super(context, cache);
+        iidCodec = new RFC7951JSONInstanceIdentifierCodec(context, this);
+    }
+
+    @Override
+    protected JSONCodec<?> instanceIdentifierCodec(final InstanceIdentifierTypeDefinition type) {
+        return iidCodec;
+    }
+
+    @Override
+    JSONCodecFactory rebaseTo(final SchemaContext newSchemaContext, final CodecCache<JSONCodec<?>> newCache) {
+        return new RFC7951JSONCodecFactory(newSchemaContext, newCache);
+    }
+
+    @Override
+    JSONCodec<?> wrapDecimalCodec(final DecimalStringCodec decimalCodec) {
+        return new QuotedJSONCodec<>(decimalCodec);
+    }
+
+    @Override
+    JSONCodec<?> wrapIntegerCodec(final AbstractIntegerStringCodec<?, ?> integerCodec) {
+        return new QuotedJSONCodec<>(integerCodec);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/RFC7951JSONInstanceIdentifierCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/RFC7951JSONInstanceIdentifierCodec.java
new file mode 100644 (file)
index 0000000..de7ad7d
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+final class RFC7951JSONInstanceIdentifierCodec extends JSONInstanceIdentifierCodec {
+    RFC7951JSONInstanceIdentifierCodec(final SchemaContext context, final JSONCodecFactory jsonCodecFactory) {
+        super(context, jsonCodecFactory);
+    }
+
+    @Override
+    protected StringBuilder appendQName(final StringBuilder sb, final QName qname, final QNameModule lastModule) {
+        if (qname.getModule().equals(lastModule)) {
+            return sb.append(qname.getLocalName());
+        }
+
+        return super.appendQName(sb, qname, lastModule);
+    }
+
+    @Override
+    protected QName createQName(final QNameModule lastModule, final String localName) {
+        checkArgument(lastModule != null, "Unprefixed leading name %s", localName);
+        return QName.create(lastModule, localName);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/UnionJSONCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/UnionJSONCodec.java
new file mode 100644 (file)
index 0000000..390bb91
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Union codec based with a pre-calculated set of sub-types.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Data representation type
+ */
+abstract class UnionJSONCodec<T> implements JSONCodec<T> {
+    private static final class Diverse extends UnionJSONCodec<Object> {
+        Diverse(final List<JSONCodec<?>> codecs) {
+            super(codecs);
+        }
+
+        @Override
+        public Class<Object> getDataType() {
+            return Object.class;
+        }
+    }
+
+    private static final class SingleType<T> extends UnionJSONCodec<T> {
+        private final Class<T> dataClass;
+
+        SingleType(final Class<T> dataClass, final List<JSONCodec<?>> codecs) {
+            super(codecs);
+            this.dataClass = requireNonNull(dataClass);
+        }
+
+        @Override
+        public Class<T> getDataType() {
+            return dataClass;
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(UnionJSONCodec.class);
+
+    private final ImmutableList<JSONCodec<?>> codecs;
+
+    UnionJSONCodec(final List<JSONCodec<?>> codecs) {
+        this.codecs = ImmutableList.copyOf(codecs);
+    }
+
+    static UnionJSONCodec<?> create(final UnionTypeDefinition type, final List<JSONCodec<?>> codecs) {
+        final Iterator<JSONCodec<?>> it = codecs.iterator();
+        verify(it.hasNext(), "Union %s has no subtypes", type);
+
+        Class<?> dataClass = it.next().getDataType();
+        while (it.hasNext()) {
+            final Class<?> next = it.next().getDataType();
+            if (!dataClass.equals(next)) {
+                LOG.debug("Type {} has diverse data classes: {} and {}", type, dataClass, next);
+                return new Diverse(codecs);
+            }
+        }
+
+        LOG.debug("Type {} has single data class {}", type, dataClass);
+        return new SingleType<>(dataClass, codecs);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    public final T parseValue(final Object ctx, final String str) {
+        for (JSONCodec<?> codec : codecs) {
+            final Object ret;
+            try {
+                ret = codec.parseValue(ctx, str);
+            } catch (RuntimeException e) {
+                LOG.debug("Codec {} did not accept input '{}'", codec, str, e);
+                continue;
+            }
+
+            return getDataType().cast(ret);
+        }
+
+        throw new IllegalArgumentException("Invalid value \"" + str + "\" for union type.");
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    public final void writeValue(final JsonWriter ctx, final T value) throws IOException {
+        for (JSONCodec<?> codec : codecs) {
+            if (!codec.getDataType().isInstance(value)) {
+                LOG.debug("Codec {} cannot accept input {}, skipping it", codec, value);
+                continue;
+            }
+
+            @SuppressWarnings("unchecked")
+            final JSONCodec<Object> objCodec = (JSONCodec<Object>) codec;
+            try {
+                objCodec.writeValue(ctx, value);
+                return;
+            } catch (RuntimeException e) {
+                LOG.debug("Codec {} failed to serialize {}", codec, value, e);
+            }
+        }
+
+        throw new IllegalArgumentException("No codecs could serialize" + value);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractComplexJsonTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractComplexJsonTest.java
new file mode 100644 (file)
index 0000000..eccb494
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public abstract class AbstractComplexJsonTest {
+    static final QName CONT_1 = QName.create("ns:complex:json", "2014-08-11", "cont1");
+    private static final QName EMPTY_LEAF = QName.create(CONT_1, "empty");
+
+    static final ContainerNode CONT1_WITH_EMPTYLEAF = Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(CONT_1))
+            .addChild(ImmutableNodes.leafNode(EMPTY_LEAF, Empty.getInstance()))
+            .build();
+
+    static SchemaContext schemaContext;
+    static JSONCodecFactory lhotkaCodecFactory;
+
+    @BeforeClass
+    public static void beforeClass() {
+        schemaContext = YangParserTestUtils.parseYangResourceDirectory("/complexjson/yang");
+        lhotkaCodecFactory = JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext);
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        lhotkaCodecFactory = null;
+        schemaContext = null;
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractYT1027Test.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractYT1027Test.java
new file mode 100644 (file)
index 0000000..c8f1afa
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static com.google.common.base.Verify.verify;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.google.gson.stream.JsonReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.math.BigDecimal;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public abstract class AbstractYT1027Test {
+    private static final QName DECIMAL = QName.create("yt1027.test", "decimal");
+    private static final QName INT64 = QName.create(DECIMAL, "int64");
+    private static final QName UINT64 = QName.create(DECIMAL, "uint64");
+
+    static final LeafNode<?> DECIMAL_DATA = ImmutableNodes.leafNode(DECIMAL, new BigDecimal("1.1"));
+    static final LeafNode<?> INT64_DATA = ImmutableNodes.leafNode(INT64, 2L);
+    static final LeafNode<?> UINT64_DATA = ImmutableNodes.leafNode(UINT64, Uint64.ONE);
+
+    static final String UNQUOTED_DECIMAL = "{\n"
+            + "  \"yt1027:decimal\": 1.1\n"
+            + "}";
+    static final String UNQUOTED_INT64 = "{\n"
+            + "  \"yt1027:int64\": 2\n"
+            + "}";
+    static final String UNQUOTED_UINT64 = "{\n"
+            + "  \"yt1027:uint64\": 1\n"
+            + "}";
+
+    static SchemaContext SCHEMA_CONTEXT;
+    private static DecimalTypeDefinition DECIMAL_TYPE;
+    private static Int64TypeDefinition INT64_TYPE;
+    private static Uint64TypeDefinition UINT64_TYPE;
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = YangParserTestUtils.parseYangResourceDirectory("/yt1027");
+        DECIMAL_TYPE = (DecimalTypeDefinition) getTypeDefinition(DECIMAL);
+        INT64_TYPE = (Int64TypeDefinition) getTypeDefinition(INT64);
+        UINT64_TYPE = (Uint64TypeDefinition) getTypeDefinition(UINT64);
+    }
+
+    private static TypeDefinition<?> getTypeDefinition(final QName name) {
+        DataSchemaNode child = SCHEMA_CONTEXT.findDataTreeChild(name).get();
+        verify(child instanceof LeafSchemaNode);
+        return ((LeafSchemaNode) child).getType();
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        DECIMAL_TYPE = null;
+        INT64_TYPE = null;
+        UINT64_TYPE = null;
+        SCHEMA_CONTEXT = null;
+    }
+
+    @Test
+    public void testDecimal() {
+        assertThat(codecFactory().decimalCodec(DECIMAL_TYPE), instanceOf(wrapperClass()));
+    }
+
+    @Test
+    public void testInt64() {
+        assertThat(codecFactory().int64Codec(INT64_TYPE), instanceOf(wrapperClass()));
+    }
+
+    @Test
+    public void testUint64() {
+        assertThat(codecFactory().uint64Codec(UINT64_TYPE), instanceOf(wrapperClass()));
+    }
+
+    @Test
+    public void testDecimalSerialization() throws IOException {
+        assertEquals(expectedDecimal(), toJSON(DECIMAL_DATA));
+    }
+
+    @Test
+    public void testInt64Serialization() throws IOException {
+        assertEquals(expectedInt64(), toJSON(INT64_DATA));
+    }
+
+    @Test
+    public void testUint64Serialization() throws IOException {
+        assertEquals(expectedUint64(), toJSON(UINT64_DATA));
+    }
+
+    @Test
+    public void testDecimalParsing() throws IOException {
+        assertEquals(DECIMAL_DATA, fromJSON(expectedDecimal()));
+    }
+
+    @Test
+    public void testInt64Parsing() throws IOException {
+        assertEquals(INT64_DATA, fromJSON(expectedInt64()));
+    }
+
+    @Test
+    public void testUint64Parsing() throws IOException {
+        assertEquals(UINT64_DATA, fromJSON(expectedUint64()));
+    }
+
+    abstract JSONCodecFactory codecFactory();
+
+    abstract Class<?> wrapperClass();
+
+    abstract String expectedDecimal();
+
+    abstract String expectedInt64();
+
+    abstract String expectedUint64();
+
+    final NormalizedNode<?, ?> fromJSON(final String input) throws IOException {
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, codecFactory());
+        jsonParser.parse(new JsonReader(new StringReader(input)));
+        return result.getResult();
+    }
+
+    private String toJSON(final NormalizedNode<?, ?> input) throws IOException {
+        final Writer writer = new StringWriter();
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter(
+            codecFactory(), SchemaPath.ROOT, null, JsonWriterFactory.createJsonWriter(writer, 2));
+        try (NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream)) {
+            nodeWriter.write(input);
+        }
+
+        return writer.toString();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlSupportTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlSupportTest.java
new file mode 100644 (file)
index 0000000..a4e2bd9
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadXmlToNormalizedNodes;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.normalizedNodesToJsonString;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.gson.stream.JsonReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.net.URISyntaxException;
+import java.util.Optional;
+import javax.xml.transform.dom.DOMSource;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+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.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+
+public class AnyXmlSupportTest extends AbstractComplexJsonTest {
+
+    private static final QName LF12_ANY = QName.create(CONT_1, "lf12-any");
+    private static final QName LF13_ANY = QName.create(CONT_1, "lf13-any");
+    private static final QName LF14_ANY = QName.create(CONT_1, "lf14-any");
+
+    @Test
+    public void anyXmlNodeWithSimpleValueInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/anyxml-node-with-simple-value-in-container.json");
+
+        // deserialization
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory);
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+
+        // lf12-any check
+        final DOMSource Lf12AnyActualValue = getParsedAnyXmlValue(transformedInput, LF12_ANY);
+        final DOMSource Lf12AnyExpectedValue = createAnyXmlSimpleValue("ns:complex:json", "lf12-any", "100.5");
+        verifyTransformedAnyXmlNodeValue(Lf12AnyExpectedValue, Lf12AnyActualValue);
+
+        // lf13-any check
+        final DOMSource Lf13AnyActualValue = getParsedAnyXmlValue(transformedInput, LF13_ANY);
+        final DOMSource Lf13AnyExpectedValue = createAnyXmlSimpleValue("ns:complex:json", "lf13-any", "true");
+        verifyTransformedAnyXmlNodeValue(Lf13AnyExpectedValue, Lf13AnyActualValue);
+
+        // lf14-any check
+        final DOMSource Lf14AnyActualValue = getParsedAnyXmlValue(transformedInput, LF14_ANY);
+        final DOMSource Lf14AnyExpectedValue = createAnyXmlSimpleValue("ns:complex:json", "lf14-any", "null");
+        verifyTransformedAnyXmlNodeValue(Lf14AnyExpectedValue, Lf14AnyActualValue);
+
+        final String serializationResult = normalizedNodesToJsonString(transformedInput, schemaContext,
+                SchemaPath.ROOT);
+
+        final JsonParser parser = new JsonParser();
+        final JsonElement expected = parser.parse(inputJson);
+        final JsonElement actual = parser.parse(serializationResult);
+        assertTrue(expected.equals(actual));
+    }
+
+    @Test
+    public void anyXmlNodeWithCompositeValueInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/anyxml-node-with-composite-value-in-container.json");
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory);
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+
+        // lf12-any check
+        final DOMSource Lf12AnyActualValue = getParsedAnyXmlValue(transformedInput, LF12_ANY);
+        final DOMSource Lf12AnyExpectedValue = createLf12AnyXmlCompositeValue("ns:complex:json", "lf12-any");
+        verifyTransformedAnyXmlNodeValue(Lf12AnyExpectedValue, Lf12AnyActualValue);
+        // lf13-any check
+        final DOMSource Lf13AnyActualValue = getParsedAnyXmlValue(transformedInput, LF13_ANY);
+        final DOMSource Lf13AnyExpectedValue = createLf13AnyXmlCompositeValue("ns:complex:json", "lf13-any");
+        verifyTransformedAnyXmlNodeValue(Lf13AnyExpectedValue, Lf13AnyActualValue);
+
+        // lf14-any check
+        final DOMSource Lf14AnyActualValue = getParsedAnyXmlValue(transformedInput, LF14_ANY);
+        final DOMSource Lf14AnyExpectedValue = createLf14AnyXmlCompositeValue("ns:complex:json", "lf14-any");
+        verifyTransformedAnyXmlNodeValue(Lf14AnyExpectedValue, Lf14AnyActualValue);
+
+        // serialization
+        final String serializationResult = normalizedNodesToJsonString(transformedInput, schemaContext,
+                SchemaPath.ROOT);
+
+        final JsonParser parser = new JsonParser();
+        final String expectedJson = loadTextFile("/bug8927/json/composite.json");
+        final JsonElement expected = parser.parse(expectedJson);
+        final JsonElement actual = parser.parse(serializationResult);
+        assertTrue(expected.equals(actual));
+    }
+
+    @Test
+    public void bug8927TestComplexArrayWithOthers() throws Exception {
+        executebug8927Test("/bug8927/xml/complex_array_with_other_elements.xml",
+            "/bug8927/json/complex_array_with_other_elements.json");
+    }
+
+    @Test
+    public void bug8927TestComplexArray() throws Exception {
+        executebug8927Test("/bug8927/xml/complex_array.xml", "/bug8927/json/complex_array.json");
+    }
+
+    @Test
+    public void bug8927TestScalarArrayWithOthers() throws Exception {
+        executebug8927Test("/bug8927/xml/scalar_array_with_other_elements.xml",
+            "/bug8927/json/scalar_array_with_other_elements.json");
+    }
+
+    @Test
+    public void bug8927TestScalarArray() throws Exception {
+        executebug8927Test("/bug8927/xml/scalar_array.xml", "/bug8927/json/scalar_array.json");
+    }
+
+    private void executebug8927Test(final String inputXmlFile, final String expectedJsonFile) throws Exception {
+        final InputStream resourceAsStream = YangModeledAnyXmlSupportTest.class
+                .getResourceAsStream(inputXmlFile);
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        loadXmlToNormalizedNodes(resourceAsStream, result, schemaContext);
+
+        assertNotNull(result.getResult());
+        assertTrue(result.getResult() instanceof ContainerNode);
+
+        final Optional<DataContainerChild<? extends PathArgument, ?>> data = ((ContainerNode) result.getResult())
+                .getChild(new NodeIdentifier(QName.create("bug8927.test", "2017-01-01", "foo")));
+        assertTrue(data.isPresent());
+        final String jsonOutput = normalizedNodesToJsonString(data.get(), schemaContext, SchemaPath.ROOT);
+        final JsonParser parser = new JsonParser();
+        final JsonElement expextedJson = parser
+                .parse(new FileReader(new File(getClass().getResource(expectedJsonFile).toURI())));
+        final JsonElement serializedJson = parser.parse(jsonOutput);
+        assertEquals(expextedJson, serializedJson);
+    }
+
+    private static DOMSource getParsedAnyXmlValue(final NormalizedNode<?, ?> transformedInput, final QName anyxmlName) {
+        assertTrue(transformedInput instanceof ContainerNode);
+        final ContainerNode cont1 = (ContainerNode) transformedInput;
+        final DataContainerChild<? extends PathArgument, ?> child = cont1.getChild(new NodeIdentifier(anyxmlName))
+                .get();
+        assertTrue(child instanceof DOMSourceAnyxmlNode);
+        final DOMSourceAnyxmlNode anyXmlNode = (DOMSourceAnyxmlNode) child;
+        return anyXmlNode.getValue();
+    }
+
+    private static void verifyTransformedAnyXmlNodeValue(final DOMSource expectedValue, final DOMSource actualValue) {
+        assertTrue(expectedValue.getNode().isEqualNode(actualValue.getNode()));
+    }
+
+    private static DOMSource createAnyXmlSimpleValue(final String ns, final String name, final String value) {
+        final Document doc = UntrustedXML.newDocumentBuilder().newDocument();
+        final Element rootElement = doc.createElementNS(ns, name);
+        doc.appendChild(rootElement);
+        final Text textNode = doc.createTextNode(value);
+        rootElement.appendChild(textNode);
+        return new DOMSource(doc.getDocumentElement());
+    }
+
+    private static DOMSource createLf12AnyXmlCompositeValue(final String ns, final String name) {
+        final Document doc = UntrustedXML.newDocumentBuilder().newDocument();
+        final Element rootElement = doc.createElementNS(ns, name);
+
+        final Element arrayElement1 = doc.createElement("array-element");
+        final Text arrayElement1Text = doc.createTextNode("true");
+        arrayElement1.appendChild(arrayElement1Text);
+
+        final Element arrayElement2 = doc.createElement("array-element");
+
+        final Element arrayElement2Baz = doc.createElement("baz");
+        final Element bazArrayElement1 = doc.createElement("array-element");
+        final Text bazArrayElement1Text = doc.createTextNode("120");
+        bazArrayElement1.appendChild(bazArrayElement1Text);
+        final Element bazArrayElement2 = doc.createElement("array-element");
+        final Text bazArrayElement2Text = doc.createTextNode("str-val");
+        bazArrayElement2.appendChild(bazArrayElement2Text);
+        final Element bazArrayElement3 = doc.createElement("array-element");
+        final Text bazArrayElement3Text = doc.createTextNode("false");
+        bazArrayElement3.appendChild(bazArrayElement3Text);
+
+        arrayElement2Baz.appendChild(bazArrayElement1);
+        arrayElement2Baz.appendChild(bazArrayElement2);
+        arrayElement2Baz.appendChild(bazArrayElement3);
+
+        arrayElement2.appendChild(arrayElement2Baz);
+
+        final Element arrayElement3 = doc.createElement("array-element");
+        final Element arrayElement3Foo = doc.createElement("foo");
+        final Text fooText = doc.createTextNode("null");
+        arrayElement3Foo.appendChild(fooText);
+        arrayElement3.appendChild(arrayElement3Foo);
+
+        rootElement.appendChild(arrayElement1);
+        rootElement.appendChild(arrayElement2);
+        rootElement.appendChild(arrayElement3);
+
+        doc.appendChild(rootElement);
+
+        return new DOMSource(doc.getDocumentElement());
+    }
+
+    private static DOMSource createLf13AnyXmlCompositeValue(final String ns, final String name) {
+        final Document doc = UntrustedXML.newDocumentBuilder().newDocument();
+        final Element rootElement = doc.createElementNS(ns, name);
+
+        final Element anyXmlArrayA = doc.createElement("anyxml-array-a");
+
+        final Element arrayAElement1 = doc.createElement("array-element");
+        final Element arrayAElement1Foo = doc.createElement("foo");
+        final Text fooText = doc.createTextNode("true");
+        arrayAElement1Foo.appendChild(fooText);
+        arrayAElement1.appendChild(arrayAElement1Foo);
+
+        final Element arrayAElement2 = doc.createElement("array-element");
+        final Text arrayAElement2Text = doc.createTextNode("10");
+        arrayAElement2.appendChild(arrayAElement2Text);
+
+        final Element arrayAElement3 = doc.createElement("array-element");
+        final Element arrayAElement3Bar = doc.createElement("bar");
+        final Text barText = doc.createTextNode("false");
+        arrayAElement3Bar.appendChild(barText);
+        arrayAElement3.appendChild(arrayAElement3Bar);
+
+        anyXmlArrayA.appendChild(arrayAElement1);
+        anyXmlArrayA.appendChild(arrayAElement2);
+        anyXmlArrayA.appendChild(arrayAElement3);
+
+        final Element anyXmlArrayB = doc.createElement("anyxml-array-b");
+
+        final Element arrayBElement1 = doc.createElement("array-element");
+        final Text arrayBElement1Text = doc.createTextNode("1");
+        arrayBElement1.appendChild(arrayBElement1Text);
+
+        final Element arrayBElement2 = doc.createElement("array-element");
+        final Text arrayBElement2Text = doc.createTextNode("2");
+        arrayBElement2.appendChild(arrayBElement2Text);
+
+        final Element arrayBElement3 = doc.createElement("array-element");
+
+        final Element arrayBElement3Element1 = doc.createElement("array-element");
+        final Text arrayBElement3Element1Text = doc.createTextNode("4");
+        arrayBElement3Element1.appendChild(arrayBElement3Element1Text);
+        final Element arrayBElement3Element2 = doc.createElement("array-element");
+        final Text arrayBElement3Element2Text = doc.createTextNode("5");
+        arrayBElement3Element2.appendChild(arrayBElement3Element2Text);
+
+        arrayBElement3.appendChild(arrayBElement3Element1);
+        arrayBElement3.appendChild(arrayBElement3Element2);
+
+        final Element arrayBElement4 = doc.createElement("array-element");
+        final Text arrayBElement4Text = doc.createTextNode("7");
+        arrayBElement4.appendChild(arrayBElement4Text);
+
+        anyXmlArrayB.appendChild(arrayBElement1);
+        anyXmlArrayB.appendChild(arrayBElement2);
+        anyXmlArrayB.appendChild(arrayBElement3);
+        anyXmlArrayB.appendChild(arrayBElement4);
+
+        rootElement.appendChild(anyXmlArrayA);
+        rootElement.appendChild(anyXmlArrayB);
+
+        doc.appendChild(rootElement);
+
+        return new DOMSource(doc.getDocumentElement());
+    }
+
+    private static DOMSource createLf14AnyXmlCompositeValue(final String ns, final String name) {
+        final Document doc = UntrustedXML.newDocumentBuilder().newDocument();
+        final Element rootElement = doc.createElementNS(ns, name);
+
+        final Element anyXmlObjectA = doc.createElement("anyxml-object-a");
+        final Element dataA1 = doc.createElement("data-a1");
+        final Text dataA1Text = doc.createTextNode("10");
+        dataA1.appendChild(dataA1Text);
+        final Element dataA2 = doc.createElement("data-a2");
+        final Text dataA2Text = doc.createTextNode("11");
+        dataA2.appendChild(dataA2Text);
+
+        anyXmlObjectA.appendChild(dataA1);
+        anyXmlObjectA.appendChild(dataA2);
+
+        final Element anyXmlObjectB = doc.createElement("anyxml-object-b");
+
+        final Element childObjectB1 = doc.createElement("child-object-b1");
+        final Element dataB1 = doc.createElement("data-b1");
+        final Text dataB1Text = doc.createTextNode("5.5");
+        dataB1.appendChild(dataB1Text);
+        childObjectB1.appendChild(dataB1);
+
+        final Element childObjectB2 = doc.createElement("child-object-b2");
+        final Element dataB2 = doc.createElement("data-b2");
+        final Text dataB2Text = doc.createTextNode("b2-val");
+        dataB2.appendChild(dataB2Text);
+        childObjectB2.appendChild(dataB2);
+
+        anyXmlObjectB.appendChild(childObjectB1);
+        anyXmlObjectB.appendChild(childObjectB2);
+
+        rootElement.appendChild(anyXmlObjectA);
+        rootElement.appendChild(anyXmlObjectB);
+
+        doc.appendChild(rootElement);
+
+        return new DOMSource(doc.getDocumentElement());
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug4501Test.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug4501Test.java
new file mode 100644 (file)
index 0000000..9cde9b3
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.stream.JsonReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.URISyntaxException;
+import java.util.Optional;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+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.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug4501Test {
+
+    private static SchemaContext schemaContext;
+
+    @BeforeClass
+    public static void initialization() {
+        schemaContext = YangParserTestUtils.parseYangResourceDirectory("/bug-4501/yang");
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        schemaContext = null;
+    }
+
+    @Test
+    public void testCorrectInput() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/bug-4501/json/foo-correct.json");
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter,
+            JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext));
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertTrue(transformedInput instanceof UnkeyedListNode);
+
+        final UnkeyedListNode hop = (UnkeyedListNode) transformedInput;
+        final Optional<DataContainerChild<? extends PathArgument, ?>> lrsBits = hop.getChild(0).getChild(
+                NodeIdentifier.create(QName.create("foo", "lrs-bits")));
+
+        final ImmutableSet<String> expectedValue = ImmutableSet.of("lookup", "rloc-probe", "strict");
+        assertEquals(expectedValue, lrsBits.get().getValue());
+    }
+
+    @Test
+    public void testIncorrectInput() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/bug-4501/json/foo-incorrect.json");
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter,
+            JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext));
+
+        try {
+            jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+            fail("IllegalArgumentException should be thrown.");
+        } catch (IllegalArgumentException e) {
+            assertEquals(e.getMessage(), "Node '(foo)lrs-bits' has already set its value to '[lookup]'");
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug4969Test.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug4969Test.java
new file mode 100644 (file)
index 0000000..c1f30b4
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gson.stream.JsonReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.URISyntaxException;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+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.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug4969Test {
+
+    @Test
+    public void newParserLeafRefTest() throws IOException, URISyntaxException {
+        SchemaContext context = YangParserTestUtils.parseYangResourceDirectory("/bug-4969/yang");
+        assertNotNull(context);
+
+        verifyNormalizedNodeResult(context);
+    }
+
+    private static void verifyNormalizedNodeResult(final SchemaContext context) throws IOException, URISyntaxException {
+        final String inputJson = TestUtils.loadTextFile("/bug-4969/json/foo.json");
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter,
+            JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(context));
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+
+        assertTrue(transformedInput instanceof ContainerNode);
+        ContainerNode root = (ContainerNode) transformedInput;
+        final Optional<DataContainerChild<? extends PathArgument, ?>> ref1 = root.getChild(NodeIdentifier.create(
+            QName.create("foo", "2016-01-22", "ref1")));
+        final Optional<DataContainerChild<? extends PathArgument, ?>> ref2 = root.getChild(NodeIdentifier.create(
+            QName.create("foo", "2016-01-22", "ref2")));
+        final Optional<DataContainerChild<? extends PathArgument, ?>> ref3 = root.getChild(NodeIdentifier.create(
+            QName.create("foo", "2016-01-22", "ref3")));
+        final Optional<DataContainerChild<? extends PathArgument, ?>> ref4 = root.getChild(NodeIdentifier.create(
+            QName.create("foo", "2016-01-22", "ref4")));
+
+        assertTrue(ref1.isPresent());
+        assertTrue(ref2.isPresent());
+        assertTrue(ref3.isPresent());
+        assertTrue(ref4.isPresent());
+
+        final Object value1 = ref1.get().getValue();
+        final Object value2 = ref2.get().getValue();
+        final Object value3 = ref3.get().getValue();
+        final Object value4 = ref4.get().getValue();
+
+        assertTrue(value1 instanceof Set);
+        assertTrue(value2 instanceof Set);
+        assertTrue(value3 instanceof Set);
+        assertTrue(value4 instanceof Set);
+
+        final Set<?> set1 = (Set<?>) value1;
+        final Set<?> set2 = (Set<?>) value2;
+        final Set<?> set3 = (Set<?>) value3;
+        final Set<?> set4 = (Set<?>) value4;
+
+        assertEquals(1, set1.size());
+        assertEquals(2, set2.size());
+        assertEquals(3, set3.size());
+        assertEquals(4, set4.size());
+
+        assertTrue(set1.contains("a"));
+        assertTrue(set2.contains("a") && set2.contains("b"));
+        assertTrue(set3.contains("a") && set3.contains("b") && set3.contains("c"));
+        assertTrue(set4.contains("a") && set4.contains("b") && set4.contains("c") && set4.contains("d"));
+    }
+
+    @Test
+    public void newParserLeafRefTest2() throws URISyntaxException, IOException {
+        SchemaContext context = YangParserTestUtils.parseYangResourceDirectory("/leafref/yang");
+        assertNotNull(context);
+
+        parseJsonToNormalizedNodes(context);
+    }
+
+    private static void parseJsonToNormalizedNodes(final SchemaContext context) throws IOException, URISyntaxException {
+        final String inputJson = TestUtils.loadTextFile("/leafref/json/data.json");
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter,
+            JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(context));
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug5446Test.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug5446Test.java
new file mode 100644 (file)
index 0000000..45d50e5
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Base64;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+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.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug5446Test {
+
+    private static final QNameModule FOO_MODULE = QNameModule.create(URI.create("foo"), Revision.of("2015-11-05"));
+    private static final QName ROOT_QNAME = QName.create(FOO_MODULE, "root");
+    private static final QName IP_ADDRESS_QNAME = QName.create(FOO_MODULE, "ip-address");
+    private static SchemaContext schemaContext;
+
+    @BeforeClass
+    public static void init() {
+        schemaContext = YangParserTestUtils.parseYangResources(Bug5446Test.class, "/bug5446/yang/foo.yang");
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        schemaContext = null;
+    }
+
+    @Test
+    public void test() throws IOException, JsonIOException, JsonSyntaxException, URISyntaxException {
+        final DataContainerChild<? extends PathArgument, ?> rootNode = createRootNode();
+
+        final Writer writer = new StringWriter();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, rootNode);
+
+        final JsonParser parser = new JsonParser();
+        final JsonElement serializedJson = parser.parse(jsonOutput);
+        final JsonElement expextedJson = parser.parse(new FileReader(new File(getClass().getResource(
+                "/bug5446/json/foo.json").toURI())));
+
+        assertEquals(expextedJson, serializedJson);
+    }
+
+    private static String normalizedNodeToJsonStreamTransformation(final Writer writer,
+            final NormalizedNode<?, ?> inputStructure) throws IOException {
+
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter(
+            JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext), SchemaPath.ROOT, null,
+            JsonWriterFactory.createJsonWriter(writer, 2));
+        final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+        nodeWriter.write(inputStructure);
+
+        nodeWriter.close();
+        return writer.toString();
+    }
+
+    private static ContainerNode createRootNode() {
+        LeafNode<byte[]> ipAddress = ImmutableNodes.leafNode(IP_ADDRESS_QNAME, Base64.getDecoder().decode("fwAAAQ=="));
+        return ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(ROOT_QNAME))
+                .withChild(ipAddress).build();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug6112Test.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug6112Test.java
new file mode 100644 (file)
index 0000000..4dd17d3
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016 Intel Corporation and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
+
+import com.google.gson.stream.JsonReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.URISyntaxException;
+import java.util.Optional;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Uint8;
+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.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug6112Test {
+    private static SchemaContext schemaContext;
+
+    @BeforeClass
+    public static void initialization() {
+        schemaContext = YangParserTestUtils.parseYangResourceDirectory("/bug-6112/yang");
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        schemaContext = null;
+    }
+
+    private static NormalizedNode<?, ?> readJson(final String jsonPath) throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile(jsonPath);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter,
+            JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext));
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        return result.getResult();
+    }
+
+    @Test
+    public void testUnionIdentityrefInput() throws IOException, URISyntaxException {
+        final NormalizedNode<?, ?> transformedInput = readJson("/bug-6112/json/data-identityref.json");
+        assertTrue(transformedInput instanceof ContainerNode);
+        ContainerNode root = (ContainerNode) transformedInput;
+        Optional<DataContainerChild<? extends PathArgument, ?>> leafValue = root.getChild(NodeIdentifier.create(
+            QName.create("union:identityref:test", "2016-07-12", "leaf-value")));
+
+        assertTrue(leafValue.isPresent());
+        Object value = leafValue.get().getValue();
+        assertTrue(value instanceof QName);
+        QName identityref = (QName) value;
+        assertEquals(QName.create("union:identityref:test", "2016-07-12", "ident-one"), identityref);
+    }
+
+    @Test
+    public void testUnionUint8Input() throws IOException, URISyntaxException {
+        final NormalizedNode<?, ?> transformedInput = readJson("/bug-6112/json/data-uint8.json");
+        assertTrue(transformedInput instanceof ContainerNode);
+        ContainerNode root = (ContainerNode) transformedInput;
+        Optional<DataContainerChild<? extends PathArgument, ?>> leafValue = root.getChild(NodeIdentifier.create(
+            QName.create("union:identityref:test", "2016-07-12", "leaf-value")));
+
+        assertTrue(leafValue.isPresent());
+        assertEquals(Uint8.valueOf(1), leafValue.get().getValue());
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug7246Test.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug7246Test.java
new file mode 100644 (file)
index 0000000..86a0057
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+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.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug7246Test {
+    private static final String NS = "my-namespace";
+
+    @Test
+    public void test() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResource("/bug7246/yang/rpc-test.yang");
+        final JsonParser parser = new JsonParser();
+        final JsonElement expextedJson = parser
+                .parse(new FileReader(new File(getClass().getResource("/bug7246/json/expected-output.json").toURI())));
+
+        final DataContainerChild<? extends PathArgument, ?> inputStructure = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(qN("my-name")))
+                .withChild(ImmutableNodes.leafNode(new NodeIdentifier(qN("my-name")), "my-value")).build();
+        final SchemaPath rootPath = SchemaPath.create(true, qN("my-name"), qN("input"));
+        final Writer writer = new StringWriter();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(schemaContext, rootPath, writer,
+                inputStructure);
+        final JsonElement serializedJson = parser.parse(jsonOutput);
+
+        assertEquals(expextedJson, serializedJson);
+    }
+
+    private static QName qN(final String localName) {
+        return QName.create(NS, localName);
+    }
+
+    private static String normalizedNodeToJsonStreamTransformation(final SchemaContext schemaContext,
+            final SchemaPath path, final Writer writer, final NormalizedNode<?, ?> inputStructure)
+            throws IOException {
+
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter(
+                JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext), path,
+                URI.create(NS), JsonWriterFactory.createJsonWriter(writer, 2));
+        final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+        nodeWriter.write(inputStructure);
+
+        nodeWriter.close();
+        return writer.toString();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug8083Test.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug8083Test.java
new file mode 100644 (file)
index 0000000..9b006ff
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug8083Test {
+    private static final QNameModule FOOMOD = QNameModule.create(URI.create("http://example.com/foomod"));
+    private static final QNameModule BARMOD = QNameModule.create(URI.create("http://example.com/barmod"));
+
+    private static final QName FOO_QNAME = QName.create(FOOMOD, "foo");
+    private static final QName FOOLIST_QNAME = QName.create(FOOMOD, "foo-list");
+    private static final QName NAME_QNAME = QName.create(FOOMOD, "name");
+    private static final QName TOP_QNAME = QName.create(FOOMOD, "top");
+    private static final QName BARCONTAINER_QNAME = QName.create(BARMOD, "bar-container");
+
+    private static final YangInstanceIdentifier TEST_IID = YangInstanceIdentifier.builder()
+            .node(TOP_QNAME)
+            .node(FOOLIST_QNAME)
+            .node(NodeIdentifierWithPredicates.of(FOOLIST_QNAME, ImmutableMap.of(NAME_QNAME, "key-value")))
+            .node(new AugmentationIdentifier(ImmutableSet.of(BARCONTAINER_QNAME)))
+            .node(BARCONTAINER_QNAME)
+            .node(QName.create(BARMOD, "bar-leaf"))
+            .build();
+
+    private static SchemaContext FULL_SCHEMA_CONTEXT;
+
+    @BeforeClass
+    public static void init() {
+        FULL_SCHEMA_CONTEXT = YangParserTestUtils.parseYangResourceDirectory("/bug8083/yang/");
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        FULL_SCHEMA_CONTEXT = null;
+    }
+
+    @Test
+    public void testInstanceIdentifierSerializeNew() throws IOException {
+        assertEquals("/example-foomod:top/foo-list[name='key-value']/example-barmod:bar-container/bar-leaf",
+            writeInstanceIdentifier(JSONCodecFactorySupplier.RFC7951));
+    }
+
+    @Test
+    public void testInstanceIdentifierSerializeOld() throws IOException {
+        assertEquals("/example-foomod:top/example-foomod:foo-list[example-foomod:name='key-value']"
+                + "/example-barmod:bar-container/example-barmod:bar-leaf",
+            writeInstanceIdentifier(JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02));
+    }
+
+    @Test
+    public void testRFC7951InstanceIdentifierPath() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/bug8083/json/foo.json");
+
+        // deserialization
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter,
+            JSONCodecFactorySupplier.RFC7951.getShared(FULL_SCHEMA_CONTEXT));
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+
+        assertTrue(transformedInput instanceof ContainerNode);
+        final ContainerNode container = (ContainerNode) transformedInput;
+        final NormalizedNode<?, ?> child = container.getChild(new NodeIdentifier(FOO_QNAME)).get();
+        assertTrue(child instanceof LeafNode);
+        assertEquals(TEST_IID, child.getValue());
+    }
+
+    @Test
+    public void testInstanceIdentifierPathWithEmptyListKey() throws IOException, URISyntaxException {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResource("/bug8083/yang/baz.yang");
+        final String inputJson = loadTextFile("/bug8083/json/baz.json");
+
+        // deserialization
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter,
+            JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext));
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void testInstanceIdentifierPathWithIdentityrefListKey() throws IOException, URISyntaxException {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResource("/bug8083/yang/zab.yang");
+        final String inputJson = loadTextFile("/bug8083/json/zab.json");
+
+        // deserialization
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter,
+            JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext));
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void testInstanceIdentifierPathWithInstanceIdentifierListKey() throws IOException, URISyntaxException {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResource("/bug8083/yang/foobar.yang");
+        final String inputJson = loadTextFile("/bug8083/json/foobar.json");
+
+        // deserialization
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter,
+            JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext));
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    private static JSONCodec<YangInstanceIdentifier> getCodec(final JSONCodecFactorySupplier supplier) {
+        final DataSchemaNode top = FULL_SCHEMA_CONTEXT.findDataChildByName(TOP_QNAME).get();
+        assertTrue(top instanceof ContainerSchemaNode);
+        final DataSchemaNode foo = ((ContainerSchemaNode) top).findDataChildByName(FOO_QNAME).get();
+        assertTrue(foo instanceof LeafSchemaNode);
+        final TypeDefinition<? extends TypeDefinition<?>> type = ((LeafSchemaNode) foo).getType();
+        assertTrue(type instanceof InstanceIdentifierTypeDefinition);
+        return (JSONCodec<YangInstanceIdentifier>) supplier.createSimple(FULL_SCHEMA_CONTEXT)
+                .instanceIdentifierCodec((InstanceIdentifierTypeDefinition) type);
+    }
+
+    private static String writeInstanceIdentifier(final JSONCodecFactorySupplier supplier) throws IOException {
+        final JsonWriter writer = mock(JsonWriter.class);
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        doReturn(writer).when(writer).value(captor.capture());
+
+        getCodec(supplier).writeValue(writer, TEST_IID);
+        verify(writer).value(any(String.class));
+        return captor.getValue();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonStreamToNormalizedNodeTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonStreamToNormalizedNodeTest.java
new file mode 100644 (file)
index 0000000..876d34d
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.augmentationBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.choiceBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.containerBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.stream.JsonReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.URISyntaxException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+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.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+/**
+ * Each test tests whether json input is correctly transformed to normalized node structure.
+ */
+public class JsonStreamToNormalizedNodeTest extends AbstractComplexJsonTest {
+    @Test
+    public void leafNodeInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/leaf-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.leafNodeInContainer());
+    }
+
+    @Test
+    public void leafNodeViaAugmentationInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/leaf-node-via-augmentation-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.leafNodeViaAugmentationInContainer());
+    }
+
+    @Test
+    public void leafListNodeInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/leaflist-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.leafListNodeInContainer());
+    }
+
+    @Test
+    public void keyedListNodeInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/keyed-list-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.keyedListNodeInContainer());
+    }
+
+    @Test
+    public void choiceNodeInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/choice-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.choiceNodeInContainer());
+    }
+
+    /**
+     * Test of translating internal augmentations to normalized nodes structure.
+     *
+     * <p>
+     * 2 nodes are added via internal augmentation A, 1 node via internal augmentation B and one node is originally
+     * member of case.
+     */
+    @Test
+    public void caseNodeAugmentationInChoiceInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/case-node-augmentation-in-choice-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.caseNodeAugmentationInChoiceInContainer());
+    }
+
+    /**
+     * also test using of namesakes (equal local names with different.
+     */
+    @Test
+    public void caseNodeExternalAugmentationInChoiceInContainer() throws IOException, URISyntaxException {
+        final String inputJson =
+                loadTextFile("/complexjson/case-node-external-augmentation-in-choice-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.caseNodeExternalAugmentationInChoiceInContainer());
+    }
+
+    /**
+     * augmentation of choice - adding new case.
+     */
+    @Test
+    public void choiceNodeAugmentationInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/choice-node-augmentation-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.choiceNodeAugmentationInContainer());
+    }
+
+    @Test
+    public void unkeyedNodeInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/unkeyed-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+            TestingNormalizedNodeStructuresCreator.unkeyedNodeInContainer());
+    }
+
+    /**
+     * Top level JSON element contains no information about module name.
+     *
+     * <p>
+     * It should be possible to find out potential module name from available schema context.
+     */
+    @Test
+    public void missingModuleInfoInTopLevelElement() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/missing-module-in-top-level.json");
+        verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.topLevelContainer());
+    }
+
+    /**
+     * Exception expected.
+     *
+     * <p>
+     * It tests case when several elements with the same name and various namespaces exists and are in JSON specified
+     * without module name prefix.
+     */
+    @Test
+    public void leafNamesakes() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/namesakes.json");
+        try {
+            //second parameter isn't necessary because error will be raised before it is used.
+            verifyTransformationToNormalizedNode(inputJson, null);
+            fail("Expected exception not raised");
+        } catch (final IllegalStateException e) {
+            final String errorMessage = e.getMessage();
+            assertTrue(errorMessage.contains("Choose suitable module name for element lf11-namesake:"));
+            assertTrue(errorMessage.contains("complexjson-augmentation"));
+            assertTrue(errorMessage.contains("complexjson-augmentation-namesake"));
+        }
+    }
+
+    @Test
+    public void emptyTypeTest() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/type-empty.json");
+        verifyTransformationToNormalizedNode(inputJson, CONT1_WITH_EMPTYLEAF);
+    }
+
+    /**
+     * Exception expected.
+     *
+     * <p>
+     * Json input contains element which doesn't exist in YANG schema
+     */
+    @Test
+    public void parsingNotExistingElement() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/not-existing-element.json");
+        try {
+            //second parameter isn't necessary because error will be raised before it is used.
+            verifyTransformationToNormalizedNode(inputJson, null);
+        } catch (final IllegalStateException e) {
+            assertTrue(e.getMessage().contains("Schema node with name dummy-element was not found"));
+        }
+    }
+
+    /**
+     * Should not fail as we set the parser to be lenient.
+     *
+     * <p>
+     * Json input contains element which doesn't exist in YANG schema
+     */
+    @Test
+    public void parsingSkipNotExistingElement() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/not-existing-element.json");
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.createLenient(streamWriter,
+            JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext));
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void listItemWithoutArray() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/keyed-list-restconf-behaviour.json");
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final SchemaNode parentNode = schemaContext.findDataChildByName(CONT_1).get();
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory, parentNode);
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void listItemWithArray() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/keyed-list-yang-json-behaviour.json");
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final SchemaNode parentNode = schemaContext.findDataChildByName(CONT_1).get();
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory, parentNode);
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void multipleChoiceAugmentation() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/multiple-choice-augmentation-in-container.json");
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final SchemaNode parentNode = schemaContext.findDataChildByName(CONT_1).get();
+
+        final QName augmentChoice1QName = QName.create(parentNode.getQName(), "augment-choice1");
+        final QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2");
+        final QName containerQName = QName.create(augmentChoice1QName, "case11-choice-case-container");
+        final QName leafQName = QName.create(augmentChoice1QName, "case11-choice-case-leaf");
+
+        final AugmentationIdentifier aug1Id = new AugmentationIdentifier(ImmutableSet.of(augmentChoice1QName));
+        final AugmentationIdentifier aug2Id = new AugmentationIdentifier(ImmutableSet.of(augmentChoice2QName));
+        final NodeIdentifier augmentChoice1Id = new NodeIdentifier(augmentChoice1QName);
+        final NodeIdentifier augmentChoice2Id = new NodeIdentifier(augmentChoice2QName);
+        final NodeIdentifier containerId = new NodeIdentifier(containerQName);
+
+        final NormalizedNode<?, ?> cont1Normalized =
+                containerBuilder().withNodeIdentifier(new NodeIdentifier(parentNode.getQName()))
+                        .withChild(augmentationBuilder().withNodeIdentifier(aug1Id)
+                                .withChild(choiceBuilder().withNodeIdentifier(augmentChoice1Id)
+                                        .withChild(augmentationBuilder().withNodeIdentifier(aug2Id)
+                                                .withChild(choiceBuilder().withNodeIdentifier(augmentChoice2Id)
+                                                        .withChild(containerBuilder().withNodeIdentifier(containerId)
+                                                                .withChild(leafNode(leafQName, "leaf-value"))
+                                                                .build())
+                                                        .build())
+                                                .build())
+                                        .build())
+                                .build()).build();
+
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory);
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+        assertEquals(cont1Normalized, transformedInput);
+    }
+
+    private static void verifyTransformationToNormalizedNode(final String inputJson,
+            final NormalizedNode<?, ?> awaitedStructure) {
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory);
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertEquals("Transformation of json input to normalized node wasn't successful.", awaitedStructure,
+                transformedInput);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Lhotka02YT1027Test.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Lhotka02YT1027Test.java
new file mode 100644 (file)
index 0000000..cdeb76f
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public class Lhotka02YT1027Test extends AbstractYT1027Test {
+    private static JSONCodecFactory CODEC_FACTORY;
+
+    @BeforeClass
+    public static void createFactory() {
+        CODEC_FACTORY = JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(SCHEMA_CONTEXT);
+    }
+
+    @AfterClass
+    public static void destroyFactory() {
+        CODEC_FACTORY = null;
+    }
+
+    @Override
+    JSONCodecFactory codecFactory() {
+        return CODEC_FACTORY;
+    }
+
+    @Override
+    Class<?> wrapperClass() {
+        return NumberJSONCodec.class;
+    }
+
+    @Override
+    String expectedDecimal() {
+        return UNQUOTED_DECIMAL;
+    }
+
+    @Override
+    String expectedInt64() {
+        return UNQUOTED_INT64;
+    }
+
+    @Override
+    String expectedUint64() {
+        return UNQUOTED_UINT64;
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/NormalizedNodeToJsonStreamTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/NormalizedNodeToJsonStreamTest.java
new file mode 100644 (file)
index 0000000..06e9db2
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.childArray;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.childPrimitive;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.resolveCont1;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.resolveCont2;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashSet;
+import java.util.Iterator;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Each test tests whether json output obtained after transformation contains is corect. The transformation takes
+ * normalized node data structure and transform it to json output. To make it easier validate json output it is loaded
+ * via gson as structure of json elements which are walked and compared with awaited values.
+ */
+public class NormalizedNodeToJsonStreamTest extends AbstractComplexJsonTest {
+    @Test
+    public void leafNodeInContainer() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.leafNodeInContainer());
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        assertNotNull(cont1);
+
+        final JsonPrimitive lf11 = childPrimitive(cont1, "complexjson:lf11", "lf11");
+        assertNotNull(lf11);
+        final int asInt = lf11.getAsInt();
+        assertEquals(453, asInt);
+    }
+
+    @Test
+    public void leafListNodeInContainerMultiline() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.leafListNodeInContainerMultiline());
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        assertNotNull(cont1);
+        final JsonArray lflst11 = childArray(cont1, "complexjson:lflst11", "lflst11");
+        assertNotNull(lflst11);
+
+        final HashSet<Object> lflst11Values = new HashSet<>();
+        for (final JsonElement jsonElement : lflst11) {
+            assertTrue(jsonElement instanceof JsonPrimitive);
+            lflst11Values.add(jsonElement.getAsString());
+        }
+
+        assertEquals(ImmutableSet.of("lflst11 value2\r\nanother line 2", "lflst11 value1\nanother line 1"),
+            lflst11Values);
+    }
+
+    @Test
+    public void leafNodeViaAugmentationInContainer() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.leafNodeViaAugmentationInContainer());
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        assertNotNull(cont1);
+
+        final JsonPrimitive lf12_1 = childPrimitive(cont1, "complexjson:lf12_1", "lf12_1");
+        assertNotNull(lf12_1);
+        final String asString = lf12_1.getAsString();
+        assertEquals("lf12 value", asString);
+    }
+
+    @Test
+    public void leafListNodeInContainer() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.leafListNodeInContainer());
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        assertNotNull(cont1);
+        final JsonArray lflst11 = childArray(cont1, "complexjson:lflst11", "lflst11");
+        assertNotNull(lflst11);
+
+        final HashSet<Object> lflst11Values = new HashSet<>();
+        for (final JsonElement jsonElement : lflst11) {
+            assertTrue(jsonElement instanceof JsonPrimitive);
+            lflst11Values.add(jsonElement.getAsString());
+        }
+
+        assertEquals(ImmutableSet.of("lflst11 value2", "lflst11 value1"), lflst11Values);
+    }
+
+    @Test
+    public void keyedListNodeInContainer() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.keyedListNodeInContainer());
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        assertNotNull(cont1);
+        final JsonArray lst11 = childArray(cont1, "complexjson:lst11", "lst11");
+        assertNotNull(lst11);
+
+        final Iterator<JsonElement> iterator = lst11.iterator();
+        assertTrue(iterator.hasNext());
+        final JsonElement lst11Entry1Raw = iterator.next();
+        assertFalse(iterator.hasNext());
+        assertTrue(lst11Entry1Raw instanceof JsonObject);
+        final JsonObject lst11Entry1 = (JsonObject) lst11Entry1Raw;
+
+        final JsonPrimitive key111 = childPrimitive(lst11Entry1, "complexjson:key111", "key111");
+        assertNotNull(key111);
+        final JsonPrimitive lf112 = childPrimitive(lst11Entry1, "complexjson:lf112", "lf112");
+        assertNotNull(lf112);
+        final JsonPrimitive lf113 = childPrimitive(lst11Entry1, "complexjson:lf113", "lf113");
+        assertNotNull(lf113);
+        final JsonPrimitive lf111 = childPrimitive(lst11Entry1, "complexjson:lf111", "lf111");
+        assertNotNull(lf111);
+
+        assertEquals("key111 value", key111.getAsString());
+        assertEquals("/complexjson:cont1/complexjson:lflst11[.='foo']", lf112.getAsString());
+        assertEquals("lf113 value", lf113.getAsString());
+        assertEquals("lf111 value", lf111.getAsString());
+    }
+
+    @Test
+    public void choiceNodeInContainer() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.choiceNodeInContainer());
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        assertNotNull(cont1);
+        final JsonPrimitive lf13 = childPrimitive(cont1, "complexjson:lf13", "lf13");
+        assertNotNull(lf13);
+
+        assertEquals("lf13 value", lf13.getAsString());
+    }
+
+    /**
+     * tested case when case c11A in choice choc11 is augmented (two leaves (augment A) and one leaf (augment B) are
+     * added).
+     *
+     * <p>
+     * after running this test following exception is raised:
+     * java.lang.IllegalArgumentException: Augmentation allowed only in DataNodeContainer
+     * [ChoiceNodeImpl[qname=(ns:complex:json?revision=2014-08-11)choc11]]
+     */
+    @Test
+    public void caseNodeAugmentationInChoiceInContainer() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.caseNodeAugmentationInChoiceInContainer());
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        assertNotNull(cont1);
+
+        final JsonPrimitive lf15_21 = childPrimitive(cont1, "complexjson:lf15_21", "lf15_21");
+        assertNotNull(lf15_21);
+        final JsonPrimitive lf13 = childPrimitive(cont1, "complexjson:lf13", "lf13");
+        assertNotNull(lf13);
+        final JsonPrimitive lf15_11 = childPrimitive(cont1, "complexjson:lf15_11", "lf15_11");
+        assertNotNull(lf15_11);
+        final JsonPrimitive lf15_12 = childPrimitive(cont1, "complexjson:lf15_12", "lf15_12");
+        assertNotNull(lf15_12);
+
+        assertEquals("lf15_21 value", lf15_21.getAsString());
+        assertEquals("lf13 value", lf13.getAsString());
+        assertTrue("one two".equals(lf15_11.getAsString()) || "two one".equals(lf15_11.getAsString()));
+        assertEquals("complexjson:ident", lf15_12.getAsString());
+    }
+
+    /**
+     * tested case when case c11A in choice choc11 is augmented (two leaves (augment A) internally and one two leaves
+     * with the same names externally (augment B) are added).
+     *
+     * <p>
+     * after running this test following exception is raised:
+     * java.lang.IllegalArgumentException: Augmentation allowed only in DataNodeContainer
+     * [ChoiceNodeImpl[qname=(ns:complex:json?revision=2014-08-11)choc11]]
+     */
+    @Test
+    public void caseNodeExternalAugmentationInChoiceInContainer() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.caseNodeExternalAugmentationInChoiceInContainer());
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        assertNotNull(cont1);
+
+        final JsonPrimitive lf15_11Augment = childPrimitive(cont1, "complexjson-augmentation:lf15_11");
+        assertNotNull(lf15_11Augment);
+        final JsonPrimitive lf15_12Augment = childPrimitive(cont1, "complexjson-augmentation:lf15_12");
+        assertNotNull(lf15_12Augment);
+        final JsonPrimitive lf13 = childPrimitive(cont1, "complexjson:lf13", "lf13");
+        assertNotNull(lf13);
+        final JsonPrimitive lf15_11 = childPrimitive(cont1, "complexjson:lf15_11", "lf15_11");
+        assertNotNull(lf15_11);
+        final JsonPrimitive lf15_12 = childPrimitive(cont1, "complexjson:lf15_12", "lf15_12");
+        assertNotNull(lf15_12);
+
+        assertEquals("lf15_11 value from augmentation", lf15_11Augment.getAsString());
+        assertEquals("lf15_12 value from augmentation", lf15_12Augment.getAsString());
+        assertEquals("lf13 value", lf13.getAsString());
+        assertTrue("one two".equals(lf15_11.getAsString()) || "two one".equals(lf15_11.getAsString()));
+        assertEquals("complexjson:ident", lf15_12.getAsString());
+    }
+
+    /**
+     * augmentation of choice - adding new case.
+     *
+     * <p>
+     * after running this test following exception is raised:
+     * java.lang.IllegalArgumentException: Augmentation allowed only in DataNodeContainer
+     * [ChoiceNodeImpl[qname=(ns:complex:json?revision=2014-08-11)choc11]]
+     */
+    @Test
+    public void choiceNodeAugmentationInContainer() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.choiceNodeAugmentationInContainer());
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        assertNotNull(cont1);
+
+        final JsonPrimitive lf17 = childPrimitive(cont1, "complexjson:lf17", "lf17");
+        assertNotNull(lf17);
+        assertEquals("lf17 value", lf17.getAsString());
+    }
+
+    @Test
+    public void unkeyedNodeInContainer() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.unkeyedNodeInContainer());
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        assertNotNull(cont1);
+
+        final JsonArray lst12 = childArray(cont1, "complexjson:lst12", "lst12");
+        assertNotNull(lst12);
+
+        final Iterator<JsonElement> iterator = lst12.iterator();
+        assertTrue(iterator.hasNext());
+        final JsonElement lst12Entry1Raw = iterator.next();
+        assertFalse(iterator.hasNext());
+
+        assertTrue(lst12Entry1Raw instanceof JsonObject);
+        final JsonObject lst12Entry1 = (JsonObject) lst12Entry1Raw;
+        final JsonPrimitive lf121 = childPrimitive(lst12Entry1, "complexjson:lf121", "lf121");
+        assertNotNull(lf121);
+
+        assertEquals("lf121 value", lf121.getAsString());
+    }
+
+    @Test
+    public void emptyTypeTest() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(CONT1_WITH_EMPTYLEAF);
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        final JsonElement emptyObj = cont1.get("empty");
+        assertNotNull(emptyObj);
+        assertTrue(emptyObj instanceof JsonArray);
+        assertEquals(1, emptyObj.getAsJsonArray().size());
+        assertTrue(emptyObj.getAsJsonArray().get(0) instanceof JsonNull);
+    }
+
+    @Test
+    public void emptyNonPresenceContainerTest() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.topLevelContainer());
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        assertNull(cont1);
+    }
+
+    @Test
+    public void emptyNonPresenceContainerInContainerTest() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.emptyContainerInContainer());
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        assertNull(cont1);
+    }
+
+    @Test
+    public void emptyPresenceContainerTest() throws IOException {
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(
+            TestingNormalizedNodeStructuresCreator.cont2Node());
+        final JsonObject cont2 = resolveCont2(jsonOutput);
+        assertNotNull(cont2);
+    }
+
+    private static String normalizedNodeToJsonStreamTransformation(final NormalizedNode<?, ?> inputStructure)
+            throws IOException {
+        final Writer writer = new StringWriter();
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter(
+            lhotkaCodecFactory, SchemaPath.ROOT, null, JsonWriterFactory.createJsonWriter(writer, 2));
+        try (NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream)) {
+            nodeWriter.write(inputStructure);
+        }
+
+        return writer.toString();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/RFC7951YT1027Test.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/RFC7951YT1027Test.java
new file mode 100644 (file)
index 0000000..c8e1ff6
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class RFC7951YT1027Test extends AbstractYT1027Test {
+    private static JSONCodecFactory CODEC_FACTORY;
+
+    @BeforeClass
+    public static void createFactory() {
+        CODEC_FACTORY = JSONCodecFactorySupplier.RFC7951.getShared(SCHEMA_CONTEXT);
+    }
+
+    @AfterClass
+    public static void destroyFactory() {
+        CODEC_FACTORY = null;
+    }
+
+    @Test
+    public void testDecimalUnquotedParsing() throws IOException {
+        assertEquals(DECIMAL_DATA, fromJSON(UNQUOTED_DECIMAL));
+    }
+
+    @Test
+    public void testInt64UnquotedParsing() throws IOException {
+        assertEquals(INT64_DATA, fromJSON(UNQUOTED_INT64));
+    }
+
+    @Test
+    public void testUint64UnquotedParsing() throws IOException {
+        assertEquals(UINT64_DATA, fromJSON(UNQUOTED_UINT64));
+    }
+
+    @Override
+    JSONCodecFactory codecFactory() {
+        return CODEC_FACTORY;
+    }
+
+    @Override
+    Class<?> wrapperClass() {
+        return QuotedJSONCodec.class;
+    }
+
+    @Override
+    String expectedDecimal() {
+        return "{\n"
+                + "  \"yt1027:decimal\": \"1.1\"\n"
+                + "}";
+    }
+
+    @Override
+    String expectedInt64() {
+        return "{\n"
+                + "  \"yt1027:int64\": \"2\"\n"
+                + "}";
+    }
+
+    @Override
+    String expectedUint64() {
+        return "{\n"
+                + "  \"yt1027:uint64\": \"1\"\n"
+                + "}";
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/StreamToNormalizedNodeTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/StreamToNormalizedNodeTest.java
new file mode 100644 (file)
index 0000000..929237c
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
+
+import com.google.gson.stream.JsonReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.net.URISyntaxException;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.LoggingNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StreamToNormalizedNodeTest extends AbstractComplexJsonTest {
+    private static final Logger LOG = LoggerFactory.getLogger(StreamToNormalizedNodeTest.class);
+    private static String streamAsString;
+
+    @BeforeClass
+    public static void initialization() throws IOException, URISyntaxException {
+        streamAsString = loadTextFile(new File(StreamToNormalizedNodeTest.class.getResource(
+                "/complexjson/complex-json.json").toURI()));
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        streamAsString = null;
+    }
+
+    /**
+     * Demonstrates how to log events produced by a {@link JsonReader}.
+     */
+    @Test
+    public void ownStreamWriterImplementationDemonstration() throws IOException {
+        // GSON's JsonReader reading from the loaded string (our event source)
+        final JsonReader reader = new JsonReader(new StringReader(streamAsString));
+
+        // StreamWriter which outputs SLF4J events
+        final LoggingNormalizedNodeStreamWriter logWriter = new LoggingNormalizedNodeStreamWriter();
+
+        // JSON -> StreamWriter parser
+        try (JsonParserStream jsonHandler = JsonParserStream.create(logWriter, lhotkaCodecFactory)) {
+            // Process multiple readers, flush()/close() as needed
+            jsonHandler.parse(reader);
+        }
+    }
+
+    /**
+     * Demonstrates how to create an immutable NormalizedNode tree from a {@link JsonReader} and
+     * then writes the data back into string representation.
+     */
+    @Test
+    public void immutableNormalizedNodeStreamWriterDemonstration() throws IOException {
+        /*
+         * This is the parsing part
+         */
+        // This is where we will output the nodes
+        NormalizedNodeResult result = new NormalizedNodeResult();
+
+        // StreamWriter which attaches NormalizedNode under parent
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        // JSON -> StreamWriter parser
+        try (JsonParserStream handler = JsonParserStream.create(streamWriter, lhotkaCodecFactory)) {
+            handler.parse(new JsonReader(new StringReader(streamAsString)));
+        }
+
+        // Finally build the node
+        final NormalizedNode<?, ?> parsedData = result.getResult();
+        LOG.debug("Parsed NormalizedNodes: {}", parsedData);
+
+        /*
+         * This is the serialization part.
+         */
+        // We want to write the first child out
+        final DataContainerChild<? extends PathArgument, ?> firstChild =
+                (DataContainerChild<? extends PathArgument, ?>) parsedData;
+        LOG.debug("Serializing first child: {}", firstChild);
+
+        // String holder
+        final StringWriter writer = new StringWriter();
+
+        // StreamWriter which outputs JSON strings
+        // StreamWriter which outputs JSON strings
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter(
+            lhotkaCodecFactory, SchemaPath.ROOT, null, JsonWriterFactory.createJsonWriter(writer, 2));
+
+        // NormalizedNode -> StreamWriter
+        final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+
+        // Write multiple NormalizedNodes fluently, flush()/close() as needed
+        nodeWriter.write(firstChild).close();
+
+        // Just to put it somewhere
+        LOG.debug("Serialized JSON: {}", writer.toString());
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestUtils.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestUtils.java
new file mode 100644 (file)
index 0000000..6075231
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertTrue;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URISyntaxException;
+import javax.xml.stream.XMLStreamReader;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public final class TestUtils {
+    private TestUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    static String loadTextFile(final File file) throws IOException {
+        final FileReader fileReader = new FileReader(file);
+        final BufferedReader bufReader = new BufferedReader(fileReader);
+
+        String line = null;
+        final StringBuilder result = new StringBuilder();
+        while ((line = bufReader.readLine()) != null) {
+            result.append(line);
+        }
+        bufReader.close();
+        return result.toString();
+    }
+
+    static String loadTextFile(final String relativePath) throws IOException, URISyntaxException {
+        return loadTextFile(new File(TestUtils.class.getResource(relativePath).toURI()));
+    }
+
+    static void loadXmlToNormalizedNodes(final InputStream xmlInputStream, final NormalizedNodeResult result,
+            final SchemaContext schemaContext) throws Exception {
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(xmlInputStream);
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, schemaContext);
+        xmlParser.parse(reader);
+    }
+
+    static String normalizedNodesToJsonString(final NormalizedNode<?, ?> data, final SchemaContext schemaContext,
+            final SchemaPath rootPath) throws IOException {
+        final Writer writer = new StringWriter();
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter(
+                JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext), rootPath, null,
+                JsonWriterFactory.createJsonWriter(writer, 2));
+        final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+        nodeWriter.write(data);
+        nodeWriter.close();
+        final String serializationResult = writer.toString();
+        return serializationResult;
+    }
+
+    static JsonObject childObject(final JsonObject jsonObject, final String... names) {
+        for (final String name : names) {
+            final JsonObject childJsonObject = jsonObject.getAsJsonObject(name);
+            if (childJsonObject != null) {
+                return childJsonObject;
+            }
+        }
+        return null;
+    }
+
+    static JsonPrimitive childPrimitive(final JsonObject jsonObject, final String... names) {
+        for (final String name : names) {
+            final JsonPrimitive childJsonPrimitive = jsonObject.getAsJsonPrimitive(name);
+            if (childJsonPrimitive != null) {
+                return childJsonPrimitive;
+            }
+        }
+        return null;
+    }
+
+    static JsonArray childArray(final JsonObject jsonObject, final String... names) {
+        for (final String name : names) {
+            final JsonArray childJsonArray = jsonObject.getAsJsonArray(name);
+            if (childJsonArray != null) {
+                return childJsonArray;
+            }
+        }
+        return null;
+    }
+
+    static JsonObject resolveCont1(final String jsonOutput) {
+        final JsonElement rootElement = new JsonParser().parse(jsonOutput);
+        assertTrue(rootElement.isJsonObject());
+        final JsonObject rootObject = rootElement.getAsJsonObject();
+        final JsonObject cont1 = childObject(rootObject, "complexjson:cont1", "cont1");
+        return cont1;
+    }
+
+    static JsonObject resolveCont2(final String jsonOutput) {
+        final JsonElement rootElement = new JsonParser().parse(jsonOutput);
+        assertTrue(rootElement.isJsonObject());
+        final JsonObject rootObject = rootElement.getAsJsonObject();
+        final JsonObject cont2 = childObject(rootObject, "complexjson:cont2", "cont2");
+        return cont2;
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestingNormalizedNodeStructuresCreator.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestingNormalizedNodeStructuresCreator.java
new file mode 100644 (file)
index 0000000..e9a25f5
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.net.URI;
+import java.util.Arrays;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+
+public final class TestingNormalizedNodeStructuresCreator {
+    private static final QNameModule COMPLEX_JSON = QNameModule.create(URI.create("ns:complex:json"),
+        Revision.of("2014-08-11"));
+    private static final QNameModule COMPLEX_JSON_AUG = QNameModule.create(URI.create("ns:complex:json:augmentation"),
+        Revision.of("2014-08-14"));
+
+    private TestingNormalizedNodeStructuresCreator() {
+        throw new UnsupportedOperationException();
+    }
+
+    @SafeVarargs
+    static ContainerNode cont1Node(final DataContainerChild<?, ?>... children) {
+        return Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "cont1")))
+                .withValue(Arrays.asList(children))
+                .build();
+    }
+
+    static ContainerNode cont2Node() {
+        return Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "cont2")))
+                .build();
+    }
+
+    private static UnkeyedListNode lst12Node() {
+        return Builders.unkeyedListBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lst12")))
+                .withChild(lst12Entry1Node())
+                .build();
+    }
+
+    private static UnkeyedListEntryNode lst12Entry1Node() {
+        return Builders.unkeyedListEntryBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lst12")))
+                .withChild(Builders.leafBuilder()
+                    .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lf121")))
+                    .withValue("lf121 value").build())
+                .build();
+    }
+
+    private static ChoiceNode choc12Node() {
+        return Builders.choiceBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "choc12")))
+                .withChild(lf17Node())
+                .build();
+    }
+
+    protected static LeafNode<Object> lf17Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lf17")))
+                .withValue("lf17 value").build();
+    }
+
+    private static AugmentationNode externalAugmentC11AWithLf15_11AndLf15_12Node() {
+        return Builders.augmentationBuilder()
+                .withNodeIdentifier(new AugmentationIdentifier(ImmutableSet.of(
+                    QName.create(COMPLEX_JSON_AUG, "lf15_11"),
+                    QName.create(COMPLEX_JSON_AUG, "lf15_12"))))
+                .withChild(lf15_11NodeExternal())
+                .withChild(lf15_12NodeExternal())
+                .build();
+    }
+
+    private static LeafNode<Object> lf15_12NodeExternal() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON_AUG, "lf15_12")))
+                .withValue("lf15_12 value from augmentation")
+                .build();
+    }
+
+    private static LeafNode<Object> lf15_11NodeExternal() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON_AUG, "lf15_11")))
+                .withValue("lf15_11 value from augmentation")
+                .build();
+    }
+
+    @SafeVarargs
+    private static ChoiceNode choc11Node(final DataContainerChild<?, ?>... children) {
+        return Builders.choiceBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "choc11")))
+                .withValue(Arrays.asList(children))
+                // choc11Builder.addChild(lf13Node());
+                // choc11Builder.addChild(augmentChoc11_c11A_lf1511AndLf1512Children());
+                // choc11Builder.addChild(augmentChoc11_c11_lf1521Children());
+                .build();
+    }
+
+    private static LeafNode<Object> lf13Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lf13")))
+                .withValue("lf13 value").build();
+    }
+
+    private static AugmentationNode augmentC11AWithLf15_21Node() {
+        return Builders.augmentationBuilder().withNodeIdentifier(
+            new AugmentationIdentifier(ImmutableSet.of(QName.create(COMPLEX_JSON, "lf15_21"))))
+                .withChild(lf15_21Node()).build();
+    }
+
+    private static LeafNode<Object> lf15_21Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lf15_21")))
+                .withValue("lf15_21 value").build();
+    }
+
+    private static AugmentationNode augmentC11AWithLf15_11AndLf15_12Node() {
+        return Builders.augmentationBuilder()
+                .withNodeIdentifier(new AugmentationIdentifier(ImmutableSet.of(
+                    QName.create(COMPLEX_JSON, "lf15_11"),
+                    QName.create(COMPLEX_JSON, "lf15_12"))))
+                .withChild(lf15_11Node())
+                .withChild(lf15_12Node())
+                .build();
+    }
+
+    private static LeafNode<Object> lf15_12Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lf15_12")))
+                .withValue(QName.create(COMPLEX_JSON, "ident")).build();
+    }
+
+    private static LeafNode<Object> lf15_11Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lf15_11")))
+                .withValue(ImmutableSet.of("one", "two")).build();
+    }
+
+    private static AugmentationNode lf12_1Node() {
+        return Builders.augmentationBuilder()
+                .withNodeIdentifier(new AugmentationIdentifier(ImmutableSet.of(
+                    QName.create(COMPLEX_JSON, "lf12_1"),
+                    QName.create(COMPLEX_JSON, "lf12_2"))))
+                .withChild(Builders.leafBuilder()
+                    .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lf12_1")))
+                    .withValue("lf12 value").build())
+                .build();
+    }
+
+    private static MapNode childLst11() {
+        return Builders.mapBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lst11")))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                    NodeIdentifierWithPredicates.of(QName.create(COMPLEX_JSON, "lst11"), ImmutableMap.of(
+                        QName.create(COMPLEX_JSON, "key111"), "key111 value",
+                        QName.create(COMPLEX_JSON, "lf111"), "lf111 value")))
+                    .withChild(Builders.leafBuilder()
+                        .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "key111")))
+                        .withValue("key111 value").build())
+                    .withChild(Builders.leafBuilder()
+                        .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lf112")))
+                        .withValue(lf112Value()).build())
+                    .withChild(Builders.leafBuilder()
+                        .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lf113")))
+                        .withValue("lf113 value").build())
+                    .withChild(Builders.leafBuilder()
+                        .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lf111")))
+                        .withValue("lf111 value").build())
+                    .build())
+                .build();
+    }
+
+    private static Object lf112Value() {
+        return YangInstanceIdentifier.create(
+                new NodeIdentifier(QName.create(COMPLEX_JSON, "cont1")),
+                new NodeIdentifier(QName.create(COMPLEX_JSON, "lflst11")),
+                new NodeWithValue<>(QName.create(COMPLEX_JSON, "lflst11"),"foo")
+        );
+    }
+
+    private static LeafSetNode<?> childLflst11() {
+        return Builders.leafSetBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lflst11")))
+                .withChild(Builders.leafSetEntryBuilder()
+                    .withNodeIdentifier(new NodeWithValue<>(QName.create(COMPLEX_JSON, "lflst11"), "lflst11 value1"))
+                    .withValue("lflst11 value1").build())
+                .withChild(Builders.leafSetEntryBuilder()
+                    .withNodeIdentifier(new NodeWithValue<>(QName.create(COMPLEX_JSON, "lflst11"), "lflst11 value2"))
+                    .withValue("lflst11 value2").build())
+                .build();
+    }
+
+    private static LeafSetNode<?> childLflst11Multiline() {
+        return Builders.leafSetBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lflst11")))
+                .withChild(Builders.leafSetEntryBuilder()
+                    .withNodeIdentifier(new NodeWithValue<>(QName.create(COMPLEX_JSON, "lflst11"),
+                            "lflst11 value1\nanother line 1"))
+                    .withValue("lflst11 value1\nanother line 1").build())
+                .withChild(Builders.leafSetEntryBuilder()
+                    .withNodeIdentifier(new NodeWithValue<>(QName.create(COMPLEX_JSON, "lflst11"),
+                            "lflst11 value2\r\nanother line 2"))
+                    .withValue("lflst11 value2\r\nanother line 2").build())
+                .build();
+    }
+
+    public static ContainerNode leafNodeInContainer() {
+        return cont1Node(Builders.leafBuilder()
+            .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "lf11")))
+            .withValue(453).build());
+    }
+
+    public static ContainerNode leafListNodeInContainer() {
+        return cont1Node(childLflst11());
+    }
+
+    public static ContainerNode leafListNodeInContainerMultiline() {
+        return cont1Node(childLflst11Multiline());
+    }
+
+    public static ContainerNode keyedListNodeInContainer() {
+        return cont1Node(childLst11());
+    }
+
+    public static ContainerNode leafNodeViaAugmentationInContainer() {
+        return cont1Node(lf12_1Node());
+    }
+
+    public static ContainerNode choiceNodeInContainer() {
+        return cont1Node(choc11Node(lf13Node()));
+    }
+
+    /**
+     * choc11 contains lf13, lf15_11 and lf15_12 are added via external augmentation.
+     */
+    public static ContainerNode caseNodeAugmentationInChoiceInContainer() {
+        return cont1Node(choc11Node(augmentC11AWithLf15_11AndLf15_12Node(), lf13Node(), augmentC11AWithLf15_21Node()));
+    }
+
+    public static ContainerNode caseNodeExternalAugmentationInChoiceInContainer() {
+        return cont1Node(choc11Node(lf13Node(), augmentC11AWithLf15_11AndLf15_12Node(),
+            externalAugmentC11AWithLf15_11AndLf15_12Node()));
+    }
+
+    public static ContainerNode choiceNodeAugmentationInContainer() {
+        return cont1Node(choc12Node());
+    }
+
+    public static ContainerNode unkeyedNodeInContainer() {
+        return cont1Node(lst12Node());
+    }
+
+    public static ContainerNode topLevelContainer() {
+        return cont1Node();
+    }
+
+    public static ContainerNode emptyContainerInContainer() {
+        return cont1Node(Builders.augmentationBuilder()
+                .withNodeIdentifier(new AugmentationIdentifier(ImmutableSet.of(QName.create(COMPLEX_JSON, "cont11"))))
+                .withChild(Builders.containerBuilder()
+                    .withNodeIdentifier(new NodeIdentifier(QName.create(COMPLEX_JSON, "cont11")))
+                    .build())
+                .build());
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YT1029Test.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YT1029Test.java
new file mode 100644 (file)
index 0000000..13cfd8f
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public class YT1029Test extends AbstractComplexJsonTest {
+    @Test
+    public void testMultipleRootChildren() throws IOException {
+        final Writer writer = new StringWriter();
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter(
+            lhotkaCodecFactory, SchemaPath.ROOT, null, JsonWriterFactory.createJsonWriter(writer, 2));
+        try (NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream)) {
+            nodeWriter.write(CONT1_WITH_EMPTYLEAF);
+            nodeWriter.write(CONT1_WITH_EMPTYLEAF);
+        }
+
+        assertEquals("{\n"
+                + "  \"complexjson:cont1\": {\n"
+                + "    \"empty\": [\n"
+                + "      null\n"
+                + "    ]\n"
+                + "  },\n"
+                + "  \"complexjson:cont1\": {\n"
+                + "    \"empty\": [\n"
+                + "      null\n"
+                + "    ]\n"
+                + "  }\n"
+                + "}", writer.toString());
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YangModeledAnyXmlSupportTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YangModeledAnyXmlSupportTest.java
new file mode 100644 (file)
index 0000000..e6f5bc9
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.gson.stream.JsonReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URISyntaxException;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.xml.sax.SAXException;
+
+public class YangModeledAnyXmlSupportTest {
+
+    private static SchemaContext schemaContext;
+    private static JSONCodecFactory lhotkaCodecFactory;
+    private static ContainerNode data;
+
+    @BeforeClass
+    public static void init() throws XMLStreamException, URISyntaxException, IOException, ParserConfigurationException,
+            SAXException {
+        schemaContext = YangParserTestUtils.parseYangResourceDirectory("/yang-modeled-anyxml/yang");
+        lhotkaCodecFactory = JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext);
+
+        final Module bazModule = schemaContext.findModules("baz").iterator().next();
+        final ContainerSchemaNode bazCont = (ContainerSchemaNode) bazModule.findDataChildByName(
+                QName.create(bazModule.getQNameModule(), "baz")).get();
+
+        final InputStream resourceAsStream = YangModeledAnyXmlSupportTest.class.getResourceAsStream(
+                "/yang-modeled-anyxml/xml/baz.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, bazCont);
+        xmlParser.parse(reader);
+
+        assertNotNull(result.getResult());
+        assertTrue(result.getResult() instanceof ContainerNode);
+        data = (ContainerNode) result.getResult();
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        lhotkaCodecFactory = null;
+        schemaContext = null;
+        data = null;
+    }
+
+    @Test
+    public void jsonToNormalizedNodesTest() throws IOException, URISyntaxException, SAXException {
+        final String inputJson = loadTextFile("/yang-modeled-anyxml/json/baz.json");
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory);
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+
+        assertEquals(data, transformedInput);
+    }
+
+    @Test
+    public void normalizedNodesToJsonTest() throws IOException, URISyntaxException, SAXException {
+        final DataContainerChild<? extends PathArgument, ?> baz = data;
+
+        final Writer writer = new StringWriter();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, baz);
+
+        final JsonParser parser = new JsonParser();
+        final JsonElement serializedJson = parser.parse(jsonOutput);
+        final JsonElement expextedJson = parser.parse(new FileReader(new File(getClass().getResource(
+                "/yang-modeled-anyxml/json/baz.json").toURI())));
+
+        assertEquals(expextedJson, serializedJson);
+    }
+
+    private static String normalizedNodeToJsonStreamTransformation(final Writer writer,
+            final NormalizedNode<?, ?> inputStructure) throws IOException {
+
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter(
+            lhotkaCodecFactory, SchemaPath.ROOT, null, JsonWriterFactory.createJsonWriter(writer, 2));
+        final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+        nodeWriter.write(inputStructure);
+
+        nodeWriter.close();
+        return writer.toString();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug-4501/json/foo-correct.json b/yang/yang-data-codec-gson/src/test/resources/bug-4501/json/foo-correct.json
new file mode 100644 (file)
index 0000000..a650d28
--- /dev/null
@@ -0,0 +1,16 @@
+{
+    "hop": [
+        {
+            "address": "192.0.2.1",
+            "lrs-bits": "lookup rloc-probe strict"
+        },
+        {
+            "address": "192.0.2.2",
+            "lrs-bits": "lookup rloc-probe"
+        },
+        {
+            "address": "192.0.2.3",
+            "lrs-bits": "lookup rloc-probe strict"
+        }
+    ]
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug-4501/json/foo-incorrect.json b/yang/yang-data-codec-gson/src/test/resources/bug-4501/json/foo-incorrect.json
new file mode 100644 (file)
index 0000000..e2b3b4c
--- /dev/null
@@ -0,0 +1,16 @@
+{
+    "hop": [
+        {
+            "address": "192.0.2.1",
+            "lrs-bits": ["lookup", "rloc-probe", "strict"]
+        },
+        {
+            "address": "192.0.2.2",
+            "lrs-bits": ["lookup", "rloc-probe"]
+        },
+        {
+            "address": "192.0.2.3",
+            "lrs-bits": ["lookup", "rloc-probe", "strict"]
+        }
+    ]
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug-4501/yang/foo.yang b/yang/yang-data-codec-gson/src/test/resources/bug-4501/yang/foo.yang
new file mode 100644 (file)
index 0000000..8a1d060
--- /dev/null
@@ -0,0 +1,30 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    list hop {
+        leaf address {
+            type string;
+        }
+
+        leaf lrs-bits {
+            type bits {
+                bit lookup {
+                    description
+                                        "Lookup bit.";
+                }
+                bit rloc-probe {
+                    description
+                                        "RLOC-probe bit.";
+                }
+                bit strict {
+                    description
+                                        "Strict bit.";
+                }
+            }
+            description
+                        "Flag bits per hop.";
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug-4969/json/foo.json b/yang/yang-data-codec-gson/src/test/resources/bug-4969/json/foo.json
new file mode 100644 (file)
index 0000000..0cb3bc4
--- /dev/null
@@ -0,0 +1,8 @@
+{
+    "foo:root" : {
+        "ref1": "a",
+        "ref2": "a b",
+        "ref3": "a b c",
+        "ref4": "a b c d"
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug-4969/yang/bar.yang b/yang/yang-data-codec-gson/src/test/resources/bug-4969/yang/bar.yang
new file mode 100644 (file)
index 0000000..b594d3d
--- /dev/null
@@ -0,0 +1,59 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    revision "2016-01-22" {
+        description "Initial version";
+    }
+
+    typedef ref1 {
+        type ref1-2;
+    }
+
+    typedef ref2 {
+        type ref2-2;
+    }
+
+    typedef ref3 {
+        type ref3-2;
+    }
+
+    typedef ref1-2 {
+        type leafref {
+            path "/root/l1";
+        }
+    }
+
+    typedef ref2-2 {
+        type leafref {
+            path "/root/l2";
+        }
+    }
+
+    typedef ref3-2 {
+        type leafref {
+            path "/root/l3";
+        }
+    }
+
+    container root {
+        leaf l1 {
+            type bits {
+                bit a;
+                bit b;
+                bit c;
+                bit d;
+            }
+        }
+        leaf l2 {
+            type leafref {
+                path "/root/l1";
+            }
+        }
+        leaf l3 {
+            type leafref {
+                path "../l1";
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug-4969/yang/foo.yang b/yang/yang-data-codec-gson/src/test/resources/bug-4969/yang/foo.yang
new file mode 100644 (file)
index 0000000..ad3ac16
--- /dev/null
@@ -0,0 +1,27 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    import bar { prefix bar; revision-date 2016-01-22; }
+
+    revision "2016-01-22" {
+        description "Initial version";
+    }
+
+    container root {
+        leaf ref1 {
+            type bar:ref1;
+        }
+        leaf ref2 {
+            type bar:ref2;
+        }
+        leaf ref3 {
+            type bar:ref3;
+        }
+        leaf ref4 {
+            type leafref {
+                path "/bar:root/bar:l1";
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug-6112/json/data-identityref.json b/yang/yang-data-codec-gson/src/test/resources/bug-6112/json/data-identityref.json
new file mode 100644 (file)
index 0000000..76d2f04
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "union:identityref:test:root": {
+        "leaf-value": "ident-one"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug-6112/json/data-uint8.json b/yang/yang-data-codec-gson/src/test/resources/bug-6112/json/data-uint8.json
new file mode 100644 (file)
index 0000000..7e47fbc
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "union:identityref:test:root": {
+        "leaf-value": "1"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug-6112/yang/union-with-identityref.yang b/yang/yang-data-codec-gson/src/test/resources/bug-6112/yang/union-with-identityref.yang
new file mode 100644 (file)
index 0000000..6dbe6e5
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016 Intel Corporation and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+module union-with-identityref {
+    yang-version 1;
+    namespace "union:identityref:test";
+    prefix "unionidentityreftest";
+
+    description "test union with identityref";
+
+    revision "2016-07-12";
+
+    identity ident-base;
+    identity ident-one {
+        base ident-base;
+    }
+
+    typedef union-type {
+        type union {
+            type uint8;
+            type identityref {
+                base ident-base;
+            }
+        }
+    }
+
+    container root {
+        leaf leaf-value {
+            type union-type;
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug5446/json/foo.json b/yang/yang-data-codec-gson/src/test/resources/bug5446/json/foo.json
new file mode 100644 (file)
index 0000000..a62eb74
--- /dev/null
@@ -0,0 +1,5 @@
+{
+   "foo:root":{
+         "ip-address":"fwAAAQ=="
+   }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug5446/yang/foo.yang b/yang/yang-data-codec-gson/src/test/resources/bug5446/yang/foo.yang
new file mode 100644 (file)
index 0000000..57ae347
--- /dev/null
@@ -0,0 +1,33 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix "foo";
+
+    revision "2015-11-05" {
+    }
+
+    typedef ipv4-address-binary {
+        type binary {
+            length "4";
+        }
+    }
+
+    typedef ipv6-address-binary {
+        type binary {
+            length "16";
+        }
+    }
+
+    typedef ip-address-binary {
+        type union {
+            type ipv4-address-binary;
+            type ipv6-address-binary;
+        }
+    }
+
+    container root {
+        leaf ip-address {
+            type ip-address-binary;
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug7246/json/expected-output.json b/yang/yang-data-codec-gson/src/test/resources/bug7246/json/expected-output.json
new file mode 100644 (file)
index 0000000..6dba63e
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "my-name": {
+        "my-name": "my-value"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug7246/yang/rpc-test.yang b/yang/yang-data-codec-gson/src/test/resources/bug7246/yang/rpc-test.yang
new file mode 100644 (file)
index 0000000..004183e
--- /dev/null
@@ -0,0 +1,37 @@
+module rpc-test {
+    namespace my-namespace;
+    prefix p;
+
+    feature my-name;
+
+    identity my-name;
+
+    extension my-name;
+
+    typedef my-name {
+        type string;
+    }
+
+    grouping my-name {
+        leaf my-name {
+            type my-name;
+        }
+    }
+
+    rpc my-name {
+        input {
+            container my-name {
+                leaf my-name {
+                    type my-name;
+                }
+            }
+        }
+        output {
+            container my-name {
+                leaf my-name {
+                    type my-name;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8083/json/baz.json b/yang/yang-data-codec-gson/src/test/resources/bug8083/json/baz.json
new file mode 100644 (file)
index 0000000..177a07a
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "top-cont": {
+    "keyed-list":[
+      {
+        "empty-key-leaf": [null],
+        "regular-leaf": 150
+      }
+    ],
+
+    "iid-leaf": "/baz:top-cont/baz:keyed-list[baz:empty-key-leaf='[null]']/baz:regular-leaf"
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8083/json/foo.json b/yang/yang-data-codec-gson/src/test/resources/bug8083/json/foo.json
new file mode 100644 (file)
index 0000000..3c6a525
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "top": {
+    "foo": "/example-foomod:top/foo-list[name='key-value']/example-barmod:bar-container/bar-leaf"
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8083/json/foobar.json b/yang/yang-data-codec-gson/src/test/resources/bug8083/json/foobar.json
new file mode 100644 (file)
index 0000000..f8a0d0d
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "top-cont": {
+    "keyed-list":[
+      {
+        "iid-key-leaf": "/foobar:top-cont/foobar:leaf-b",
+        "regular-leaf": 150
+      }
+    ],
+
+    "iid-leaf":
+      "/foobar:top-cont/foobar:keyed-list[foobar:iid-key-leaf='/foobar:top-cont/foobar:leaf-b']/foobar:regular-leaf",
+    "leaf-b": 200
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8083/json/zab.json b/yang/yang-data-codec-gson/src/test/resources/bug8083/json/zab.json
new file mode 100644 (file)
index 0000000..3016a8b
--- /dev/null
@@ -0,0 +1,12 @@
+{
+  "top-cont": {
+    "keyed-list":[
+      {
+        "identityref-key-leaf": "derived-id",
+        "regular-leaf": 150
+      }
+    ],
+
+    "iid-leaf": "/zab:top-cont/zab:keyed-list[zab:identityref-key-leaf='derived-id']/zab:regular-leaf"
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8083/yang/bar.yang b/yang/yang-data-codec-gson/src/test/resources/bug8083/yang/bar.yang
new file mode 100644 (file)
index 0000000..7e6ad76
--- /dev/null
@@ -0,0 +1,17 @@
+module example-barmod {
+
+    namespace "http://example.com/barmod";
+    prefix "barmod";
+
+    import example-foomod {
+        prefix "foomod";
+    }
+
+    augment "/foomod:top/foomod:foo-list" {
+        container bar-container {
+            leaf bar-leaf {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8083/yang/baz.yang b/yang/yang-data-codec-gson/src/test/resources/bug8083/yang/baz.yang
new file mode 100644 (file)
index 0000000..0690b1f
--- /dev/null
@@ -0,0 +1,23 @@
+module baz {
+
+    namespace baz-ns;
+    prefix baz-prefix;
+
+    container top-cont {
+        list keyed-list {
+            key empty-key-leaf;
+
+            leaf empty-key-leaf {
+                type empty;
+            }
+
+            leaf regular-leaf {
+                type int32;
+            }
+        }
+
+        leaf iid-leaf {
+            type instance-identifier;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8083/yang/foo.yang b/yang/yang-data-codec-gson/src/test/resources/bug8083/yang/foo.yang
new file mode 100644 (file)
index 0000000..478bfc7
--- /dev/null
@@ -0,0 +1,19 @@
+module example-foomod {
+
+    namespace "http://example.com/foomod";
+    prefix "foomod";
+
+    container top {
+        leaf foo {
+            type instance-identifier;
+        }
+
+        list foo-list {
+            key name;
+
+            leaf name {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8083/yang/foobar.yang b/yang/yang-data-codec-gson/src/test/resources/bug8083/yang/foobar.yang
new file mode 100644 (file)
index 0000000..ec42bab
--- /dev/null
@@ -0,0 +1,27 @@
+module foobar {
+
+    namespace foobar-ns;
+    prefix foobar-prefix;
+
+    container top-cont {
+        list keyed-list {
+            key iid-key-leaf;
+
+            leaf iid-key-leaf {
+                type instance-identifier;
+            }
+
+            leaf regular-leaf {
+                type int32;
+            }
+        }
+
+        leaf iid-leaf {
+            type instance-identifier;
+        }
+
+        leaf leaf-b {
+            type int32;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8083/yang/zab.yang b/yang/yang-data-codec-gson/src/test/resources/bug8083/yang/zab.yang
new file mode 100644 (file)
index 0000000..5af9529
--- /dev/null
@@ -0,0 +1,31 @@
+module zab {
+
+    namespace zab-ns;
+    prefix zab-prefix;
+
+    identity base-id;
+
+    identity derived-id {
+        base base-id;
+    }
+
+    container top-cont {
+        list keyed-list {
+            key identityref-key-leaf;
+
+            leaf identityref-key-leaf {
+                type identityref {
+                    base base-id;
+                }
+            }
+
+            leaf regular-leaf {
+                type int32;
+            }
+        }
+
+        leaf iid-leaf {
+            type instance-identifier;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8927/json/complex_array.json b/yang/yang-data-codec-gson/src/test/resources/bug8927/json/complex_array.json
new file mode 100644 (file)
index 0000000..0d14d97
--- /dev/null
@@ -0,0 +1,23 @@
+{
+    "bug8927:foo": {
+        "alarm-information": {
+            "alarm-detail": [{
+                "alarm-time": "2017-08-02 19:03:44 UTC",
+                "alarm-class": "Minor",
+                "alarm-description": [{
+                    "test": 123
+                }, {
+                "test": 456
+                }],
+                "alarm-short-description": "gnf-creation",
+                "alarm-type": "License"
+            }, {
+            "alarm-time": "2017-07-25 16:04:31 UTC",
+            "alarm-class": ["Minor", "Minor1"],
+            "alarm-description": "VMHost RE 0 host application failed",
+            "alarm-short-description": "VMHost RE 0 host ap",
+            "alarm-type": "Chassis"
+            }]
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8927/json/complex_array_with_other_elements.json b/yang/yang-data-codec-gson/src/test/resources/bug8927/json/complex_array_with_other_elements.json
new file mode 100644 (file)
index 0000000..ff4ef05
--- /dev/null
@@ -0,0 +1,122 @@
+{
+    "bug8927:foo": {
+        "alarm-information": {
+            "alarm-summary": {
+                "active-alarm-count": ""
+            },
+            "alarm-detail":[ {
+                "alarm-time": "2017-08-02 19:03:44 UTC",
+                "alarm-class": "Minor",
+                "alarm-description": "desc",
+                "alarm-short-description": "gnf-creation",
+                "alarm-type": "License"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Minor",
+                "alarm-description": "VMHost RE 0 host application failed",
+                "alarm-short-description": "VMHost RE 0 host ap",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Major",
+                "alarm-description": "PDM 0 incompatible with chassis type",
+                "alarm-short-description": "PDM 0 incompatible",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Major",
+                "alarm-description": "PDM 1 incompatible with chassis type",
+                "alarm-short-description": "PDM 1 incompatible",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Major",
+                "alarm-description": "PDM 2 incompatible with chassis type",
+                "alarm-short-description": "PDM 2 incompatible",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Major",
+                "alarm-description": "PDM 3 incompatible with chassis type",
+                "alarm-short-description": "PDM 3 incompatible",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Major",
+                "alarm-description": "No Input Feed Selected for PSM 1",
+                "alarm-short-description": "No Input Feed Selec",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Major",
+                "alarm-description": "No Input Feed Selected for PSM 2",
+                "alarm-short-description": "No Input Feed Selec",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Major",
+                "alarm-description": "No Input Feed Selected for PSM 3",
+                "alarm-short-description": "No Input Feed Selec",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Major",
+                "alarm-description": "No Input Feed Selected for PSM 4",
+                "alarm-short-description": "No Input Feed Selec",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Major",
+                "alarm-description": "No Input Feed Selected for PSM 10",
+                "alarm-short-description": "No Input Feed Selec",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Major",
+                "alarm-description": "No Input Feed Selected for PSM 11",
+                "alarm-short-description": "No Input Feed Selec",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Major",
+                "alarm-description": "No Input Feed Selected for PSM 12",
+                "alarm-short-description": "No Input Feed Selec",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Major",
+                "alarm-description": "No Input Feed Selected for PSM 13",
+                "alarm-short-description": "No Input Feed Selec",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Minor",
+                "alarm-description": "VMHost RE 1 host application failed",
+                "alarm-short-description": "VMHost RE 1 host ap",
+                "alarm-type": "Chassis"
+            },
+            {
+                "alarm-time": "2017-07-25 16:04:31 UTC",
+                "alarm-class": "Minor",
+                "alarm-description": "Check plane 1 Fabric Chip",
+                "alarm-short-description": "Check plane 1 FCHIP",
+                "alarm-type": "Chassis"
+            }
+            ]
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8927/json/composite.json b/yang/yang-data-codec-gson/src/test/resources/bug8927/json/composite.json
new file mode 100644 (file)
index 0000000..a9eb333
--- /dev/null
@@ -0,0 +1,41 @@
+{
+    "complexjson:cont1": {
+        "lf14-any": {
+            "anyxml-object-a": {
+                "data-a1": 10,
+                "data-a2": 11
+            },
+            "anyxml-object-b": {
+                "child-object-b1": {
+                    "data-b1": 5.5
+                },
+                "child-object-b2": {
+                    "data-b2": "b2-val"
+                }
+            }
+        },
+        "lf12-any": {
+            "array-element": [true, {
+                "baz": {
+                    "array-element": [120, "str-val", false]
+                }
+            }, {
+            "foo": null
+            }]
+        },
+        "lf13-any": {
+            "anyxml-array-a": {
+                "array-element": [{
+                    "foo": true
+                }, 10, {
+                "bar": false
+                }]
+            },
+            "anyxml-array-b": {
+                "array-element": [1, 2, {
+                    "array-element": [4, 5]
+                }, 7]
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8927/json/expected.json b/yang/yang-data-codec-gson/src/test/resources/bug8927/json/expected.json
new file mode 100644 (file)
index 0000000..9496672
--- /dev/null
@@ -0,0 +1,121 @@
+{
+  "bug8927:foo": {
+    "alarm-information": {
+      "alarm-summary": {
+        "active-alarm-count": ""
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-08-02 19:03:44 UTC",
+        "alarm-class": "Minor",
+        "alarm-description": "desc",
+        "alarm-short-description": "gnf-creation",
+        "alarm-type": "License"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Minor",
+        "alarm-description": "VMHost RE 0 host application failed",
+        "alarm-short-description": "VMHost RE 0 host ap",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Major",
+        "alarm-description": "PDM 0 incompatible with chassis type",
+        "alarm-short-description": "PDM 0 incompatible",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Major",
+        "alarm-description": "PDM 1 incompatible with chassis type",
+        "alarm-short-description": "PDM 1 incompatible",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Major",
+        "alarm-description": "PDM 2 incompatible with chassis type",
+        "alarm-short-description": "PDM 2 incompatible",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Major",
+        "alarm-description": "PDM 3 incompatible with chassis type",
+        "alarm-short-description": "PDM 3 incompatible",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Major",
+        "alarm-description": "No Input Feed Selected for PSM 1",
+        "alarm-short-description": "No Input Feed Selec",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Major",
+        "alarm-description": "No Input Feed Selected for PSM 2",
+        "alarm-short-description": "No Input Feed Selec",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Major",
+        "alarm-description": "No Input Feed Selected for PSM 3",
+        "alarm-short-description": "No Input Feed Selec",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Major",
+        "alarm-description": "No Input Feed Selected for PSM 4",
+        "alarm-short-description": "No Input Feed Selec",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Major",
+        "alarm-description": "No Input Feed Selected for PSM 10",
+        "alarm-short-description": "No Input Feed Selec",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Major",
+        "alarm-description": "No Input Feed Selected for PSM 11",
+        "alarm-short-description": "No Input Feed Selec",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Major",
+        "alarm-description": "No Input Feed Selected for PSM 12",
+        "alarm-short-description": "No Input Feed Selec",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Major",
+        "alarm-description": "No Input Feed Selected for PSM 13",
+        "alarm-short-description": "No Input Feed Selec",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Minor",
+        "alarm-description": "VMHost RE 1 host application failed",
+        "alarm-short-description": "VMHost RE 1 host ap",
+        "alarm-type": "Chassis"
+      },
+      "alarm-detail": {
+        "alarm-time": "2017-07-25 16:04:31 UTC",
+        "alarm-class": "Minor",
+        "alarm-description": "Check plane 1 Fabric Chip",
+        "alarm-short-description": "Check plane 1 FCHIP",
+        "alarm-type": "Chassis"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8927/json/scalar_array.json b/yang/yang-data-codec-gson/src/test/resources/bug8927/json/scalar_array.json
new file mode 100644 (file)
index 0000000..a2181ae
--- /dev/null
@@ -0,0 +1,10 @@
+{
+    "bug8927:foo": {
+        "alarm-information": {
+            "alarm-summary": {
+                "active-alarm-count": ""
+            },
+            "elmt": [1, 2, 3]
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8927/json/scalar_array_with_other_elements.json b/yang/yang-data-codec-gson/src/test/resources/bug8927/json/scalar_array_with_other_elements.json
new file mode 100644 (file)
index 0000000..56a03fe
--- /dev/null
@@ -0,0 +1,11 @@
+{
+    "bug8927:foo": {
+        "alarm-information": {
+            "alarm-summary": {
+                "active-alarm-count": ""
+            },
+            "elmt": [1, 2, 3],
+            "test": 123
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8927/xml/complex_array.xml b/yang/yang-data-codec-gson/src/test/resources/bug8927/xml/complex_array.xml
new file mode 100644 (file)
index 0000000..ce49a3f
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+<foo xmlns="bug8927.test">
+    <alarm-information xmlns="bug8927.test">
+        <alarm-detail>
+            <alarm-time>
+                2017-08-02 19:03:44 UTC
+            </alarm-time>
+            <alarm-class>Minor</alarm-class>
+            <alarm-description>
+               <test>123</test>
+            </alarm-description>
+            <alarm-description>
+               <test>456</test>
+            </alarm-description>
+            <alarm-short-description>gnf-creation</alarm-short-description>
+            <alarm-type>License</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Minor</alarm-class>
+            <alarm-class>Minor1</alarm-class>
+            <alarm-description>VMHost RE 0 host application failed</alarm-description>
+            <alarm-short-description>VMHost RE 0 host ap</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+    </alarm-information>
+</foo>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8927/xml/complex_array_with_other_elements.xml b/yang/yang-data-codec-gson/src/test/resources/bug8927/xml/complex_array_with_other_elements.xml
new file mode 100644 (file)
index 0000000..034c9fe
--- /dev/null
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+<foo xmlns="bug8927.test">
+    <alarm-information xmlns="bug8927.test">
+        <alarm-summary>
+            <active-alarm-count/>
+        </alarm-summary>
+
+        <alarm-detail>
+            <alarm-time>
+                2017-08-02 19:03:44 UTC
+            </alarm-time>
+            <alarm-class>Minor</alarm-class>
+            <alarm-description>desc</alarm-description>
+            <alarm-short-description>gnf-creation</alarm-short-description>
+            <alarm-type>License</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Minor</alarm-class>
+            <alarm-description>VMHost RE 0 host application failed</alarm-description>
+            <alarm-short-description>VMHost RE 0 host ap</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>PDM 0 incompatible with chassis type</alarm-description>
+            <alarm-short-description>PDM 0 incompatible</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>PDM 1 incompatible with chassis type</alarm-description>
+            <alarm-short-description>PDM 1 incompatible</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>PDM 2 incompatible with chassis type</alarm-description>
+            <alarm-short-description>PDM 2 incompatible</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>PDM 3 incompatible with chassis type</alarm-description>
+            <alarm-short-description>PDM 3 incompatible</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 1</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 2</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 3</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 4</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 10</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 11</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 12</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 13</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Minor</alarm-class>
+            <alarm-description>VMHost RE 1 host application failed</alarm-description>
+            <alarm-short-description>VMHost RE 1 host ap</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Minor</alarm-class>
+            <alarm-description>Check plane 1 Fabric Chip</alarm-description>
+            <alarm-short-description>Check plane 1 FCHIP</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+    </alarm-information>
+</foo>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8927/xml/input.xml b/yang/yang-data-codec-gson/src/test/resources/bug8927/xml/input.xml
new file mode 100644 (file)
index 0000000..2d17641
--- /dev/null
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+<foo xmlns="bug8927.test">
+    <alarm-information xmlns="bug8927.test">
+        <alarm-summary>
+            <active-alarm-count/>
+        </alarm-summary>
+        <alarm-detail>
+            <alarm-time>
+                2017-08-02 19:03:44 UTC
+            </alarm-time>
+            <alarm-class>Minor</alarm-class>
+            <alarm-description>desc</alarm-description>
+            <alarm-short-description>gnf-creation</alarm-short-description>
+            <alarm-type>License</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Minor</alarm-class>
+            <alarm-description>VMHost RE 0 host application failed</alarm-description>
+            <alarm-short-description>VMHost RE 0 host ap</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>PDM 0 incompatible with chassis type</alarm-description>
+            <alarm-short-description>PDM 0 incompatible</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>PDM 1 incompatible with chassis type</alarm-description>
+            <alarm-short-description>PDM 1 incompatible</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>PDM 2 incompatible with chassis type</alarm-description>
+            <alarm-short-description>PDM 2 incompatible</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>PDM 3 incompatible with chassis type</alarm-description>
+            <alarm-short-description>PDM 3 incompatible</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 1</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 2</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 3</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 4</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 10</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 11</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 12</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Major</alarm-class>
+            <alarm-description>No Input Feed Selected for PSM 13</alarm-description>
+            <alarm-short-description>No Input Feed Selec</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Minor</alarm-class>
+            <alarm-description>VMHost RE 1 host application failed</alarm-description>
+            <alarm-short-description>VMHost RE 1 host ap</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+        <alarm-detail>
+            <alarm-time>
+                2017-07-25 16:04:31 UTC
+            </alarm-time>
+            <alarm-class>Minor</alarm-class>
+            <alarm-description>Check plane 1 Fabric Chip</alarm-description>
+            <alarm-short-description>Check plane 1 FCHIP</alarm-short-description>
+            <alarm-type>Chassis</alarm-type>
+        </alarm-detail>
+    </alarm-information>
+</foo>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8927/xml/input4.xml b/yang/yang-data-codec-gson/src/test/resources/bug8927/xml/input4.xml
new file mode 100644 (file)
index 0000000..29c1432
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<root xmlns="complexjson">
+    <cont1 xmlns="ns:complex:json" >
+        <lf12-any>true</lf12-any>
+        <lf12-any>
+            <baz>120</baz>
+            <baz>str-val</baz>
+            <baz>false</baz>
+        </lf12-any>
+        <lf12-any>
+            <foo />
+        </lf12-any>
+        <lf13-any>
+            <anyxml-array-a>
+                <foo>true</foo>
+            </anyxml-array-a>
+            <anyxml-array-a>10</anyxml-array-a>
+            <anyxml-array-a>
+                <bar>false</bar>
+            </anyxml-array-a>
+            <anyxml-array-b>1</anyxml-array-b>
+            <anyxml-array-b>2</anyxml-array-b>
+            <anyxml-array-b>4</anyxml-array-b>
+            <anyxml-array-b>5</anyxml-array-b>
+            <anyxml-array-b>7</anyxml-array-b>
+        </lf13-any>
+        <lf14-any>
+            <anyxml-object-a>
+                <data-a1>10</data-a1>
+                <data-a2>11</data-a2>
+            </anyxml-object-a>
+            <anyxml-object-b>
+                <child-object-b1>
+                    <data-b1>5.5</data-b1>
+                </child-object-b1>
+                <child-object-b2>
+                    <data-b2>b2-val</data-b2>
+                </child-object-b2>
+            </anyxml-object-b>
+        </lf14-any>
+    </cont1>
+    <root>
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8927/xml/scalar_array.xml b/yang/yang-data-codec-gson/src/test/resources/bug8927/xml/scalar_array.xml
new file mode 100644 (file)
index 0000000..be0a9b2
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+<foo xmlns="bug8927.test">
+    <alarm-information xmlns="bug8927.test">
+        <alarm-summary>
+            <active-alarm-count/>
+        </alarm-summary>
+        <elmt>1</elmt>
+        <elmt>2</elmt>
+        <elmt>3</elmt>
+    </alarm-information>
+</foo>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug8927/xml/scalar_array_with_other_elements.xml b/yang/yang-data-codec-gson/src/test/resources/bug8927/xml/scalar_array_with_other_elements.xml
new file mode 100644 (file)
index 0000000..08b29e2
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+<foo xmlns="bug8927.test">
+    <alarm-information xmlns="bug8927.test">
+        <alarm-summary>
+            <active-alarm-count/>
+        </alarm-summary>
+        <elmt>1</elmt>
+        <elmt>2</elmt>
+        <elmt>3</elmt>
+        <test>123</test>
+    </alarm-information>
+</foo>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/anyxml-node-with-composite-value-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/anyxml-node-with-composite-value-in-container.json
new file mode 100644 (file)
index 0000000..1bb60c4
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "complexjson:cont1": {
+    "lf12-any": [true, {"baz": [120, "str-val", false]}, {"foo": null}],
+
+    "lf13-any": {
+      "anyxml-array-a": [{"foo": true}, 10, {"bar": false}],
+      "anyxml-array-b": [1, 2, [4, 5], 7]
+    },
+
+    "lf14-any": {
+      "anyxml-object-a": {
+        "data-a1": 10,
+        "data-a2": 11
+      },
+
+      "anyxml-object-b": {
+        "child-object-b1": {
+          "data-b1": 5.5
+        },
+
+        "child-object-b2": {
+          "data-b2": "b2-val"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/anyxml-node-with-simple-value-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/anyxml-node-with-simple-value-in-container.json
new file mode 100644 (file)
index 0000000..930ba22
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "complexjson:cont1": {
+    "lf12-any": 100.5,
+    "lf13-any": true,
+    "lf14-any": null
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-augmentation-in-choice-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-augmentation-in-choice-in-container.json
new file mode 100644 (file)
index 0000000..fc9898e
--- /dev/null
@@ -0,0 +1,8 @@
+{
+    "complexjson:cont1": {
+        "lf15_11" : "one two",
+        "lf13" : "lf13 value",
+        "lf15_21" : "lf15_21 value",
+        "lf15_12" : "complexjson:ident"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-external-augmentation-in-choice-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/case-node-external-augmentation-in-choice-in-container.json
new file mode 100644 (file)
index 0000000..d8d81dc
--- /dev/null
@@ -0,0 +1,9 @@
+{
+    "complexjson:cont1": {
+        "complexjson-augmentation:lf15_11" : "lf15_11 value from augmentation",
+        "lf15_11" : "one two",
+        "lf13" : "lf13 value",
+        "complexjson-augmentation:lf15_12" : "lf15_12 value from augmentation",
+        "lf15_12" : "complexjson:ident"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/choice-node-augmentation-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/choice-node-augmentation-in-container.json
new file mode 100644 (file)
index 0000000..ccf7a71
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "complexjson:cont1": {
+        "lf17" : "lf17 value"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/choice-node-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/choice-node-in-container.json
new file mode 100644 (file)
index 0000000..fbbd2a8
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "complexjson:cont1": {
+        "lf13" : "lf13 value"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/complex-json.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/complex-json.json
new file mode 100644 (file)
index 0000000..068a2bd
--- /dev/null
@@ -0,0 +1,42 @@
+{
+    "complexjson:cont1": {
+        "empty": [null],
+        "lf12-any":[
+            {
+                "anyxml-in-data":"foo"
+            }
+        ],
+
+        "lf13-any":{
+            "anyxml-in-data":"foo"
+        },
+
+        "lf14-any":"anyxml data",
+
+        "lflst11":["lflst11 value1","lflst11 value2"],
+
+        "lst11":[
+            {
+                "key111":"key111 value",
+                "lf112":"/complexjson:cont1/complexjson:lflst11[.='foo']",
+                "lf113":"lf113 value",
+                "lf111":"lf111 value"
+            }
+        ],
+        "lf11" : "453",
+        "lf12_1" : "lf12 value",
+        "lf13" : "lf13 value",
+        "complexjson-augmentation:lf15_11" : "lf15_11 value from augmentation",
+        "complexjson-augmentation:lf15_12" : "lf15_12 value from augmentation",
+        "lf15_11" : "one two",
+        "lf15_12" : "complexjson:ident",
+        "lf15_21" : "lf15_21 value",
+        "lf17" : "lf17 value",
+
+        "lst12":[
+            {
+                "lf121":"lf121 value"
+            }
+        ]
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/keyed-list-node-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/keyed-list-node-in-container.json
new file mode 100644 (file)
index 0000000..dbcc8a9
--- /dev/null
@@ -0,0 +1,12 @@
+{
+    "complexjson:cont1": {
+        "lst11":[
+            {
+                "key111":"key111 value",
+                "lf112":"/complexjson:cont1/complexjson:lflst11[.='foo']",
+                "lf113":"lf113 value",
+                "lf111":"lf111 value"
+            }
+        ]
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/keyed-list-restconf-behaviour.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/keyed-list-restconf-behaviour.json
new file mode 100644 (file)
index 0000000..2844d7d
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "lst11": {
+                "key111":"key111 value",
+                "lf112":"/complexjson:cont1/complexjson:lflst11[.='foo']",
+                "lf113":"lf113 value",
+                "lf111":"lf111 value"
+            }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/keyed-list-yang-json-behaviour.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/keyed-list-yang-json-behaviour.json
new file mode 100644 (file)
index 0000000..af6b3ec
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "lst11": [{
+                "key111":"key111 value",
+                "lf112":"/complexjson:cont1/complexjson:lflst11[.='foo']",
+                "lf113":"lf113 value",
+                "lf111":"lf111 value"
+            }]
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/leaf-node-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/leaf-node-in-container.json
new file mode 100644 (file)
index 0000000..74ad7d9
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "complexjson:cont1": {
+        "lf11" : "453"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/leaf-node-via-augmentation-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/leaf-node-via-augmentation-in-container.json
new file mode 100644 (file)
index 0000000..4995124
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "complexjson:cont1": {
+        "lf12_1" : "lf12 value"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/leaflist-node-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/leaflist-node-in-container.json
new file mode 100644 (file)
index 0000000..8941a4e
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "complexjson:cont1": {
+        "lflst11":["lflst11 value1","lflst11 value2"]
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/missing-module-in-top-level.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/missing-module-in-top-level.json
new file mode 100644 (file)
index 0000000..1070c36
--- /dev/null
@@ -0,0 +1,4 @@
+{
+    "cont1": {
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/multiple-choice-augmentation-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/multiple-choice-augmentation-in-container.json
new file mode 100644 (file)
index 0000000..bd9a102
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "complexjson:cont1": {
+    "case11-choice-case-container" : {
+      "case11-choice-case-leaf" : "leaf-value"
+    }
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/namesakes.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/namesakes.json
new file mode 100644 (file)
index 0000000..9c0fab4
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "cont1": {
+        "lf11-namesake":"value lf11"
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/not-existing-element.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/not-existing-element.json
new file mode 100644 (file)
index 0000000..ebc44bb
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "cont1": {
+        "dummy-element":"value lf11"
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/type-empty.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/type-empty.json
new file mode 100644 (file)
index 0000000..0e9b8fc
--- /dev/null
@@ -0,0 +1,6 @@
+{
+    "complexjson:cont1":
+    {
+        "empty": [null]
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/unkeyed-node-in-container.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/unkeyed-node-in-container.json
new file mode 100644 (file)
index 0000000..61bd71d
--- /dev/null
@@ -0,0 +1,9 @@
+{
+    "complexjson:cont1": {
+        "lst12":[
+            {
+                "lf121":"lf121 value"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/bug8927.yang b/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/bug8927.yang
new file mode 100644 (file)
index 0000000..05637fd
--- /dev/null
@@ -0,0 +1,12 @@
+module bug8927 {
+    namespace "bug8927.test";
+    prefix tst;
+
+    revision 2017-01-01 {
+    }
+
+    container foo {
+        anyxml alarm-information;
+    }
+}
+
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation-namesake.yang b/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation-namesake.yang
new file mode 100644 (file)
index 0000000..e94acb2
--- /dev/null
@@ -0,0 +1,18 @@
+module complexjson-augmentation-namesake {
+    namespace "ns:complex:json:augmentation:namesake";
+    prefix cjaugnmsk;
+
+  import complexjson {
+    prefix cj;
+  }
+
+    revision "2014-08-14" {
+    }
+
+    augment "/cj:cont1" {
+        leaf lf11-namesake {
+            type string;
+        }
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation.yang b/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation.yang
new file mode 100644 (file)
index 0000000..a4cf5ad
--- /dev/null
@@ -0,0 +1,43 @@
+module complexjson-augmentation {
+    namespace "ns:complex:json:augmentation";
+    prefix cjaug;
+
+  import complexjson {
+    prefix cj;
+  }
+
+    revision "2014-08-14" {
+    }
+
+    augment "/cj:cont1" {
+        leaf lf11-namesake {
+            type string;
+        }
+    }
+
+    augment "/cj:cont1/cj:choc11/cj:c11A" {
+        leaf lf15_11 {
+                    type string;
+                }
+        leaf lf15_12 {
+                    type string;
+                }
+
+    }
+
+    augment "/cj:cont1" {
+        leaf lf12_1aug {
+                    type string;
+                }
+        leaf lf12_2aug {
+            type string;
+        }
+    }
+
+    augment "/cj:cont1/cj:choc11/cj:c11A" {
+        leaf lf15_21aug {
+                    type string;
+                }
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson.yang b/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson.yang
new file mode 100644 (file)
index 0000000..2717bb1
--- /dev/null
@@ -0,0 +1,183 @@
+module complexjson {
+    namespace "ns:complex:json";
+    prefix cj;
+
+    revision "2014-08-11" {
+    }
+
+    typedef wrapped-instance-identifier {
+        description "Wrapped instance Identifier";
+        type instance-identifier;
+    }
+
+    identity ident;
+
+    container cont1 {
+
+        leaf empty {
+            type empty;
+        }
+
+        anyxml lf12-any;
+        anyxml lf13-any;
+        anyxml lf14-any;
+
+        leaf lf11 {
+            type int32;
+        }
+
+        leaf-list lflst11 {
+            type string;
+        }
+
+        list lst11 {
+            key "key111 lf111";
+            leaf key111 {
+                type string;
+            }
+            leaf lf111 {
+                type string;
+            }
+            leaf lf112 {
+                type wrapped-instance-identifier;
+            }
+            leaf lf113 {
+                type string;
+            }
+        }
+
+        list lst12 {
+            leaf lf121 {
+                type string;
+            }
+            leaf lf122 {
+                type string;
+            }
+        }
+
+
+        choice choc11 {
+            case c11A {
+                leaf lf13 {
+                    type string;
+                }
+            }
+            leaf lf16 {
+                type string;
+            }
+        }
+
+        choice choc12 {
+            case c12A {
+            }
+        }
+    }
+
+    container cont2 {
+        presence "presence container";
+    }
+
+    augment "/cont1/choc12" {
+        case c12B {
+            leaf lf17 {
+                type string;
+            }
+        }
+    }
+
+    augment "/cont1" {
+        container cont11 {
+            leaf lf111 {
+                type string;
+            }
+        }
+    }
+
+    augment "/cont1" {
+        leaf lf12_1 {
+            type string;
+        }
+        leaf lf12_2 {
+            type string;
+        }
+    }
+
+    augment "/cont1" {
+        leaf lf12_3 {
+            type string;
+        }
+    }
+
+    augment "/cont1/choc11" {
+        case c11B {
+            leaf lf14_1  {
+                type string;
+            }
+        }
+    }
+
+    augment "/cont1/choc11" {
+        case c11C {
+            leaf lf14_2  {
+                type string;
+            }
+        }
+    }
+
+    augment "/cont1/choc11/c11A" {
+        leaf lf15_11  {
+            type bits {
+                bit one;
+                bit two;
+                bit three;
+            }
+        }
+        leaf lf15_12  {
+            type identityref {
+                base ident;
+            }
+        }
+
+    }
+
+    augment "/cont1/choc11/c11A" {
+        leaf lf15_21 {
+            type string;
+        }
+    }
+
+    augment "/cont1" {
+        /*ext:augment-identifier top-choice-augment1;*/
+        choice augment-choice1 {
+            case case1 {
+                container case1-container {
+                    leaf case1-leaf {
+                        type string;
+                    }
+                }
+            }
+
+            case case2 {
+                container case2-container {
+                    leaf case2-leaf {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+    augment "/cont1/augment-choice1/case1" {
+        /*ext:augment-identifier top-choice-augment2;*/
+        choice augment-choice2 {
+            case case11 {
+                container case11-choice-case-container {
+                    leaf case11-choice-case-leaf {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/leafref/json/data.json b/yang/yang-data-codec-gson/src/test/resources/leafref/json/data.json
new file mode 100644 (file)
index 0000000..df5bcef
--- /dev/null
@@ -0,0 +1,7 @@
+{
+    "leafref-module:cont":{
+        "lf1":121,
+        "lf2":121,
+        "lf4":"pcc://39.39.39.39"
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/leafref/yang/augment-leafref-module.yang b/yang/yang-data-codec-gson/src/test/resources/leafref/yang/augment-leafref-module.yang
new file mode 100644 (file)
index 0000000..b8b5750
--- /dev/null
@@ -0,0 +1,19 @@
+module augment-leafref-module {
+    namespace "augment:leafref:module";
+
+    prefix "auglfrfmo";
+    revision 2014-12-16 {
+    }
+
+    typedef leafreftype {
+        type leafref {
+            path "/cont/lf3";
+        }
+    }
+
+    container cont {
+        leaf lf3 {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/leafref/yang/leafref-module.yang b/yang/yang-data-codec-gson/src/test/resources/leafref/yang/leafref-module.yang
new file mode 100644 (file)
index 0000000..3428cfe
--- /dev/null
@@ -0,0 +1,23 @@
+module leafref-module {
+    namespace "leafref:module";
+
+    prefix "lfrfmo";
+
+    import augment-leafref-module { prefix augleafref; revision-date 2014-12-16; }
+    revision 2013-11-18 {
+    }
+
+    container cont {
+        leaf lf1 {
+            type int32;
+        }
+        leaf lf2 {
+            type leafref {
+                path "/cont/lf1";
+            }
+        }
+        leaf lf4 {
+            type augleafref:leafreftype;
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/json/baz.json b/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/json/baz.json
new file mode 100644 (file)
index 0000000..11c65c2
--- /dev/null
@@ -0,0 +1,19 @@
+{
+   "baz:baz":{
+      "my-container-1":{
+         "my-leaf-1":"value1"
+      },
+      "my-container-2":{
+         "inner-container":{
+            "my-leaf-2":"value2"
+         },
+         "my-leaf-3":"value3"
+      },
+      "my-anyxml-data":{
+         "inner-container":{
+            "my-leaf-2":"any-xml-leaf-2-value"
+         },
+         "my-leaf-3":"any-xml-leaf-3-value"
+      }
+   }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/xml/baz.xml b/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/xml/baz.xml
new file mode 100644 (file)
index 0000000..82f99d5
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<baz xmlns="baz">
+    <my-container-1>
+        <my-leaf-1>value1</my-leaf-1>
+    </my-container-1>
+    <my-container-2>
+        <inner-container>
+            <my-leaf-2>value2</my-leaf-2>
+        </inner-container>
+        <my-leaf-3>value3</my-leaf-3>
+    </my-container-2>
+
+    <my-anyxml-data>
+        <inner-container>
+            <my-leaf-2>any-xml-leaf-2-value</my-leaf-2>
+        </inner-container>
+        <my-leaf-3>any-xml-leaf-3-value</my-leaf-3>
+    </my-anyxml-data>
+</baz>
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/yang/baz.yang b/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/yang/baz.yang
new file mode 100644 (file)
index 0000000..9cd2233
--- /dev/null
@@ -0,0 +1,29 @@
+module baz {
+    namespace "baz";
+    prefix baz;
+
+    import yang-ext { prefix ext; revision-date 2013-07-09; }
+
+    container baz {
+        container my-container-1 {
+            leaf my-leaf-1 {
+                type string;
+            }
+        }
+
+        container my-container-2 {
+            container inner-container {
+                leaf my-leaf-2 {
+                    type string;
+                }
+            }
+            leaf my-leaf-3 {
+                type string;
+            }
+        }
+
+        anyxml my-anyxml-data {
+            ext:anyxml-schema-location "/baz/my-container-2";
+        }
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/yang/yang-ext.yang b/yang/yang-data-codec-gson/src/test/resources/yang-modeled-anyxml/yang/yang-ext.yang
new file mode 100644 (file)
index 0000000..99eb4b6
--- /dev/null
@@ -0,0 +1,75 @@
+module yang-ext {
+    yang-version 1;
+    namespace "urn:opendaylight:yang:extension:yang-ext";
+    prefix "ext";
+
+    contact "Anton Tkacik <ttkacik@cisco.com>";
+
+    description
+        "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";
+
+    revision "2013-07-09" {
+        description "";
+    }
+
+    // Augmentation name
+
+    extension "augment-identifier" {
+        description
+                "YANG language extension which assigns an identifier to augmentation.
+                Augment identifier is used to identify specific augment statement
+                by name. The identifier syntax is defined formally defined by
+                the rule 'identifier' in Section 12 of RFC 6020. All augment identifiers
+                defined in a namespace MUST be unique. The namespace of augment
+                identifiers is shared by module and its submodules.";
+
+        /*
+                Discussion:
+                This extension allows for ease of development / debug
+                of YANG modules and it is suitable for code generation,
+                where each augment statement is nicely identified by
+                unique name instead of combination of augment target
+                and when condition.
+            */
+        argument "identifier";
+    }
+
+    // Context-aware RPCs
+
+    grouping rpc-context-ref {
+        description
+                "A reference to RPC context.";
+        leaf context-instance {
+            type instance-identifier;
+            description "Pointer to the context. ";
+        }
+    }
+
+    extension "rpc-context-instance" {
+        description
+                "YANG language extension which defines enclosing (parent) schema
+                node as referencable context for RPCs. The argument is identity
+                which is used to identify RPC context type.";
+
+        argument "context-type";
+    }
+
+    extension "context-reference" {
+        argument "context-type";
+    }
+
+    extension "context-instance" {
+        argument "context-type";
+    }
+
+    extension "instance-target" {
+        argument "path";
+    }
+
+    extension "anyxml-schema-location" {
+        argument "target-node";
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/yt1027/yt1027.yang b/yang/yang-data-codec-gson/src/test/resources/yt1027/yt1027.yang
new file mode 100644 (file)
index 0000000..12e756b
--- /dev/null
@@ -0,0 +1,19 @@
+module yt1027 {
+  namespace "yt1027.test";
+  prefix tst;
+
+  leaf uint64 {
+    type uint64;
+  }
+  leaf int64 {
+    type int64;
+  }
+  leaf decimal {
+    type decimal64 {
+      fraction-digits 1;
+    }
+  }
+}
+
diff --git a/yang/yang-data-codec-xml/pom.xml b/yang/yang-data-codec-xml/pom.xml
new file mode 100644 (file)
index 0000000..4f77ad7
--- /dev/null
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2016 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-data-codec-xml</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.codehaus.woodstox</groupId>
+            <artifactId>stax2-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-test-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.xmlunit</groupId>
+            <artifactId>xmlunit-legacy</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.data.codec.xml</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractXmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractXmlCodec.java
new file mode 100644 (file)
index 0000000..942e4db
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static java.util.Objects.requireNonNull;
+
+import javax.xml.namespace.NamespaceContext;
+import org.opendaylight.yangtools.yang.data.impl.codec.DataStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+
+/**
+ * Abstract base implementation of {@link XmlCodec}, which wraps a {@link TypeDefinitionAwareCodec}.
+ *
+ * @param <T> Deserialized object type
+ */
+abstract class AbstractXmlCodec<T> implements XmlCodec<T> {
+
+    private final DataStringCodec<T> codec;
+
+    protected AbstractXmlCodec(final DataStringCodec<T> codec) {
+        this.codec = requireNonNull(codec);
+    }
+
+    @Override
+    public final Class<T> getDataType() {
+        return codec.getInputClass();
+    }
+
+    @Override
+    public final T parseValue(final NamespaceContext namespaceContext, final String str) {
+        return codec.deserialize(str);
+    }
+
+    final String serialize(final T input) {
+        return codec.serialize(input);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/BooleanXmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/BooleanXmlCodec.java
new file mode 100644 (file)
index 0000000..59e152c
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016 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;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.DataStringCodec;
+
+final class BooleanXmlCodec extends AbstractXmlCodec<Boolean> {
+    BooleanXmlCodec(final DataStringCodec<Boolean> codec) {
+        super(codec);
+    }
+
+    @Override
+    public void writeValue(final XMLStreamWriter ctx, final Boolean value) throws XMLStreamException {
+        ctx.writeCharacters(String.valueOf(value));
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceAnydata.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceAnydata.java
new file mode 100644 (file)
index 0000000..bb160fc
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.AbstractNormalizableAnydata;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.xml.sax.SAXException;
+
+/**
+ * Internal parser representation of a parsed-out chunk of XML. This format is completely internal to the parser
+ * and can be changed at any time. Current implementation uses W3C DOM tree as the backing implementations, but others
+ * are possible as well.
+ *
+ * <p>
+ * Note that the DOMSource is expected to contain a top-level synthetic element, which acts as holder of namespace
+ * declarations coming from parsing context but is otherwise ignored. Parser-side of things is expected to reuse the
+ * {@code anydata} element name for this purpose. Writer-side of things is expected to skip this element except for
+ * its namespace declarations.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault
+final class DOMSourceAnydata extends AbstractNormalizableAnydata {
+    private final DOMSource source;
+
+    DOMSourceAnydata(final DOMSource source) {
+        this.source = requireNonNull(source);
+    }
+
+    XMLStreamReader toStreamReader() throws XMLStreamException {
+        return new DOMSourceXMLStreamReader(source);
+    }
+
+    @Override
+    protected void writeTo(final NormalizedNodeStreamWriter streamWriter, final SchemaContext schemaContext,
+            final DataSchemaNode contextNode) throws IOException {
+        final XmlParserStream xmlParser;
+        try {
+            xmlParser = XmlParserStream.create(streamWriter, schemaContext, contextNode);
+        } catch (IllegalArgumentException e) {
+            throw new IOException("Failed to instantiate XML parser", e);
+        }
+
+        try {
+            final XMLStreamReader reader = toStreamReader();
+            reader.nextTag();
+
+            xmlParser.parse(reader).flush();
+        } catch (XMLStreamException | URISyntaxException | SAXException e) {
+            throw new IOException("Failed to parse payload", e);
+        }
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("source", source);
+    }
+
+    @VisibleForTesting
+    DOMSource getSource() {
+        return source;
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceMountPointChild.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceMountPointChild.java
new file mode 100644 (file)
index 0000000..b33abba
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.AbstractMountPointChild;
+import org.xml.sax.SAXException;
+
+/**
+ * Internal MountPointChild implementation, reusing data bits from {@link DOMSourceAnydata}.
+ */
+@NonNullByDefault
+final class DOMSourceMountPointChild extends AbstractMountPointChild {
+    private final DOMSource source;
+
+    DOMSourceMountPointChild(final DOMSource source) {
+        this.source = requireNonNull(source);
+    }
+
+    @Override
+    public void writeTo(final NormalizedNodeStreamWriter writer, final MountPointContext mountCtx) throws IOException {
+
+        final XmlParserStream xmlParser;
+        try {
+            xmlParser = XmlParserStream.create(writer, mountCtx, mountCtx.getSchemaContext());
+        } catch (IllegalArgumentException e) {
+            throw new IOException("Failed to instantiate XML parser", e);
+        }
+
+        try {
+            final XMLStreamReader reader = new DOMSourceXMLStreamReader(source);
+            xmlParser.parse(reader).flush();
+        } catch (XMLStreamException | URISyntaxException | SAXException e) {
+            throw new IOException("Failed to parse payload", e);
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceXMLStreamReader.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceXMLStreamReader.java
new file mode 100644 (file)
index 0000000..1cb532d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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;
+
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.dom.DOMSource;
+import org.codehaus.stax2.ri.dom.DOMWrappingReader;
+
+final class DOMSourceXMLStreamReader extends DOMWrappingReader {
+    DOMSourceXMLStreamReader(final DOMSource src) throws XMLStreamException {
+        super(src, true, true);
+    }
+
+    @Override
+    public Object getProperty(final String name) {
+        return null;
+    }
+
+    @Override
+    public boolean isPropertySupported(final String name) {
+        return false;
+    }
+
+    @Override
+    public boolean setProperty(final String name, final Object value) {
+        return false;
+    }
+
+    @Override
+    protected void throwStreamException(final String msg, final Location loc) throws XMLStreamException {
+        throw new XMLStreamException(msg, loc);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/EmptyXmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/EmptyXmlCodec.java
new file mode 100644 (file)
index 0000000..b598c80
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static java.util.Objects.requireNonNull;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.common.Empty;
+
+final class EmptyXmlCodec implements XmlCodec<Empty> {
+
+    static final EmptyXmlCodec INSTANCE = new EmptyXmlCodec();
+
+    private EmptyXmlCodec() {
+
+    }
+
+    @Override
+    public Class<Empty> getDataType() {
+        return Empty.class;
+    }
+
+    @Override
+    public Empty parseValue(final NamespaceContext ctx, final String str) {
+        return Empty.getInstance();
+    }
+
+    @Override
+    public void writeValue(final XMLStreamWriter ctx, final Empty value) throws XMLStreamException {
+        requireNonNull(value);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/IdentityrefXmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/IdentityrefXmlCodec.java
new file mode 100644 (file)
index 0000000..87623bc
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import java.net.URI;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.util.codec.IdentityCodecUtil;
+import org.opendaylight.yangtools.yang.data.util.codec.QNameCodecUtil;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+final class IdentityrefXmlCodec implements XmlCodec<QName> {
+    private final SchemaContext schemaContext;
+    private final QNameModule parentModule;
+
+    IdentityrefXmlCodec(final SchemaContext context, final QNameModule parentModule) {
+        this.schemaContext = requireNonNull(context);
+        this.parentModule = requireNonNull(parentModule);
+    }
+
+    @Override
+    public Class<QName> getDataType() {
+        return QName.class;
+    }
+
+    @Override
+    public QName parseValue(final NamespaceContext ctx, final String str) {
+        return IdentityCodecUtil.parseIdentity(str, schemaContext, prefix -> {
+            if (prefix.isEmpty()) {
+                return parentModule;
+            }
+
+            final String prefixedNS = ctx.getNamespaceURI(prefix);
+            final Iterator<Module> modules = schemaContext.findModules(URI.create(prefixedNS)).iterator();
+            checkArgument(modules.hasNext(), "Could not find module for namespace %s", prefixedNS);
+            return modules.next().getQNameModule();
+        }).getQName();
+    }
+
+    @Override
+    public void writeValue(final XMLStreamWriter ctx, final QName value) throws XMLStreamException {
+        final RandomPrefix prefixes = new RandomPrefix(ctx.getNamespaceContext());
+        final String str = QNameCodecUtil.encodeQName(value, uri -> prefixes.encodePrefix(uri.getNamespace()));
+
+        for (Entry<URI, String> e : prefixes.getPrefixes()) {
+            ctx.writeNamespace(e.getValue(), e.getKey().toString());
+        }
+        ctx.writeCharacters(str);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/NullXmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/NullXmlCodec.java
new file mode 100644 (file)
index 0000000..69a7fd5
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class NullXmlCodec implements XmlCodec<Object> {
+    static final NullXmlCodec INSTANCE = new NullXmlCodec();
+    private static final Logger LOG = LoggerFactory.getLogger(NullXmlCodec.class);
+
+    private NullXmlCodec() {
+
+    }
+
+    @Override
+    public Class<Object> getDataType() {
+        return Object.class;
+    }
+
+    @Override
+    public Object parseValue(final NamespaceContext ctx, final String str) {
+        LOG.warn("Call of the deserializeString method on null codec. No operation performed.");
+        return null;
+    }
+
+    @Override
+    public void writeValue(final XMLStreamWriter ctx, final Object value) throws XMLStreamException {
+        // NOOP since codec is unkwown.
+        LOG.warn("Call of the serializeToWriter method on null codec. No operation performed.");
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/NumberXmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/NumberXmlCodec.java
new file mode 100644 (file)
index 0000000..6035bb9
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016 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;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.DataStringCodec;
+
+final class NumberXmlCodec<T extends Number> extends AbstractXmlCodec<T> {
+    NumberXmlCodec(final DataStringCodec<T> codec) {
+        super(codec);
+    }
+
+    @Override
+    public void writeValue(final XMLStreamWriter ctx, final T value) throws XMLStreamException {
+        ctx.writeCharacters(String.valueOf(value));
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/QuotedXmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/QuotedXmlCodec.java
new file mode 100644 (file)
index 0000000..720728b
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016 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;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.DataStringCodec;
+
+final class QuotedXmlCodec<T> extends AbstractXmlCodec<T> {
+    QuotedXmlCodec(final DataStringCodec<T> codec) {
+        super(codec);
+    }
+
+    @Override
+    public void writeValue(final XMLStreamWriter ctx, final T value) throws XMLStreamException {
+        ctx.writeCharacters(serialize(value));
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/RandomPrefix.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/RandomPrefix.java
new file mode 100644 (file)
index 0000000..674efb1
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.xml;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import java.net.URI;
+import java.util.Map.Entry;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+
+class RandomPrefix {
+    // 32 characters, carefully chosen
+    private static final String LOOKUP = "abcdefghiknoprstABCDEFGHIKNOPRST";
+    private static final int MASK = 0x1f;
+    private static final int SHIFT = 5;
+
+    private int counter = 0;
+
+    // BiMap to make values lookup faster
+    private final BiMap<URI, String> prefixes = HashBiMap.create();
+    private final NamespaceContext context;
+
+    RandomPrefix(final NamespaceContext context) {
+        this.context = context;
+    }
+
+    Iterable<Entry<URI, String>> getPrefixes() {
+        return prefixes.entrySet();
+    }
+
+    String encodePrefix(final URI namespace) {
+        String prefix = prefixes.get(namespace);
+        if (prefix != null) {
+            return prefix;
+        }
+
+        if (context != null) {
+            prefix = context.getPrefix(namespace.toString());
+            if (prefix != null) {
+                return prefix;
+            }
+        }
+
+        do {
+            prefix = encode(counter);
+            counter++;
+        } while (alreadyUsedPrefix(prefix));
+
+        prefixes.put(namespace, prefix);
+        return prefix;
+    }
+
+    private boolean alreadyUsedPrefix(final String prefix) {
+        if (context == null) {
+            return false;
+        }
+
+        // It seems JDK8 is violating the API contract of NamespaceContext by returning null for unbound prefixes,
+        // rather than specified NULL_NS_URI. Work this around by checking explicitly for null.
+        final String str = context.getNamespaceURI(prefix);
+        return str != null && !XMLConstants.NULL_NS_URI.equals(str);
+    }
+
+    @VisibleForTesting
+    static int decode(final String str) {
+        int ret = 0;
+        for (char c : str.toCharArray()) {
+            int idx = LOOKUP.indexOf(c);
+            checkArgument(idx != -1, "Invalid string %s", str);
+            ret = (ret << SHIFT) + idx;
+        }
+
+        return ret;
+    }
+
+    @VisibleForTesting
+    static String encode(int num) {
+        final StringBuilder sb = new StringBuilder();
+
+        do {
+            sb.append(LOOKUP.charAt(num & MASK));
+            num >>>= SHIFT;
+        } while (num != 0);
+
+        return sb.reverse().toString();
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/RandomPrefixInstanceIdentifierSerializer.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/RandomPrefixInstanceIdentifierSerializer.java
new file mode 100644 (file)
index 0000000..6a666a0
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.xml;
+
+import java.net.URI;
+import java.util.Map.Entry;
+import javax.xml.namespace.NamespaceContext;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.util.AbstractStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+final class RandomPrefixInstanceIdentifierSerializer extends AbstractStringInstanceIdentifierCodec {
+    private final @NonNull DataSchemaContextTree schemaTree;
+    private final RandomPrefix prefixes;
+
+    RandomPrefixInstanceIdentifierSerializer(final @NonNull SchemaContext schemaContext,
+        final NamespaceContext nsContext) {
+        schemaTree = DataSchemaContextTree.from(schemaContext);
+        prefixes = new RandomPrefix(nsContext);
+    }
+
+    Iterable<Entry<URI, String>> getPrefixes() {
+        return prefixes.getPrefixes();
+    }
+
+    @Override
+    protected String prefixForNamespace(final URI namespace) {
+        return prefixes.encodePrefix(namespace);
+    }
+
+    @Override
+    protected QName createQName(final String prefix, final String localName) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    protected DataSchemaContextTree getDataContextTree() {
+        return schemaTree;
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..9c60bb7
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2016 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.yang.data.codec.xml;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.util.Optional;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNode;
+import org.opendaylight.yangtools.yang.common.QName;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+
+final class SchemaAwareXMLStreamNormalizedNodeStreamWriter extends XMLStreamNormalizedNodeStreamWriter<SchemaNode>
+        implements SchemaContextProvider {
+    private final SchemaTracker tracker;
+    private final SchemaAwareXMLStreamWriterUtils streamUtils;
+
+    SchemaAwareXMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer, final SchemaContext context,
+            final SchemaTracker tracker) {
+        super(writer);
+        this.tracker = requireNonNull(tracker);
+        this.streamUtils = new SchemaAwareXMLStreamWriterUtils(context);
+    }
+
+    @Override
+    String encodeValue(final ValueWriter xmlWriter, final Object value, final SchemaNode schemaNode)
+            throws XMLStreamException {
+        return streamUtils.encodeValue(xmlWriter, schemaNode, value, schemaNode.getQName().getModule());
+    }
+
+    @Override
+    String encodeAnnotationValue(final ValueWriter xmlWriter, final QName qname, final Object value)
+            throws XMLStreamException {
+        final Optional<AnnotationSchemaNode> optAnnotation = AnnotationSchemaNode.find(streamUtils.getSchemaContext(),
+            qname);
+        if (optAnnotation.isPresent()) {
+            return streamUtils.encodeValue(xmlWriter, optAnnotation.get(), value, qname.getModule());
+        }
+
+        checkArgument(!qname.getRevision().isPresent(), "Failed to find bound annotation %s", qname);
+        checkArgument(value instanceof String, "Invalid non-string value %s for unbound annotation %s", value, qname);
+        return (String) value;
+    }
+
+    @Override
+    void startList(final NodeIdentifier name) {
+        tracker.startList(name);
+    }
+
+    @Override
+    void startListItem(final PathArgument name) throws IOException {
+        tracker.startListItem(name);
+        startElement(name.getNodeType());
+    }
+
+    @Override
+    public void endNode() throws IOException {
+        final Object schema = tracker.endNode();
+        if (schema instanceof ListSchemaNode || schema instanceof LeafListSchemaNode) {
+            // For lists, we only emit end element on the inner frame
+            final Object parent = tracker.getParent();
+            if (parent == schema) {
+                endElement();
+            }
+        } else if (schema instanceof ContainerSchemaNode || schema instanceof LeafSchemaNode
+                || schema instanceof AnydataSchemaNode || schema instanceof AnyxmlSchemaNode) {
+            endElement();
+        }
+    }
+
+    @Override
+    public void startLeafNode(final NodeIdentifier name) throws IOException {
+        tracker.startLeafNode(name);
+        startElement(name.getNodeType());
+    }
+
+    @Override
+    public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
+        tracker.startLeafSetEntryNode(name);
+        startElement(name.getNodeType());
+    }
+
+    @Override
+    public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
+        tracker.startLeafSet(name);
+    }
+
+    @Override
+    public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
+        tracker.startLeafSet(name);
+    }
+
+    @Override
+    public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        final SchemaNode schema = tracker.startContainerNode(name);
+        startElement(schema.getQName());
+    }
+
+    @Override
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
+        tracker.startChoiceNode(name);
+    }
+
+    @Override
+    public void startAugmentationNode(final AugmentationIdentifier identifier) {
+        tracker.startAugmentationNode(identifier);
+    }
+
+    @Override
+    public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        final SchemaNode schema = tracker.startYangModeledAnyXmlNode(name);
+        startElement(schema.getQName());
+    }
+
+    @Override
+    public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        if (DOMSource.class.isAssignableFrom(objectModel)) {
+            tracker.startAnyxmlNode(name);
+            startElement(name.getNodeType());
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public SchemaContext getSchemaContext() {
+        return streamUtils.getSchemaContext();
+    }
+
+    @Override
+    public void scalarValue(final Object value) throws IOException {
+        final Object current = tracker.getParent();
+        if (current instanceof TypedDataSchemaNode) {
+            writeValue(value, (SchemaNode) current);
+        } else if (current instanceof AnydataSchemaNode) {
+            anydataValue(value);
+        } else {
+            throw new IllegalStateException("Unexpected scalar value " + value + " with " + current);
+        }
+    }
+
+    @Override
+    public void domSourceValue(final DOMSource value) throws IOException {
+        final Object current = tracker.getParent();
+        checkState(current instanceof AnyxmlSchemaNode, "Unexpected scala value %s with %s", value, current);
+        anyxmlValue(value);
+    }
+
+    @Override
+    void startAnydata(final NodeIdentifier name) {
+        tracker.startAnydataNode(name);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamWriterUtils.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamWriterUtils.java
new file mode 100644 (file)
index 0000000..fe7168a
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 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;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.net.URI;
+import java.util.Map.Entry;
+import javax.xml.stream.XMLStreamException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+final class SchemaAwareXMLStreamWriterUtils extends XMLStreamWriterUtils implements SchemaContextProvider {
+    private final @NonNull SchemaContext schemaContext;
+
+    SchemaAwareXMLStreamWriterUtils(final SchemaContext schemaContext) {
+        this.schemaContext = requireNonNull(schemaContext);
+    }
+
+    @Override
+    TypeDefinition<?> getBaseTypeForLeafRef(final SchemaNode schemaNode, final LeafrefTypeDefinition type) {
+        final TypeDefinition<?> ret = SchemaContextUtil.getBaseTypeForLeafRef(type, schemaContext, schemaNode);
+        return verifyNotNull(ret, "Unable to find base type for leafref node '%s'.", schemaNode.getPath());
+    }
+
+    @Override
+    String encodeInstanceIdentifier(final ValueWriter writer, final YangInstanceIdentifier value)
+            throws XMLStreamException {
+        RandomPrefixInstanceIdentifierSerializer iiCodec = new RandomPrefixInstanceIdentifierSerializer(schemaContext,
+            writer.getNamespaceContext());
+        String serializedValue = iiCodec.serialize(value);
+
+        for (Entry<URI, String> e : iiCodec.getPrefixes()) {
+            writer.writeNamespace(e.getValue(), e.getKey().toString());
+        }
+
+        return serializedValue;
+    }
+
+    @Override
+    public SchemaContext getSchemaContext() {
+        return schemaContext;
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriter.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..8d1b68c
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2016 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.yang.data.codec.xml;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.yangtools.yang.common.QName;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+final class SchemalessXMLStreamNormalizedNodeStreamWriter extends XMLStreamNormalizedNodeStreamWriter<Object> {
+    private enum NodeType {
+        CONTAINER,
+        LEAF_SET,
+        LIST,
+        LIST_ITEM,
+        YANG_MODELED_ANY_XML,
+        CHOICE,
+        AUGMENTATION,
+        SCALAR,
+        ANY_XML,
+        ANYDATA,
+    }
+
+    private final Deque<NodeType> nodeTypeStack = new ArrayDeque<>();
+
+    SchemalessXMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer) {
+        super(writer);
+    }
+
+    @Override
+    public void startLeafNode(final NodeIdentifier name) throws IOException {
+        nodeTypeStack.push(NodeType.SCALAR);
+        startElement(name.getNodeType());
+    }
+
+    @Override
+    public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
+        nodeTypeStack.push(NodeType.SCALAR);
+        startElement(name.getNodeType());
+    }
+
+    @Override
+    public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        nodeTypeStack.push(NodeType.LEAF_SET);
+    }
+
+    @Override
+    public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        nodeTypeStack.push(NodeType.LEAF_SET);
+    }
+
+    @Override
+    public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        nodeTypeStack.push(NodeType.CONTAINER);
+        startElement(name.getNodeType());
+    }
+
+    @Override
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        nodeTypeStack.push(NodeType.CHOICE);
+    }
+
+    @Override
+    public void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
+        nodeTypeStack.push(NodeType.AUGMENTATION);
+    }
+
+    @Override
+    public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        if (DOMSource.class.isAssignableFrom(objectModel)) {
+            nodeTypeStack.push(NodeType.ANY_XML);
+            startElement(name.getNodeType());
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        nodeTypeStack.push(NodeType.YANG_MODELED_ANY_XML);
+        startElement(name.getNodeType());
+    }
+
+    @Override
+    String encodeValue(final ValueWriter xmlWriter, final Object value, final Object context) {
+        return value.toString();
+    }
+
+    @Override
+    String encodeAnnotationValue(final ValueWriter xmlWriter, final QName qname, final Object value) {
+        return value.toString();
+    }
+
+    @Override
+    void startList(final NodeIdentifier name) {
+        nodeTypeStack.push(NodeType.LIST);
+    }
+
+    @Override
+    void startListItem(final PathArgument name) throws IOException {
+        nodeTypeStack.push(NodeType.LIST_ITEM);
+        startElement(name.getNodeType());
+    }
+
+    @Override
+    public void scalarValue(final Object value) throws IOException {
+        final NodeType type = nodeTypeStack.peek();
+        if (type == NodeType.SCALAR) {
+            writeValue(value, null);
+        } else if (type == NodeType.ANYDATA) {
+            anydataValue(value);
+        } else {
+            throw new IllegalStateException("Unexpected scalar " + value + " in type " + type);
+        }
+    }
+
+    @Override
+    public void domSourceValue(final DOMSource value) throws IOException {
+        final NodeType type = nodeTypeStack.peek();
+        checkState(type == NodeType.ANY_XML, "Unexpected DOMSource %s in %s", value, type);
+        anyxmlValue(value);
+    }
+
+    @Override
+    public void endNode() throws IOException {
+        NodeType type = nodeTypeStack.pop();
+        switch (type) {
+            case CONTAINER:
+            case LIST_ITEM:
+            case SCALAR:
+            case ANY_XML:
+            case ANYDATA:
+                endElement();
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    void startAnydata(final NodeIdentifier name) {
+        nodeTypeStack.push(NodeType.ANYDATA);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamWriterUtils.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamWriterUtils.java
new file mode 100644 (file)
index 0000000..7563327
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 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;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+
+final class SchemalessXMLStreamWriterUtils extends XMLStreamWriterUtils {
+    static final SchemalessXMLStreamWriterUtils INSTANCE = new SchemalessXMLStreamWriterUtils();
+
+    private SchemalessXMLStreamWriterUtils() {
+        // Hidden on purpose
+    }
+
+    @Override
+    TypeDefinition<?> getBaseTypeForLeafRef(final SchemaNode schemaNode, final LeafrefTypeDefinition type) {
+        return type;
+    }
+
+    @Override
+    String encodeInstanceIdentifier(final ValueWriter writer, final YangInstanceIdentifier value) {
+        throw new UnsupportedOperationException("Schema context not present in " + this + ", cannot serialize "
+            + value);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/StreamWriterFacade.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/StreamWriterFacade.java
new file mode 100644 (file)
index 0000000..19c05c2
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Strings;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedAnydata;
+import org.opendaylight.yangtools.yang.data.util.SingleChildDataNodeContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The sole implementation of {@link ValueWriter}, tasked with synchronizing access to XMLStreamWriter state. The only
+ * class referencing this class should be {@link XMLStreamNormalizedNodeStreamWriter}.
+ */
+final class StreamWriterFacade extends ValueWriter {
+    private static final Logger LOG = LoggerFactory.getLogger(StreamWriterFacade.class);
+    private static final Set<String> BROKEN_NAMESPACES = ConcurrentHashMap.newKeySet();
+    private static final Set<String> LEGACY_ATTRIBUTES = ConcurrentHashMap.newKeySet();
+
+    private final XMLStreamWriter writer;
+    private final RandomPrefix prefixes;
+
+    // QName of an element we delayed emitting. This only happens if it is a naked element, without any attributes,
+    // namespace declarations or value.
+    private QName openElement;
+
+    StreamWriterFacade(final XMLStreamWriter writer) {
+        this.writer = requireNonNull(writer);
+        prefixes = new RandomPrefix(writer.getNamespaceContext());
+    }
+
+    void writeCharacters(final String text) throws XMLStreamException {
+        if (!Strings.isNullOrEmpty(text)) {
+            flushElement();
+            writer.writeCharacters(text);
+        }
+    }
+
+    @Override
+    void writeNamespace(final String prefix, final String namespaceURI) throws XMLStreamException {
+        flushElement();
+        writer.writeNamespace(prefix, namespaceURI);
+    }
+
+    @Override
+    void writeAttribute(final String localName, final String value) throws XMLStreamException {
+        flushElement();
+        writer.writeAttribute(localName, value);
+    }
+
+    @Override
+    void writeAttribute(final String prefix, final String namespaceURI, final String localName, final String value)
+            throws XMLStreamException {
+        flushElement();
+        writer.writeAttribute(prefix, namespaceURI, localName, value);
+    }
+
+    @Override
+    NamespaceContext getNamespaceContext() {
+        // Accessing namespace context is okay, because a delayed element is known to have no effect on the result
+        return writer.getNamespaceContext();
+    }
+
+    private void flushElement() throws XMLStreamException {
+        if (openElement != null) {
+            writer.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, openElement.getLocalName(),
+                openElement.getNamespace().toString());
+            openElement = null;
+        }
+    }
+
+    void writeStartElement(final QName qname) throws XMLStreamException {
+        flushElement();
+
+        final String namespace = qname.getNamespace().toString();
+        final NamespaceContext context = writer.getNamespaceContext();
+        final boolean reuseNamespace;
+        if (context != null) {
+            reuseNamespace = namespace.equals(context.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX));
+        } else {
+            reuseNamespace = XMLConstants.DEFAULT_NS_PREFIX.equals(writer.getPrefix(namespace));
+        }
+
+        if (!reuseNamespace) {
+            writer.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, qname.getLocalName(), namespace);
+            writer.writeDefaultNamespace(namespace);
+        } else {
+            openElement = qname;
+        }
+    }
+
+    void writeEndElement() throws XMLStreamException {
+        if (openElement != null) {
+            writer.writeEmptyElement(XMLConstants.DEFAULT_NS_PREFIX, openElement.getLocalName(),
+                openElement.getNamespace().toString());
+            openElement = null;
+        } else {
+            writer.writeEndElement();
+        }
+    }
+
+    String getPrefix(final URI uri, final String str) throws XMLStreamException {
+        final String prefix = writer.getPrefix(str);
+        if (prefix != null) {
+            return prefix;
+        }
+
+        // This is needed to recover from attributes emitted while the namespace was not declared. Ordinarily
+        // attribute namespaces would be bound in the writer, so the resulting XML is efficient, but we cannot rely
+        // on that having been done.
+        if (BROKEN_NAMESPACES.add(str)) {
+            LOG.info("Namespace {} was not bound, please fix the caller", str, new Throwable());
+        }
+
+        return prefixes.encodePrefix(uri);
+    }
+
+    void close() throws XMLStreamException {
+        // Mighty careful stepping here, we must end up closing the writer
+        XMLStreamException failure = null;
+        try {
+            flushElement();
+        } catch (XMLStreamException e) {
+            failure = e;
+            throw e;
+        } finally {
+            try {
+                writer.close();
+            } catch (XMLStreamException e) {
+                if (failure != null) {
+                    failure.addSuppressed(e);
+                } else {
+                    throw e;
+                }
+            }
+        }
+    }
+
+    void flush() throws XMLStreamException {
+        flushElement();
+        writer.flush();
+    }
+
+    void anydataWriteStreamReader(final XMLStreamReader reader) throws XMLStreamException {
+        flushElement();
+
+        // Do not emit top-level element
+        int depth = 0;
+        while (reader.hasNext()) {
+            final int event = reader.next();
+            switch (event) {
+                case XMLStreamConstants.START_ELEMENT:
+                    if (depth != 0) {
+                        forwardStartElement(reader);
+                    } else {
+                        // anydata: forward namespaces only, skipping the default namespace
+                        for (int i = 0; i < reader.getNamespaceCount(); ++i) {
+                            final String prefix = reader.getNamespacePrefix(i);
+                            if (!XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
+                                writer.writeNamespace(prefix, reader.getNamespaceURI(i));
+                            }
+                        }
+                    }
+                    ++depth;
+                    break;
+                case XMLStreamConstants.END_ELEMENT:
+                    --depth;
+                    if (depth != 0) {
+                        writer.writeEndElement();
+                    }
+                    break;
+                case XMLStreamConstants.CHARACTERS:
+                    writer.writeCharacters(reader.getText());
+                    break;
+                case XMLStreamConstants.COMMENT:
+                case XMLStreamConstants.SPACE:
+                    // Ignore comments and insignificant whitespace
+                    break;
+                case XMLStreamConstants.START_DOCUMENT:
+                case XMLStreamConstants.END_DOCUMENT:
+                    // We are embedded: ignore start/end document events
+                    break;
+                case XMLStreamConstants.ATTRIBUTE:
+                    forwardAttributes(reader);
+                    break;
+                case XMLStreamConstants.CDATA:
+                    writer.writeCData(reader.getText());
+                    break;
+                case XMLStreamConstants.NAMESPACE:
+                    forwardNamespaces(reader);
+                    break;
+                case XMLStreamConstants.DTD:
+                case XMLStreamConstants.NOTATION_DECLARATION:
+                case XMLStreamConstants.ENTITY_DECLARATION:
+                case XMLStreamConstants.ENTITY_REFERENCE:
+                case XMLStreamConstants.PROCESSING_INSTRUCTION:
+                default:
+                    throw new IllegalStateException("Unhandled event " + event);
+            }
+        }
+    }
+
+    void anyxmlWriteStreamReader(final DOMSourceXMLStreamReader reader) throws XMLStreamException {
+        flushElement();
+
+        // Do not emit top-level element
+        int depth = 0;
+        while (reader.hasNext()) {
+            final int event = reader.next();
+            switch (event) {
+                case XMLStreamConstants.START_ELEMENT:
+                    if (depth != 0) {
+                        forwardStartElement(reader);
+                    } else {
+                        forwardNamespaces(reader);
+                        // anyxml, hence we need to forward attributes
+                        forwardAttributes(reader);
+                    }
+                    ++depth;
+                    break;
+                case XMLStreamConstants.END_ELEMENT:
+                    --depth;
+                    if (depth != 0) {
+                        writer.writeEndElement();
+                    }
+                    break;
+                case XMLStreamConstants.PROCESSING_INSTRUCTION:
+                    forwardProcessingInstruction(reader);
+                    break;
+                case XMLStreamConstants.CHARACTERS:
+                    writer.writeCharacters(reader.getText());
+                    break;
+                case XMLStreamConstants.COMMENT:
+                    writer.writeComment(reader.getText());
+                    break;
+                case XMLStreamConstants.SPACE:
+                    // Ignore insignificant whitespace
+                    break;
+                case XMLStreamConstants.START_DOCUMENT:
+                case XMLStreamConstants.END_DOCUMENT:
+                    // We are embedded: ignore start/end document events
+                    break;
+                case XMLStreamConstants.ENTITY_REFERENCE:
+                    writer.writeEntityRef(reader.getLocalName());
+                    break;
+                case XMLStreamConstants.ATTRIBUTE:
+                    forwardAttributes(reader);
+                    break;
+                case XMLStreamConstants.DTD:
+                    writer.writeDTD(reader.getText());
+                    break;
+                case XMLStreamConstants.CDATA:
+                    writer.writeCData(reader.getText());
+                    break;
+                case XMLStreamConstants.NAMESPACE:
+                    forwardNamespaces(reader);
+                    break;
+                case XMLStreamConstants.NOTATION_DECLARATION:
+                case XMLStreamConstants.ENTITY_DECLARATION:
+                default:
+                    throw new IllegalStateException("Unhandled event " + event);
+            }
+        }
+    }
+
+    void emitNormalizedAnydata(final NormalizedAnydata anydata) throws XMLStreamException {
+        flushElement();
+        try {
+            anydata.writeTo(XMLStreamNormalizedNodeStreamWriter.create(writer, anydata.getSchemaContext(),
+                new SingleChildDataNodeContainer(anydata.getContextNode())));
+        } catch (IOException e) {
+            throw new XMLStreamException("Failed to emit anydata " + anydata, e);
+        }
+    }
+
+    static void warnLegacyAttribute(final String localName) {
+        if (LEGACY_ATTRIBUTES.add(localName)) {
+            LOG.info("Encountered annotation {} not bound to module. Please examine the call stack and fix this "
+                    + "warning by defining a proper YANG annotation to cover it", localName,
+                    new Throwable("Call stack"));
+        }
+    }
+
+    private void forwardAttributes(final XMLStreamReader reader) throws XMLStreamException {
+        for (int i = 0, count = reader.getAttributeCount(); i < count; ++i) {
+            final String localName = reader.getAttributeLocalName(i);
+            final String value = reader.getAttributeValue(i);
+            final String prefix = reader.getAttributePrefix(i);
+            if (prefix != null) {
+                writer.writeAttribute(prefix, reader.getAttributeNamespace(i), localName, value);
+            } else {
+                writer.writeAttribute(localName, value);
+            }
+        }
+    }
+
+    private void forwardNamespaces(final XMLStreamReader reader) throws XMLStreamException {
+        for (int i = 0; i < reader.getNamespaceCount(); ++i) {
+            writer.writeNamespace(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
+        }
+    }
+
+    private void forwardProcessingInstruction(final XMLStreamReader reader) throws XMLStreamException {
+        final String target = reader.getPITarget();
+        final String data = reader.getPIData();
+        if (data != null) {
+            writer.writeProcessingInstruction(target, data);
+        } else {
+            writer.writeProcessingInstruction(target);
+        }
+    }
+
+    private void forwardStartElement(final XMLStreamReader reader) throws XMLStreamException {
+        final String localName = reader.getLocalName();
+        final String prefix = reader.getPrefix();
+        if (prefix != null) {
+            writer.writeStartElement(prefix, localName, reader.getNamespaceURI());
+        } else {
+            writer.writeStartElement(localName);
+        }
+
+        forwardNamespaces(reader);
+        forwardAttributes(reader);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/UnionXmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/UnionXmlCodec.java
new file mode 100644 (file)
index 0000000..0abe970
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016 Intel Corporation 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;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class UnionXmlCodec<T> implements XmlCodec<T> {
+    private static final class Diverse extends UnionXmlCodec<Object> {
+        Diverse(final List<XmlCodec<?>> codecs) {
+            super(codecs);
+        }
+
+        @Override
+        public Class<Object> getDataType() {
+            return Object.class;
+        }
+    }
+
+    private static final class SingleType<T> extends UnionXmlCodec<T> {
+        private final Class<T> dataClass;
+
+        SingleType(final Class<T> dataClass, final List<XmlCodec<?>> codecs) {
+            super(codecs);
+            this.dataClass = requireNonNull(dataClass);
+        }
+
+        @Override
+        public Class<T> getDataType() {
+            return dataClass;
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(UnionXmlCodec.class);
+
+    private final ImmutableList<XmlCodec<?>> codecs;
+
+    UnionXmlCodec(final List<XmlCodec<?>> codecs) {
+        this.codecs = ImmutableList.copyOf(codecs);
+    }
+
+    static UnionXmlCodec<?> create(final UnionTypeDefinition type, final List<XmlCodec<?>> codecs) {
+        final Iterator<XmlCodec<?>> it = codecs.iterator();
+        verify(it.hasNext(), "Union %s has no subtypes", type);
+
+        Class<?> dataClass = it.next().getDataType();
+        while (it.hasNext()) {
+            final Class<?> next = it.next().getDataType();
+            if (!dataClass.equals(next)) {
+                LOG.debug("Type {} has diverse data classes: {} and {}", type, dataClass, next);
+                return new Diverse(codecs);
+            }
+        }
+
+        LOG.debug("Type {} has single data class {}", type, dataClass);
+        return new SingleType<>(dataClass, codecs);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    public final T parseValue(final NamespaceContext ctx, final String str) {
+        for (XmlCodec<?> codec : codecs) {
+            final Object ret;
+            try {
+                ret = codec.parseValue(ctx, str);
+            } catch (RuntimeException e) {
+                LOG.debug("Codec {} did not accept input '{}'", codec, str, e);
+                continue;
+            }
+
+            return getDataType().cast(ret);
+        }
+
+        throw new IllegalArgumentException("Invalid value \"" + str + "\" for union type.");
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    public void writeValue(final XMLStreamWriter ctx, final Object value) throws XMLStreamException {
+        for (XmlCodec<?> codec : codecs) {
+            if (!codec.getDataType().isInstance(value)) {
+                LOG.debug("Codec {} cannot accept input {}, skipping it", codec, value);
+                continue;
+            }
+
+            @SuppressWarnings("unchecked")
+            final XmlCodec<Object> objCodec = (XmlCodec<Object>) codec;
+            try {
+                objCodec.writeValue(ctx, value);
+                return;
+            } catch (RuntimeException e) {
+                LOG.debug("Codec {} failed to serialize {}", codec, value, e);
+            }
+        }
+
+        throw new IllegalArgumentException("No codecs could serialize" + value);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/ValueWriter.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/ValueWriter.java
new file mode 100644 (file)
index 0000000..592f3a3
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o. 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;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+/**
+ * A minimal facade for exposing just enough information from {@link XMLStreamWriter} for the purposes of encoding
+ * leaf values. While most leaves are simple strings, some (like identityref) need to interact with XML namespaces
+ * in order to correctly encode their value.
+ *
+ * <p>
+ * See XMLStreamWriter for description of methods.
+ */
+abstract class ValueWriter {
+    abstract void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException;
+
+    abstract void writeAttribute(String localName, String value) throws XMLStreamException;
+
+    abstract void writeAttribute(String prefix, String namespaceURI, String localName, String value)
+            throws XMLStreamException;
+
+    // Note: lookup results may change if there is other interaction
+    abstract NamespaceContext getNamespaceContext();
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamNormalizedNodeStreamWriter.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..f39dac6
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.xml;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.common.QName;
+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.NormalizedAnydata;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriterExtension;
+import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Node;
+
+/**
+ * A {@link NormalizedNodeStreamWriter} which translates the events into an {@link XMLStreamWriter}, resulting in an
+ * RFC6020 XML encoding. There are 2 versions of this class, one that takes a SchemaContext and encodes values
+ * appropriately according to the YANG schema. The other is schema-less and merely outputs values using toString. The
+ * latter is intended for debugging where doesn't have a SchemaContext available and isn't meant for production use.
+ *
+ * <p>
+ * Due to backwards compatibility reasons this writer recognizes RFC7952 metadata include keys QNames with empty URI
+ * (as exposed via {@link XmlParserStream#LEGACY_ATTRIBUTE_NAMESPACE}) as their QNameModule. These indicate an
+ * unqualified XML attribute and their value can be assumed to be a String. Furthermore, this extends to qualified
+ * attributes, which uses the proper namespace, but will not bind to a proper module revision. This caveat will be
+ * removed in a future version.
+ */
+public abstract class XMLStreamNormalizedNodeStreamWriter<T> implements NormalizedNodeStreamWriter,
+        StreamWriterMetadataExtension {
+    private static final Logger LOG = LoggerFactory.getLogger(XMLStreamNormalizedNodeStreamWriter.class);
+    private static final Set<String> BROKEN_ATTRIBUTES = ConcurrentHashMap.newKeySet();
+
+    private final @NonNull StreamWriterFacade facade;
+
+    XMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer) {
+        facade = new StreamWriterFacade(writer);
+    }
+
+    /**
+     * Create a new writer with the specified context as its root.
+     *
+     * @param writer Output {@link XMLStreamWriter}
+     * @param context Associated {@link SchemaContext}.
+     * @return A new {@link NormalizedNodeStreamWriter}
+     */
+    public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer,
+            final SchemaContext context) {
+        return create(writer, context, context);
+    }
+
+    /**
+     * Create a new writer with the specified context and rooted at the specified node.
+     *
+     * @param writer Output {@link XMLStreamWriter}
+     * @param context Associated {@link SchemaContext}.
+     * @param rootNode Root node
+     * @return A new {@link NormalizedNodeStreamWriter}
+     */
+    public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context,
+            final DataNodeContainer rootNode) {
+        return new SchemaAwareXMLStreamNormalizedNodeStreamWriter(writer, context, SchemaTracker.create(rootNode));
+    }
+
+    /**
+     * Create a new writer with the specified context and rooted in the specified schema path.
+     *
+     * @param writer Output {@link XMLStreamWriter}
+     * @param context Associated {@link SchemaContext}.
+     * @param path path
+     * @return A new {@link NormalizedNodeStreamWriter}
+     */
+    public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context,
+            final SchemaPath path) {
+        return new SchemaAwareXMLStreamNormalizedNodeStreamWriter(writer, context, SchemaTracker.create(context, path));
+    }
+
+    /**
+     * Create a new schema-less writer. Note that this version is intended for debugging
+     * where doesn't have a SchemaContext available and isn't meant for production use.
+     *
+     * @param writer Output {@link XMLStreamWriter}
+     *
+     * @return A new {@link NormalizedNodeStreamWriter}
+     */
+    public static @NonNull NormalizedNodeStreamWriter createSchemaless(final XMLStreamWriter writer) {
+        return new SchemalessXMLStreamNormalizedNodeStreamWriter(writer);
+    }
+
+    @Override
+    public final ClassToInstanceMap<NormalizedNodeStreamWriterExtension> getExtensions() {
+        return ImmutableClassToInstanceMap.of(StreamWriterMetadataExtension.class, this);
+    }
+
+    abstract void startAnydata(NodeIdentifier name);
+
+    abstract void startList(NodeIdentifier name);
+
+    abstract void startListItem(PathArgument name) throws IOException;
+
+    abstract String encodeAnnotationValue(@NonNull ValueWriter xmlWriter, @NonNull QName qname, @NonNull Object value)
+            throws XMLStreamException;
+
+    abstract String encodeValue(@NonNull ValueWriter xmlWriter, @NonNull Object value, T context)
+            throws XMLStreamException;
+
+    final void writeValue(final @NonNull Object value, final T context) throws IOException {
+        try {
+            facade.writeCharacters(encodeValue(facade, value, context));
+        } catch (XMLStreamException e) {
+            throw new IOException("Failed to write value", e);
+        }
+    }
+
+    final void startElement(final QName qname) throws IOException {
+        try {
+            facade.writeStartElement(qname);
+        } catch (XMLStreamException e) {
+            throw new IOException("Failed to start element", e);
+        }
+    }
+
+    final void endElement() throws IOException {
+        try {
+            facade.writeEndElement();
+        } catch (XMLStreamException e) {
+            throw new IOException("Failed to end element", e);
+        }
+    }
+
+    final void anydataValue(final Object value) throws IOException {
+        if (value instanceof DOMSourceAnydata) {
+            try {
+                facade.anydataWriteStreamReader(((DOMSourceAnydata) value).toStreamReader());
+            } catch (XMLStreamException e) {
+                throw new IOException("Unable to transform anydata value: " + value, e);
+            }
+        } else if (value instanceof NormalizedAnydata) {
+            try {
+                facade.emitNormalizedAnydata((NormalizedAnydata) value);
+            } catch (XMLStreamException e) {
+                throw new IOException("Unable to emit anydata value: " + value, e);
+            }
+        } else {
+            throw new IllegalStateException("Unexpected anydata value " + value);
+        }
+    }
+
+    final void anyxmlValue(final DOMSource domSource) throws IOException {
+        if (domSource != null) {
+            final Node domNode = requireNonNull(domSource.getNode());
+            try {
+                facade.anyxmlWriteStreamReader(new DOMSourceXMLStreamReader(domSource));
+            } catch (XMLStreamException e) {
+                throw new IOException("Unable to transform anyXml value: " + domNode, e);
+            }
+        }
+    }
+
+    @Override
+    public final void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        startListItem(name);
+    }
+
+    @Override
+    public final void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+            throws IOException {
+        startListItem(identifier);
+    }
+
+    @Override
+    public final void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
+        startList(name);
+    }
+
+    @Override
+    public final void startMapNode(final NodeIdentifier name, final int childSizeHint) {
+        startList(name);
+    }
+
+    @Override
+    public final void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
+        startList(name);
+    }
+
+    @Override
+    public final void close() throws IOException {
+        try {
+            facade.close();
+        } catch (XMLStreamException e) {
+            throw new IOException("Failed to close writer", e);
+        }
+    }
+
+    @Override
+    public final void flush() throws IOException {
+        try {
+            facade.flush();
+        } catch (XMLStreamException e) {
+            throw new IOException("Failed to flush writer", e);
+        }
+    }
+
+    @Override
+    public final void metadata(final ImmutableMap<QName, Object> attributes) throws IOException {
+        for (final Entry<QName, Object> entry : attributes.entrySet()) {
+            final QName qname = entry.getKey();
+            final String namespace = qname.getNamespace().toString();
+            final String localName = qname.getLocalName();
+            final Object value = entry.getValue();
+
+            // FIXME: remove this handling once we have complete mapping to metadata
+            try {
+                if (namespace.isEmpty()) {
+                    // Legacy attribute, which is expected to be a String
+                    StreamWriterFacade.warnLegacyAttribute(localName);
+                    if (!(value instanceof String)) {
+                        if (BROKEN_ATTRIBUTES.add(localName)) {
+                            LOG.warn("Unbound annotation {} does not have a String value, ignoring it. Please fix the "
+                                    + "source of this annotation either by formatting it to a String or removing its "
+                                    + "use", localName, new Throwable("Call stack"));
+                        }
+                        LOG.debug("Ignoring annotation {} value {}", localName, value);
+                    } else {
+                        facade.writeAttribute(localName, (String) value);
+                        continue;
+                    }
+                } else {
+                    final String prefix = facade.getPrefix(qname.getNamespace(), namespace);
+                    final String attrValue = encodeAnnotationValue(facade, qname, value);
+                    facade.writeAttribute(prefix, namespace, localName, attrValue);
+                }
+            } catch (final XMLStreamException e) {
+                throw new IOException("Unable to emit attribute " + qname, e);
+            }
+        }
+    }
+
+    @Override
+    public final boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        if (DOMSourceAnydata.class.isAssignableFrom(objectModel)
+                || NormalizedAnydata.class.isAssignableFrom(objectModel)) {
+            startAnydata(name);
+            startElement(name.getNodeType());
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamWriterUtils.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamWriterUtils.java
new file mode 100644 (file)
index 0000000..d684c96
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2015 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.VisibleForTesting;
+import javax.xml.stream.XMLStreamException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeAware;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for bridging JAXP Stream and YANG Data APIs. Note that the definition of this class
+ * by no means final and subject to change as more functionality is centralized here.
+ */
+abstract class XMLStreamWriterUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(XMLStreamWriterUtils.class);
+
+    /**
+     * Encode a value into a String in the context of a XML stream writer. This method assumes the start and end of
+     * element is emitted by the caller.
+     *
+     * @param writer XML Stream writer
+     * @param schemaNode Schema node that describes the value
+     * @param value data value
+     * @param parent module QName owning the leaf definition
+     * @return String characters to be written
+     * @throws XMLStreamException if an encoding problem occurs
+     */
+    String encodeValue(final @NonNull ValueWriter writer, final @NonNull SchemaNode schemaNode,
+            final @NonNull Object value, final QNameModule parent) throws XMLStreamException {
+        checkArgument(schemaNode instanceof TypeAware,
+            "Unable to write value for node %s, only nodes of type: leaf, leaf-list and annotations can be written "
+                    + "at this point", schemaNode.getQName());
+
+        TypeDefinition<?> type = ((TypeAware) schemaNode).getType();
+        if (type instanceof LeafrefTypeDefinition) {
+            type = getBaseTypeForLeafRef(schemaNode, (LeafrefTypeDefinition) type);
+        }
+
+        return encodeValue(writer, type, value, parent);
+    }
+
+    /**
+     * Write a value into a XML stream writer. This method assumes the start and end of element is
+     * emitted by the caller.
+     *
+     * @param writer XML Stream writer
+     * @param type data type. In case of leaf ref this should be the type of leaf being referenced
+     * @param value data value
+     * @param parent optional parameter of a module QName owning the leaf definition
+     * @return String characters to be written
+     * @throws XMLStreamException if an encoding problem occurs
+     */
+    private String encodeValue(final @NonNull ValueWriter writer, final @NonNull TypeDefinition<?> type,
+            final @NonNull Object value, final QNameModule parent) throws XMLStreamException {
+        if (type instanceof IdentityrefTypeDefinition) {
+            return encode(writer, (IdentityrefTypeDefinition) type, value, parent);
+        } else if (type instanceof InstanceIdentifierTypeDefinition) {
+            return encode(writer, (InstanceIdentifierTypeDefinition) type, value);
+        } else {
+            return serialize(type, value);
+        }
+    }
+
+    private static String serialize(final @NonNull TypeDefinition<?> type, final @NonNull Object value) {
+        final TypeDefinitionAwareCodec<Object, ?> codec = TypeDefinitionAwareCodec.from(type);
+        if (codec == null) {
+            LOG.warn("Failed to find codec for {}, falling back to using stream", type);
+            return value.toString();
+        }
+
+        try {
+            return codec.serialize(value);
+        } catch (ClassCastException e) {
+            LOG.warn("Provided node value {} did not have type {} required by mapping. Using stream instead.",
+                value, type, e);
+            return value.toString();
+        }
+    }
+
+    @VisibleForTesting
+    static String encode(final @NonNull ValueWriter writer, final @NonNull IdentityrefTypeDefinition type,
+            final @NonNull Object value, final QNameModule parent) throws XMLStreamException {
+        if (value instanceof QName) {
+            final QName qname = (QName) value;
+
+            //in case parent is present and same as element namespace write value without namespace
+            if (qname.getNamespace().equals(parent.getNamespace())) {
+                return qname.getLocalName();
+            }
+
+            final String ns = qname.getNamespace().toString();
+            final String prefix = "x";
+            writer.writeNamespace(prefix, ns);
+            return prefix + ':' + qname.getLocalName();
+        }
+
+        final QName qname = type.getQName();
+        LOG.debug("Value of {}:{} is not a QName but {}", qname.getNamespace(), qname.getLocalName(), value.getClass());
+        return value.toString();
+    }
+
+    private String encode(final @NonNull ValueWriter writer, final @NonNull InstanceIdentifierTypeDefinition type,
+            final @NonNull Object value) throws XMLStreamException {
+        if (value instanceof YangInstanceIdentifier) {
+            return encodeInstanceIdentifier(writer, (YangInstanceIdentifier)value);
+        }
+
+        final QName qname = type.getQName();
+        LOG.warn("Value of {}:{} is not an InstanceIdentifier but {}", qname.getNamespace(), qname.getLocalName(),
+            value.getClass());
+        return value.toString();
+    }
+
+    abstract @NonNull TypeDefinition<?> getBaseTypeForLeafRef(SchemaNode schemaNode,
+            @NonNull LeafrefTypeDefinition type);
+
+    abstract String encodeInstanceIdentifier(@NonNull ValueWriter writer, YangInstanceIdentifier value)
+            throws XMLStreamException;
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodec.java
new file mode 100644 (file)
index 0000000..32cd8d9
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2016 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;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.data.util.codec.TypeAwareCodec;
+
+interface XmlCodec<T> extends TypeAwareCodec<T, NamespaceContext, XMLStreamWriter> {
+    @Override
+    void writeValue(XMLStreamWriter ctx, T value) throws XMLStreamException;
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java
new file mode 100644 (file)
index 0000000..13663a3
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.List;
+import org.opendaylight.yangtools.rcf8528.data.util.EmptyMountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.impl.codec.AbstractIntegerStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.BinaryStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.BitsStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.BooleanStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.DecimalStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.EnumStringCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.StringStringCodec;
+import org.opendaylight.yangtools.yang.data.util.codec.AbstractCodecFactory;
+import org.opendaylight.yangtools.yang.data.util.codec.SharedCodecCache;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnknownTypeDefinition;
+
+/**
+ * A thread-safe factory for instantiating {@link XmlCodec}s.
+ */
+@Beta
+public final class XmlCodecFactory extends AbstractCodecFactory<XmlCodec<?>> {
+    private final MountPointContext mountCtx;
+
+    private XmlCodecFactory(final MountPointContext mountCtx) {
+        super(mountCtx.getSchemaContext(), new SharedCodecCache<>());
+        this.mountCtx = requireNonNull(mountCtx);
+    }
+
+    MountPointContext mountPointContext() {
+        return mountCtx;
+    }
+
+    /**
+     * Instantiate a new codec factory attached to a particular context.
+     *
+     * @param context MountPointContext to which the factory should be bound
+     * @return A codec factory instance.
+     */
+    public static XmlCodecFactory create(final MountPointContext context) {
+        return new XmlCodecFactory(context);
+    }
+
+    /**
+     * Instantiate a new codec factory attached to a particular context.
+     *
+     * @param context SchemaContext to which the factory should be bound
+     * @return A codec factory instance.
+     */
+    public static XmlCodecFactory create(final SchemaContext context) {
+        return create(new EmptyMountPointContext(context));
+    }
+
+    @Override
+    protected XmlCodec<?> binaryCodec(final BinaryTypeDefinition type) {
+        return new QuotedXmlCodec<>(BinaryStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> booleanCodec(final BooleanTypeDefinition type) {
+        return new BooleanXmlCodec(BooleanStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> bitsCodec(final BitsTypeDefinition type) {
+        return new QuotedXmlCodec<>(BitsStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> emptyCodec(final EmptyTypeDefinition type) {
+        return EmptyXmlCodec.INSTANCE;
+    }
+
+    @Override
+    protected XmlCodec<?> enumCodec(final EnumTypeDefinition type) {
+        return new QuotedXmlCodec<>(EnumStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> identityRefCodec(final IdentityrefTypeDefinition type, final QNameModule module) {
+        return new IdentityrefXmlCodec(getSchemaContext(), module);
+    }
+
+    @Override
+    protected XmlCodec<?> instanceIdentifierCodec(final InstanceIdentifierTypeDefinition type) {
+        return new XmlStringInstanceIdentifierCodec(getSchemaContext(), this);
+    }
+
+    @Override
+    protected XmlCodec<?> int8Codec(final Int8TypeDefinition type) {
+        return new NumberXmlCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> int16Codec(final Int16TypeDefinition type) {
+        return new NumberXmlCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> int32Codec(final Int32TypeDefinition type) {
+        return new NumberXmlCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> int64Codec(final Int64TypeDefinition type) {
+        return new NumberXmlCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> decimalCodec(final DecimalTypeDefinition type) {
+        return new NumberXmlCodec<>(DecimalStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> stringCodec(final StringTypeDefinition type) {
+        return new QuotedXmlCodec<>(StringStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> uint8Codec(final Uint8TypeDefinition type) {
+        return new NumberXmlCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> uint16Codec(final Uint16TypeDefinition type) {
+        return new NumberXmlCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> uint32Codec(final Uint32TypeDefinition type) {
+        return new NumberXmlCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> uint64Codec(final Uint64TypeDefinition type) {
+        return new NumberXmlCodec<>(AbstractIntegerStringCodec.from(type));
+    }
+
+    @Override
+    protected XmlCodec<?> unionCodec(final UnionTypeDefinition type, final List<XmlCodec<?>> codecs) {
+        return UnionXmlCodec.create(type, codecs);
+    }
+
+    @Override
+    protected XmlCodec<?> unknownCodec(final UnknownTypeDefinition type) {
+        return NullXmlCodec.INSTANCE;
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java
new file mode 100644 (file)
index 0000000..53952b1
--- /dev/null
@@ -0,0 +1,663 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.util.StreamReaderDelegate;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stax.StAXSource;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+import org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNode;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.rfc8528.data.api.YangLibraryConstants;
+import org.opendaylight.yangtools.rfc8528.data.api.YangLibraryConstants.ContainerName;
+import org.opendaylight.yangtools.rfc8528.model.api.MountPointSchemaNode;
+import org.opendaylight.yangtools.rfc8528.model.api.SchemaMountConstants;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.util.AbstractMountPointDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.AbstractNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.AnyXmlNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.AnydataNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.CompositeNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.ContainerNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.LeafListEntryNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.LeafListNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.LeafNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.ListEntryNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.ListNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.MountPointData;
+import org.opendaylight.yangtools.yang.data.util.OperationAsContainer;
+import org.opendaylight.yangtools.yang.data.util.ParserStreamUtils;
+import org.opendaylight.yangtools.yang.data.util.SimpleNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.data.util.YangModeledAnyXmlNodeDataWithSchema;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+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.Module;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+/**
+ * This class provides functionality for parsing an XML source containing YANG-modeled data. It disallows multiple
+ * instances of the same element except for leaf-list and list entries. It also expects that the YANG-modeled data in
+ * the XML source are wrapped in a root element. This class is NOT thread-safe.
+ *
+ * <p>
+ * Due to backwards compatibility reasons, RFC7952 metadata emitted by this parser may include key QNames with empty URI
+ * (as exposed via {@link #LEGACY_ATTRIBUTE_NAMESPACE}) as their QNameModule. These indicate an unqualified XML
+ * attribute and their value can be assumed to be a String. Furthermore, this extends to qualified attributes, which
+ * uses the proper namespace, but will not bind to a proper module revision -- these need to be reconciled with a
+ * particular SchemaContext and are expected to either be fully decoded, or contain a String value. Handling of such
+ * annotations is at the discretion of the user encountering it: preferred way of handling is to either filter or
+ * normalize them to proper QNames/values when encountered. This caveat will be removed in a future version.
+ */
+@Beta
+public final class XmlParserStream implements Closeable, Flushable {
+    /**
+     * {@link QNameModule} for use with legacy XML attributes.
+     * @deprecated The use on this namespace is discouraged and users are strongly encouraged to proper RFC7952 metadata
+     *             annotations.
+     */
+    @Deprecated
+    public static final QNameModule LEGACY_ATTRIBUTE_NAMESPACE = QNameModule.create(URI.create("")).intern();
+
+    private static final Logger LOG = LoggerFactory.getLogger(XmlParserStream.class);
+    private static final String XML_STANDARD_VERSION = "1.0";
+    private static final String COM_SUN_TRANSFORMER =
+            "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl";
+
+    private static final TransformerFactory TRANSFORMER_FACTORY;
+
+    static {
+        TransformerFactory fa = TransformerFactory.newInstance();
+        if (!fa.getFeature(StAXSource.FEATURE)) {
+            LOG.warn("Platform-default TransformerFactory {} does not support StAXSource, attempting fallback to {}",
+                    fa, COM_SUN_TRANSFORMER);
+            fa = TransformerFactory.newInstance(COM_SUN_TRANSFORMER, null);
+            if (!fa.getFeature(StAXSource.FEATURE)) {
+                throw new TransformerFactoryConfigurationError("No TransformerFactory supporting StAXResult found.");
+            }
+        }
+
+        TRANSFORMER_FACTORY = fa;
+    }
+
+    // Cache of nsUri Strings to QNameModules, as resolved in context
+    private final Map<String, Optional<QNameModule>> resolvedNamespaces = new HashMap<>();
+    // Cache of nsUri Strings to QNameModules, as inferred from document
+    private final Map<String, QNameModule> rawNamespaces = new HashMap<>();
+    private final NormalizedNodeStreamWriter writer;
+    private final XmlCodecFactory codecs;
+    private final DataSchemaNode parentNode;
+    private final boolean strictParsing;
+
+    private XmlParserStream(final NormalizedNodeStreamWriter writer, final XmlCodecFactory codecs,
+            final DataSchemaNode parentNode, final boolean strictParsing) {
+        this.writer = requireNonNull(writer);
+        this.codecs = requireNonNull(codecs);
+        this.parentNode = parentNode;
+        this.strictParsing = strictParsing;
+    }
+
+    /**
+     * Construct a new {@link XmlParserStream} with strict parsing mode switched on.
+     *
+     * @param writer Output writer
+     * @param codecs Shared codecs
+     * @param parentNode Parent root node
+     * @return A new stream instance
+     */
+    public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final XmlCodecFactory codecs,
+            final SchemaNode parentNode) {
+        return create(writer, codecs, parentNode, true);
+    }
+
+    /**
+     * Construct a new {@link XmlParserStream}.
+     *
+     * @param writer Output writer
+     * @param codecs Shared codecs
+     * @param parentNode Parent root node
+     * @param strictParsing parsing mode
+     *            if set to true, the parser will throw an exception if it encounters unknown child nodes
+     *            (nodes, that are not defined in the provided SchemaContext) in containers and lists
+     *            if set to false, the parser will skip unknown child nodes
+     * @return A new stream instance
+     */
+    public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final XmlCodecFactory codecs,
+            final SchemaNode parentNode, final boolean strictParsing) {
+        final DataSchemaNode parent;
+        if (parentNode instanceof DataSchemaNode) {
+            parent = (DataSchemaNode) parentNode;
+        } else if (parentNode instanceof OperationDefinition) {
+            parent = OperationAsContainer.of((OperationDefinition) parentNode);
+        } else {
+            throw new IllegalArgumentException("Illegal parent node " + parentNode);
+        }
+        return new XmlParserStream(writer, codecs, parent, strictParsing);
+    }
+
+    /**
+     * Utility method for use when caching {@link XmlCodecFactory} is not feasible. Users with high performance
+     * requirements should use {@link #create(NormalizedNodeStreamWriter, XmlCodecFactory, SchemaNode)} instead and
+     * maintain a {@link XmlCodecFactory} to match the current {@link SchemaContext}.
+     */
+    public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext,
+            final SchemaNode parentNode) {
+        return create(writer, schemaContext, parentNode, true);
+    }
+
+    /**
+     * Utility method for use when caching {@link XmlCodecFactory} is not feasible. Users with high performance
+     * requirements should use {@link #create(NormalizedNodeStreamWriter, XmlCodecFactory, SchemaNode)} instead and
+     * maintain a {@link XmlCodecFactory} to match the current {@link SchemaContext}.
+     */
+    public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext,
+            final SchemaNode parentNode, final boolean strictParsing) {
+        return create(writer, XmlCodecFactory.create(schemaContext), parentNode, strictParsing);
+    }
+
+    /**
+     * Utility method for use when caching {@link XmlCodecFactory} is not feasible. Users with high performance
+     * requirements should use {@link #create(NormalizedNodeStreamWriter, XmlCodecFactory, SchemaNode)} instead and
+     * maintain a {@link XmlCodecFactory} to match the current {@link MountPointContext}.
+     */
+    public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final MountPointContext mountCtx,
+            final SchemaNode parentNode) {
+        return create(writer, mountCtx, parentNode, true);
+    }
+
+    /**
+     * Utility method for use when caching {@link XmlCodecFactory} is not feasible. Users with high performance
+     * requirements should use {@link #create(NormalizedNodeStreamWriter, XmlCodecFactory, SchemaNode)} instead and
+     * maintain a {@link XmlCodecFactory} to match the current {@link MountPointContext}.
+     */
+    public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final MountPointContext mountCtx,
+            final SchemaNode parentNode, final boolean strictParsing) {
+        return create(writer, XmlCodecFactory.create(mountCtx), parentNode, strictParsing);
+    }
+
+    /**
+     * This method parses the XML source and emits node events into a NormalizedNodeStreamWriter based on the
+     * YANG-modeled data contained in the XML source.
+     *
+     * @param reader
+     *              StAX reader which is to used to walk through the XML source
+     * @return
+     *              instance of XmlParserStream
+     * @throws XMLStreamException
+     *              if a well-formedness error or an unexpected processing condition occurs while parsing the XML
+     * @throws URISyntaxException
+     *              if the namespace URI of an XML element contains a syntax error
+     * @throws IOException
+     *              if an error occurs while parsing the value of an anyxml node
+     * @throws SAXException
+     *              if an error occurs while parsing the value of an anyxml node
+     */
+    public XmlParserStream parse(final XMLStreamReader reader) throws XMLStreamException, URISyntaxException,
+            IOException, SAXException {
+        if (reader.hasNext()) {
+            reader.nextTag();
+            final AbstractNodeDataWithSchema<?> nodeDataWithSchema;
+            if (parentNode instanceof ContainerSchemaNode) {
+                nodeDataWithSchema = new ContainerNodeDataWithSchema((ContainerSchemaNode) parentNode);
+            } else if (parentNode instanceof ListSchemaNode) {
+                nodeDataWithSchema = new ListNodeDataWithSchema((ListSchemaNode) parentNode);
+            } else if (parentNode instanceof YangModeledAnyxmlSchemaNode) {
+                nodeDataWithSchema = new YangModeledAnyXmlNodeDataWithSchema((YangModeledAnyxmlSchemaNode) parentNode);
+            } else if (parentNode instanceof AnyxmlSchemaNode) {
+                nodeDataWithSchema = new AnyXmlNodeDataWithSchema((AnyxmlSchemaNode) parentNode);
+            } else if (parentNode instanceof LeafSchemaNode) {
+                nodeDataWithSchema = new LeafNodeDataWithSchema((LeafSchemaNode) parentNode);
+            } else if (parentNode instanceof LeafListSchemaNode) {
+                nodeDataWithSchema = new LeafListNodeDataWithSchema((LeafListSchemaNode) parentNode);
+            } else if (parentNode instanceof AnydataSchemaNode) {
+                nodeDataWithSchema = new AnydataNodeDataWithSchema((AnydataSchemaNode) parentNode);
+            } else {
+                throw new IllegalStateException("Unsupported schema node type " + parentNode.getClass() + ".");
+            }
+
+            read(reader, nodeDataWithSchema, reader.getLocalName());
+            nodeDataWithSchema.write(writer);
+        }
+
+        return this;
+    }
+
+    /**
+     * This method traverses a {@link DOMSource} and emits node events into a NormalizedNodeStreamWriter based on the
+     * YANG-modeled data contained in the source.
+     *
+     * @param src
+     *              {@link DOMSource} to be traversed
+     * @return
+     *              instance of XmlParserStream
+     * @throws XMLStreamException
+     *              if a well-formedness error or an unexpected processing condition occurs while parsing the XML
+     * @throws URISyntaxException
+     *              if the namespace URI of an XML element contains a syntax error
+     * @throws IOException
+     *              if an error occurs while parsing the value of an anyxml node
+     * @throws SAXException
+     *              if an error occurs while parsing the value of an anyxml node
+     */
+    @Beta
+    public XmlParserStream traverse(final DOMSource src) throws XMLStreamException, URISyntaxException, IOException,
+            SAXException {
+        return parse(new DOMSourceXMLStreamReader(src));
+    }
+
+    private ImmutableMap<QName, Object> getElementAttributes(final XMLStreamReader in) {
+        checkState(in.isStartElement(), "Attributes can be extracted only from START_ELEMENT.");
+        final Map<QName, Object> attributes = new LinkedHashMap<>();
+
+        for (int attrIndex = 0; attrIndex < in.getAttributeCount(); attrIndex++) {
+            final String attributeNS = in.getAttributeNamespace(attrIndex);
+
+            // Skip namespace definitions
+            if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(attributeNS)) {
+                continue;
+            }
+
+            final String localName = in.getAttributeLocalName(attrIndex);
+            final String attrValue = in.getAttributeValue(attrIndex);
+            if (Strings.isNullOrEmpty(attributeNS)) {
+                StreamWriterFacade.warnLegacyAttribute(localName);
+                attributes.put(QName.create(LEGACY_ATTRIBUTE_NAMESPACE, localName), attrValue);
+                continue;
+            }
+
+            // Cross-relate attribute namespace to the module
+            final Optional<QNameModule> optModule = resolveXmlNamespace(attributeNS);
+            if (optModule.isPresent()) {
+                final QName qname = QName.create(optModule.get(), localName);
+                final Optional<AnnotationSchemaNode> optAnnotation = AnnotationSchemaNode.find(
+                    codecs.getSchemaContext(), qname);
+                if (optAnnotation.isPresent()) {
+                    final AnnotationSchemaNode schema = optAnnotation.get();
+                    final Object value = codecs.codecFor(schema).parseValue(in.getNamespaceContext(), attrValue);
+                    attributes.put(schema.getQName(), value);
+                    continue;
+                }
+
+                LOG.debug("Annotation for {} not found, using legacy QName", qname);
+            }
+
+            attributes.put(QName.create(rawXmlNamespace(attributeNS), localName), attrValue);
+        }
+
+        return ImmutableMap.copyOf(attributes);
+    }
+
+    private static Document readAnyXmlValue(final XMLStreamReader in) throws XMLStreamException {
+        // Underlying reader might return null when asked for version, however when such reader is plugged into
+        // Stax -> DOM transformer, it fails with NPE due to null version. Use default xml version in such case.
+        final XMLStreamReader inWrapper;
+        if (in.getVersion() == null) {
+            inWrapper = new StreamReaderDelegate(in) {
+                @Override
+                public String getVersion() {
+                    final String ver = super.getVersion();
+                    return ver != null ? ver : XML_STANDARD_VERSION;
+                }
+            };
+        } else {
+            inWrapper = in;
+        }
+
+        final DOMResult result = new DOMResult();
+        try {
+            TRANSFORMER_FACTORY.newTransformer().transform(new StAXSource(inWrapper), result);
+        } catch (final TransformerException e) {
+            throw new XMLStreamException("Unable to read anyxml value", e);
+        }
+        return (Document) result.getNode();
+    }
+
+    private void read(final XMLStreamReader in, final AbstractNodeDataWithSchema<?> parent, final String rootElement)
+            throws XMLStreamException {
+        if (!in.hasNext()) {
+            return;
+        }
+
+        if (parent instanceof LeafNodeDataWithSchema || parent instanceof LeafListEntryNodeDataWithSchema) {
+            parent.setAttributes(getElementAttributes(in));
+            setValue((SimpleNodeDataWithSchema<?>) parent, in.getElementText().trim(), in.getNamespaceContext());
+            if (isNextEndDocument(in)) {
+                return;
+            }
+
+            if (!isAtElement(in)) {
+                in.nextTag();
+            }
+            return;
+        }
+
+        if (parent instanceof ListEntryNodeDataWithSchema || parent instanceof ContainerNodeDataWithSchema) {
+            parent.setAttributes(getElementAttributes(in));
+        }
+
+        if (parent instanceof LeafListNodeDataWithSchema || parent instanceof ListNodeDataWithSchema) {
+            String xmlElementName = in.getLocalName();
+            while (xmlElementName.equals(parent.getSchema().getQName().getLocalName())) {
+                read(in, newEntryNode(parent), rootElement);
+                if (in.getEventType() == XMLStreamConstants.END_DOCUMENT
+                        || in.getEventType() == XMLStreamConstants.END_ELEMENT) {
+                    break;
+                }
+                xmlElementName = in.getLocalName();
+            }
+
+            return;
+        }
+
+        if (parent instanceof AnyXmlNodeDataWithSchema) {
+            setValue((AnyXmlNodeDataWithSchema) parent, readAnyXmlValue(in), in.getNamespaceContext());
+            if (isNextEndDocument(in)) {
+                return;
+            }
+
+            if (!isAtElement(in)) {
+                in.nextTag();
+            }
+
+            return;
+        }
+
+        if (parent instanceof AnydataNodeDataWithSchema) {
+            final AnydataNodeDataWithSchema anydata = (AnydataNodeDataWithSchema) parent;
+            anydata.setObjectModel(DOMSourceAnydata.class);
+            anydata.setAttributes(getElementAttributes(in));
+            setValue(anydata, readAnyXmlValue(in), in.getNamespaceContext());
+            if (isNextEndDocument(in)) {
+                return;
+            }
+
+            if (!isAtElement(in)) {
+                in.nextTag();
+            }
+
+            return;
+        }
+
+        if (parent instanceof YangModeledAnyxmlSchemaNode) {
+            parent.setAttributes(getElementAttributes(in));
+        }
+
+        switch (in.nextTag()) {
+            case XMLStreamConstants.START_ELEMENT:
+                // FIXME: why do we even need this tracker? either document it or remove it
+                final Set<Entry<String, String>> namesakes = new HashSet<>();
+                while (in.hasNext()) {
+                    final String xmlElementName = in.getLocalName();
+
+                    DataSchemaNode parentSchema = parent.getSchema();
+
+                    final String parentSchemaName = parentSchema.getQName().getLocalName();
+                    if (parentSchemaName.equals(xmlElementName)
+                            && in.getEventType() == XMLStreamConstants.END_ELEMENT) {
+                        if (isNextEndDocument(in)) {
+                            break;
+                        }
+
+                        if (!isAtElement(in)) {
+                            in.nextTag();
+                        }
+                        break;
+                    }
+
+                    if (in.isEndElement() && rootElement.equals(xmlElementName)) {
+                        break;
+                    }
+
+                    if (parentSchema instanceof YangModeledAnyxmlSchemaNode) {
+                        parentSchema = ((YangModeledAnyxmlSchemaNode) parentSchema).getSchemaOfAnyXmlData();
+                    }
+
+                    final String elementNS = in.getNamespaceURI();
+                    if (!namesakes.add(new SimpleImmutableEntry<>(elementNS, xmlElementName))) {
+                        throw new XMLStreamException(String.format(
+                            "Duplicate namespace \"%s\" element \"%s\" in XML input", elementNS, xmlElementName),
+                            in.getLocation());
+                    }
+
+                    final URI nsUri;
+                    try {
+                        nsUri = rawXmlNamespace(elementNS).getNamespace();
+                    } catch (IllegalArgumentException e) {
+                        throw new XMLStreamException("Failed to convert namespace " + xmlElementName, in.getLocation(),
+                            e);
+                    }
+
+                    final Deque<DataSchemaNode> childDataSchemaNodes =
+                            ParserStreamUtils.findSchemaNodeByNameAndNamespace(parentSchema, xmlElementName, nsUri);
+                    if (!childDataSchemaNodes.isEmpty()) {
+                        // We have a match, proceed with it
+                        read(in, ((CompositeNodeDataWithSchema<?>) parent).addChild(childDataSchemaNodes), rootElement);
+                        continue;
+                    }
+
+                    if (parent instanceof AbstractMountPointDataWithSchema) {
+                        // Parent can potentially hold a mount point, let's see if there is a label present
+                        final Optional<MountPointSchemaNode> optMount;
+                        if (parentSchema instanceof ContainerSchemaNode) {
+                            optMount = MountPointSchemaNode.streamAll((ContainerSchemaNode) parentSchema).findFirst();
+                        } else if (parentSchema instanceof ListSchemaNode) {
+                            optMount = MountPointSchemaNode.streamAll((ListSchemaNode) parentSchema).findFirst();
+                        } else {
+                            throw new XMLStreamException("Unhandled mount-aware schema " + parentSchema);
+                        }
+
+                        if (optMount.isPresent()) {
+                            final MountPointIdentifier mountId = MountPointIdentifier.of(optMount.get().getQName());
+                            LOG.debug("Assuming node {} and namespace {} belongs to mount point {}", xmlElementName,
+                                nsUri, mountId);
+
+                            final Optional<MountPointContextFactory> optFactory = codecs.mountPointContext()
+                                    .findMountPoint(mountId);
+                            if (optFactory.isPresent()) {
+                                final MountPointData mountData = ((AbstractMountPointDataWithSchema<?>) parent)
+                                        .getMountPointData(mountId, optFactory.get());
+                                addMountPointChild(mountData, nsUri, xmlElementName,
+                                    new DOMSource(readAnyXmlValue(in).getDocumentElement()));
+                                continue;
+                            }
+
+                            LOG.debug("Mount point {} not attached", mountId);
+                        }
+                    }
+
+                    // We have not handled the node -- let's decide what to do about that
+                    if (strictParsing) {
+                        throw new XMLStreamException(String.format(
+                            "Schema for node with name %s and namespace %s does not exist at %s", xmlElementName,
+                            elementNS, parentSchema.getPath(), in.getLocation()));
+                    }
+
+                    LOG.debug("Skipping unknown node ns=\"{}\" localName=\"{}\" at path {}", elementNS, xmlElementName,
+                        parentSchema.getPath());
+                    skipUnknownNode(in);
+                }
+                break;
+            case XMLStreamConstants.END_ELEMENT:
+                if (isNextEndDocument(in)) {
+                    break;
+                }
+
+                if (!isAtElement(in)) {
+                    in.nextTag();
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    private static void addMountPointChild(final MountPointData mount, final URI namespace, final String localName,
+            final DOMSource source) {
+        final DOMSourceMountPointChild child = new DOMSourceMountPointChild(source);
+        if (YangLibraryConstants.MODULE_NAMESPACE.equals(namespace)) {
+            final Optional<ContainerName> optName = ContainerName.forLocalName(localName);
+            if (optName.isPresent()) {
+                mount.setContainer(optName.get(), child);
+                return;
+            }
+
+            LOG.warn("Encountered unknown element {} from YANG Library namespace", localName);
+        } else if (SchemaMountConstants.RFC8528_MODULE.getNamespace().equals(namespace)) {
+            mount.setSchemaMounts(child);
+            return;
+        }
+
+        mount.addChild(child);
+    }
+
+    private static boolean isNextEndDocument(final XMLStreamReader in) throws XMLStreamException {
+        return !in.hasNext() || in.next() == XMLStreamConstants.END_DOCUMENT;
+    }
+
+    private static boolean isAtElement(final XMLStreamReader in) {
+        return in.getEventType() == XMLStreamConstants.START_ELEMENT
+                || in.getEventType() == XMLStreamConstants.END_ELEMENT;
+    }
+
+    private static void skipUnknownNode(final XMLStreamReader in) throws XMLStreamException {
+        // in case when the unknown node and at least one of its descendant nodes have the same name
+        // we cannot properly reach the end just by checking if the current node is an end element and has the same name
+        // as the root unknown element. therefore we ignore the names completely and just track the level of nesting
+        int levelOfNesting = 0;
+        while (in.hasNext()) {
+            // in case there are text characters in an element, we cannot skip them by calling nextTag()
+            // therefore we skip them by calling next(), and then proceed to next element
+            in.next();
+            if (!isAtElement(in)) {
+                in.nextTag();
+            }
+            if (in.isStartElement()) {
+                levelOfNesting++;
+            }
+
+            if (in.isEndElement()) {
+                if (levelOfNesting == 0) {
+                    break;
+                }
+
+                levelOfNesting--;
+            }
+        }
+
+        in.nextTag();
+    }
+
+    private void setValue(final SimpleNodeDataWithSchema<?> parent, final Object value,
+            final NamespaceContext nsContext) {
+        final DataSchemaNode schema = parent.getSchema();
+        final Object prev = parent.getValue();
+        checkArgument(prev == null, "Node '%s' has already set its value to '%s'", schema.getQName(), prev);
+        parent.setValue(translateValueByType(value, schema, nsContext));
+    }
+
+    private Object translateValueByType(final Object value, final DataSchemaNode node,
+            final NamespaceContext namespaceCtx) {
+        if (node instanceof AnyxmlSchemaNode) {
+            checkArgument(value instanceof Document);
+            /*
+             * FIXME: Figure out some YANG extension dispatch, which will reuse JSON parsing or XML parsing -
+             *        anyxml is not well-defined in JSON.
+             */
+            return new DOMSource(((Document) value).getDocumentElement());
+        }
+        if (node instanceof AnydataSchemaNode) {
+            checkArgument(value instanceof Document);
+            return new DOMSourceAnydata(new DOMSource(((Document) value).getDocumentElement()));
+        }
+
+        checkArgument(node instanceof TypedDataSchemaNode);
+        checkArgument(value instanceof String);
+        return codecs.codecFor((TypedDataSchemaNode) node).parseValue(namespaceCtx, (String) value);
+    }
+
+    private static AbstractNodeDataWithSchema<?> newEntryNode(final AbstractNodeDataWithSchema<?> parent) {
+        final AbstractNodeDataWithSchema<?> newChild;
+        if (parent instanceof ListNodeDataWithSchema) {
+            newChild = ListEntryNodeDataWithSchema.forSchema(((ListNodeDataWithSchema) parent).getSchema());
+        } else {
+            verify(parent instanceof LeafListNodeDataWithSchema, "Unexpected parent %s", parent);
+            newChild = new LeafListEntryNodeDataWithSchema(((LeafListNodeDataWithSchema) parent).getSchema());
+        }
+        ((CompositeNodeDataWithSchema<?>) parent).addChild(newChild);
+        return newChild;
+    }
+
+    @Override
+    public void close() throws IOException {
+        writer.flush();
+        writer.close();
+    }
+
+    @Override
+    public void flush() throws IOException {
+        writer.flush();
+    }
+
+    private Optional<QNameModule> resolveXmlNamespace(final String xmlNamespace) {
+        return resolvedNamespaces.computeIfAbsent(xmlNamespace, nsUri -> {
+            final Iterator<Module> it = codecs.getSchemaContext().findModules(URI.create(nsUri)).iterator();
+            return it.hasNext() ? Optional.of(it.next().getQNameModule()) : Optional.empty();
+        });
+    }
+
+    private QNameModule rawXmlNamespace(final String xmlNamespace) {
+        return rawNamespaces.computeIfAbsent(xmlNamespace, nsUri -> QNameModule.create(URI.create(nsUri)));
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java
new file mode 100644 (file)
index 0000000..dc3aa59
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import java.net.URI;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Iterator;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+final class XmlStringInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec
+        implements XmlCodec<YangInstanceIdentifier> {
+
+    private static final ThreadLocal<Deque<NamespaceContext>> TL_CONTEXT = new ThreadLocal<>();
+
+    private final @NonNull DataSchemaContextTree dataContextTree;
+    private final @NonNull XmlCodecFactory codecFactory;
+    private final @NonNull SchemaContext context;
+
+    XmlStringInstanceIdentifierCodec(final SchemaContext context, final XmlCodecFactory xmlCodecFactory) {
+        this.context = requireNonNull(context);
+        this.dataContextTree = DataSchemaContextTree.from(context);
+        this.codecFactory = requireNonNull(xmlCodecFactory);
+    }
+
+    @Override
+    protected Module moduleForPrefix(final String prefix) {
+        final String prefixedNS = getNamespaceContext().getNamespaceURI(prefix);
+        final Iterator<Module> modules = context.findModules(URI.create(prefixedNS)).iterator();
+        return modules.hasNext() ? modules.next() : null;
+    }
+
+    @Override
+    protected String prefixForNamespace(final URI namespace) {
+        final Iterator<Module> modules = context.findModules(namespace).iterator();
+        return modules.hasNext() ? modules.next().getName() : null;
+    }
+
+    @Override
+    protected DataSchemaContextTree getDataContextTree() {
+        return dataContextTree;
+    }
+
+    @Override
+    protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final String value) {
+        requireNonNull(schemaNode, "schemaNode cannot be null");
+        checkArgument(schemaNode instanceof LeafSchemaNode, "schemaNode must be of type LeafSchemaNode");
+        final XmlCodec<?> objectXmlCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode);
+        return objectXmlCodec.parseValue(getNamespaceContext(), value);
+    }
+
+    @Override
+    public Class<YangInstanceIdentifier> getDataType() {
+        return YangInstanceIdentifier.class;
+    }
+
+    @Override
+    public YangInstanceIdentifier parseValue(final NamespaceContext ctx, final String str) {
+        pushNamespaceContext(ctx);
+        try {
+            return deserialize(str);
+        } finally {
+            popNamespaceContext();
+        }
+    }
+
+    @Override
+    public void writeValue(final XMLStreamWriter ctx, final YangInstanceIdentifier value)
+            throws XMLStreamException {
+        ctx.writeCharacters(serialize(value));
+    }
+
+    private static NamespaceContext getNamespaceContext() {
+        return TL_CONTEXT.get().getFirst();
+    }
+
+    private static void popNamespaceContext() {
+        final Deque<NamespaceContext> stack = TL_CONTEXT.get();
+        stack.pop();
+        if (stack.isEmpty()) {
+            TL_CONTEXT.set(null);
+        }
+    }
+
+    private static void pushNamespaceContext(final NamespaceContext context) {
+        Deque<NamespaceContext> stack = TL_CONTEXT.get();
+        if (stack == null) {
+            stack = new ArrayDeque<>(1);
+            TL_CONTEXT.set(stack);
+        }
+        stack.push(context);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractAnydataTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractAnydataTest.java
new file mode 100644 (file)
index 0000000..2e9340a
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+public abstract class AbstractAnydataTest {
+    static final QName FOO_QNAME = QName.create("test-anydata", "foo");
+    static final QName CONT_QNAME = QName.create(FOO_QNAME, "cont");
+    static final QName CONT_ANY_QNAME = QName.create(FOO_QNAME, "cont-any");
+    static final QName CONT_LEAF_QNAME = QName.create(FOO_QNAME, "cont-leaf");
+
+    static final NodeIdentifier FOO_NODEID = NodeIdentifier.create(FOO_QNAME);
+    static final NodeIdentifier CONT_NODEID = NodeIdentifier.create(CONT_QNAME);
+    static final NodeIdentifier CONT_ANY_NODEID = NodeIdentifier.create(CONT_ANY_QNAME);
+    static final NodeIdentifier CONT_LEAF_NODEID = NodeIdentifier.create(CONT_LEAF_QNAME);
+
+    static final LeafNode<?> CONT_LEAF = ImmutableLeafNodeBuilder.create().withNodeIdentifier(CONT_LEAF_NODEID)
+            .withValue("abc").build();
+
+    static SchemaContext SCHEMA_CONTEXT;
+
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        SCHEMA_CONTEXT = YangParserTestUtils.parseYangResource("/test-anydata.yang");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    static DOMSourceAnydata toDOMSource(final String str) {
+        try {
+            return new DOMSourceAnydata(new DOMSource(
+                // DOMSource must have a single document element, which we are ignoring
+                readXmlToDocument(toInputStream("<IGNORED>" + str + "</IGNORED>")).getDocumentElement()));
+        } catch (IOException | SAXException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    static InputStream toInputStream(final String str) {
+        return new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));
+    }
+
+    static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+        final Document doc = UntrustedXML.newDocumentBuilder().parse(xmlContent);
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    /**
+     * Load external XML resources.
+     * @param xmlPath {@link String} path to source
+     * @return {@link Document}
+     * @throws IOException Exception in Loading file
+     * @throws SAXException XML parse exception
+     */
+    static Document loadXmlDocument(final String xmlPath) throws IOException, SAXException {
+        return requireNonNull(readXmlToDocument(loadResourcesAsInputStream(xmlPath)));
+    }
+
+    /**
+     * Load external resources as {@link InputStream}.
+     * @param xmlPath {@link String} Path to file
+     * @return {@link InputStream}
+     */
+    static InputStream loadResourcesAsInputStream(final String xmlPath) {
+        return SchemalessXMLStreamNormalizedNodeStreamWriterTest.class
+                .getResourceAsStream(xmlPath);
+    }
+
+    /**
+     * Transform Node object to formatted XML string.
+     * @param xml {@link Node}
+     * @return {@link String}
+     */
+    static String toString(final Node xml) {
+        try {
+            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+            final StreamResult result = new StreamResult(new StringWriter());
+            final DOMSource source = new DOMSource(xml);
+            transformer.transform(source, result);
+
+            return result.getWriter().toString();
+        } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+            throw new RuntimeException("Unable to serialize xml element " + xml, e);
+        }
+    }
+
+    /**
+     * Transform DomSource object to formatted XML string.
+     * @param node {@link DOMSource}
+     * @return {@link String}
+     * @throws TransformerException Internal {@link Transformer} exception
+     */
+    static String getXmlFromDOMSource(final DOMSource node) throws TransformerException {
+        StringWriter writer = new StringWriter();
+        Transformer transformer = TransformerFactory.newInstance().newTransformer();
+        transformer.transform(node, new StreamResult(writer));
+        return writer.toString();
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnyXmlWithParamsParsingTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnyXmlWithParamsParsingTest.java
new file mode 100644 (file)
index 0000000..6c33dd7
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2018 FRINX 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;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.io.InputStream;
+import java.io.StringWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+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.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Document;
+
+public class AnyXmlWithParamsParsingTest {
+
+    private static final InputStream EDIT_CONFIG = XmlToNormalizedNodesTest.class.getResourceAsStream(
+            "/anyxml-support/params/edit.xml");
+
+    private static final SchemaContext SCHEMA = YangParserTestUtils.parseYangResourceDirectory(
+            "/anyxml-support/params/");
+
+    private static final SchemaNode SCHEMA_NODE = SCHEMA.getOperations().stream()
+            .filter(o -> o.getQName().getLocalName().equals("edit-config"))
+            .findFirst()
+            .map(OperationDefinition::getInput)
+            .get();
+
+    @Test
+    public void testAnyXmlWithParams() throws Exception {
+        final Document doc = UntrustedXML.newDocumentBuilder().parse(EDIT_CONFIG);
+
+        final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(writer, SCHEMA, SCHEMA_NODE);
+        xmlParser.traverse(new DOMSource(doc.getDocumentElement()));
+        final NormalizedNode<?, ?> parsed = resultHolder.getResult();
+
+        final DataContainerChild<? extends PathArgument, ?> editCfg = ((ContainerNode) parsed)
+                .getChild(getNodeId(parsed, "edit-content")).get();
+
+        final DOMSource anyXmlParsedDom = ((DOMSourceAnyxmlNode) ((ChoiceNode) editCfg)
+                .getChild(getNodeId(parsed, "config")).get())
+                .getValue();
+
+        assertNotNull(anyXmlParsedDom);
+        final String anyXmlParsedDomString = toStringDom(anyXmlParsedDom);
+
+        assertThat(anyXmlParsedDomString, containsString(
+                "active xmpref:prefixed2=\"attribute2\""));
+        assertThat(anyXmlParsedDomString, containsString(
+                "interface-name xmpref:prefixed3=\"attribute3\""));
+        assertThat(anyXmlParsedDomString, containsString(
+                "interface-configuration xmlns:xmpref=\"xml:namespace:prefix\" simple=\"attribute\""));
+        assertThat(anyXmlParsedDomString, containsString(
+                "interface-configurations xmlns=\"http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg\""));
+    }
+
+    private static NodeIdentifier getNodeId(final NormalizedNode<?, ?> parsed, final String localName) {
+        return new NodeIdentifier(QName.create(parsed.getNodeType(), localName));
+    }
+
+    private static String toStringDom(final DOMSource source) {
+        try {
+            final StringWriter sw = new StringWriter();
+            final TransformerFactory tf = TransformerFactory.newInstance();
+            final Transformer transformer = tf.newTransformer();
+            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
+            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+            transformer.transform(source, new StreamResult(sw));
+            return sw.toString();
+        } catch (final TransformerException ex) {
+            throw new RuntimeException("Error converting to String", ex);
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataNormalizeToContainerTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataNormalizeToContainerTest.java
new file mode 100644 (file)
index 0000000..b65b22b
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
+import org.opendaylight.yangtools.yang.data.api.schema.AnydataNormalizationException;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedAnydata;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.xml.sax.SAXException;
+
+public class AnydataNormalizeToContainerTest extends AbstractAnydataTest {
+
+    @Test
+    public void testAnydataNormalizeToContainer()
+            throws XMLStreamException, SAXException, IOException, URISyntaxException, AnydataNormalizationException {
+        //Create Data Scheme from yang file
+        SchemaPath anydataPath = SchemaPath.create(true, FOO_QNAME);
+        final SchemaNode fooSchemaNode = SchemaContextUtil.findDataSchemaNode(SCHEMA_CONTEXT, anydataPath);
+        assertTrue(fooSchemaNode instanceof AnydataSchemaNode);
+        final AnydataSchemaNode anyDataSchemaNode = (AnydataSchemaNode) fooSchemaNode;
+
+        SchemaPath containerPath = SchemaPath.create(true, CONT_QNAME);
+        final SchemaNode barSchemaNode = SchemaContextUtil.findDataSchemaNode(SCHEMA_CONTEXT, containerPath);
+        assertTrue(barSchemaNode instanceof ContainerSchemaNode);
+        final ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) barSchemaNode;
+
+        // deserialization
+        final XMLStreamReader reader
+                = UntrustedXML.createXMLStreamReader(toInputStream("<foo xmlns=\"test-anydata\">"
+                                                                  +     "<bar xmlns=\"test-anydata\">"
+                                                                  +         "<cont-leaf>somedata</cont-leaf>"
+                                                                  +     "</bar>"
+                                                                  + "</foo>"));
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, SCHEMA_CONTEXT, anyDataSchemaNode);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+        assertTrue(transformedInput instanceof AnydataNode);
+        AnydataNode<?> anydataNode = (AnydataNode<?>) transformedInput;
+
+        //Normalize anydata content to specific container element
+        DOMSourceAnydata domSourceAnydata = (DOMSourceAnydata) anydataNode.getValue();
+        NormalizedAnydata normalizedAnydata = domSourceAnydata.normalizeTo(SCHEMA_CONTEXT, containerSchemaNode);
+        assertNotNull(normalizedAnydata);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataParseTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataParseTest.java
new file mode 100644 (file)
index 0000000..fc67047
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.xml.sax.SAXException;
+
+public class AnydataParseTest extends AbstractAnydataTest {
+
+    @Test
+    public void testAnydata() throws XMLStreamException, IOException, URISyntaxException, SAXException {
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(
+            toInputStream("<foo xmlns=\"test-anydata\"><bar/></foo>"));
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, SCHEMA_CONTEXT,
+            SCHEMA_CONTEXT.findDataChildByName(FOO_QNAME).get(), true);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> parsed = result.getResult();
+        assertTrue(parsed instanceof AnydataNode);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataSerializeTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataSerializeTest.java
new file mode 100644 (file)
index 0000000..b3acfb8
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.DifferenceListener;
+import org.custommonkey.xmlunit.IgnoreTextAndAttributeValuesDifferenceListener;
+import org.custommonkey.xmlunit.XMLAssert;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedAnydata;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAnydataNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.util.ImmutableNormalizedAnydata;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+@RunWith(Parameterized.class)
+public class AnydataSerializeTest extends AbstractAnydataTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        return TestFactories.junitParameters();
+    }
+
+    private final XMLOutputFactory factory;
+
+    public AnydataSerializeTest(final String factoryMode, final XMLOutputFactory factory) {
+        this.factory = factory;
+    }
+
+    @Test
+    public void testDOMAnydata() throws XMLStreamException, IOException {
+        final StringWriter writer = new StringWriter();
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(writer);
+
+        final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(
+            xmlStreamWriter, SCHEMA_CONTEXT);
+        final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(
+            xmlNormalizedNodeStreamWriter);
+        normalizedNodeWriter.write(ImmutableAnydataNodeBuilder.create(DOMSourceAnydata.class)
+            .withNodeIdentifier(FOO_NODEID).withValue(toDOMSource("<bar xmlns=\"test-anydata\"/>")).build());
+        normalizedNodeWriter.flush();
+
+        final String serializedXml = writer.toString();
+        assertEquals("<foo xmlns=\"test-anydata\"><bar xmlns=\"test-anydata\"></bar></foo>", serializedXml);
+    }
+
+    @Test
+    public void testXmlParseAnydata()
+            throws IOException, SAXException, XMLStreamException, URISyntaxException, TransformerException {
+
+        //Create Data Scheme from yang file
+        SchemaPath anydataPath = SchemaPath.create(true, FOO_QNAME);
+        final SchemaNode dataSchemaNode = SchemaContextUtil.findDataSchemaNode(SCHEMA_CONTEXT, anydataPath);
+        assertTrue(dataSchemaNode instanceof AnydataSchemaNode);
+        final AnydataSchemaNode anyDataSchemaNode = (AnydataSchemaNode) dataSchemaNode;
+
+        // deserialization
+        final XMLStreamReader reader
+                = UntrustedXML.createXMLStreamReader(loadResourcesAsInputStream("/test-anydata.xml"));
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, SCHEMA_CONTEXT, anyDataSchemaNode);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+        assertTrue(transformedInput instanceof AnydataNode);
+        AnydataNode<?> anydataNode = (AnydataNode<?>) transformedInput;
+
+        // serialization
+        final StringWriter writer = new StringWriter();
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(writer);
+        final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(
+                xmlStreamWriter, SCHEMA_CONTEXT);
+        final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(
+                xmlNormalizedNodeStreamWriter);
+        normalizedNodeWriter.write(transformedInput);
+        normalizedNodeWriter.flush();
+
+        final String serializedXml = writer.toString();
+        final String deserializeXml = getXmlFromDOMSource(((DOMSourceAnydata) anydataNode.getValue()).getSource());
+        assertFalse(serializedXml.isEmpty());
+
+        // Check if is Serialize Node same as Deserialize Node
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setNormalize(true);
+        final Diff diff = new Diff(deserializeXml, serializedXml);
+        final DifferenceListener differenceListener = new IgnoreTextAndAttributeValuesDifferenceListener();
+        diff.overrideDifferenceListener(differenceListener);
+
+        XMLAssert.assertXMLEqual(diff, true);
+    }
+
+    @Test
+    public void testAnydataLoadFromXML() throws IOException, SAXException, XMLStreamException, URISyntaxException {
+        // Load XML file
+        Document doc = loadXmlDocument("/test-anydata.xml");
+        final DOMSource domSource = new DOMSource(doc.getDocumentElement());
+
+        //Load XML from file and write it with xmlParseStream
+        final DOMResult domResult = new DOMResult(UntrustedXML.newDocumentBuilder().newDocument());
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(domResult);
+        final AnydataSchemaNode anyDataSchemaNode = (AnydataSchemaNode) SchemaContextUtil.findDataSchemaNode(
+                SCHEMA_CONTEXT, SchemaPath.create(true, FOO_QNAME));
+        final NormalizedNodeStreamWriter streamWriter = XMLStreamNormalizedNodeStreamWriter.create(
+                xmlStreamWriter, SCHEMA_CONTEXT);
+        final XMLStreamReader reader = new DOMSourceXMLStreamReader(domSource);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, SCHEMA_CONTEXT, anyDataSchemaNode);
+
+        xmlParser.parse(reader);
+        xmlParser.flush();
+
+        //Set XML comparing
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setNormalize(true);
+
+        // Check diff
+        final String expectedXml = toString(doc.getDocumentElement());
+        final String serializedXml = toString(domResult.getNode());
+        final Diff diff = new Diff(expectedXml, serializedXml);
+        final DifferenceListener differenceListener = new IgnoreTextAndAttributeValuesDifferenceListener();
+        diff.overrideDifferenceListener(differenceListener);
+
+        XMLAssert.assertXMLEqual(diff, true);
+    }
+
+    @Test
+    public void testAnydataSerialization()
+            throws IOException, SAXException, XMLStreamException, URISyntaxException, TransformerException {
+        //Get XML Data.
+        Document doc = loadXmlDocument("/test-anydata.xml");
+        final DOMSource domSource = new DOMSource(doc.getDocumentElement());
+
+        //Get specific attribute from Yang file.
+        final AnydataSchemaNode contWithAttr = (AnydataSchemaNode) SchemaContextUtil.findDataSchemaNode(
+                SCHEMA_CONTEXT, SchemaPath.create(true, FOO_QNAME));
+
+        //Create NormalizedNodeResult
+        NormalizedNodeResult normalizedResult = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(normalizedResult);
+
+        //Initialize Reader with XML file
+        final XMLStreamReader reader = new DOMSourceXMLStreamReader(domSource);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, SCHEMA_CONTEXT, contWithAttr);
+        xmlParser.parse(reader);
+        xmlParser.flush();
+
+        //Get Result
+        final NormalizedNode<?, ?> node = normalizedResult.getResult();
+        assertTrue(node instanceof AnydataNode);
+        final AnydataNode<?> anydataResult = (AnydataNode<?>) node;
+
+        //Get Result in formatted String
+        assertTrue(anydataResult.getValue() instanceof DOMSourceAnydata);
+        final String serializedXml = getXmlFromDOMSource(((DOMSourceAnydata)anydataResult.getValue()).getSource());
+        final String expectedXml = toString(doc.getDocumentElement());
+
+        //Looking for difference in Serialized xml and in Loaded XML
+        final Diff diff = new Diff(expectedXml, serializedXml);
+        final DifferenceListener differenceListener = new IgnoreTextAndAttributeValuesDifferenceListener();
+        diff.overrideDifferenceListener(differenceListener);
+
+        XMLAssert.assertXMLEqual(diff, true);
+    }
+
+    @Test
+    public void testSiblingSerialize() throws IOException, XMLStreamException {
+        final StringWriter writer = new StringWriter();
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(writer);
+
+        final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(
+            xmlStreamWriter, SCHEMA_CONTEXT);
+        final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(
+            xmlNormalizedNodeStreamWriter);
+        normalizedNodeWriter.write(ImmutableContainerNodeBuilder.create().withNodeIdentifier(CONT_NODEID)
+            .withChild(ImmutableAnydataNodeBuilder.create(DOMSourceAnydata.class).withNodeIdentifier(CONT_ANY_NODEID)
+                .withValue(toDOMSource("<bar xmlns=\"test-anydata\"/>")).build())
+            .withChild(CONT_LEAF)
+            .build());
+        normalizedNodeWriter.flush();
+
+        final String serializedXml = writer.toString();
+        assertEquals("<cont xmlns=\"test-anydata\"><cont-any><bar xmlns=\"test-anydata\"></bar></cont-any>"
+                + "<cont-leaf>abc</cont-leaf></cont>", serializedXml);
+    }
+
+    @Test
+    public void testNormalizedSerialize() throws IOException, XMLStreamException {
+        final StringWriter writer = new StringWriter();
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(writer);
+
+        final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(
+            xmlStreamWriter, SCHEMA_CONTEXT);
+        final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(
+            xmlNormalizedNodeStreamWriter);
+        normalizedNodeWriter.write(ImmutableContainerNodeBuilder.create().withNodeIdentifier(CONT_NODEID)
+            .withChild(ImmutableAnydataNodeBuilder.create(NormalizedAnydata.class).withNodeIdentifier(CONT_ANY_NODEID)
+                .withValue(new ImmutableNormalizedAnydata(SCHEMA_CONTEXT,
+                    SCHEMA_CONTEXT.findDataChildByName(CONT_QNAME).get(), ImmutableContainerNodeBuilder.create()
+                    .withNodeIdentifier(CONT_NODEID).build())).build())
+            .build());
+        normalizedNodeWriter.flush();
+
+        final String serializedXml = writer.toString();
+        assertEquals("<cont xmlns=\"test-anydata\"><cont-any><cont/></cont-any></cont>", serializedXml);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug5396Test.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug5396Test.java
new file mode 100644 (file)
index 0000000..c26c2d6
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Optional;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+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.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug5396Test {
+
+    private QNameModule fooModuleQName;
+    private SchemaContext schemaContext;
+
+    @Before
+    public void setUp() throws Exception {
+        fooModuleQName = QNameModule.create(new URI("foo"), Revision.of("2016-03-22"));
+        schemaContext = YangParserTestUtils.parseYangResource("/bug5396/yang/foo.yang");
+    }
+
+    @Test
+    public void test() throws Exception {
+        testInputXML("/bug5396/xml/foo.xml", "dp1o34");
+        testInputXML("/bug5396/xml/foo2.xml", "dp0s3f9");
+        testInputXML("/bug5396/xml/foo3.xml", "dp09P1p2s3");
+        testInputXML("/bug5396/xml/foo4.xml", "dp0p3p1");
+        testInputXML("/bug5396/xml/foo5.xml", "dp0s3");
+
+        try {
+            testInputXML("/bug5396/xml/invalid-foo.xml", null);
+            fail("Test should fail due to invalid input string");
+        } catch (IllegalArgumentException e) {
+            assertTrue(e.getMessage().startsWith("Invalid value \"dp09P1p2s1234\" for union type."));
+        }
+    }
+
+    private void testInputXML(final String xmlPath, final String expectedValue) throws Exception {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream(xmlPath);
+        final Module fooModule = schemaContext.getModules().iterator().next();
+        final ContainerSchemaNode rootCont = (ContainerSchemaNode) fooModule.findDataChildByName(
+                QName.create(fooModule.getQNameModule(), "root")).get();
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, rootCont);
+        xmlParser.parse(reader);
+
+        assertNotNull(result.getResult());
+        assertTrue(result.getResult() instanceof ContainerNode);
+        final ContainerNode rootContainer = (ContainerNode) result.getResult();
+
+        Optional<DataContainerChild<? extends PathArgument, ?>> myLeaf = rootContainer.getChild(new NodeIdentifier(
+                QName.create(fooModuleQName, "my-leaf")));
+        assertTrue(myLeaf.orElse(null) instanceof LeafNode);
+
+        assertEquals(expectedValue, myLeaf.get().getValue());
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug5446Test.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug5446Test.java
new file mode 100644 (file)
index 0000000..f9f396c
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.Base64;
+import java.util.Optional;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.XMLTestCase;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+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.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+public class Bug5446Test extends XMLTestCase {
+    private static final QNameModule FOO_MODULE = QNameModule.create(URI.create("foo"), Revision.of("2015-11-05"));
+    private static final QName ROOT_QNAME = QName.create(FOO_MODULE, "root");
+    private static final QName IP_ADDRESS_QNAME = QName.create(FOO_MODULE, "ip-address");
+
+    @Test
+    public void test() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResource("/bug5446/yang/foo.yang");
+        final Document doc = loadDocument("/bug5446/xml/foo.xml");
+
+        final ContainerNode docNode = createDocNode();
+
+        Optional<DataContainerChild<? extends PathArgument, ?>> root = docNode.getChild(new NodeIdentifier(ROOT_QNAME));
+        assertTrue(root.orElse(null) instanceof ContainerNode);
+
+        Optional<DataContainerChild<? extends PathArgument, ?>> child = ((ContainerNode) root.orElse(null))
+                .getChild(new NodeIdentifier(IP_ADDRESS_QNAME));
+        assertTrue(child.orElse(null) instanceof LeafNode);
+        LeafNode<?> ipAdress = (LeafNode<?>) child.get();
+
+        Object value = ipAdress.getValue();
+        assertTrue(value instanceof byte[]);
+        assertEquals("fwAAAQ==", Base64.getEncoder().encodeToString((byte[]) value));
+
+        DOMResult serializationResult = writeNormalizedNode(docNode, schemaContext);
+        assertNotNull(serializationResult);
+
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setIgnoreComments(true);
+        XMLUnit.setIgnoreAttributeOrder(true);
+        XMLUnit.setNormalize(true);
+
+        String expectedXMLString = toString(doc.getDocumentElement());
+        String serializationResultXMLString = toString(serializationResult.getNode());
+
+        assertXMLEqual(expectedXMLString, serializationResultXMLString);
+    }
+
+    private static ContainerNode createDocNode() {
+        LeafNode<byte[]> ipAddress = ImmutableNodes.leafNode(IP_ADDRESS_QNAME, Base64.getDecoder().decode("fwAAAQ=="));
+        ContainerNode root = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(ROOT_QNAME))
+                .withChild(ipAddress).build();
+        return ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(ROOT_QNAME)).withChild(root)
+                .build();
+    }
+
+    private static DOMResult writeNormalizedNode(final ContainerNode normalized, final SchemaContext context)
+            throws IOException, XMLStreamException {
+        final Document doc = UntrustedXML.newDocumentBuilder().newDocument();
+        final DOMResult result = new DOMResult(doc);
+        NormalizedNodeWriter normalizedNodeWriter = null;
+        NormalizedNodeStreamWriter normalizedNodeStreamWriter = null;
+        XMLStreamWriter writer = null;
+        try {
+            writer = TestFactories.DEFAULT_OUTPUT_FACTORY.createXMLStreamWriter(result);
+            normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, context);
+            normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter);
+
+            for (NormalizedNode<?, ?> child : normalized.getValue()) {
+                normalizedNodeWriter.write(child);
+            }
+
+            normalizedNodeWriter.flush();
+        } finally {
+            if (normalizedNodeWriter != null) {
+                normalizedNodeWriter.close();
+            }
+            if (normalizedNodeStreamWriter != null) {
+                normalizedNodeStreamWriter.close();
+            }
+            if (writer != null) {
+                writer.close();
+            }
+        }
+
+        return result;
+    }
+
+    private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+        final InputStream resourceAsStream = Bug5446Test.class.getResourceAsStream(xmlPath);
+        return requireNonNull(readXmlToDocument(resourceAsStream));
+    }
+
+    private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+        final Document doc = UntrustedXML.newDocumentBuilder().parse(xmlContent);
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    private static String toString(final Node xml) {
+        try {
+            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+            final StreamResult result = new StreamResult(new StringWriter());
+            final DOMSource source = new DOMSource(xml);
+            transformer.transform(source, result);
+
+            return result.getWriter().toString();
+        } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+            throw new RuntimeException("Unable to serialize xml element " + xml, e);
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug8083Test.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug8083Test.java
new file mode 100644 (file)
index 0000000..7d8606a
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017 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;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.InputStream;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug8083Test {
+
+    @Test
+    public void testInstanceIdentifierPathWithEmptyListKey() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResource("/bug8083/yang/baz.yang");
+        final Module bazModule = schemaContext.getModules().iterator().next();
+        final ContainerSchemaNode topCont = (ContainerSchemaNode) bazModule.findDataChildByName(
+                QName.create(bazModule.getQNameModule(), "top-cont")).get();
+
+        final InputStream resourceAsStream = Bug8083Test.class.getResourceAsStream("/bug8083/xml/baz.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        // deserialization
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, topCont);
+        xmlParser.parse(reader);
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void testInstanceIdentifierPathWithIdentityrefListKey() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResource("/bug8083/yang/zab.yang");
+        final Module zabModule = schemaContext.getModules().iterator().next();
+        final ContainerSchemaNode topCont = (ContainerSchemaNode) zabModule.findDataChildByName(
+                QName.create(zabModule.getQNameModule(), "top-cont")).get();
+
+        final InputStream resourceAsStream = Bug8083Test.class.getResourceAsStream("/bug8083/xml/zab.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        // deserialization
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, topCont);
+        xmlParser.parse(reader);
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void testInstanceIdentifierPathWithInstanceIdentifierListKey() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResource("/bug8083/yang/foobar.yang");
+        final Module foobarModule = schemaContext.getModules().iterator().next();
+        final ContainerSchemaNode topCont = (ContainerSchemaNode) foobarModule.findDataChildByName(
+                QName.create(foobarModule.getQNameModule(), "top-cont")).get();
+
+        final InputStream resourceAsStream = Bug8083Test.class.getResourceAsStream("/bug8083/xml/foobar.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        // deserialization
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, topCont);
+        xmlParser.parse(reader);
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug8675Test.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug8675Test.java
new file mode 100644 (file)
index 0000000..cc1895e
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.InputStream;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug8675Test {
+
+    private static SchemaContext schemaContext;
+    private static Module fooModule;
+
+    @BeforeClass
+    public static void setup() {
+        schemaContext = YangParserTestUtils.parseYangResource("/bug8675/foo.yang");
+        fooModule = schemaContext.getModules().iterator().next();
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        fooModule = null;
+        schemaContext = null;
+    }
+
+    @Test
+    public void testParsingEmptyElements() throws Exception {
+        final ContainerSchemaNode topLevelContainer = (ContainerSchemaNode) fooModule.findDataChildByName(
+                QName.create(fooModule.getQNameModule(), "top-level-container")).get();
+
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream(
+                "/bug8675/foo.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, topLevelContainer);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void testParsingEmptyRootElement() throws Exception {
+        final ContainerSchemaNode topLevelContainer = (ContainerSchemaNode) fooModule.findDataChildByName(
+                QName.create(fooModule.getQNameModule(), "top-level-container")).get();
+
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream(
+                "/bug8675/foo-2.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, topLevelContainer);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void testListAsRootElement() throws Exception {
+        final ListSchemaNode topLevelList = (ListSchemaNode) fooModule.findDataChildByName(
+                QName.create(fooModule.getQNameModule(), "top-level-list")).get();
+
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/bug8675/foo-3.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, topLevelList);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void testAnyXmlAsRootElement() throws Exception {
+        final AnyxmlSchemaNode topLevelAnyXml = (AnyxmlSchemaNode) fooModule.findDataChildByName(
+                QName.create(fooModule.getQNameModule(), "top-level-anyxml")).get();
+
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/bug8675/foo-4.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, topLevelAnyXml);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void testLeafAsRootElement() throws Exception {
+        final LeafSchemaNode topLevelLeaf = (LeafSchemaNode) fooModule.findDataChildByName(
+                QName.create(fooModule.getQNameModule(), "top-level-leaf")).get();
+
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/bug8675/foo-5.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, topLevelLeaf);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void testLeafListAsRootElement() throws Exception {
+        final LeafListSchemaNode topLevelLeafList = (LeafListSchemaNode) fooModule.findDataChildByName(
+                QName.create(fooModule.getQNameModule(), "top-level-leaf-list")).get();
+
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/bug8675/foo-6.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, topLevelLeafList);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug8745Test.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug8745Test.java
new file mode 100644 (file)
index 0000000..475769e
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.Collection;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLAssert;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+@RunWith(Parameterized.class)
+public class Bug8745Test {
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        return TestFactories.junitParameters();
+    }
+
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    private final XMLOutputFactory factory;
+
+    public Bug8745Test(final String factoryMode, final XMLOutputFactory factory) {
+        this.factory = factory;
+    }
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = YangParserTestUtils.parseYangResource("/bug8745/foo.yang");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    @Test
+    public void testParsingAttributes() throws Exception {
+        final QName contWithAttributes = QName.create("foo", "cont-with-attributes");
+        final ContainerSchemaNode contWithAttr = (ContainerSchemaNode) SchemaContextUtil.findDataSchemaNode(
+            SCHEMA_CONTEXT, SchemaPath.create(true, contWithAttributes));
+
+        final Document doc = loadDocument("/bug8745/foo.xml");
+        final DOMSource domSource = new DOMSource(doc.getDocumentElement());
+
+        final DOMResult domResult = new DOMResult(UntrustedXML.newDocumentBuilder().newDocument());
+
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(domResult);
+
+        final NormalizedNodeStreamWriter streamWriter = XMLStreamNormalizedNodeStreamWriter.create(
+                xmlStreamWriter, SCHEMA_CONTEXT);
+
+        final XMLStreamReader reader = new DOMSourceXMLStreamReader(domSource);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, SCHEMA_CONTEXT, contWithAttr);
+        xmlParser.parse(reader);
+
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setNormalize(true);
+
+        final String expectedXml = toString(doc.getDocumentElement());
+        final String serializedXml = toString(domResult.getNode());
+        final Diff diff = new Diff(expectedXml, serializedXml);
+
+        XMLAssert.assertXMLEqual(diff, true);
+    }
+
+    private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+        final InputStream resourceAsStream = NormalizedNodesToXmlTest.class.getResourceAsStream(xmlPath);
+        return requireNonNull(readXmlToDocument(resourceAsStream));
+    }
+
+    private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+        final Document doc = UntrustedXML.newDocumentBuilder().parse(xmlContent);
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    private static String toString(final Node xml) {
+        try {
+            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+            final StreamResult result = new StreamResult(new StringWriter());
+            final DOMSource source = new DOMSource(xml);
+            transformer.transform(source, result);
+
+            return result.getWriter().toString();
+        } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+            throw new RuntimeException("Unable to serialize xml element " + xml, e);
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug8803Test.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug8803Test.java
new file mode 100644 (file)
index 0000000..ea1a026
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.Collection;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+@RunWith(Parameterized.class)
+public class Bug8803Test {
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        return TestFactories.junitParameters();
+    }
+
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    private final XMLOutputFactory factory;
+
+    public Bug8803Test(final String factoryMode, final XMLOutputFactory factory) {
+        this.factory = factory;
+    }
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = YangParserTestUtils.parseYangResourceDirectory("/bug8803");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    @Test
+    public void test() throws Exception {
+        final SchemaPath topContPath = SchemaPath.create(true, QName.create("foo-ns", "top-cont"));
+        final SchemaNode dataSchemaNode = SchemaContextUtil.findDataSchemaNode(SCHEMA_CONTEXT, topContPath);
+        assertTrue(dataSchemaNode instanceof ContainerSchemaNode);
+        final ContainerSchemaNode topContSchema = (ContainerSchemaNode) dataSchemaNode;
+
+        final InputStream resourceAsStream = Bug8803Test.class.getResourceAsStream("/bug8803/foo.xml");
+
+        // deserialization
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, SCHEMA_CONTEXT, topContSchema);
+        xmlParser.parse(reader);
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+
+        // serialization
+        final StringWriter writer = new StringWriter();
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(writer);
+
+        final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(
+                xmlStreamWriter, SCHEMA_CONTEXT);
+
+        final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(
+                xmlNormalizedNodeStreamWriter);
+        normalizedNodeWriter.write(transformedInput);
+        normalizedNodeWriter.flush();
+
+        final String serializedXml = writer.toString();
+        assertFalse(serializedXml.isEmpty());
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug890Test.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Bug890Test.java
new file mode 100644 (file)
index 0000000..6afd5d6
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies s.r.o. 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Optional;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+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.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+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.test.util.YangParserTestUtils;
+
+public class Bug890Test {
+    private static final QNameModule FOO_MODULE = QNameModule.create(URI.create("foo"), Revision.of("2018-07-10"));
+    private static final QName OUTGOING_LABELS_QNAME = QName.create(FOO_MODULE, "outgoing-labels");
+    private static final QName INDEX_QNAME = QName.create(FOO_MODULE, "index");
+
+    private SchemaContext schemaContext;
+
+    @Before
+    public void setUp() throws Exception {
+        schemaContext = YangParserTestUtils.parseYangResource("/bug890/yang/foo.yang");
+    }
+
+    @Test
+    public void testinputXml() throws Exception {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/bug890/xml/foo.xml");
+        final Module fooModule = schemaContext.getModules().iterator().next();
+        final Optional<DataSchemaNode> rootCont = fooModule.findDataChildByName(
+                QName.create(fooModule.getQNameModule(), "root"));
+        assertTrue(rootCont.isPresent());
+        ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) rootCont.get();
+        assertNotNull(containerSchemaNode);
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, containerSchemaNode);
+        xmlParser.parse(reader);
+
+        assertNotNull(result.getResult());
+        assertTrue(result.getResult() instanceof ContainerNode);
+        final ContainerNode rootContainer = (ContainerNode) result.getResult();
+
+        Optional<DataContainerChild<? extends PathArgument, ?>> myLeaf =
+                rootContainer.getChild(new NodeIdentifier(OUTGOING_LABELS_QNAME));
+        assertTrue(myLeaf.orElse(null) instanceof ContainerNode);
+
+        ContainerNode outgoingLabelsContainer = (ContainerNode)myLeaf.get();
+        Optional<DataContainerChild<? extends PathArgument, ?>> outgoingLabelsList =
+                outgoingLabelsContainer.getChild(new NodeIdentifier(OUTGOING_LABELS_QNAME));
+        assertTrue(outgoingLabelsList.orElse(null) instanceof MapNode);
+        MapNode outgoingLabelsMap = (MapNode) outgoingLabelsList.get();
+
+        assertEquals(2, outgoingLabelsMap.getValue().size());
+        Collection<MapEntryNode> labels = outgoingLabelsMap.getValue();
+        NodeIdentifierWithPredicates firstNodeId =
+                NodeIdentifierWithPredicates.of(OUTGOING_LABELS_QNAME, INDEX_QNAME, 0);
+        NodeIdentifierWithPredicates secondNodeId =
+                NodeIdentifierWithPredicates.of(OUTGOING_LABELS_QNAME, INDEX_QNAME, 1);
+        assertTrue(labels.stream().anyMatch(mapEntryNode -> mapEntryNode.getIdentifier().compareTo(firstNodeId) == 0));
+        assertTrue(labels.stream().anyMatch(mapEntryNode -> mapEntryNode.getIdentifier().compareTo(secondNodeId) == 0));
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceXMLStreamReaderTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceXMLStreamReaderTest.java
new file mode 100644 (file)
index 0000000..d76283b
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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;
+
+import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.Collection;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+@RunWith(Parameterized.class)
+public class DOMSourceXMLStreamReaderTest {
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        return TestFactories.junitParameters();
+    }
+
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    private final XMLOutputFactory factory;
+
+    public DOMSourceXMLStreamReaderTest(final String factoryMode, final XMLOutputFactory factory) {
+        this.factory = factory;
+    }
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = YangParserTestUtils.parseYangResourceDirectory("/dom-reader-test");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    @Test
+    public void test() throws Exception {
+        final ContainerSchemaNode outerContainerSchema = (ContainerSchemaNode) SchemaContextUtil
+                .findNodeInSchemaContext(SCHEMA_CONTEXT, ImmutableList.of(QName.create("foo-ns", "top-cont")));
+        assertNotNull(outerContainerSchema);
+
+        // deserialization
+        final Document doc = loadDocument("/dom-reader-test/foo.xml");
+        final DOMSource inputXml = new DOMSource(doc.getDocumentElement());
+        XMLStreamReader domXMLReader = new DOMSourceXMLStreamReader(inputXml);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, SCHEMA_CONTEXT, outerContainerSchema);
+        xmlParser.parse(domXMLReader);
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+
+        // serialization
+        //final StringWriter writer = new StringWriter();
+        final DOMResult domResult = new DOMResult(UntrustedXML.newDocumentBuilder().newDocument());
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(domResult);
+
+        final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(
+                xmlStreamWriter, SCHEMA_CONTEXT);
+
+        final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(
+                xmlNormalizedNodeStreamWriter);
+        normalizedNodeWriter.write(transformedInput);
+
+        //final String serializedXml = writer.toString();
+        final String serializedXml = toString(domResult.getNode());
+        assertFalse(serializedXml.isEmpty());
+    }
+
+    private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+        final InputStream resourceAsStream = NormalizedNodesToXmlTest.class.getResourceAsStream(xmlPath);
+        return requireNonNull(readXmlToDocument(resourceAsStream));
+    }
+
+    private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+        final Document doc = UntrustedXML.newDocumentBuilder().parse(xmlContent);
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    private static String toString(final Node xml) {
+        try {
+            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+            final StreamResult result = new StreamResult(new StringWriter());
+            final DOMSource source = new DOMSource(xml);
+            transformer.transform(source, result);
+
+            return result.getWriter().toString();
+        } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+            throw new RuntimeException("Unable to serialize xml element " + xml, e);
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/NormalizedNodeXmlTranslationTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/NormalizedNodeXmlTranslationTest.java
new file mode 100644 (file)
index 0000000..811b2a9
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.augmentationBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.choiceBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.containerBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.ElementNameAndTextQualifier;
+import org.custommonkey.xmlunit.IgnoreTextAndAttributeValuesDifferenceListener;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.Uint32;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+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.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+@RunWith(Parameterized.class)
+public class NormalizedNodeXmlTranslationTest {
+    private final SchemaContext schema;
+
+    @Parameterized.Parameters()
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {
+                { "/schema/augment_choice_hell.yang", "/schema/augment_choice_hell_ok.xml", augmentChoiceHell() },
+                { "/schema/augment_choice_hell.yang", "/schema/augment_choice_hell_ok2.xml", null },
+                { "/schema/augment_choice_hell.yang", "/schema/augment_choice_hell_ok3.xml", augmentChoiceHell2() },
+                { "/schema/test.yang", "/schema/simple.xml", null },
+                { "/schema/test.yang", "/schema/simple2.xml", null },
+                // TODO check attributes
+                { "/schema/test.yang", "/schema/simple_xml_with_attributes.xml", withAttributes() }
+        });
+    }
+
+    private static final String NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:test";
+    private static final Revision REVISION = Revision.of("2014-03-13");
+
+    private static ContainerNode augmentChoiceHell2() {
+        final NodeIdentifier container = getNodeIdentifier("container");
+        final QName augmentChoice1QName = QName.create(container.getNodeType(), "augment-choice1");
+        final QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2");
+        final QName containerQName = QName.create(augmentChoice1QName, "case11-choice-case-container");
+        final QName leafQName = QName.create(augmentChoice1QName, "case11-choice-case-leaf");
+
+        final AugmentationIdentifier aug1Id = new AugmentationIdentifier(ImmutableSet.of(augmentChoice1QName));
+        final AugmentationIdentifier aug2Id = new AugmentationIdentifier(ImmutableSet.of(augmentChoice2QName));
+        final NodeIdentifier augmentChoice1Id = new NodeIdentifier(augmentChoice1QName);
+        final NodeIdentifier augmentChoice2Id = new NodeIdentifier(augmentChoice2QName);
+        final NodeIdentifier containerId = new NodeIdentifier(containerQName);
+
+        return containerBuilder().withNodeIdentifier(container)
+                .withChild(augmentationBuilder().withNodeIdentifier(aug1Id)
+                        .withChild(choiceBuilder().withNodeIdentifier(augmentChoice1Id)
+                                .withChild(augmentationBuilder().withNodeIdentifier(aug2Id)
+                                        .withChild(choiceBuilder().withNodeIdentifier(augmentChoice2Id)
+                                                .withChild(containerBuilder().withNodeIdentifier(containerId)
+                                                        .withChild(leafNode(leafQName, "leaf-value"))
+                                                        .build())
+                                                .build())
+                                        .build())
+                                .build())
+                        .build()).build();
+    }
+
+    private static ContainerNode withAttributes() {
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> b = containerBuilder();
+        b.withNodeIdentifier(getNodeIdentifier("container"));
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> listBuilder = Builders.mapBuilder().withNodeIdentifier(
+                getNodeIdentifier("list"));
+
+        final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> list1Builder = Builders
+                .mapEntryBuilder().withNodeIdentifier(NodeIdentifierWithPredicates.of(
+                                getNodeIdentifier("list").getNodeType(),
+                                getNodeIdentifier("uint32InList").getNodeType(), Uint32.valueOf(3)));
+        final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> uint32InListBuilder = Builders
+                .leafBuilder().withNodeIdentifier(getNodeIdentifier("uint32InList"));
+
+        list1Builder.withChild(uint32InListBuilder.withValue(Uint32.valueOf(3)).build());
+
+        listBuilder.withChild(list1Builder.build());
+        b.withChild(listBuilder.build());
+
+        final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> booleanBuilder = Builders
+                .leafBuilder().withNodeIdentifier(getNodeIdentifier("boolean"));
+        booleanBuilder.withValue(Boolean.FALSE);
+        b.withChild(booleanBuilder.build());
+
+        final ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafListBuilder = Builders.leafSetBuilder()
+                .withNodeIdentifier(getNodeIdentifier("leafList"));
+
+        final NormalizedNodeBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> leafList1Builder = Builders
+                .leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue(getNodeIdentifier("leafList").getNodeType(), "a"));
+
+        leafList1Builder.withValue("a");
+
+        leafListBuilder.withChild(leafList1Builder.build());
+        b.withChild(leafListBuilder.build());
+
+        return b.build();
+    }
+
+    private static ContainerNode augmentChoiceHell() {
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> b = containerBuilder();
+        b.withNodeIdentifier(getNodeIdentifier("container"));
+
+        b.withChild(choiceBuilder()
+                .withNodeIdentifier(getNodeIdentifier("ch2"))
+                .withChild(
+                        Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("c2Leaf")).withValue("2").build())
+                .withChild(
+                        choiceBuilder()
+                                .withNodeIdentifier(getNodeIdentifier("c2DeepChoice"))
+                                .withChild(
+                                        Builders.leafBuilder()
+                                                .withNodeIdentifier(getNodeIdentifier("c2DeepChoiceCase1Leaf2"))
+                                                .withValue("2").build()).build()).build());
+
+        b.withChild(choiceBuilder()
+                .withNodeIdentifier(getNodeIdentifier("ch3"))
+                .withChild(
+                        Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("c3Leaf")).withValue("3").build())
+                .build());
+
+        b.withChild(augmentationBuilder()
+                .withNodeIdentifier(getAugmentIdentifier("augLeaf"))
+                .withChild(
+                        Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("augLeaf")).withValue("augment")
+                                .build()).build());
+
+        b.withChild(augmentationBuilder()
+                .withNodeIdentifier(getAugmentIdentifier("ch"))
+                .withChild(
+                        choiceBuilder()
+                                .withNodeIdentifier(getNodeIdentifier("ch"))
+                                .withChild(
+                                        Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("c1Leaf"))
+                                                .withValue("1").build())
+                                .withChild(
+                                        augmentationBuilder()
+                                                .withNodeIdentifier(
+                                                        getAugmentIdentifier("c1Leaf_AnotherAugment", "deepChoice"))
+                                                .withChild(
+                                                        Builders.leafBuilder()
+                                                                .withNodeIdentifier(
+                                                                        getNodeIdentifier("c1Leaf_AnotherAugment"))
+                                                                .withValue("1").build())
+                                                .withChild(
+                                                        choiceBuilder()
+                                                                .withNodeIdentifier(getNodeIdentifier("deepChoice"))
+                                                                .withChild(
+                                                                        Builders.leafBuilder()
+                                                                                .withNodeIdentifier(
+                                                                                        getNodeIdentifier("deepLeafc1"))
+                                                                                .withValue("1").build()).build())
+                                                .build()).build()).build());
+
+        return b.build();
+    }
+
+    private static NodeIdentifier getNodeIdentifier(final String localName) {
+        return new NodeIdentifier(QName.create(URI.create(NAMESPACE), REVISION, localName));
+    }
+
+    private static AugmentationIdentifier getAugmentIdentifier(final String... childNames) {
+        final Set<QName> qn = new HashSet<>();
+
+        for (final String childName : childNames) {
+            qn.add(getNodeIdentifier(childName).getNodeType());
+        }
+
+        return new AugmentationIdentifier(qn);
+    }
+
+    public NormalizedNodeXmlTranslationTest(final String yangPath, final String xmlPath,
+            final ContainerNode expectedNode) {
+        this.schema = YangParserTestUtils.parseYangResource(yangPath);
+        this.xmlPath = xmlPath;
+        this.containerNode = (ContainerSchemaNode) getSchemaNode(schema, "test", "container");
+        this.expectedNode = expectedNode;
+    }
+
+    private final ContainerNode expectedNode;
+    private final ContainerSchemaNode containerNode;
+    private final String xmlPath;
+
+    @Test
+    public void testTranslationRepairing() throws Exception {
+        testTranslation(TestFactories.REPAIRING_OUTPUT_FACTORY);
+    }
+
+    @Test
+    public void testTranslation() throws Exception {
+        testTranslation(TestFactories.DEFAULT_OUTPUT_FACTORY);
+    }
+
+    private void testTranslation(final XMLOutputFactory factory) throws Exception {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream(xmlPath);
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schema, containerNode);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> built = result.getResult();
+        assertNotNull(built);
+
+        if (expectedNode != null) {
+            org.junit.Assert.assertEquals(expectedNode, built);
+        }
+
+        final Document document = UntrustedXML.newDocumentBuilder().newDocument();
+        final DOMResult domResult = new DOMResult(document);
+
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(domResult);
+
+        final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter
+                .create(xmlStreamWriter, schema);
+
+        final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(
+                xmlNormalizedNodeStreamWriter);
+
+        normalizedNodeWriter.write(built);
+
+        final Document doc = loadDocument(xmlPath);
+
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setIgnoreComments(true);
+        XMLUnit.setIgnoreAttributeOrder(true);
+        XMLUnit.setNormalize(true);
+
+        final String expectedXml = toString(doc.getDocumentElement());
+        final String serializedXml = toString(domResult.getNode());
+
+        final Diff diff = new Diff(expectedXml, serializedXml);
+        diff.overrideDifferenceListener(new IgnoreTextAndAttributeValuesDifferenceListener());
+        diff.overrideElementQualifier(new ElementNameAndTextQualifier());
+
+        // FIXME the comparison cannot be performed, since the qualifiers supplied by XMlUnit do not work correctly in
+        // this case
+        // We need to implement custom qualifier so that the element ordering does not mess the DIFF
+        // dd.overrideElementQualifier(new MultiLevelElementNameAndTextQualifier(100, true));
+        // assertTrue(dd.toString(), dd.similar());
+
+        //new XMLTestCase() {}.assertXMLEqual(diff, true);
+    }
+
+    private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+        final InputStream resourceAsStream = NormalizedNodeXmlTranslationTest.class.getResourceAsStream(xmlPath);
+        return requireNonNull(readXmlToDocument(resourceAsStream));
+    }
+
+    private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+        final Document doc = UntrustedXML.newDocumentBuilder().parse(xmlContent);
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    private static String toString(final Node xml) {
+        try {
+            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+            final StreamResult result = new StreamResult(new StringWriter());
+            final DOMSource source = new DOMSource(xml);
+            transformer.transform(source, result);
+
+            return result.getWriter().toString();
+        } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+            throw new RuntimeException("Unable to serialize xml element " + xml, e);
+        }
+    }
+
+    private static DataSchemaNode getSchemaNode(final SchemaContext context, final String moduleName,
+                                               final String childNodeName) {
+        for (Module module : context.getModules()) {
+            if (module.getName().equals(moduleName)) {
+                DataSchemaNode found = findChildNode(module, childNodeName);
+                checkState(found != null, "Unable to find %s", childNodeName);
+                return found;
+            }
+        }
+        throw new IllegalStateException("Unable to find child node " + childNodeName);
+    }
+
+    // FIXME: duplicate of NormalizedDataBuilderTest.findChildNode()
+    private static DataSchemaNode findChildNode(final DataNodeContainer container, final String name) {
+        for (DataSchemaNode dataSchemaNode : container.getChildNodes()) {
+            if (dataSchemaNode.getQName().getLocalName().equals(name)) {
+                return dataSchemaNode;
+            }
+            if (dataSchemaNode instanceof DataNodeContainer) {
+                DataSchemaNode retVal = findChildNode((DataNodeContainer) dataSchemaNode, name);
+                if (retVal != null) {
+                    return retVal;
+                }
+            } else if (dataSchemaNode instanceof ChoiceSchemaNode) {
+                for (CaseSchemaNode caseNode : ((ChoiceSchemaNode) dataSchemaNode).getCases().values()) {
+                    DataSchemaNode retVal = findChildNode(caseNode, name);
+                    if (retVal != null) {
+                        return retVal;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/NormalizedNodesToXmlTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/NormalizedNodesToXmlTest.java
new file mode 100644 (file)
index 0000000..8f5f13e
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.DifferenceListener;
+import org.custommonkey.xmlunit.IgnoreTextAndAttributeValuesDifferenceListener;
+import org.custommonkey.xmlunit.XMLTestCase;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+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.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+@RunWith(Parameterized.class)
+public class NormalizedNodesToXmlTest {
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        return TestFactories.junitParameters();
+    }
+
+    private QNameModule bazModule;
+
+    private QName outerContainer;
+
+    private QName myContainer1;
+    private QName myKeyedList;
+    private QName myKeyLeaf;
+    private QName myLeafInList1;
+    private QName myLeafInList2;
+    private QName myLeaf1;
+    private QName myLeafList;
+
+    private QName myContainer2;
+    private QName innerContainer;
+    private QName myLeaf2;
+    private QName myLeaf3;
+    private QName myChoice;
+    private QName myLeafInCase2;
+
+    private QName myContainer3;
+    private QName myDoublyKeyedList;
+    private QName myFirstKeyLeaf;
+    private QName mySecondKeyLeaf;
+    private QName myLeafInList3;
+
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    private final XMLOutputFactory factory;
+
+    public NormalizedNodesToXmlTest(final String factoryMode, final XMLOutputFactory factory) {
+        this.factory = factory;
+    }
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = YangParserTestUtils.parseYangResource("/baz.yang");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    @Before
+    public void setup() {
+        bazModule = QNameModule.create(URI.create("baz-namespace"));
+
+        outerContainer = QName.create(bazModule, "outer-container");
+
+        myContainer1 = QName.create(bazModule, "my-container-1");
+        myKeyedList = QName.create(bazModule, "my-keyed-list");
+        myKeyLeaf = QName.create(bazModule, "my-key-leaf");
+        myLeafInList1 = QName.create(bazModule, "my-leaf-in-list-1");
+        myLeafInList2 = QName.create(bazModule, "my-leaf-in-list-2");
+        myLeaf1 = QName.create(bazModule, "my-leaf-1");
+        myLeafList = QName.create(bazModule, "my-leaf-list");
+
+        myContainer2 = QName.create(bazModule, "my-container-2");
+        innerContainer = QName.create(bazModule, "inner-container");
+        myLeaf2 = QName.create(bazModule, "my-leaf-2");
+        myLeaf3 = QName.create(bazModule, "my-leaf-3");
+        myChoice = QName.create(bazModule, "my-choice");
+        myLeafInCase2 = QName.create(bazModule, "my-leaf-in-case-2");
+
+        myContainer3 = QName.create(bazModule, "my-container-3");
+        myDoublyKeyedList = QName.create(bazModule, "my-doubly-keyed-list");
+        myFirstKeyLeaf = QName.create(bazModule, "my-first-key-leaf");
+        mySecondKeyLeaf = QName.create(bazModule, "my-second-key-leaf");
+        myLeafInList3 = QName.create(bazModule, "my-leaf-in-list-3");
+    }
+
+    @Test
+    public void testNormalizedNodeToXmlSerialization() throws XMLStreamException, IOException, SAXException {
+        final Document doc = loadDocument("/baz.xml");
+
+        final DOMResult domResult = new DOMResult(UntrustedXML.newDocumentBuilder().newDocument());
+
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(domResult);
+
+        final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(
+                xmlStreamWriter, SCHEMA_CONTEXT);
+
+        final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(
+                xmlNormalizedNodeStreamWriter);
+
+        normalizedNodeWriter.write(buildOuterContainerNode());
+
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setNormalize(true);
+
+        final String expectedXml = toString(doc.getDocumentElement());
+        final String serializedXml = toString(domResult.getNode());
+        final Diff diff = new Diff(expectedXml, serializedXml);
+
+        final DifferenceListener differenceListener = new IgnoreTextAndAttributeValuesDifferenceListener();
+        diff.overrideDifferenceListener(differenceListener);
+
+        new XMLTestCase() {}.assertXMLEqual(diff, true);
+    }
+
+    private NormalizedNode<?, ?> buildOuterContainerNode() {
+        // my-container-1
+        MapNode myKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myKeyedList))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myKeyedList, myKeyLeaf, "listkeyvalue1"))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList1))
+                                .withValue("listleafvalue1").build())
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList2))
+                                .withValue("listleafvalue2").build()).build())
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myKeyedList, myKeyLeaf, "listkeyvalue2"))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList1))
+                                .withValue("listleafvalue12").build())
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList2))
+                                .withValue("listleafvalue22").build()).build()).build();
+
+        LeafNode<?> myLeaf1Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf1))
+                .withValue("value1").build();
+
+        LeafSetNode<?> myLeafListNode = Builders.leafSetBuilder().withNodeIdentifier(new NodeIdentifier(myLeafList))
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(myLeafList, "lflvalue1")).withValue("lflvalue1").build())
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(myLeafList, "lflvalue2")).withValue("lflvalue2").build()).build();
+
+        ContainerNode myContainer1Node = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer1))
+                .withChild(myKeyedListNode)
+                .withChild(myLeaf1Node)
+                .withChild(myLeafListNode).build();
+
+        // my-container-2
+        ContainerNode innerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(innerContainer))
+                .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf2))
+                        .withValue("value2").build()).build();
+
+        LeafNode<?> myLeaf3Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf3))
+                .withValue("value3").build();
+
+        ChoiceNode myChoiceNode = Builders.choiceBuilder().withNodeIdentifier(new NodeIdentifier(myChoice))
+                .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInCase2))
+                        .withValue("case2value").build()).build();
+
+        ContainerNode myContainer2Node = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer2))
+                .withChild(innerContainerNode)
+                .withChild(myLeaf3Node)
+                .withChild(myChoiceNode).build();
+
+        // my-container-3
+        Map<QName, Object> keys = new HashMap<>();
+        keys.put(myFirstKeyLeaf, "listkeyvalue1");
+        keys.put(mySecondKeyLeaf, "listkeyvalue2");
+
+        MapNode myDoublyKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myDoublyKeyedList))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myDoublyKeyedList, keys))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(
+                                new NodeIdentifier(myLeafInList3)).withValue("listleafvalue1").build()).build())
+                .build();
+
+        AugmentationNode myDoublyKeyedListAugNode = Builders.augmentationBuilder().withNodeIdentifier(
+                new AugmentationIdentifier(ImmutableSet.of(myDoublyKeyedList)))
+                .withChild(myDoublyKeyedListNode).build();
+
+        ContainerNode myContainer3Node = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer3))
+                .withChild(myDoublyKeyedListAugNode).build();
+
+        AugmentationNode myContainer3AugNode = Builders.augmentationBuilder().withNodeIdentifier(
+                new AugmentationIdentifier(ImmutableSet.of(myContainer3)))
+                .withChild(myContainer3Node).build();
+
+        ContainerNode outerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(outerContainer))
+                .withChild(myContainer1Node)
+                .withChild(myContainer2Node)
+                .withChild(myContainer3AugNode).build();
+
+        return outerContainerNode;
+    }
+
+    private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+        final InputStream resourceAsStream = NormalizedNodesToXmlTest.class.getResourceAsStream(xmlPath);
+        return requireNonNull(readXmlToDocument(resourceAsStream));
+    }
+
+    private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+        final Document doc = UntrustedXML.newDocumentBuilder().parse(xmlContent);
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    private static String toString(final Node xml) {
+        try {
+            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+            final StreamResult result = new StreamResult(new StringWriter());
+            final DOMSource source = new DOMSource(xml);
+            transformer.transform(source, result);
+
+            return result.getWriter().toString();
+        } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+            throw new RuntimeException("Unable to serialize xml element " + xml, e);
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/RandomPrefixTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/RandomPrefixTest.java
new file mode 100644 (file)
index 0000000..45c146a
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.xml;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.google.common.collect.Iterables;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+
+public class RandomPrefixTest {
+    static final int MAX_COUNTER = 4000;
+
+    @Test
+    public void testEncodeDecode() throws Exception {
+
+        final List<String> allGenerated = new ArrayList<>(MAX_COUNTER);
+        for (int i = 0; i < MAX_COUNTER; i++) {
+            final String encoded = RandomPrefix.encode(i);
+            assertEquals(RandomPrefix.decode(encoded), i);
+            allGenerated.add(encoded);
+        }
+
+        assertEquals(allGenerated.size(), MAX_COUNTER);
+        assertEquals("dPT", allGenerated.get(MAX_COUNTER - 1));
+        assertEquals("a", allGenerated.get(0));
+        assertEquals(allGenerated.size(), new HashSet<>(allGenerated).size());
+    }
+
+    @Test
+    public void testQNameWithPrefix() throws Exception {
+        final RandomPrefix a = new RandomPrefix(null);
+
+        final List<String> allGenerated = new ArrayList<>();
+        for (int i = 0; i < MAX_COUNTER; i++) {
+            final String prefix = RandomPrefix.encode(i);
+            final URI uri = new URI("localhost:" + prefix);
+            final QName qname = QName.create(QNameModule.create(uri, Revision.of("2000-01-01")), "local-name");
+            allGenerated.add(a.encodePrefix(qname.getNamespace()));
+        }
+
+        assertEquals(MAX_COUNTER, allGenerated.size());
+        // We are generating MAX_COUNTER_VALUE + 27 prefixes total, so we should encounter a reset in prefix a start
+        // from 0 at some point. At the end, there should be only 27 values in RandomPrefix cache
+        assertEquals(MAX_COUNTER, Iterables.size(a.getPrefixes()));
+        assertThat(allGenerated, CoreMatchers.not(CoreMatchers.hasItem("xml")));
+        assertThat(allGenerated, CoreMatchers.not(CoreMatchers.hasItem("xmla")));
+        assertThat(allGenerated, CoreMatchers.not(CoreMatchers.hasItem("xmlz")));
+
+        assertEquals(1, Iterables.frequency(allGenerated, "a"));
+    }
+
+    @Test
+    public void test2QNames1Namespace() throws Exception {
+        final RandomPrefix a = new RandomPrefix(null);
+
+        final URI uri = URI.create("localhost");
+        final QName qname = QName.create(QNameModule.create(uri, Revision.of("2000-01-01")), "local-name");
+        final QName qname2 = QName.create(QNameModule.create(uri, Revision.of("2000-01-01")), "local-name");
+
+        assertEquals(a.encodePrefix(qname.getNamespace()), a.encodePrefix(qname2.getNamespace()));
+    }
+
+    @Test
+    public void testQNameNoPrefix() throws Exception {
+        final RandomPrefix a = new RandomPrefix(null);
+
+        final URI uri = URI.create("localhost");
+        QName qname = QName.create(uri, Revision.of("2000-01-01"), "local-name");
+        assertEquals("a", a.encodePrefix(qname.getNamespace()));
+        qname = QName.create(QNameModule.create(uri, Revision.of("2000-01-01")), "local-name");
+        assertEquals("a", a.encodePrefix(qname.getNamespace()));
+        qname = QName.create(QNameModule.create(URI.create("second"), Revision.of("2000-01-01")), "local-name");
+        assertEquals("b", a.encodePrefix(qname.getNamespace()));
+
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaOrderedNormalizedNodeWriterTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaOrderedNormalizedNodeWriterTest.java
new file mode 100644 (file)
index 0000000..0e63b5e
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2016 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;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLAssert;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.SchemaOrderedNormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.xml.sax.SAXException;
+
+@RunWith(Parameterized.class)
+public class SchemaOrderedNormalizedNodeWriterTest {
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        return TestFactories.junitParameters();
+    }
+
+    private static final String EXPECTED_1 = ""
+        + "<root xmlns=\"foo\">\n"
+        + "    <policy>\n"
+        + "        <name>policy1</name>\n"
+        + "        <rule>\n"
+        + "            <name>rule1</name>\n"
+        + "        </rule>\n"
+        + "        <rule>\n"
+        + "            <name>rule2</name>\n"
+        + "        </rule>\n"
+        + "        <rule>\n"
+        + "            <name>rule3</name>\n"
+        + "        </rule>\n"
+        + "        <rule>\n"
+        + "            <name>rule4</name>\n"
+        + "        </rule>\n"
+        + "    </policy>\n"
+        + "    <policy>\n"
+        + "        <name>policy2</name>\n"
+        + "    </policy>\n"
+        + "</root>\n";
+
+
+    private static final String EXPECTED_2 = ""
+        + "<root xmlns=\"order\">\n"
+        + "    <id>id1</id>\n"
+        + "    <cont>\n"
+        + "        <content>content1</content>\n"
+        + "    </cont>\n"
+        + "</root>";
+
+    private static final String FOO_NAMESPACE = "foo";
+    private static final String RULE_NODE = "rule";
+    private static final String NAME_NODE = "name";
+    private static final String POLICY_NODE = "policy";
+    private static final String ORDER_NAMESPACE = "order";
+
+    private final XMLOutputFactory factory;
+
+    public SchemaOrderedNormalizedNodeWriterTest(final String factoryMode, final XMLOutputFactory factory) {
+        this.factory = factory;
+        XMLUnit.setIgnoreWhitespace(true);
+    }
+
+    @Test
+    public void testWrite() throws XMLStreamException, IOException, SAXException {
+        final StringWriter stringWriter = new StringWriter();
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(stringWriter);
+
+        SchemaContext schemaContext = getSchemaContext("/bug1848/foo.yang");
+        NormalizedNodeStreamWriter writer = XMLStreamNormalizedNodeStreamWriter.create(xmlStreamWriter, schemaContext);
+
+        try (SchemaOrderedNormalizedNodeWriter nnw = new SchemaOrderedNormalizedNodeWriter(writer, schemaContext,
+            SchemaPath.ROOT)) {
+
+            List<MapEntryNode> rule1Names = new ArrayList<>();
+            rule1Names.add(ImmutableNodes.mapEntry(createQName(FOO_NAMESPACE, RULE_NODE),
+                createQName(FOO_NAMESPACE, NAME_NODE), "rule1"));
+            rule1Names.add(ImmutableNodes.mapEntry(createQName(FOO_NAMESPACE, RULE_NODE),
+                createQName(FOO_NAMESPACE, NAME_NODE), "rule2"));
+
+            List<MapEntryNode> rule2Names = new ArrayList<>();
+            rule1Names.add(ImmutableNodes.mapEntry(createQName(FOO_NAMESPACE, RULE_NODE),
+                createQName(FOO_NAMESPACE, NAME_NODE), "rule3"));
+            rule1Names.add(ImmutableNodes.mapEntry(createQName(FOO_NAMESPACE, RULE_NODE),
+                createQName(FOO_NAMESPACE, NAME_NODE), "rule4"));
+
+            DataContainerChild<?, ?> rules1 = Builders.orderedMapBuilder()
+                    .withNodeIdentifier(getNodeIdentifier(FOO_NAMESPACE, RULE_NODE))
+                    .withValue(rule1Names)
+                    .build();
+            DataContainerChild<?, ?> rules2 = Builders.orderedMapBuilder()
+                    .withNodeIdentifier(getNodeIdentifier(FOO_NAMESPACE, RULE_NODE))
+                    .withValue(rule2Names)
+                    .build();
+
+            List<MapEntryNode> policyNodes = new ArrayList<>();
+
+
+            final MapEntryNode pn1 = ImmutableNodes
+                    .mapEntryBuilder(createQName(FOO_NAMESPACE, POLICY_NODE),
+                        createQName(FOO_NAMESPACE, NAME_NODE), "policy1")
+                    .withChild(rules1)
+                    .build();
+            final MapEntryNode pn2 = ImmutableNodes
+                    .mapEntryBuilder(createQName(FOO_NAMESPACE, POLICY_NODE),
+                        createQName(FOO_NAMESPACE, NAME_NODE), "policy2")
+                    .withChild(rules2)
+                    .build();
+            policyNodes.add(pn1);
+            policyNodes.add(pn2);
+
+            DataContainerChild<?, ?> policy = Builders.orderedMapBuilder()
+                    .withNodeIdentifier(getNodeIdentifier(FOO_NAMESPACE, POLICY_NODE))
+                    .withValue(policyNodes)
+                    .build();
+            NormalizedNode<?, ?> root = Builders.containerBuilder()
+                    .withNodeIdentifier(getNodeIdentifier(FOO_NAMESPACE, "root"))
+                    .withChild(policy).build();
+            nnw.write(root);
+        }
+
+        XMLAssert.assertXMLIdentical(new Diff(EXPECTED_1, stringWriter.toString()), true);
+    }
+
+    @Test
+    public void testWriteOrder() throws XMLStreamException, IOException, SAXException {
+        final StringWriter stringWriter = new StringWriter();
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(stringWriter);
+        SchemaContext schemaContext = getSchemaContext("/bug1848/order.yang");
+        NormalizedNodeStreamWriter writer = XMLStreamNormalizedNodeStreamWriter.create(xmlStreamWriter, schemaContext);
+
+        try (NormalizedNodeWriter nnw = new SchemaOrderedNormalizedNodeWriter(writer, schemaContext, SchemaPath.ROOT)) {
+
+            DataContainerChild<?, ?> cont = Builders.containerBuilder()
+                    .withNodeIdentifier(getNodeIdentifier(ORDER_NAMESPACE, "cont"))
+                    .withChild(ImmutableNodes.leafNode(createQName(ORDER_NAMESPACE, "content"), "content1"))
+                    .build();
+
+            NormalizedNode<?, ?> root = Builders.containerBuilder()
+                    .withNodeIdentifier(getNodeIdentifier(ORDER_NAMESPACE, "root"))
+                    .withChild(cont)
+                    .withChild(ImmutableNodes.leafNode(createQName(ORDER_NAMESPACE, "id"), "id1"))
+                    .build();
+
+            nnw.write(root);
+        }
+
+        XMLAssert.assertXMLIdentical(new Diff(EXPECTED_2, stringWriter.toString()), true);
+    }
+
+    private static SchemaContext getSchemaContext(final String filePath) {
+        return YangParserTestUtils.parseYangResource(filePath);
+    }
+
+    private static YangInstanceIdentifier.NodeIdentifier getNodeIdentifier(final String ns, final String name) {
+        return YangInstanceIdentifier.NodeIdentifier.create(createQName(ns, name));
+    }
+
+    private static QName createQName(final String ns, final String name) {
+        return QName.create(ns, "2016-02-17", name);
+    }
+
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriterTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriterTest.java
new file mode 100644 (file)
index 0000000..d2daa1f
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.DifferenceListener;
+import org.custommonkey.xmlunit.IgnoreTextAndAttributeValuesDifferenceListener;
+import org.custommonkey.xmlunit.XMLAssert;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+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.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+@RunWith(Parameterized.class)
+public class SchemalessXMLStreamNormalizedNodeStreamWriterTest {
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        return TestFactories.junitParameters();
+    }
+
+    private final XMLOutputFactory factory;
+
+    private QNameModule foobarModule;
+
+    private QName outerContainer;
+
+    private QName myContainer1;
+    private QName myKeyedList;
+    private QName myKeyLeaf;
+    private QName myLeafInList1;
+    private QName myLeafInList2;
+    private QName myOrderedList;
+    private QName myKeyLeafInOrderedList;
+    private QName myLeafInOrderedList1;
+    private QName myLeafInOrderedList2;
+    private QName myUnkeyedList;
+    private QName myLeafInUnkeyedList;
+    private QName myLeaf1;
+    private QName myLeafList;
+    private QName myOrderedLeafList;
+
+    private QName myContainer2;
+    private QName innerContainer;
+    private QName myLeaf2;
+    private QName myLeaf3;
+    private QName myChoice;
+    private QName myLeafInCase2;
+    private QName myAnyxml;
+
+    private QName myContainer3;
+    private QName myDoublyKeyedList;
+    private QName myFirstKeyLeaf;
+    private QName mySecondKeyLeaf;
+    private QName myLeafInList3;
+
+    private DOMSource anyxmlDomSource;
+
+    public SchemalessXMLStreamNormalizedNodeStreamWriterTest(final String factoryMode, final XMLOutputFactory factory) {
+        this.factory = factory;
+    }
+
+    @Before
+    public void setup() {
+        foobarModule = QNameModule.create(URI.create("foobar-namespace"), Revision.of("2016-09-19"));
+
+        outerContainer = QName.create(foobarModule, "outer-container");
+
+        myContainer1 = QName.create(foobarModule, "my-container-1");
+        myKeyedList = QName.create(foobarModule, "my-keyed-list");
+        myKeyLeaf = QName.create(foobarModule, "my-key-leaf");
+        myLeafInList1 = QName.create(foobarModule, "my-leaf-in-list-1");
+        myLeafInList2 = QName.create(foobarModule, "my-leaf-in-list-2");
+        myOrderedList = QName.create(foobarModule, "my-ordered-list");
+        myKeyLeafInOrderedList = QName.create(foobarModule, "my-key-leaf-in-ordered-list");
+        myLeafInOrderedList1 = QName.create(foobarModule, "my-leaf-in-ordered-list-1");
+        myLeafInOrderedList2 = QName.create(foobarModule, "my-leaf-in-ordered-list-2");
+        myUnkeyedList = QName.create(foobarModule, "my-unkeyed-list");
+        myLeafInUnkeyedList = QName.create(foobarModule, "my-leaf-in-unkeyed-list");
+        myLeaf1 = QName.create(foobarModule, "my-leaf-1");
+        myLeafList = QName.create(foobarModule, "my-leaf-list");
+        myOrderedLeafList = QName.create(foobarModule, "my-ordered-leaf-list");
+
+        myContainer2 = QName.create(foobarModule, "my-container-2");
+        innerContainer = QName.create(foobarModule, "inner-container");
+        myLeaf2 = QName.create(foobarModule, "my-leaf-2");
+        myLeaf3 = QName.create(foobarModule, "my-leaf-3");
+        myChoice = QName.create(foobarModule, "my-choice");
+        myLeafInCase2 = QName.create(foobarModule, "my-leaf-in-case-2");
+        myAnyxml = QName.create(foobarModule, "my-anyxml");
+
+        myContainer3 = QName.create(foobarModule, "my-container-3");
+        myDoublyKeyedList = QName.create(foobarModule, "my-doubly-keyed-list");
+        myFirstKeyLeaf = QName.create(foobarModule, "my-first-key-leaf");
+        mySecondKeyLeaf = QName.create(foobarModule, "my-second-key-leaf");
+        myLeafInList3 = QName.create(foobarModule, "my-leaf-in-list-3");
+
+        anyxmlDomSource = new DOMSource();
+    }
+
+    @Test
+    public void testWrite() throws XMLStreamException, IOException, SAXException {
+        final Document doc = loadDocument("/foobar.xml");
+
+        final DOMResult domResult = new DOMResult(UntrustedXML.newDocumentBuilder().newDocument());
+
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(domResult);
+
+        final NormalizedNodeStreamWriter schemalessXmlNormalizedNodeStreamWriter =
+                XMLStreamNormalizedNodeStreamWriter.createSchemaless(xmlStreamWriter);
+
+        final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(
+                schemalessXmlNormalizedNodeStreamWriter);
+
+        normalizedNodeWriter.write(buildOuterContainerNode());
+
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setNormalize(true);
+
+        final String expectedXml = toString(doc.getDocumentElement());
+        final String serializedXml = toString(domResult.getNode());
+        final Diff diff = new Diff(expectedXml, serializedXml);
+
+        final DifferenceListener differenceListener = new IgnoreTextAndAttributeValuesDifferenceListener();
+        diff.overrideDifferenceListener(differenceListener);
+
+        XMLAssert.assertXMLEqual(diff, true);
+    }
+
+    private NormalizedNode<?, ?> buildOuterContainerNode() {
+        // my-container-1
+        MapNode myKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myKeyedList))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myKeyedList, myKeyLeaf, "listkeyvalue1"))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList1))
+                                .withValue("listleafvalue1").build())
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList2))
+                                .withValue("listleafvalue2").build()).build())
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myKeyedList, myKeyLeaf, "listkeyvalue2"))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList1))
+                                .withValue("listleafvalue12").build())
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList2))
+                                .withValue("listleafvalue22").build()).build()).build();
+
+        OrderedMapNode myOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+                new NodeIdentifier(myOrderedList)).withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myOrderedList, myKeyLeafInOrderedList, "olistkeyvalue1"))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInOrderedList1))
+                                .withValue("olistleafvalue1").build())
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInOrderedList2))
+                                .withValue("olistleafvalue2").build()).build())
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myOrderedList, myKeyLeafInOrderedList, "olistkeyvalue2"))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInOrderedList1))
+                                .withValue("olistleafvalue12").build())
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInOrderedList2))
+                                .withValue("olistleafvalue22").build()).build()).build();
+
+        final UnkeyedListEntryNode unkeyedListEntry = Builders.unkeyedListEntryBuilder()
+                .withNodeIdentifier(new NodeIdentifier(myUnkeyedList))
+                .withChild(ImmutableNodes.leafNode(myLeafInUnkeyedList, "foo")).build();
+
+        final List<UnkeyedListEntryNode> unkeyedListEntries = new ArrayList<>();
+        unkeyedListEntries.add(unkeyedListEntry);
+
+        UnkeyedListNode myUnkeyedListNode = Builders.unkeyedListBuilder().withNodeIdentifier(
+                new NodeIdentifier(myUnkeyedList))
+                .withValue(unkeyedListEntries).build();
+
+        LeafNode<?> myLeaf1Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf1))
+                .withValue("value1").build();
+
+        LeafSetNode<?> myLeafListNode = Builders.leafSetBuilder().withNodeIdentifier(new NodeIdentifier(myLeafList))
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(myLeafList, "lflvalue1")).withValue("lflvalue1").build())
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(myLeafList, "lflvalue2")).withValue("lflvalue2").build()).build();
+
+        LeafSetNode<?> myOrderedLeafListNode = Builders.orderedLeafSetBuilder().withNodeIdentifier(
+                new NodeIdentifier(myOrderedLeafList))
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(myOrderedLeafList, "olflvalue1")).withValue("olflvalue1").build())
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(myOrderedLeafList, "olflvalue2")).withValue("olflvalue2").build()).build();
+
+        ContainerNode myContainer1Node = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer1))
+                .withChild(myKeyedListNode)
+                .withChild(myOrderedListNode)
+                .withChild(myUnkeyedListNode)
+                .withChild(myLeaf1Node)
+                .withChild(myLeafListNode)
+                .withChild(myOrderedLeafListNode)
+                .build();
+
+        // my-container-2
+        ContainerNode innerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(innerContainer))
+                .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf2))
+                        .withValue("value2").build()).build();
+
+        LeafNode<?> myLeaf3Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf3))
+                .withValue("value3").build();
+
+        ChoiceNode myChoiceNode = Builders.choiceBuilder().withNodeIdentifier(new NodeIdentifier(myChoice))
+                .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInCase2))
+                        .withValue("case2value").build()).build();
+
+        DOMSourceAnyxmlNode myAnyxmlNode = Builders.anyXmlBuilder().withNodeIdentifier(new NodeIdentifier(myAnyxml))
+                .withValue(anyxmlDomSource).build();
+
+        ContainerNode myContainer2Node = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer2))
+                .withChild(innerContainerNode)
+                .withChild(myLeaf3Node)
+                .withChild(myChoiceNode).build();
+                //.withChild(myAnyxmlNode).build();
+
+        // my-container-3
+        Map<QName, Object> keys = new HashMap<>();
+        keys.put(myFirstKeyLeaf, "listkeyvalue1");
+        keys.put(mySecondKeyLeaf, "listkeyvalue2");
+
+        MapNode myDoublyKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myDoublyKeyedList))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myDoublyKeyedList, keys))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(
+                                new NodeIdentifier(myLeafInList3)).withValue("listleafvalue1").build()).build())
+                .build();
+
+        AugmentationNode myDoublyKeyedListAugNode = Builders.augmentationBuilder().withNodeIdentifier(
+                new AugmentationIdentifier(ImmutableSet.of(myDoublyKeyedList)))
+                .withChild(myDoublyKeyedListNode).build();
+
+        ContainerNode myContainer3Node = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer3))
+                .withChild(myDoublyKeyedListAugNode).build();
+
+        AugmentationNode myContainer3AugNode = Builders.augmentationBuilder().withNodeIdentifier(
+                new AugmentationIdentifier(ImmutableSet.of(myContainer3)))
+                .withChild(myContainer3Node).build();
+
+        ContainerNode outerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(outerContainer))
+                .withChild(myContainer1Node)
+                .withChild(myContainer2Node)
+                .withChild(myContainer3AugNode).build();
+
+        return outerContainerNode;
+    }
+
+    private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+        final InputStream resourceAsStream = SchemalessXMLStreamNormalizedNodeStreamWriterTest.class
+            .getResourceAsStream(xmlPath);
+        return requireNonNull(readXmlToDocument(resourceAsStream));
+    }
+
+    private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+        final Document doc = UntrustedXML.newDocumentBuilder().parse(xmlContent);
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    private static String toString(final Node xml) {
+        try {
+            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+            final StreamResult result = new StreamResult(new StringWriter());
+            final DOMSource source = new DOMSource(xml);
+            transformer.transform(source, result);
+
+            return result.getWriter().toString();
+        } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+            throw new RuntimeException("Unable to serialize xml element " + xml, e);
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/StrictParsingModeTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/StrictParsingModeTest.java
new file mode 100644 (file)
index 0000000..3d425eb
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.io.InputStream;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class StrictParsingModeTest {
+
+    @Test
+    public void testLenientParsing() throws Exception {
+        // unknown child nodes in the top-level-container node will be skipped when the strictParsing is set to false
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResource(
+                "/strict-parsing-mode-test/foo.yang");
+        final Module fooModule = schemaContext.getModules().iterator().next();
+        final ContainerSchemaNode topLevelContainer = (ContainerSchemaNode) fooModule.findDataChildByName(
+                QName.create(fooModule.getQNameModule(), "top-level-container")).get();
+
+        final InputStream resourceAsStream = StrictParsingModeTest.class.getResourceAsStream(
+                "/strict-parsing-mode-test/foo.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, topLevelContainer, false);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void testStrictParsing() throws Exception {
+        // should fail because strictParsing is switched on and the top-level-container node contains child nodes
+        // which are not defined in the provided YANG model
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResource(
+                "/strict-parsing-mode-test/foo.yang");
+        final Module fooModule = schemaContext.getModules().iterator().next();
+        final ContainerSchemaNode topLevelContainer = (ContainerSchemaNode) fooModule.findDataChildByName(
+                QName.create(fooModule.getQNameModule(), "top-level-container")).get();
+
+        final InputStream resourceAsStream = StrictParsingModeTest.class.getResourceAsStream(
+                "/strict-parsing-mode-test/foo.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, topLevelContainer, true);
+        try {
+            xmlParser.parse(reader);
+            fail("XMLStreamException should have been thrown because of an unknown child node.");
+        } catch (XMLStreamException ex) {
+            assertEquals("Schema for node with name unknown-container-a and namespace foo does not exist at "
+                    + "AbsoluteSchemaPath{path=[(foo)top-level-container]}", ex.getMessage());
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/TestFactories.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/TestFactories.java
new file mode 100644 (file)
index 0000000..da595c9
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o. 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;
+
+import java.util.Arrays;
+import java.util.Collection;
+import javax.xml.stream.XMLOutputFactory;
+
+final class TestFactories {
+    /**
+     * Non-repairing XMLOutputFactory.
+     */
+    static final XMLOutputFactory DEFAULT_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
+
+    /**
+     * Repairing XMLOuputFactory.
+     */
+    static final XMLOutputFactory REPAIRING_OUTPUT_FACTORY;
+
+    static {
+        REPAIRING_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
+        REPAIRING_OUTPUT_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE);
+    }
+
+    static Collection<Object[]> junitParameters() {
+        return Arrays.asList(new Object[][] {
+            { "default", DEFAULT_OUTPUT_FACTORY },
+            { "repairing", REPAIRING_OUTPUT_FACTORY },
+        });
+    }
+
+    private TestFactories() {
+
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStreamUtilsTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStreamUtilsTest.java
new file mode 100644 (file)
index 0000000..569a043
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class XmlStreamUtilsTest {
+    @FunctionalInterface
+    interface XMLStreamWriterConsumer {
+        void accept(XMLStreamWriter writer) throws XMLStreamException;
+    }
+
+    private static SchemaContext schemaContext;
+    private static Module leafRefModule;
+
+    @BeforeClass
+    public static void initialize() {
+        schemaContext = YangParserTestUtils.parseYangResource("/leafref-test.yang");
+        assertNotNull(schemaContext);
+        assertEquals(1, schemaContext.getModules().size());
+        leafRefModule = schemaContext.getModules().iterator().next();
+        assertNotNull(leafRefModule);
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        leafRefModule = null;
+        schemaContext = null;
+    }
+
+    @Test
+    public void testWriteIdentityRef() throws Exception {
+        final QNameModule parent = QNameModule.create(URI.create("parent:uri"), Revision.of("2000-01-01"));
+
+        String xmlAsString = createXml(writer -> {
+            writer.writeStartElement("element");
+            final StreamWriterFacade facade = new StreamWriterFacade(writer);
+            facade.writeCharacters(XMLStreamWriterUtils.encode(facade, null, QName.create(parent, "identity"), parent));
+            facade.flush();
+            writer.writeEndElement();
+        });
+
+        assertThat(xmlAsString, containsString("element>identity"));
+
+        xmlAsString = createXml(writer -> {
+            writer.writeStartElement("elementDifferent");
+            final StreamWriterFacade facade = new StreamWriterFacade(writer);
+            facade.writeCharacters(XMLStreamWriterUtils.encode(facade, null, QName.create("different:namespace",
+                "identity"), parent));
+            facade.flush();
+            writer.writeEndElement();
+        });
+
+        final Pattern prefixedIdentityPattern = Pattern.compile(".*\"different:namespace\">(.*):identity.*");
+        final Matcher matcher = prefixedIdentityPattern.matcher(xmlAsString);
+        assertTrue("Xml: " + xmlAsString + " should match: " + prefixedIdentityPattern, matcher.matches());
+    }
+
+    private static String createXml(final XMLStreamWriterConsumer consumer) throws XMLStreamException, IOException {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        final XMLStreamWriter writer = TestFactories.DEFAULT_OUTPUT_FACTORY.createXMLStreamWriter(out);
+
+        consumer.accept(writer);
+
+        writer.close();
+        out.close();
+
+        return new String(out.toByteArray()).replaceAll("\\s*", "");
+    }
+
+    /**
+     * One leafref reference to other leafref via relative references.
+     */
+    @Test
+    public void testLeafRefRelativeChaining() {
+        getTargetNodeForLeafRef("leafname3", StringTypeDefinition.class);
+    }
+
+    @Test
+    public void testLeafRefRelative() {
+        getTargetNodeForLeafRef("pointToStringLeaf", StringTypeDefinition.class);
+    }
+
+    @Test
+    public void testLeafRefAbsoluteWithSameTarget() {
+        getTargetNodeForLeafRef("absname", InstanceIdentifierTypeDefinition.class);
+    }
+
+    /**
+     * Tests relative path with double point inside path (e. g. "../../lf:interface/../lf:cont2/lf:stringleaf")
+     */
+    // ignored because this isn't implemented
+    @Ignore
+    @Test
+    public void testLeafRefWithDoublePointInPath() {
+        getTargetNodeForLeafRef("lf-with-double-point-inside", StringTypeDefinition.class);
+    }
+
+    @Test
+    public void testLeafRefRelativeAndAbsoluteWithSameTarget() {
+        final TypeDefinition<?> targetNodeForAbsname = getTargetNodeForLeafRef("absname",
+            InstanceIdentifierTypeDefinition.class);
+        final TypeDefinition<?> targetNodeForRelname = getTargetNodeForLeafRef("relname",
+            InstanceIdentifierTypeDefinition.class);
+        assertEquals(targetNodeForAbsname, targetNodeForRelname);
+    }
+
+    private TypeDefinition<?> getTargetNodeForLeafRef(final String nodeName, final Class<?> clas) {
+        final LeafSchemaNode schemaNode = findSchemaNodeWithLeafrefType(leafRefModule, nodeName);
+        assertNotNull(schemaNode);
+        final LeafrefTypeDefinition leafrefTypedef = findLeafrefType(schemaNode);
+        assertNotNull(leafrefTypedef);
+        final TypeDefinition<?> targetBaseType = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypedef, schemaContext,
+                schemaNode);
+        assertTrue("Wrong class found.", clas.isInstance(targetBaseType));
+        return targetBaseType;
+    }
+
+    private static Map<String, String> mapPrefixed(final Iterable<Map.Entry<URI, String>> prefixes) {
+        final Map<String, String> mappedPrefixes = new HashMap<>();
+        for (final Map.Entry<URI, String> prefix : prefixes) {
+            mappedPrefixes.put(prefix.getKey().toString(), prefix.getValue());
+        }
+        return mappedPrefixes;
+    }
+
+    private static QName getAttrQName(final String namespace, final String revision, final String localName,
+            final Optional<String> prefix) {
+        if (prefix.isPresent()) {
+            final QName moduleQName = QName.create(namespace, revision, "module");
+            final QNameModule module = QNameModule.create(moduleQName.getNamespace(), moduleQName.getRevision());
+            return QName.create(module, localName);
+        }
+        return QName.create(namespace, revision, localName);
+    }
+
+    private LeafSchemaNode findSchemaNodeWithLeafrefType(final DataNodeContainer module, final String nodeName) {
+        for (final DataSchemaNode childNode : module.getChildNodes()) {
+            if (childNode instanceof DataNodeContainer) {
+                LeafSchemaNode leafrefFromRecursion = findSchemaNodeWithLeafrefType((DataNodeContainer) childNode,
+                        nodeName);
+                if (leafrefFromRecursion != null) {
+                    return leafrefFromRecursion;
+                }
+            } else if (childNode.getQName().getLocalName().equals(nodeName) && childNode instanceof LeafSchemaNode) {
+                final TypeDefinition<?> leafSchemaNodeType = ((LeafSchemaNode) childNode).getType();
+                if (leafSchemaNodeType instanceof LeafrefTypeDefinition) {
+                    return (LeafSchemaNode) childNode;
+                }
+            }
+        }
+        return null;
+    }
+
+    private static LeafrefTypeDefinition findLeafrefType(final LeafSchemaNode schemaNode) {
+        final TypeDefinition<?> type = schemaNode.getType();
+        if (type instanceof LeafrefTypeDefinition) {
+            return (LeafrefTypeDefinition) type;
+        }
+        return null;
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlToNormalizedNodesTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlToNormalizedNodesTest.java
new file mode 100644 (file)
index 0000000..c233e00
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+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.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.xml.sax.SAXException;
+
+public class XmlToNormalizedNodesTest {
+
+    private static final QNameModule FOO_MODULE = QNameModule.create(URI.create("foo-namespace"));
+    private static final QName PARENT_CONTAINER = QName.create(FOO_MODULE, "parent-container");
+
+    private static final QNameModule BAZ_MODULE = QNameModule.create(URI.create("baz-namespace"));
+    private static final QName OUTER_CONTAINER = QName.create(BAZ_MODULE, "outer-container");
+
+    private static final QName MY_CONTAINER_1 = QName.create(BAZ_MODULE, "my-container-1");
+    private static final QName MY_KEYED_LIST = QName.create(BAZ_MODULE, "my-keyed-list");
+    private static final QName MY_KEY_LEAF = QName.create(BAZ_MODULE, "my-key-leaf");
+    private static final QName MY_LEAF_IN_LIST_1 = QName.create(BAZ_MODULE, "my-leaf-in-list-1");
+    private static final QName MY_LEAF_IN_LIST_2 = QName.create(BAZ_MODULE, "my-leaf-in-list-2");
+    private static final QName MY_LEAF_1 = QName.create(BAZ_MODULE, "my-leaf-1");
+    private static final QName MY_LEAFLIST = QName.create(BAZ_MODULE, "my-leaf-list");
+
+    private static final QName MY_CONTAINER_2 = QName.create(BAZ_MODULE, "my-container-2");
+    private static final QName INNER_CONTAINER = QName.create(BAZ_MODULE, "inner-container");
+    private static final QName MY_LEAF_2 = QName.create(BAZ_MODULE, "my-leaf-2");
+    private static final QName MY_LEAF_3 = QName.create(BAZ_MODULE, "my-leaf-3");
+    private static final QName MY_CHOICE = QName.create(BAZ_MODULE, "my-choice");
+    private static final QName MY_LEAF_IN_CASE_2 = QName.create(BAZ_MODULE, "my-leaf-in-case-2");
+
+    private static final QName MY_CONTAINER_3 = QName.create(BAZ_MODULE, "my-container-3");
+    private static final QName MY_DOUBLY_KEYED_LIST = QName.create(BAZ_MODULE, "my-doubly-keyed-list");
+    private static final QName MY_FIRST_KEY_LEAF = QName.create(BAZ_MODULE, "my-first-key-leaf");
+    private static final QName MY_SECOND_KEY_LEAF = QName.create(BAZ_MODULE, "my-second-key-leaf");
+    private static final QName MY_LEAF_IN_LIST_3 = QName.create(BAZ_MODULE, "my-leaf-in-list-3");
+
+    private static SchemaContext schemaContext;
+    private static ContainerSchemaNode outerContainerSchema;
+    private static ContainerSchemaNode parentContainerSchema;
+
+    @BeforeClass
+    public static void setup() {
+        schemaContext = YangParserTestUtils.parseYangResourceDirectory("/");
+        parentContainerSchema = (ContainerSchemaNode) SchemaContextUtil.findNodeInSchemaContext(schemaContext,
+                ImmutableList.of(PARENT_CONTAINER));
+        outerContainerSchema = (ContainerSchemaNode) SchemaContextUtil.findNodeInSchemaContext(schemaContext,
+                ImmutableList.of(OUTER_CONTAINER));
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        schemaContext = null;
+        parentContainerSchema = null;
+        outerContainerSchema = null;
+    }
+
+    @Test
+    public void testComplexXmlParsing() throws IOException, SAXException, URISyntaxException, XMLStreamException,
+            ParserConfigurationException {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/baz.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, outerContainerSchema);
+        xmlParser.parse(reader);
+
+        xmlParser.flush();
+        xmlParser.close();
+
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+
+        final NormalizedNode<?, ?> expectedNormalizedNode = buildOuterContainerNode();
+        assertNotNull(expectedNormalizedNode);
+
+        assertEquals(expectedNormalizedNode, transformedInput);
+    }
+
+    @Test
+    public void testSimpleXmlParsing() throws IOException, URISyntaxException, XMLStreamException,
+            ParserConfigurationException, SAXException {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/foo.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, parentContainerSchema);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void shouldFailOnDuplicateLeaf() throws XMLStreamException, IOException,
+            ParserConfigurationException, SAXException, URISyntaxException {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, parentContainerSchema);
+        try {
+            xmlParser.parse(reader);
+            fail("XMLStreamException should have been thrown because of duplicate leaf.");
+        } catch (XMLStreamException ex) {
+            assertThat(ex.getMessage(), containsString("Duplicate namespace \"foo-namespace\" element "
+                    + "\"decimal64-leaf\" in XML input"));
+        }
+    }
+
+    @Test
+    public void shouldFailOnDuplicateAnyXml() throws XMLStreamException, IOException,
+            ParserConfigurationException, SAXException, URISyntaxException {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo-2.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, parentContainerSchema);
+        try {
+            xmlParser.parse(reader);
+            fail("XMLStreamException should have been thrown because of duplicate anyxml");
+        } catch (XMLStreamException ex) {
+            assertThat(ex.getMessage(), containsString("Duplicate namespace \"foo-namespace\" element \"my-anyxml\""));
+        }
+    }
+
+    @Test
+    public void shouldFailOnDuplicateContainer() throws XMLStreamException, IOException,
+            ParserConfigurationException, SAXException, URISyntaxException {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo-3.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, parentContainerSchema);
+        try {
+            xmlParser.parse(reader);
+            fail("XMLStreamException should have been thrown because of duplicate container");
+        } catch (XMLStreamException ex) {
+            assertThat(ex.getMessage(), containsString("Duplicate namespace \"foo-namespace\" element "
+                    + "\"leaf-container\" in XML input"));
+        }
+    }
+
+    @Test
+    public void shouldFailOnUnterminatedLeafElement() throws XMLStreamException, IOException,
+            ParserConfigurationException, SAXException, URISyntaxException {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, outerContainerSchema);
+        try {
+            xmlParser.parse(reader);
+            fail("XMLStreamException should have been thrown because of unterminated leaf element.");
+        } catch (XMLStreamException ex) {
+            assertThat(ex.getMessage(), containsString(" START_ELEMENT "));
+        }
+    }
+
+    @Test
+    public void shouldFailOnUnterminatedLeafElement2() throws XMLStreamException, IOException,
+            ParserConfigurationException, SAXException, URISyntaxException {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-2.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, outerContainerSchema);
+        try {
+            xmlParser.parse(reader);
+            fail("XMLStreamException should have been thrown because of unterminated leaf element.");
+        } catch (XMLStreamException ex) {
+            assertThat(ex.getMessage(), containsString("</my-leaf-1>"));
+        }
+    }
+
+    @Test
+    public void shouldFailOnUnterminatedContainerElement() throws XMLStreamException, IOException,
+            ParserConfigurationException, SAXException, URISyntaxException {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-4.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, outerContainerSchema);
+        try {
+            xmlParser.parse(reader);
+            fail("XMLStreamException should have been thrown because of unterminated container element.");
+        } catch (XMLStreamException ex) {
+            assertThat(ex.getMessage(), containsString("</my-container-1>"));
+        }
+    }
+
+    @Test
+    public void shouldFailOnUnknownChildNode() throws XMLStreamException, IOException,
+            ParserConfigurationException, SAXException, URISyntaxException {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-3.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, outerContainerSchema);
+        try {
+            xmlParser.parse(reader);
+            fail("XMLStreamException should have been thrown because of an unknown child node.");
+        } catch (XMLStreamException ex) {
+            assertEquals("Schema for node with name my-container-1 and namespace baz-namespace does not exist at "
+                    + "AbsoluteSchemaPath{path=[(baz-namespace)outer-container, (baz-namespace)my-container-1]}",
+                    ex.getMessage());
+        }
+    }
+
+    private static NormalizedNode<?, ?> buildOuterContainerNode() {
+        // my-container-1
+        MapNode myKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_KEYED_LIST))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(MY_KEYED_LIST, MY_KEY_LEAF, "listkeyvalue1"))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_1))
+                                .withValue("listleafvalue1").build())
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_2))
+                                .withValue("listleafvalue2").build()).build())
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(MY_KEYED_LIST, MY_KEY_LEAF, "listkeyvalue2"))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_1))
+                                .withValue("listleafvalue12").build())
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_2))
+                                .withValue("listleafvalue22").build()).build()).build();
+
+        LeafNode<?> myLeaf1Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_1))
+                .withValue("value1").build();
+
+        LeafSetNode<?> myLeafListNode = Builders.leafSetBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAFLIST))
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(MY_LEAFLIST, "lflvalue1")).withValue("lflvalue1").build())
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(MY_LEAFLIST, "lflvalue2")).withValue("lflvalue2").build()).build();
+
+        ContainerNode myContainer1Node = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(MY_CONTAINER_1))
+                .withChild(myKeyedListNode)
+                .withChild(myLeaf1Node)
+                .withChild(myLeafListNode).build();
+
+        // my-container-2
+        ContainerNode innerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(INNER_CONTAINER))
+                .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_2))
+                        .withValue("value2").build()).build();
+
+        LeafNode<?> myLeaf3Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_3))
+                .withValue("value3").build();
+
+        ChoiceNode myChoiceNode = Builders.choiceBuilder().withNodeIdentifier(new NodeIdentifier(MY_CHOICE))
+                .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_CASE_2))
+                        .withValue("case2value").build()).build();
+
+        ContainerNode myContainer2Node = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(MY_CONTAINER_2))
+                .withChild(innerContainerNode)
+                .withChild(myLeaf3Node)
+                .withChild(myChoiceNode).build();
+
+        // my-container-3
+        Map<QName, Object> keys = new HashMap<>();
+        keys.put(MY_FIRST_KEY_LEAF, "listkeyvalue1");
+        keys.put(MY_SECOND_KEY_LEAF, "listkeyvalue2");
+
+        MapNode myDoublyKeyedListNode = Builders.mapBuilder()
+                .withNodeIdentifier(new NodeIdentifier(MY_DOUBLY_KEYED_LIST))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(MY_DOUBLY_KEYED_LIST, keys))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(
+                                new NodeIdentifier(MY_LEAF_IN_LIST_3)).withValue("listleafvalue1").build()).build())
+                .build();
+
+        AugmentationNode myDoublyKeyedListAugNode = Builders.augmentationBuilder().withNodeIdentifier(
+                new AugmentationIdentifier(ImmutableSet.of(MY_DOUBLY_KEYED_LIST)))
+                .withChild(myDoublyKeyedListNode).build();
+
+        ContainerNode myContainer3Node = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(MY_CONTAINER_3))
+                .withChild(myDoublyKeyedListAugNode).build();
+
+        AugmentationNode myContainer3AugNode = Builders.augmentationBuilder().withNodeIdentifier(
+                new AugmentationIdentifier(ImmutableSet.of(MY_CONTAINER_3)))
+                .withChild(myContainer3Node).build();
+
+        ContainerNode outerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(OUTER_CONTAINER))
+                .withChild(myContainer1Node)
+                .withChild(myContainer2Node)
+                .withChild(myContainer3AugNode).build();
+
+        return outerContainerNode;
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YangModeledAnyXMLDeserializationTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YangModeledAnyXMLDeserializationTest.java
new file mode 100644 (file)
index 0000000..4ec503d
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Optional;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+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.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class YangModeledAnyXMLDeserializationTest {
+
+    private QNameModule fooModuleQName;
+    private QNameModule barModuleQName;
+    private QName myContainer1;
+    private QName myContainer2;
+    private QName innerContainer;
+    private QName myLeaf3;
+    private QName myLeaf2;
+    private QName myLeaf1;
+    private QName myAnyXMLDataBar;
+    private QName myAnyXMLDataFoo;
+    private SchemaContext schemaContext;
+
+    @Before
+    public void setUp() {
+        barModuleQName = QNameModule.create(URI.create("bar"));
+        myContainer1 = QName.create(barModuleQName, "my-container-1");
+        myLeaf1 = QName.create(barModuleQName, "my-leaf-1");
+        myAnyXMLDataBar = QName.create(barModuleQName, "my-anyxml-data");
+
+        fooModuleQName = QNameModule.create(URI.create("foo"));
+        myContainer2 = QName.create(fooModuleQName, "my-container-2");
+        innerContainer = QName.create(fooModuleQName, "inner-container");
+        myLeaf3 = QName.create(fooModuleQName, "my-leaf-3");
+        myLeaf2 = QName.create(fooModuleQName, "my-leaf-2");
+        myAnyXMLDataFoo = QName.create(fooModuleQName, "my-anyxml-data");
+
+        schemaContext = YangParserTestUtils.parseYangResourceDirectory("/anyxml-support/yang");
+    }
+
+    @Test
+    public void testRawAnyXMLFromBar() throws Exception {
+        DataSchemaNode barContainer = schemaContext.findDataChildByName(QName.create(barModuleQName, "bar")).get();
+        assertTrue(barContainer instanceof ContainerSchemaNode);
+        final YangModeledAnyxmlSchemaNode yangModeledAnyXML = new YangModeledAnyXMLSchemaNodeImplTest(myAnyXMLDataBar,
+                (ContainerSchemaNode) barContainer);
+
+        final InputStream resourceAsStream = YangModeledAnyXMLDeserializationTest.class.getResourceAsStream(
+                "/anyxml-support/xml/bar.xml");
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, yangModeledAnyXML);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> output = result.getResult();
+        assertTrue(output instanceof YangModeledAnyXmlNode);
+        final YangModeledAnyXmlNode yangModeledAnyXmlNode = (YangModeledAnyXmlNode) output;
+
+        Collection<DataContainerChild<? extends PathArgument, ?>> value = yangModeledAnyXmlNode.getValue();
+        assertEquals(2, value.size());
+
+        Optional<DataContainerChild<? extends PathArgument, ?>> child = yangModeledAnyXmlNode
+                .getChild(new NodeIdentifier(myContainer1));
+        assertTrue(child.orElse(null) instanceof ContainerNode);
+        ContainerNode myContainerNode1 = (ContainerNode) child.get();
+
+        Optional<DataContainerChild<? extends PathArgument, ?>> child2 = myContainerNode1.getChild(new NodeIdentifier(
+                myLeaf1));
+        assertTrue(child2.orElse(null) instanceof LeafNode);
+        LeafNode<?> leafNode1 = (LeafNode<?>) child2.get();
+
+        Object leafNode1Value = leafNode1.getValue();
+        assertEquals("value1", leafNode1Value);
+    }
+
+    @Test
+    public void testRealSchemaContextFromFoo() throws Exception {
+        final InputStream resourceAsStream = YangModeledAnyXMLDeserializationTest.class.getResourceAsStream(
+                "/anyxml-support/xml/foo.xml");
+        final Module foo = schemaContext.findModules("foo").iterator().next();
+        final YangModeledAnyxmlSchemaNode myAnyXmlData = (YangModeledAnyxmlSchemaNode) foo.findDataChildByName(
+                QName.create(foo.getQNameModule(), "my-anyxml-data")).get();
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, myAnyXmlData);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> output = result.getResult();
+        assertTrue(output instanceof YangModeledAnyXmlNode);
+        final YangModeledAnyXmlNode yangModeledAnyXmlNode = (YangModeledAnyXmlNode) output;
+
+        DataSchemaNode schemaOfAnyXmlData = yangModeledAnyXmlNode.getSchemaOfAnyXmlData();
+        DataSchemaNode expectedSchemaOfAnyXmlData = schemaContext.findDataChildByName(myContainer2).get();
+        assertEquals(expectedSchemaOfAnyXmlData, schemaOfAnyXmlData);
+
+        Collection<DataContainerChild<? extends PathArgument, ?>> value = yangModeledAnyXmlNode.getValue();
+        assertEquals(2, value.size());
+
+        Optional<DataContainerChild<? extends PathArgument, ?>> child2 = yangModeledAnyXmlNode
+                .getChild(new NodeIdentifier(innerContainer));
+        assertTrue(child2.orElse(null) instanceof ContainerNode);
+        ContainerNode innerContainerNode = (ContainerNode) child2.get();
+
+        Optional<DataContainerChild<? extends PathArgument, ?>> child3 = innerContainerNode
+                .getChild(new NodeIdentifier(myLeaf2));
+        assertTrue(child3.orElse(null) instanceof LeafNode);
+        LeafNode<?> leafNode2 = (LeafNode<?>) child3.get();
+
+        Object leafNode2Value = leafNode2.getValue();
+        assertEquals("any-xml-leaf-2-value", leafNode2Value);
+
+        Optional<DataContainerChild<? extends PathArgument, ?>> child4 = yangModeledAnyXmlNode
+                .getChild(new NodeIdentifier(myLeaf3));
+        assertTrue(child4.orElse(null) instanceof LeafNode);
+        LeafNode<?> leafNode3 = (LeafNode<?>) child4.get();
+
+        Object leafNode3Value = leafNode3.getValue();
+        assertEquals("any-xml-leaf-3-value", leafNode3Value);
+    }
+
+    private static final class YangModeledAnyXMLSchemaNodeImplTest implements YangModeledAnyxmlSchemaNode {
+        private final QName qname;
+        private final ContainerSchemaNode contentSchema;
+
+        YangModeledAnyXMLSchemaNodeImplTest(final QName qname, final ContainerSchemaNode contentSchema) {
+            this.qname = qname;
+            this.contentSchema = contentSchema;
+        }
+
+        @Override
+        public boolean isAugmenting() {
+            return false;
+        }
+
+        @Override
+        public boolean isAddedByUses() {
+            return false;
+        }
+
+        @Override
+        public boolean isConfiguration() {
+            return false;
+        }
+
+        @Override
+        public boolean isMandatory() {
+            return false;
+        }
+
+        @Override
+        public QName getQName() {
+            return qname;
+        }
+
+        @Override
+        public SchemaPath getPath() {
+            return null;
+        }
+
+        @Override
+        public Optional<String> getDescription() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<String> getReference() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Status getStatus() {
+            return Status.CURRENT;
+        }
+
+        @Override
+        public ContainerSchemaNode getSchemaOfAnyXmlData() {
+            return contentSchema;
+        }
+
+        @Override
+        public Collection<MustDefinition> getMustConstraints() {
+            return ImmutableSet.of();
+        }
+
+        @Override
+        public Optional<RevisionAwareXPath> getWhenCondition() {
+            return Optional.empty();
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YangModeledAnyXMLSerializationTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YangModeledAnyXMLSerializationTest.java
new file mode 100644 (file)
index 0000000..cb52144
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2016 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;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Optional;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.XMLTestCase;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+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.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+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.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+@RunWith(Parameterized.class)
+public class YangModeledAnyXMLSerializationTest extends XMLTestCase {
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> data() {
+        return TestFactories.junitParameters();
+    }
+
+    private final QNameModule bazModuleQName = QNameModule.create(URI.create("baz"));
+    private final QName myAnyXMLDataBaz = QName.create(bazModuleQName, "my-anyxml-data");
+    private final QName bazQName = QName.create(bazModuleQName, "baz");
+    private final QName myContainer2QName = QName.create(bazModuleQName, "my-container-2");
+
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    private final XMLOutputFactory factory;
+
+    public YangModeledAnyXMLSerializationTest(final String factoryMode, final XMLOutputFactory factory) {
+        this.factory = factory;
+    }
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = YangParserTestUtils.parseYangResourceDirectory("/anyxml-support/serialization");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    @Test
+    public void testSerializationOfBaz() throws Exception {
+        final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream(
+                "/anyxml-support/serialization/baz.xml");
+        final Module bazModule = SCHEMA_CONTEXT.findModules("baz").iterator().next();
+        final ContainerSchemaNode bazCont = (ContainerSchemaNode) bazModule.findDataChildByName(
+                QName.create(bazModule.getQNameModule(), "baz")).get();
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, SCHEMA_CONTEXT, bazCont);
+        xmlParser.parse(reader);
+
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+
+        assertTrue(transformedInput instanceof ContainerNode);
+        ContainerNode bazContainer = (ContainerNode) transformedInput;
+        assertEquals(bazContainer.getNodeType(), bazQName);
+
+        Optional<DataContainerChild<? extends PathArgument, ?>> bazContainerChild = bazContainer.getChild(
+                new NodeIdentifier(myAnyXMLDataBaz));
+        assertTrue(bazContainerChild.orElse(null) instanceof YangModeledAnyXmlNode);
+        YangModeledAnyXmlNode yangModeledAnyXmlNode = (YangModeledAnyXmlNode) bazContainerChild.get();
+
+        DataSchemaNode schemaOfAnyXmlData = yangModeledAnyXmlNode.getSchemaOfAnyXmlData();
+        SchemaNode myContainer2SchemaNode = SchemaContextUtil.findDataSchemaNode(SCHEMA_CONTEXT,
+                SchemaPath.create(true, bazQName, myContainer2QName));
+        assertTrue(myContainer2SchemaNode instanceof ContainerSchemaNode);
+        assertEquals(myContainer2SchemaNode, schemaOfAnyXmlData);
+
+        final Document document = UntrustedXML.newDocumentBuilder().newDocument();
+        final DOMResult domResult = new DOMResult(document);
+
+        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(domResult);
+
+        final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter
+                .create(xmlStreamWriter, SCHEMA_CONTEXT);
+
+        final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(
+                xmlNormalizedNodeStreamWriter);
+
+        normalizedNodeWriter.write(transformedInput);
+
+        final Document doc = loadDocument("/anyxml-support/serialization/baz.xml");
+
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setIgnoreComments(true);
+        XMLUnit.setIgnoreAttributeOrder(true);
+        XMLUnit.setNormalize(true);
+
+        String expectedXml = toString(doc.getDocumentElement());
+        String serializedXml = toString(domResult.getNode());
+
+        assertXMLEqual(expectedXml, serializedXml);
+    }
+
+    private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+        final InputStream resourceAsStream = YangModeledAnyXMLSerializationTest.class.getResourceAsStream(xmlPath);
+        return requireNonNull(readXmlToDocument(resourceAsStream));
+    }
+
+    private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+        final Document doc = UntrustedXML.newDocumentBuilder().parse(xmlContent);
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    private static String toString(final Node xml) {
+        try {
+            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+            final StreamResult result = new StreamResult(new StringWriter());
+            final DOMSource source = new DOMSource(xml);
+            transformer.transform(source, result);
+
+            return result.getWriter().toString();
+        } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+            throw new RuntimeException("Unable to serialize xml element " + xml, e);
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools821Test.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools821Test.java
new file mode 100644 (file)
index 0000000..7fe9412
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2018 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;
+
+import java.io.InputStream;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefDataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefValidation;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Yangtools821Test {
+    private static final String NS = "urn:opendaylight:params:xml:ns:yang:foo";
+    private static final String REV = "2018-07-18";
+    private static final QName ROOT = QName.create(NS, REV, "root");
+    private static final YangInstanceIdentifier ROOT_ID = YangInstanceIdentifier.of(ROOT);
+
+    private SchemaContext schemaContext;
+    private LeafRefContext leafRefContext;
+    private DataTree dataTree;
+
+    @Before
+    public void setup() {
+        schemaContext = YangParserTestUtils.parseYangResource("/yangtools821/foo.yang");
+        leafRefContext = LeafRefContext.create(schemaContext);
+        dataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
+    }
+
+    @Test
+    public void testValidRefFromAugmentation() throws Exception {
+        final NormalizedNode<?, ?> root = readNode(schemaContext, "/yangtools821/foo-valid.xml");
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        writeModification.write(ROOT_ID, root);
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+
+        LeafRefValidation.validate(writeContributorsCandidate, leafRefContext);
+        dataTree.commit(writeContributorsCandidate);
+    }
+
+    @Test(expected = LeafRefDataValidationFailedException.class)
+    public void testInvalidRefFromAugmentation() throws Exception {
+        final NormalizedNode<?, ?> root = readNode(schemaContext, "/yangtools821/foo-invalid.xml");
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        writeModification.write(ROOT_ID, root);
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+
+        LeafRefValidation.validate(writeContributorsCandidate, leafRefContext);
+    }
+
+    @Test
+    public void testValidRefInContainerFromAugmentation() throws Exception {
+        final NormalizedNode<?, ?> root = readNode(schemaContext, "/yangtools821/foo-ref-in-container-valid.xml");
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        writeModification.write(ROOT_ID, root);
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+
+        LeafRefValidation.validate(writeContributorsCandidate, leafRefContext);
+        dataTree.commit(writeContributorsCandidate);
+    }
+
+    @Test(expected = LeafRefDataValidationFailedException.class)
+    public void testInvalidRefInContainerFromAugmentation() throws Exception {
+        final NormalizedNode<?, ?> root = readNode(schemaContext, "/yangtools821/foo-ref-in-container-invalid.xml");
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        writeModification.write(ROOT_ID, root);
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+
+        LeafRefValidation.validate(writeContributorsCandidate, leafRefContext);
+    }
+
+    private static NormalizedNode<?, ?> readNode(final SchemaContext context, final String filename) throws Exception {
+        final InputStream resourceAsStream = Yangtools821Test.class.getResourceAsStream(filename);
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final Module fooModule = context.getModules().iterator().next();
+        final ContainerSchemaNode rootContainer = (ContainerSchemaNode) fooModule.findDataChildByName(ROOT).get();
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, context, rootContainer);
+        xmlParser.parse(reader);
+        return result.getResult();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools891Test.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools891Test.java
new file mode 100644 (file)
index 0000000..23ade82
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2018 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;
+
+import java.io.InputStream;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefDataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefValidation;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Yangtools891Test {
+    private static final String FOO = "foo";
+    private static final String FOO_NS = "urn:opendaylight:params:xml:ns:yang:foo";
+    private static final String FOO_REV = "2018-07-27";
+    private static final QName FOO_TOP = QName.create(FOO_NS, FOO_REV, "foo-top");
+    private static final YangInstanceIdentifier FOO_TOP_ID = YangInstanceIdentifier.of(FOO_TOP);
+    private static final String BAZ = "baz";
+    private static final String BAZ_NS = "urn:opendaylight:params:xml:ns:yang:baz";
+    private static final String BAZ_REV = "2018-07-27";
+    private static final QName BAZ_TOP = QName.create(BAZ_NS, BAZ_REV, "baz-top");
+    private static final YangInstanceIdentifier BAZ_TOP_ID = YangInstanceIdentifier.of(BAZ_TOP);
+
+    private SchemaContext schemaContext;
+    private LeafRefContext leafRefContext;
+    private DataTree dataTree;
+    private ContainerSchemaNode fooTopNode;
+    private ContainerSchemaNode bazTopNode;
+
+    @Before
+    public void setup() {
+        schemaContext = YangParserTestUtils.parseYangResourceDirectory("/yangtools891");
+        leafRefContext = LeafRefContext.create(schemaContext);
+        dataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
+        final Module fooModule = schemaContext.findModule(FOO, Revision.of(FOO_REV)).get();
+        fooTopNode = (ContainerSchemaNode) fooModule.findDataChildByName(FOO_TOP).get();
+        final Module bazModule = schemaContext.findModule(BAZ, Revision.of(BAZ_REV)).get();
+        bazTopNode = (ContainerSchemaNode) bazModule.findDataChildByName(BAZ_TOP).get();
+    }
+
+    @Test
+    public void testValid() throws Exception {
+        final NormalizedNode<?, ?> fooTop = readNode("/yangtools891/grouping-with-list-valid.xml", fooTopNode);
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        writeModification.write(FOO_TOP_ID, fooTop);
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+
+        LeafRefValidation.validate(writeContributorsCandidate, leafRefContext);
+        dataTree.commit(writeContributorsCandidate);
+    }
+
+    @Test(expected = LeafRefDataValidationFailedException.class)
+    public void testInvalid() throws Exception {
+        final NormalizedNode<?, ?> fooTop = readNode("/yangtools891/grouping-with-list-invalid.xml", fooTopNode);
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        writeModification.write(FOO_TOP_ID, fooTop);
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+
+        LeafRefValidation.validate(writeContributorsCandidate, leafRefContext);
+    }
+
+    @Test
+    public void testGroupingWithLeafrefValid() throws Exception {
+        final NormalizedNode<?, ?> bazTop = readNode("/yangtools891/baz-top.xml", bazTopNode);
+        final NormalizedNode<?, ?> fooTop = readNode("/yangtools891/grouping-with-leafref-valid.xml", fooTopNode);
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        writeModification.write(BAZ_TOP_ID, bazTop);
+        writeModification.write(FOO_TOP_ID, fooTop);
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+
+        LeafRefValidation.validate(writeContributorsCandidate, leafRefContext);
+    }
+
+    @Test(expected = LeafRefDataValidationFailedException.class)
+    public void testGroupingWithLeafrefInvalid() throws Exception {
+        final NormalizedNode<?, ?> bazTop = readNode("/yangtools891/baz-top.xml", bazTopNode);
+        final NormalizedNode<?, ?> fooTop = readNode("/yangtools891/grouping-with-leafref-invalid.xml", fooTopNode);
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        writeModification.write(BAZ_TOP_ID, bazTop);
+        writeModification.write(FOO_TOP_ID, fooTop);
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+
+        LeafRefValidation.validate(writeContributorsCandidate, leafRefContext);
+    }
+
+    private NormalizedNode<?, ?> readNode(final String filename, final ContainerSchemaNode node) throws Exception {
+        final InputStream resourceAsStream = Yangtools891Test.class.getResourceAsStream(filename);
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, node);
+        xmlParser.parse(reader);
+        return result.getResult();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools892Test.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/Yangtools892Test.java
new file mode 100644 (file)
index 0000000..e564b41
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2018 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;
+
+import java.io.InputStream;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefValidation;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Yangtools892Test {
+    private static final String TEST_BGP_NAME = "test-bgp";
+    private static final String TEST_BGP_NS = "urn:opendaylight:params:xml:ns:yang:test:bgp";
+    private static final String TEST_BGP_REV = "2018-08-14";
+    private static final QName BGP = QName.create(TEST_BGP_NS, TEST_BGP_REV, "bgp");
+    private static final YangInstanceIdentifier BGP_ID = YangInstanceIdentifier.of(BGP);
+
+    private static final String NETWORK_INSTANCE_NAME = "test-network-instance";
+    private static final String NETWORK_INSTANCE_NS = "urn:opendaylight:params:xml:ns:yang:test:network:instance";
+    private static final String NETWORK_INSTANCE_REV = "2018-08-14";
+    private static final QName NETWORK_INSTANCES =
+        QName.create(NETWORK_INSTANCE_NS, NETWORK_INSTANCE_REV, "network-instances");
+    private static final YangInstanceIdentifier NETWORK_INSTANCES_ID = YangInstanceIdentifier.of(NETWORK_INSTANCES);
+
+    private SchemaContext schemaContext;
+    private LeafRefContext leafRefContext;
+    private DataTree dataTree;
+    private ContainerSchemaNode bgpNode;
+    private ContainerSchemaNode networkInstancesNode;
+
+    @Before
+    public void setup() {
+        schemaContext = YangParserTestUtils.parseYangResourceDirectory("/yangtools892");
+        leafRefContext = LeafRefContext.create(schemaContext);
+        dataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
+        final Module testBgpModule = schemaContext.findModule(TEST_BGP_NAME, Revision.of(TEST_BGP_REV)).get();
+        bgpNode = (ContainerSchemaNode) testBgpModule.findDataChildByName(BGP).get();
+        final Module networkInstanceModule =
+            schemaContext.findModule(NETWORK_INSTANCE_NAME, Revision.of(NETWORK_INSTANCE_REV)).get();
+        networkInstancesNode = (ContainerSchemaNode) networkInstanceModule.findDataChildByName(NETWORK_INSTANCES).get();
+    }
+
+    @Test
+    public void testWriteBgpNeighbour() throws Exception {
+        final DataTreeModification writeModification = dataTree.takeSnapshot().newModification();
+        final NormalizedNode<?, ?> bgp = readNode("/yangtools892/peer-groups.xml", bgpNode);
+        writeModification.write(BGP_ID, bgp);
+        final NormalizedNode<?, ?> networkInstances = readNode("/yangtools892/neighbour.xml", networkInstancesNode);
+        writeModification.write(NETWORK_INSTANCES_ID, networkInstances);
+
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = dataTree.prepare(writeModification);
+        LeafRefValidation.validate(writeContributorsCandidate, leafRefContext);
+        dataTree.commit(writeContributorsCandidate);
+    }
+
+    private NormalizedNode<?, ?> readNode(final String filename, final ContainerSchemaNode node) throws Exception {
+        final InputStream resourceAsStream = Yangtools891Test.class.getResourceAsStream(filename);
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext, node);
+        xmlParser.parse(reader);
+        return result.getResult();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/anyxml-support/params/edit.xml b/yang/yang-data-codec-xml/src/test/resources/anyxml-support/params/edit.xml
new file mode 100644 (file)
index 0000000..c59cb68
--- /dev/null
@@ -0,0 +1,16 @@
+<input xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <target>
+        <candidate/>
+    </target>
+    <default-operation>none</default-operation>
+    <config>
+        <interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
+            <interface-configuration xmlns:xmpref="xml:namespace:prefix" simple="attribute">
+                <active xmpref:prefixed2="attribute2">pre</active>
+                <interface-name xmpref:prefixed3="attribute3">TenGigE0/0/0/0</interface-name>\n" +
+                <interface-mode-non-physical>default</interface-mode-non-physical>
+                <description>test</description>
+            </interface-configuration>
+        </interface-configurations>
+    </config>
+</input>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/anyxml-support/params/ietf-netconf@2011-06-01.yang b/yang/yang-data-codec-xml/src/test/resources/anyxml-support/params/ietf-netconf@2011-06-01.yang
new file mode 100644 (file)
index 0000000..3622516
--- /dev/null
@@ -0,0 +1,197 @@
+module ietf-netconf {
+
+  // the namespace for NETCONF XML definitions is unchanged
+  // from RFC 4741, which this document replaces
+  namespace "urn:ietf:params:xml:ns:netconf:base:1.0";
+
+  prefix nc;
+
+  organization
+    "IETF NETCONF (Network Configuration) Working Group";
+
+  contact
+    "WG Web:   <http://tools.ietf.org/wg/netconf/>
+     WG List:  <netconf@ietf.org>
+
+     WG Chair: Bert Wijnen
+               <bertietf@bwijnen.net>
+
+     WG Chair: Mehmet Ersue
+               <mehmet.ersue@nsn.com>
+
+     Editor:   Martin Bjorklund
+               <mbj@tail-f.com>
+
+     Editor:   Juergen Schoenwaelder
+               <j.schoenwaelder@jacobs-university.de>
+
+     Editor:   Andy Bierman
+               <andy.bierman@brocade.com>";
+  description
+    "NETCONF Protocol Data Types and Protocol Operations.
+
+     Copyright (c) 2011 IETF Trust and the persons identified as
+     the document authors.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD License
+     set forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6241; see
+     the RFC itself for full legal notices.";
+
+  revision 2011-06-01 {
+    description
+      "Initial revision;";
+    reference
+      "RFC 6241: Network Configuration Protocol";
+  }
+
+  // NETCONF capabilities defined as features
+  feature writable-running;
+  feature candidate;
+  feature validate;
+  feature url;
+
+  // NETCONF Simple Types
+
+  typedef edit-operation-type {
+    type enumeration {
+      enum merge {
+      }
+      enum replace {
+      }
+      enum create {
+      }
+      enum delete {
+      }
+      enum remove {
+      }
+    }
+    default "merge";
+    description "NETCONF 'operation' attribute values";
+    reference "RFC 6241, Section 7.2";
+  }
+
+  // NETCONF Standard Protocol Operations
+
+  rpc edit-config {
+    description
+      "The <edit-config> operation loads all or part of a specified
+       configuration to the specified target configuration.";
+
+    reference "RFC 6241, Section 7.2";
+
+    input {
+      container target {
+        description
+          "Particular configuration to edit.";
+
+        choice config-target {
+          mandatory true;
+          description
+            "The configuration target.";
+
+          leaf candidate {
+            if-feature candidate;
+            type empty;
+            description
+              "The candidate configuration is the config target.";
+          }
+          leaf running {
+            if-feature writable-running;
+            type empty;
+            description
+              "The running configuration is the config source.";
+          }
+        }
+      }
+
+      leaf default-operation {
+        type enumeration {
+          enum merge {
+            description
+              "The default operation is merge.";
+          }
+          enum replace {
+            description
+              "The default operation is replace.";
+          }
+          enum none {
+            description
+              "There is no default operation.";
+          }
+        }
+        default "merge";
+        description
+          "The default operation to use.";
+      }
+
+      leaf test-option {
+        if-feature validate;
+        type enumeration {
+          enum test-then-set {
+            description
+              "The server will test and then set if no errors.";
+          }
+          enum set {
+            description
+              "The server will set without a test first.";
+          }
+
+          enum test-only {
+            description
+              "The server will only test and not set, even
+               if there are no errors.";
+          }
+        }
+        default "test-then-set";
+        description
+          "The test option to use.";
+      }
+
+      leaf error-option {
+        type enumeration {
+          enum stop-on-error {
+            description
+              "The server will stop on errors.";
+          }
+          enum continue-on-error {
+            description
+              "The server may continue on errors.";
+          }
+          enum rollback-on-error {
+            description
+              "The server will roll back on errors.
+               This value can only be used if the 'rollback-on-error'
+               feature is supported.";
+          }
+        }
+        default "stop-on-error";
+        description
+          "The error option to use.";
+      }
+
+      choice edit-content {
+        mandatory true;
+        description
+          "The content for the edit operation.";
+
+        anyxml config {
+          description
+            "Inline Config content.";
+        }
+        leaf url {
+          if-feature url;
+          type string;
+          description
+            "URL-based config content.";
+        }
+      }
+    }
+  }
+
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/anyxml-support/serialization/baz.xml b/yang/yang-data-codec-xml/src/test/resources/anyxml-support/serialization/baz.xml
new file mode 100644 (file)
index 0000000..82f99d5
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<baz xmlns="baz">
+    <my-container-1>
+        <my-leaf-1>value1</my-leaf-1>
+    </my-container-1>
+    <my-container-2>
+        <inner-container>
+            <my-leaf-2>value2</my-leaf-2>
+        </inner-container>
+        <my-leaf-3>value3</my-leaf-3>
+    </my-container-2>
+
+    <my-anyxml-data>
+        <inner-container>
+            <my-leaf-2>any-xml-leaf-2-value</my-leaf-2>
+        </inner-container>
+        <my-leaf-3>any-xml-leaf-3-value</my-leaf-3>
+    </my-anyxml-data>
+</baz>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/anyxml-support/serialization/baz.yang b/yang/yang-data-codec-xml/src/test/resources/anyxml-support/serialization/baz.yang
new file mode 100644 (file)
index 0000000..9cd2233
--- /dev/null
@@ -0,0 +1,29 @@
+module baz {
+    namespace "baz";
+    prefix baz;
+
+    import yang-ext { prefix ext; revision-date 2013-07-09; }
+
+    container baz {
+        container my-container-1 {
+            leaf my-leaf-1 {
+                type string;
+            }
+        }
+
+        container my-container-2 {
+            container inner-container {
+                leaf my-leaf-2 {
+                    type string;
+                }
+            }
+            leaf my-leaf-3 {
+                type string;
+            }
+        }
+
+        anyxml my-anyxml-data {
+            ext:anyxml-schema-location "/baz/my-container-2";
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/anyxml-support/serialization/yang-ext.yang b/yang/yang-data-codec-xml/src/test/resources/anyxml-support/serialization/yang-ext.yang
new file mode 100644 (file)
index 0000000..99eb4b6
--- /dev/null
@@ -0,0 +1,75 @@
+module yang-ext {
+    yang-version 1;
+    namespace "urn:opendaylight:yang:extension:yang-ext";
+    prefix "ext";
+
+    contact "Anton Tkacik <ttkacik@cisco.com>";
+
+    description
+        "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";
+
+    revision "2013-07-09" {
+        description "";
+    }
+
+    // Augmentation name
+
+    extension "augment-identifier" {
+        description
+                "YANG language extension which assigns an identifier to augmentation.
+                Augment identifier is used to identify specific augment statement
+                by name. The identifier syntax is defined formally defined by
+                the rule 'identifier' in Section 12 of RFC 6020. All augment identifiers
+                defined in a namespace MUST be unique. The namespace of augment
+                identifiers is shared by module and its submodules.";
+
+        /*
+                Discussion:
+                This extension allows for ease of development / debug
+                of YANG modules and it is suitable for code generation,
+                where each augment statement is nicely identified by
+                unique name instead of combination of augment target
+                and when condition.
+            */
+        argument "identifier";
+    }
+
+    // Context-aware RPCs
+
+    grouping rpc-context-ref {
+        description
+                "A reference to RPC context.";
+        leaf context-instance {
+            type instance-identifier;
+            description "Pointer to the context. ";
+        }
+    }
+
+    extension "rpc-context-instance" {
+        description
+                "YANG language extension which defines enclosing (parent) schema
+                node as referencable context for RPCs. The argument is identity
+                which is used to identify RPC context type.";
+
+        argument "context-type";
+    }
+
+    extension "context-reference" {
+        argument "context-type";
+    }
+
+    extension "context-instance" {
+        argument "context-type";
+    }
+
+    extension "instance-target" {
+        argument "path";
+    }
+
+    extension "anyxml-schema-location" {
+        argument "target-node";
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/anyxml-support/xml/bar.xml b/yang/yang-data-codec-xml/src/test/resources/anyxml-support/xml/bar.xml
new file mode 100644 (file)
index 0000000..2bf7dbe
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<my-anyxml-data xmlns="bar">
+    <my-container-1>
+        <my-leaf-1>value1</my-leaf-1>
+    </my-container-1>
+    <my-container-2>
+        <my-leaf-2>value2</my-leaf-2>
+    </my-container-2>
+</my-anyxml-data>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/anyxml-support/xml/foo.xml b/yang/yang-data-codec-xml/src/test/resources/anyxml-support/xml/foo.xml
new file mode 100644 (file)
index 0000000..6f13854
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<my-anyxml-data xmlns="foo">
+    <inner-container>
+        <my-leaf-2>any-xml-leaf-2-value</my-leaf-2>
+    </inner-container>
+    <my-leaf-3>any-xml-leaf-3-value</my-leaf-3>
+</my-anyxml-data>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/anyxml-support/yang/bar.yang b/yang/yang-data-codec-xml/src/test/resources/anyxml-support/yang/bar.yang
new file mode 100644 (file)
index 0000000..b8c57a8
--- /dev/null
@@ -0,0 +1,18 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    container bar {
+        container my-container-1 {
+            leaf my-leaf-1 {
+                type string;
+            }
+        }
+
+        container my-container-2 {
+            leaf my-leaf-2 {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/anyxml-support/yang/foo.yang b/yang/yang-data-codec-xml/src/test/resources/anyxml-support/yang/foo.yang
new file mode 100644 (file)
index 0000000..70ffa0b
--- /dev/null
@@ -0,0 +1,27 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    import yang-ext { prefix ext; revision-date 2013-07-09; }
+
+    container my-container-1 {
+        leaf my-leaf-1 {
+            type string;
+        }
+    }
+
+    container my-container-2 {
+        container inner-container {
+            leaf my-leaf-2 {
+                type string;
+            }
+        }
+        leaf my-leaf-3 {
+            type string;
+        }
+    }
+
+    anyxml my-anyxml-data {
+        ext:anyxml-schema-location "/my-container-2";
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/anyxml-support/yang/yang-ext.yang b/yang/yang-data-codec-xml/src/test/resources/anyxml-support/yang/yang-ext.yang
new file mode 100644 (file)
index 0000000..99eb4b6
--- /dev/null
@@ -0,0 +1,75 @@
+module yang-ext {
+    yang-version 1;
+    namespace "urn:opendaylight:yang:extension:yang-ext";
+    prefix "ext";
+
+    contact "Anton Tkacik <ttkacik@cisco.com>";
+
+    description
+        "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";
+
+    revision "2013-07-09" {
+        description "";
+    }
+
+    // Augmentation name
+
+    extension "augment-identifier" {
+        description
+                "YANG language extension which assigns an identifier to augmentation.
+                Augment identifier is used to identify specific augment statement
+                by name. The identifier syntax is defined formally defined by
+                the rule 'identifier' in Section 12 of RFC 6020. All augment identifiers
+                defined in a namespace MUST be unique. The namespace of augment
+                identifiers is shared by module and its submodules.";
+
+        /*
+                Discussion:
+                This extension allows for ease of development / debug
+                of YANG modules and it is suitable for code generation,
+                where each augment statement is nicely identified by
+                unique name instead of combination of augment target
+                and when condition.
+            */
+        argument "identifier";
+    }
+
+    // Context-aware RPCs
+
+    grouping rpc-context-ref {
+        description
+                "A reference to RPC context.";
+        leaf context-instance {
+            type instance-identifier;
+            description "Pointer to the context. ";
+        }
+    }
+
+    extension "rpc-context-instance" {
+        description
+                "YANG language extension which defines enclosing (parent) schema
+                node as referencable context for RPCs. The argument is identity
+                which is used to identify RPC context type.";
+
+        argument "context-type";
+    }
+
+    extension "context-reference" {
+        argument "context-type";
+    }
+
+    extension "context-instance" {
+        argument "context-type";
+    }
+
+    extension "instance-target" {
+        argument "path";
+    }
+
+    extension "anyxml-schema-location" {
+        argument "target-node";
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/baz.xml b/yang/yang-data-codec-xml/src/test/resources/baz.xml
new file mode 100644 (file)
index 0000000..3752c03
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<outer-container xmlns="baz-namespace">
+    <my-container-1>
+        <my-keyed-list>
+            <my-key-leaf>listkeyvalue1</my-key-leaf>
+            <my-leaf-in-list-1>listleafvalue1</my-leaf-in-list-1>
+            <my-leaf-in-list-2>listleafvalue2</my-leaf-in-list-2>
+        </my-keyed-list>
+
+        <my-keyed-list>
+            <my-key-leaf>listkeyvalue2</my-key-leaf>
+            <my-leaf-in-list-1>listleafvalue12</my-leaf-in-list-1>
+            <my-leaf-in-list-2>listleafvalue22</my-leaf-in-list-2>
+        </my-keyed-list>
+
+        <my-leaf-1>value1</my-leaf-1>
+
+        <my-leaf-list>lflvalue1</my-leaf-list>
+        <my-leaf-list>lflvalue2</my-leaf-list>
+    </my-container-1>
+
+    <my-container-2>
+        <inner-container>
+            <my-leaf-2>value2</my-leaf-2>
+        </inner-container>
+        <my-leaf-3>value3</my-leaf-3>
+        <my-leaf-in-case-2>case2value</my-leaf-in-case-2>
+    </my-container-2>
+
+    <my-container-3>
+        <my-doubly-keyed-list>
+            <my-first-key-leaf>listkeyvalue1</my-first-key-leaf>
+            <my-second-key-leaf>listkeyvalue2</my-second-key-leaf>
+            <my-leaf-in-list-3>listleafvalue1</my-leaf-in-list-3>
+        </my-doubly-keyed-list>
+    </my-container-3>
+</outer-container>
diff --git a/yang/yang-data-codec-xml/src/test/resources/baz.yang b/yang/yang-data-codec-xml/src/test/resources/baz.yang
new file mode 100644 (file)
index 0000000..d61077e
--- /dev/null
@@ -0,0 +1,82 @@
+module baz {
+    namespace "baz-namespace";
+    prefix bz;
+
+    container outer-container {
+        container my-container-1 {
+            list my-keyed-list {
+                key "my-key-leaf";
+
+                leaf my-key-leaf {
+                    type string;
+                }
+
+                leaf my-leaf-in-list-1 {
+                    type string;
+                }
+
+                leaf my-leaf-in-list-2 {
+                    type string;
+                }
+            }
+
+            leaf my-leaf-1 {
+                type string;
+            }
+
+            leaf-list my-leaf-list {
+                type string;
+            }
+        }
+
+        container my-container-2 {
+            container inner-container {
+                leaf my-leaf-2 {
+                    type string;
+                }
+            }
+
+            leaf my-leaf-3 {
+                type string;
+            }
+
+            choice my-choice {
+                case my-case-1 {
+                    leaf my-leaf-in-case-1 {
+                        type string;
+                    }
+                }
+
+                case my-case-2 {
+                    leaf my-leaf-in-case-2 {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+    augment "/outer-container" {
+        container my-container-3 {
+
+        }
+    }
+
+    augment "/outer-container/my-container-3" {
+        list my-doubly-keyed-list {
+            key "my-first-key-leaf my-second-key-leaf";
+
+            leaf "my-first-key-leaf" {
+                type string;
+            }
+
+            leaf my-second-key-leaf {
+                type string;
+            }
+
+            leaf my-leaf-in-list-3 {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug1848/foo.yang b/yang/yang-data-codec-xml/src/test/resources/bug1848/foo.yang
new file mode 100644 (file)
index 0000000..82e8d92
--- /dev/null
@@ -0,0 +1,20 @@
+module foo {
+    namespace "foo";
+    prefix "foo";
+    revision "2016-02-17";
+
+    container root {
+        list policy {
+            key name;
+            leaf name {
+                type string;
+            }
+            list rule {
+                key name;
+                leaf name {
+                    type string;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug1848/order.yang b/yang/yang-data-codec-xml/src/test/resources/bug1848/order.yang
new file mode 100644 (file)
index 0000000..5cc8349
--- /dev/null
@@ -0,0 +1,16 @@
+module order {
+    namespace "order";
+    prefix "order";
+    revision "2016-02-17";
+
+    container root {
+        leaf id {
+            type string;
+        }
+        container cont {
+            leaf content {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo.xml b/yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo.xml
new file mode 100644 (file)
index 0000000..744500b
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="foo">
+    <my-leaf>dp1o34</my-leaf>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo2.xml b/yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo2.xml
new file mode 100644 (file)
index 0000000..90dd302
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="foo">
+    <my-leaf>dp0s3f9</my-leaf>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo3.xml b/yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo3.xml
new file mode 100644 (file)
index 0000000..507b654
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="foo">
+    <my-leaf>dp09P1p2s3</my-leaf>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo4.xml b/yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo4.xml
new file mode 100644 (file)
index 0000000..b9ee3fa
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="foo">
+    <my-leaf>dp0p3p1</my-leaf>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo5.xml b/yang/yang-data-codec-xml/src/test/resources/bug5396/xml/foo5.xml
new file mode 100644 (file)
index 0000000..e69616a
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="foo">
+    <my-leaf>dp0s3</my-leaf>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug5396/xml/invalid-foo.xml b/yang/yang-data-codec-xml/src/test/resources/bug5396/xml/invalid-foo.xml
new file mode 100644 (file)
index 0000000..aeed2f6
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="foo">
+    <my-leaf>dp09P1p2s1234</my-leaf>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug5396/yang/foo.yang b/yang/yang-data-codec-xml/src/test/resources/bug5396/yang/foo.yang
new file mode 100644 (file)
index 0000000..bad4e39
--- /dev/null
@@ -0,0 +1,32 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix "foo";
+
+    revision "2016-03-22" {
+        description "test";
+    }
+
+    container root {
+        leaf my-leaf {
+            type my-type;
+        }
+    }
+
+    typedef my-type {
+        type union {
+            type string {
+                pattern "dp[0-9]+o[0-9]+";
+            }
+            type string {
+                pattern "dp[0-9]+s[0-9]+(f[0-9]+)?(d[0-9]+)?";
+            }
+            type string {
+                pattern "dp[0-9]+(P[0-9]+)?p[0-9]{1,3}s[0-9]{1,3}(f[0-9]+)?(d[0-9]+)?";
+            }
+            type string {
+                pattern "dp[0-9]+p[0-9]+p[0-9]+";
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug5446/xml/foo.xml b/yang/yang-data-codec-xml/src/test/resources/bug5446/xml/foo.xml
new file mode 100644 (file)
index 0000000..142ed14
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="foo">
+    <ip-address>fwAAAQ==</ip-address>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug5446/yang/foo.yang b/yang/yang-data-codec-xml/src/test/resources/bug5446/yang/foo.yang
new file mode 100644 (file)
index 0000000..57ae347
--- /dev/null
@@ -0,0 +1,33 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix "foo";
+
+    revision "2015-11-05" {
+    }
+
+    typedef ipv4-address-binary {
+        type binary {
+            length "4";
+        }
+    }
+
+    typedef ipv6-address-binary {
+        type binary {
+            length "16";
+        }
+    }
+
+    typedef ip-address-binary {
+        type union {
+            type ipv4-address-binary;
+            type ipv6-address-binary;
+        }
+    }
+
+    container root {
+        leaf ip-address {
+            type ip-address-binary;
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8083/xml/baz.xml b/yang/yang-data-codec-xml/src/test/resources/bug8083/xml/baz.xml
new file mode 100644 (file)
index 0000000..eff39ac
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<top-cont xmlns="baz-ns">
+    <keyed-list>
+        <empty-key-leaf/>
+        <regular-leaf>150</regular-leaf>
+    </keyed-list>
+
+    <iid-leaf xmlns:baz="baz-ns">/baz:top-cont/baz:keyed-list[baz:empty-key-leaf='']/baz:regular-leaf</iid-leaf>
+</top-cont>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8083/xml/foobar.xml b/yang/yang-data-codec-xml/src/test/resources/bug8083/xml/foobar.xml
new file mode 100644 (file)
index 0000000..c317acd
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<top-cont xmlns="foobar-ns">
+    <keyed-list>
+        <iid-key-leaf xmlns:foobar="foobar-ns">/foobar:top-cont/foobar:leaf-b</iid-key-leaf>
+        <regular-leaf>150</regular-leaf>
+    </keyed-list>
+
+    <iid-leaf xmlns:foobar="foobar-ns">
+        /foobar:top-cont/foobar:keyed-list[foobar:iid-key-leaf='/foobar:top-cont/foobar:leaf-b']/foobar:regular-leaf</iid-leaf>
+    <leaf-b>50</leaf-b>
+</top-cont>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8083/xml/zab.xml b/yang/yang-data-codec-xml/src/test/resources/bug8083/xml/zab.xml
new file mode 100644 (file)
index 0000000..3d31218
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<top-cont xmlns="zab-ns">
+    <keyed-list>
+        <identityref-key-leaf>derived-id</identityref-key-leaf>
+        <regular-leaf>150</regular-leaf>
+    </keyed-list>
+
+    <iid-leaf xmlns:zab="zab-ns">
+        /zab:top-cont/zab:keyed-list[zab:identityref-key-leaf='derived-id']/zab:regular-leaf</iid-leaf>
+</top-cont>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8083/yang/baz.yang b/yang/yang-data-codec-xml/src/test/resources/bug8083/yang/baz.yang
new file mode 100644 (file)
index 0000000..0690b1f
--- /dev/null
@@ -0,0 +1,23 @@
+module baz {
+
+    namespace baz-ns;
+    prefix baz-prefix;
+
+    container top-cont {
+        list keyed-list {
+            key empty-key-leaf;
+
+            leaf empty-key-leaf {
+                type empty;
+            }
+
+            leaf regular-leaf {
+                type int32;
+            }
+        }
+
+        leaf iid-leaf {
+            type instance-identifier;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8083/yang/foobar.yang b/yang/yang-data-codec-xml/src/test/resources/bug8083/yang/foobar.yang
new file mode 100644 (file)
index 0000000..ec42bab
--- /dev/null
@@ -0,0 +1,27 @@
+module foobar {
+
+    namespace foobar-ns;
+    prefix foobar-prefix;
+
+    container top-cont {
+        list keyed-list {
+            key iid-key-leaf;
+
+            leaf iid-key-leaf {
+                type instance-identifier;
+            }
+
+            leaf regular-leaf {
+                type int32;
+            }
+        }
+
+        leaf iid-leaf {
+            type instance-identifier;
+        }
+
+        leaf leaf-b {
+            type int32;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8083/yang/zab.yang b/yang/yang-data-codec-xml/src/test/resources/bug8083/yang/zab.yang
new file mode 100644 (file)
index 0000000..5af9529
--- /dev/null
@@ -0,0 +1,31 @@
+module zab {
+
+    namespace zab-ns;
+    prefix zab-prefix;
+
+    identity base-id;
+
+    identity derived-id {
+        base base-id;
+    }
+
+    container top-cont {
+        list keyed-list {
+            key identityref-key-leaf;
+
+            leaf identityref-key-leaf {
+                type identityref {
+                    base base-id;
+                }
+            }
+
+            leaf regular-leaf {
+                type int32;
+            }
+        }
+
+        leaf iid-leaf {
+            type instance-identifier;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8675/foo-2.xml b/yang/yang-data-codec-xml/src/test/resources/bug8675/foo-2.xml
new file mode 100644 (file)
index 0000000..5859bad
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<top-level-container xmlns="foo">
+</top-level-container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8675/foo-3.xml b/yang/yang-data-codec-xml/src/test/resources/bug8675/foo-3.xml
new file mode 100644 (file)
index 0000000..fd71a36
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<top-level-list xmlns="foo">
+    <key-leaf>key-value</key-leaf>
+    <ordinary-leaf>leaf-value</ordinary-leaf>
+    <cont-in-list></cont-in-list>
+    <inner-list>
+        <inner-key-leaf>inner-key-value</inner-key-leaf>
+        <inner-ordinary-leaf>inner-leaf-value</inner-ordinary-leaf>
+    </inner-list>
+</top-level-list>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8675/foo-4.xml b/yang/yang-data-codec-xml/src/test/resources/bug8675/foo-4.xml
new file mode 100644 (file)
index 0000000..fad3721
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<top-level-anyxml xmlns="foo">
+    <data>data-value</data>
+</top-level-anyxml>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8675/foo-5.xml b/yang/yang-data-codec-xml/src/test/resources/bug8675/foo-5.xml
new file mode 100644 (file)
index 0000000..648cffe
--- /dev/null
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<top-level-leaf xmlns="foo">100</top-level-leaf>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8675/foo-6.xml b/yang/yang-data-codec-xml/src/test/resources/bug8675/foo-6.xml
new file mode 100644 (file)
index 0000000..5389603
--- /dev/null
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<top-level-leaf-list xmlns="foo">100</top-level-leaf-list>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8675/foo.xml b/yang/yang-data-codec-xml/src/test/resources/bug8675/foo.xml
new file mode 100644 (file)
index 0000000..41d8802
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<top-level-container xmlns="foo">
+    <inner-container-1></inner-container-1><inner-container-2></inner-container-2>
+</top-level-container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8675/foo.yang b/yang/yang-data-codec-xml/src/test/resources/bug8675/foo.yang
new file mode 100644 (file)
index 0000000..06ff0eb
--- /dev/null
@@ -0,0 +1,47 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    revision 2017-06-13;
+
+    list top-level-list {
+        key key-leaf;
+
+        leaf key-leaf {
+            type string;
+        }
+
+        leaf ordinary-leaf {
+            type string;
+        }
+
+        container cont-in-list {}
+
+        list inner-list {
+            key inner-key-leaf;
+
+            leaf inner-key-leaf {
+                type string;
+            }
+
+            leaf inner-ordinary-leaf {
+                type string;
+            }
+        }
+    }
+
+    container top-level-container {
+        container inner-container-1 {}
+        container inner-container-2 {}
+    }
+
+    anyxml top-level-anyxml;
+
+    leaf top-level-leaf {
+        type int32;
+    }
+
+    leaf-list top-level-leaf-list {
+        type int32;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8745/foo.xml b/yang/yang-data-codec-xml/src/test/resources/bug8745/foo.xml
new file mode 100644 (file)
index 0000000..304b11b
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<cont-with-attributes xmlns="foo" xmlns:a="attribute-ns" a:operation="delete">
+    <leaf-with-attributes xmlns:a="attribute-ns" a:operation="create">lf-val</leaf-with-attributes>
+    <leaf-list-with-attributes xmlns:a="attribute-ns" a:operation="replace">lfl-val</leaf-list-with-attributes>
+    <list-with-attributes xmlns:a="attribute-ns" a:operation="merge">
+        <list-key>key-val</list-key>
+    </list-with-attributes>
+</cont-with-attributes>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8745/foo.yang b/yang/yang-data-codec-xml/src/test/resources/bug8745/foo.yang
new file mode 100644 (file)
index 0000000..c2958d5
--- /dev/null
@@ -0,0 +1,22 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    container cont-with-attributes {
+        leaf leaf-with-attributes {
+            type string;
+        }
+
+        leaf-list leaf-list-with-attributes {
+            type string;
+        }
+
+        list list-with-attributes {
+            key list-key;
+
+            leaf list-key {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8803/bar.yang b/yang/yang-data-codec-xml/src/test/resources/bug8803/bar.yang
new file mode 100644 (file)
index 0000000..64adc82
--- /dev/null
@@ -0,0 +1,14 @@
+module bar {
+    namespace bar-ns;
+    prefix bar;
+
+    import foo {
+        prefix foo;
+    }
+
+    augment "/foo:top-cont/foo:keyed-list" {
+        leaf iid-leaf {
+            type instance-identifier;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8803/baz.yang b/yang/yang-data-codec-xml/src/test/resources/bug8803/baz.yang
new file mode 100644 (file)
index 0000000..abe4a57
--- /dev/null
@@ -0,0 +1,14 @@
+module baz {
+    namespace baz-ns;
+    prefix baz;
+
+    container top-cont {
+        list keyed-list {
+            key key-leaf;
+
+            leaf key-leaf {
+                type int32;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8803/foo.xml b/yang/yang-data-codec-xml/src/test/resources/bug8803/foo.xml
new file mode 100644 (file)
index 0000000..8f7bf1c
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<top-cont xmlns="foo-ns">
+    <keyed-list>
+        <key-leaf>150</key-leaf>
+        <iid-leaf xmlns="bar-ns" xmlns:baz="baz-ns">
+            /baz:top-cont/baz:keyed-list[baz:key-leaf='50']</iid-leaf>
+    </keyed-list>
+</top-cont>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug8803/foo.yang b/yang/yang-data-codec-xml/src/test/resources/bug8803/foo.yang
new file mode 100644 (file)
index 0000000..7c8bae4
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace foo-ns;
+    prefix foo;
+
+    container top-cont {
+        list keyed-list {
+            key key-leaf;
+
+            leaf key-leaf {
+                type int32;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug890/xml/foo.xml b/yang/yang-data-codec-xml/src/test/resources/bug890/xml/foo.xml
new file mode 100644 (file)
index 0000000..4bd91b1
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="foo">
+    <outgoing-labels>
+        <outgoing-labels>
+            <index>0</index>
+            <config>
+                <index>0</index>
+                <label>103</label>
+            </config>
+        </outgoing-labels>
+        <outgoing-labels>
+            <index>1</index>
+            <config>
+                <index>1</index>
+                <label>104</label>
+            </config>
+        </outgoing-labels>
+    </outgoing-labels>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/bug890/yang/foo.yang b/yang/yang-data-codec-xml/src/test/resources/bug890/yang/foo.yang
new file mode 100644 (file)
index 0000000..4caaf48
--- /dev/null
@@ -0,0 +1,54 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix "foo";
+
+    revision "2018-07-10" {
+    }
+
+    grouping path-outgoing-labels_config {
+        description "Path outgoing labels grouping";
+        leaf index {
+              type uint8 {
+                range "0..255";
+              }
+              description "Index of the label. Index 0 indicates top of the label stack";
+        }
+        leaf label {
+          type uint32;
+          description "The outgoing MPLS labels to impose";
+        }
+    }
+
+    grouping path-outgoing-labels {
+        description "Path outgoing labels grouping";
+        container outgoing-labels {
+            description "List of outgoing labels";
+            list outgoing-labels {
+                key "index";
+                description "Outgoing label list";
+                leaf index {
+                    type leafref {
+                        path "../config/index";
+                    }
+                    description
+                    "Index of the label. Index 0 indicates
+                     top of the label stack";
+                }
+                container config {
+                    description "Configuration intended parameters";
+                    uses path-outgoing-labels_config;
+                }
+                container state {
+                    config false;
+                    description "Configuration applied parameters and state";
+                    uses path-outgoing-labels_config;
+                }
+            }
+        }
+    }
+
+    container root {
+        uses path-outgoing-labels;
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/dom-reader-test/bar.yang b/yang/yang-data-codec-xml/src/test/resources/dom-reader-test/bar.yang
new file mode 100644 (file)
index 0000000..64adc82
--- /dev/null
@@ -0,0 +1,14 @@
+module bar {
+    namespace bar-ns;
+    prefix bar;
+
+    import foo {
+        prefix foo;
+    }
+
+    augment "/foo:top-cont/foo:keyed-list" {
+        leaf iid-leaf {
+            type instance-identifier;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/dom-reader-test/baz.yang b/yang/yang-data-codec-xml/src/test/resources/dom-reader-test/baz.yang
new file mode 100644 (file)
index 0000000..fc39bdc
--- /dev/null
@@ -0,0 +1,14 @@
+module baz {
+    namespace baz-ns;
+    prefix baz;
+
+    container top-cont {
+        /*list keyed-list {
+            key key-leaf;
+
+            leaf key-leaf {
+                type int32;
+            }
+        }*/
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/dom-reader-test/foo.xml b/yang/yang-data-codec-xml/src/test/resources/dom-reader-test/foo.xml
new file mode 100644 (file)
index 0000000..b495a5a
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<top-cont xmlns="foo-ns">
+    <keyed-list>
+        <key-leaf>150</key-leaf>
+        <iid-leaf xmlns="bar-ns" xmlns:baz="baz-ns" xmlns:rab="rab-ns">
+            /baz:top-cont/rab:keyed-list[rab:key-leaf='50']</iid-leaf>
+        <ordinary-leaf>50</ordinary-leaf>
+        <idref-leaf xmlns:rab="rab-ns">rab:id-foo</idref-leaf>
+        <aug-anyxml xmlns="zab-ns">
+            <node-a>
+                <node-b>anyxml-value</node-b>
+            </node-a>
+        </aug-anyxml>
+    </keyed-list>
+</top-cont>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/dom-reader-test/foo.yang b/yang/yang-data-codec-xml/src/test/resources/dom-reader-test/foo.yang
new file mode 100644 (file)
index 0000000..b09776d
--- /dev/null
@@ -0,0 +1,28 @@
+module foo {
+    namespace foo-ns;
+    prefix foo;
+
+    import rab {
+        prefix rab;
+    }
+
+    container top-cont {
+        list keyed-list {
+            key key-leaf;
+
+            leaf key-leaf {
+                type int32;
+            }
+
+            leaf idref-leaf {
+                type identityref {
+                    base rab:base-id;
+                }
+            }
+
+            leaf ordinary-leaf {
+                type int32;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/dom-reader-test/rab.yang b/yang/yang-data-codec-xml/src/test/resources/dom-reader-test/rab.yang
new file mode 100644 (file)
index 0000000..dc085ec
--- /dev/null
@@ -0,0 +1,23 @@
+module rab {
+    namespace rab-ns;
+    prefix rab;
+
+    import baz {
+        prefix baz;
+    }
+
+    augment "/baz:top-cont" {
+        list keyed-list {
+            key key-leaf;
+
+            leaf key-leaf {
+                type int32;
+            }
+        }
+    }
+
+    identity base-id;
+    identity id-foo {
+        base base-id;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/dom-reader-test/zab.yang b/yang/yang-data-codec-xml/src/test/resources/dom-reader-test/zab.yang
new file mode 100644 (file)
index 0000000..d31199e
--- /dev/null
@@ -0,0 +1,16 @@
+module zab {
+    namespace zab-ns;
+    prefix zab;
+
+    import foo {
+        prefix foo;
+    }
+
+    augment "/foo:top-cont/foo:keyed-list/" {
+        /*leaf ordinary-leaf {
+            type int32;
+        }*/
+
+        anyxml aug-anyxml;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/foo.xml b/yang/yang-data-codec-xml/src/test/resources/foo.xml
new file mode 100644 (file)
index 0000000..c010ba1
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<parent-container xmlns="foo-namespace">
+    <leaf-container>
+        <union-identityref-leaf xmlns:prefix="foo-namespace">prefix:ident-one</union-identityref-leaf>
+        <int32-leaf>1500</int32-leaf>
+        <decimal64-leaf>150.45</decimal64-leaf>
+        <string-leaf>hello world</string-leaf>
+        <leafref-leaf>hello world</leafref-leaf>
+        <empty-leaf/>
+        <boolean-leaf>true</boolean-leaf>
+        <enum-leaf>five</enum-leaf>
+    </leaf-container>
+    <anyxml-container>
+        <my-anyxml><my-element><my-sub-element>&lt; &amp; sub-element value</my-sub-element></my-element></my-anyxml>
+    </anyxml-container>
+</parent-container>
diff --git a/yang/yang-data-codec-xml/src/test/resources/foo.yang b/yang/yang-data-codec-xml/src/test/resources/foo.yang
new file mode 100644 (file)
index 0000000..ea912d0
--- /dev/null
@@ -0,0 +1,72 @@
+module foo {
+    namespace "foo-namespace";
+    prefix "f";
+
+    identity ident-base;
+    identity ident-one {
+        base ident-base;
+    }
+
+    typedef union-type {
+        type union {
+            type uint8;
+            type identityref {
+                base ident-base;
+            }
+        }
+    }
+
+    container parent-container {
+
+        container leaf-container {
+
+            leaf union-identityref-leaf {
+                type union-type;
+            }
+
+            leaf int32-leaf {
+                type int32;
+            }
+
+            leaf decimal64-leaf {
+                type decimal64 {
+                    fraction-digits 2;
+                }
+            }
+
+            leaf string-leaf {
+                type string;
+            }
+
+            leaf leafref-leaf {
+                type leafref {
+                    path "../string-leaf";
+                }
+            }
+
+            leaf empty-leaf {
+                type empty;
+            }
+
+            leaf boolean-leaf {
+                type boolean;
+            }
+
+            leaf enum-leaf {
+                type enumeration {
+                    enum zero;
+                    enum one;
+                    enum five {
+                        value 5;
+                    }
+                }
+            }
+        }
+
+        container anyxml-container {
+            anyxml my-anyxml;
+        }
+    }
+
+
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/foobar.xml b/yang/yang-data-codec-xml/src/test/resources/foobar.xml
new file mode 100644 (file)
index 0000000..5d65738
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<outer-container xmlns="foobar-namespace">
+    <my-container-1>
+        <my-keyed-list>
+            <my-key-leaf>listkeyvalue1</my-key-leaf>
+            <my-leaf-in-list-1>listleafvalue1</my-leaf-in-list-1>
+            <my-leaf-in-list-2>listleafvalue2</my-leaf-in-list-2>
+        </my-keyed-list>
+
+        <my-keyed-list>
+            <my-key-leaf>listkeyvalue2</my-key-leaf>
+            <my-leaf-in-list-1>listleafvalue12</my-leaf-in-list-1>
+            <my-leaf-in-list-2>listleafvalue22</my-leaf-in-list-2>
+        </my-keyed-list>
+
+        <my-ordered-list>
+            <my-key-leaf-in-ordered-list>olistkeyvalue1</my-key-leaf-in-ordered-list>
+            <my-leaf-in-ordered-list-1>olistleafvalue1</my-leaf-in-ordered-list-1>
+            <my-leaf-in-ordered-list-2>olistleafvalue2</my-leaf-in-ordered-list-2>
+        </my-ordered-list>
+
+        <my-ordered-list>
+            <my-key-leaf-in-ordered-list>olistkeyvalue2</my-key-leaf-in-ordered-list>
+            <my-leaf-in-ordered-list-1>olistleafvalue12</my-leaf-in-ordered-list-1>
+            <my-leaf-in-ordered-list-2>olistleafvalue22</my-leaf-in-ordered-list-2>
+        </my-ordered-list>
+
+        <my-unkeyed-list>
+            <my-leaf-in-unkeyed-list>foo</my-leaf-in-unkeyed-list>
+        </my-unkeyed-list>
+
+        <my-leaf-1>value1</my-leaf-1>
+
+        <my-leaf-list>lflvalue1</my-leaf-list>
+        <my-leaf-list>lflvalue2</my-leaf-list>
+
+        <my-ordered-leaf-list>olflvalue1</my-ordered-leaf-list>
+        <my-ordered-leaf-list>olflvalue2</my-ordered-leaf-list>
+    </my-container-1>
+
+    <my-container-2>
+        <inner-container>
+            <my-leaf-2>value2</my-leaf-2>
+        </inner-container>
+        <my-leaf-3>value3</my-leaf-3>
+        <my-leaf-in-case-2>case2value</my-leaf-in-case-2>
+    </my-container-2>
+
+    <my-container-3>
+        <my-doubly-keyed-list>
+            <my-first-key-leaf>listkeyvalue1</my-first-key-leaf>
+            <my-second-key-leaf>listkeyvalue2</my-second-key-leaf>
+            <my-leaf-in-list-3>listleafvalue1</my-leaf-in-list-3>
+        </my-doubly-keyed-list>
+    </my-container-3>
+</outer-container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-baz-2.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-baz-2.xml
new file mode 100644 (file)
index 0000000..ae7b62c
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<outer-container xmlns="baz-namespace">
+    <my-container-1>
+        <my-keyed-list>
+            <my-key-leaf>listkeyvalue1</my-key-leaf>
+            <my-leaf-in-list-1>listleafvalue1</my-leaf-in-list-1>
+            <my-leaf-in-list-2>listleafvalue2</my-leaf-in-list-2>
+        </my-keyed-list>
+
+        <my-keyed-list>
+            <my-key-leaf>listkeyvalue2</my-key-leaf>
+            <my-leaf-in-list-1>listleafvalue12</my-leaf-in-list-1>
+            <my-leaf-in-list-2>listleafvalue22</my-leaf-in-list-2>
+        </my-keyed-list>
+
+        <my-leaf-1>value1</invalid-my-leaf-1>
+
+        <my-leaf-list>lflvalue1</my-leaf-list>
+        <my-leaf-list>lflvalue2</my-leaf-list>
+    </my-container-1>
+
+    <my-container-2>
+        <inner-container>
+            <my-leaf-2>value2</my-leaf-2>
+        </inner-container>
+        <my-leaf-3>value3</my-leaf-3>
+        <my-leaf-in-case-2>case2value</my-leaf-in-case-2>
+    </my-container-2>
+
+    <my-container-3>
+        <my-doubly-keyed-list>
+            <my-first-key-leaf>listkeyvalue1</my-first-key-leaf>
+            <my-second-key-leaf>listkeyvalue2</my-second-key-leaf>
+            <my-leaf-in-list-3>listleafvalue3</my-leaf-in-list-3>
+        </my-doubly-keyed-list>
+    </my-container-3>
+</outer-container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-baz-3.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-baz-3.xml
new file mode 100644 (file)
index 0000000..6b280b1
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<outer-container xmlns="baz-namespace">
+    <my-container-1>
+        <my-keyed-list>
+            <my-key-leaf>listkeyvalue1</my-key-leaf>
+            <my-leaf-in-list-1>listleafvalue1</my-leaf-in-list-1>
+            <my-leaf-in-list-2>listleafvalue2</my-leaf-in-list-2>
+        </my-keyed-list>
+
+        <my-keyed-list>
+            <my-key-leaf>listkeyvalue2</my-key-leaf>
+            <my-leaf-in-list-1>listleafvalue12</my-leaf-in-list-1>
+            <my-leaf-in-list-2>listleafvalue22</my-leaf-in-list-2>
+        </my-keyed-list>
+
+        <my-leaf-1>value1</my-leaf-1>
+
+        <my-leaf-list>lflvalue1</my-leaf-list>
+        <my-leaf-list>lflvalue2</my-leaf-list>
+    <my-container-1>
+
+    <my-container-2>
+        <inner-container>
+            <my-leaf-2>value2</my-leaf-2>
+        </inner-container>
+        <my-leaf-3>value3</my-leaf-3>
+        <my-leaf-in-case-2>case2value</my-leaf-in-case-2>
+    </my-container-2>
+
+    <my-container-3>
+        <my-doubly-keyed-list>
+            <my-first-key-leaf>listkeyvalue1</my-first-key-leaf>
+            <my-second-key-leaf>listkeyvalue2</my-second-key-leaf>
+            <my-leaf-in-list-3>listleafvalue3</my-leaf-in-list-3>
+        </my-doubly-keyed-list>
+    </my-container-3>
+</outer-container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-baz-4.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-baz-4.xml
new file mode 100644 (file)
index 0000000..5b50fbd
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<outer-container xmlns="baz-namespace">
+    <my-container-1>
+        <my-keyed-list>
+            <my-key-leaf>listkeyvalue1</my-key-leaf>
+            <my-leaf-in-list-1>listleafvalue1</my-leaf-in-list-1>
+            <my-leaf-in-list-2>listleafvalue2</my-leaf-in-list-2>
+        </my-keyed-list>
+
+        <my-keyed-list>
+            <my-key-leaf>listkeyvalue2</my-key-leaf>
+            <my-leaf-in-list-1>listleafvalue12</my-leaf-in-list-1>
+            <my-leaf-in-list-2>listleafvalue22</my-leaf-in-list-2>
+        </my-keyed-list>
+
+        <my-leaf-1>value1</my-leaf-1>
+
+        <my-leaf-list>lflvalue1</my-leaf-list>
+        <my-leaf-list>lflvalue2</my-leaf-list>
+    </invalid-my-container-1>
+
+    <my-container-2>
+        <inner-container>
+            <my-leaf-2>value2</my-leaf-2>
+        </inner-container>
+        <my-leaf-3>value3</my-leaf-3>
+        <my-leaf-in-case-2>case2value</my-leaf-in-case-2>
+    </my-container-2>
+
+    <my-container-3>
+        <my-doubly-keyed-list>
+            <my-first-key-leaf>listkeyvalue1</my-first-key-leaf>
+            <my-second-key-leaf>listkeyvalue2</my-second-key-leaf>
+            <my-leaf-in-list-3>listleafvalue3</my-leaf-in-list-3>
+        </my-doubly-keyed-list>
+    </my-container-3>
+</outer-container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-baz.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-baz.xml
new file mode 100644 (file)
index 0000000..5ec7781
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<outer-container xmlns="baz-namespace">
+    <my-container-1>
+        <my-keyed-list>
+            <my-key-leaf>listkeyvalue1</my-key-leaf>
+            <my-leaf-in-list-1>listleafvalue1</my-leaf-in-list-1>
+            <my-leaf-in-list-2>listleafvalue2</my-leaf-in-list-2>
+        </my-keyed-list>
+
+        <my-keyed-list>
+            <my-key-leaf>listkeyvalue2</my-key-leaf>
+            <my-leaf-in-list-1>listleafvalue12</my-leaf-in-list-1>
+            <my-leaf-in-list-2>listleafvalue22</my-leaf-in-list-2>
+        </my-keyed-list>
+
+        <my-leaf-1>value1<my-leaf-1>
+
+        <my-leaf-list>lflvalue1</my-leaf-list>
+        <my-leaf-list>lflvalue2</my-leaf-list>
+    </my-container-1>
+
+    <my-container-2>
+        <inner-container>
+            <my-leaf-2>value2</my-leaf-2>
+        </inner-container>
+        <my-leaf-3>value3</my-leaf-3>
+        <my-leaf-in-case-2>case2value</my-leaf-in-case-2>
+    </my-container-2>
+
+    <my-container-3>
+        <my-doubly-keyed-list>
+            <my-first-key-leaf>listkeyvalue1</my-first-key-leaf>
+            <my-second-key-leaf>listkeyvalue2</my-second-key-leaf>
+            <my-leaf-in-list-3>listleafvalue3</my-leaf-in-list-3>
+        </my-doubly-keyed-list>
+    </my-container-3>
+</outer-container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-foo-2.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-foo-2.xml
new file mode 100644 (file)
index 0000000..84ce5c5
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<parent-container xmlns="foo-namespace">
+    <leaf-container>
+        <int32-leaf>1500</int32-leaf>
+        <decimal64-leaf>150.45</decimal64-leaf>
+        <string-leaf>hello world</string-leaf>
+        <leafref-leaf>hello world</leafref-leaf>
+        <empty-leaf/>
+        <boolean-leaf>true</boolean-leaf>
+        <enum-leaf>five</enum-leaf>
+    </leaf-container>
+    <anyxml-container>
+        <my-anyxml>
+            <my-element>
+                <my-sub-element>sub-element value</my-sub-element>
+            </my-element>
+        </my-anyxml>
+        <my-anyxml>
+            <my-element-2>
+                <my-sub-element-2>sub-element value 2</my-sub-element-2>
+            </my-element-2>
+        </my-anyxml>
+    </anyxml-container>
+</parent-container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-foo-3.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-foo-3.xml
new file mode 100644 (file)
index 0000000..0021427
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<parent-container xmlns="foo-namespace">
+    <leaf-container>
+        <int32-leaf>1500</int32-leaf>
+        <decimal64-leaf>150.45</decimal64-leaf>
+        <string-leaf>hello world</string-leaf>
+        <leafref-leaf>hello world</leafref-leaf>
+        <empty-leaf/>
+        <boolean-leaf>true</boolean-leaf>
+        <enum-leaf>five</enum-leaf>
+    </leaf-container>
+    <leaf-container>
+        <int32-leaf>2500</int32-leaf>
+        <decimal64-leaf>250.45</decimal64-leaf>
+        <string-leaf>goodbye world</string-leaf>
+    </leaf-container>
+    <anyxml-container>
+        <my-anyxml>
+            <my-element>
+                <my-sub-element>sub-element value</my-sub-element>
+            </my-element>
+        </my-anyxml>
+    </anyxml-container>
+</parent-container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-foo.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-foo.xml
new file mode 100644 (file)
index 0000000..8734999
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<parent-container xmlns="foo-namespace">
+    <leaf-container>
+        <int32-leaf>1500</int32-leaf>
+        <decimal64-leaf>150.45</decimal64-leaf>
+        <decimal64-leaf>240.35</decimal64-leaf>
+        <string-leaf>hello world</string-leaf>
+        <leafref-leaf>hello world</leafref-leaf>
+        <empty-leaf/>
+        <boolean-leaf>true</boolean-leaf>
+        <enum-leaf>five</enum-leaf>
+    </leaf-container>
+    <anyxml-container>
+        <my-anyxml>
+            <my-element>
+                <my-sub-element>sub-element value</my-sub-element>
+            </my-element>
+        </my-anyxml>
+    </anyxml-container>
+</parent-container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/leafref-test.yang b/yang/yang-data-codec-xml/src/test/resources/leafref-test.yang
new file mode 100644 (file)
index 0000000..9be60a3
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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
+ */
+module leafref-test {
+    yang-version 1;
+    namespace "urn:opendaylight:yangtools:leafref:test";
+    prefix "lt";
+
+    revision 2014-11-04 {
+        description "Test deserialization value of leafref type.";
+    }
+
+    identity test-identity-base {
+    }
+
+    identity test-identity {
+      base lt:test-identity-base;
+    }
+
+    container interface {
+        leaf simpleValue {
+            type instance-identifier;
+        }
+    }
+
+    container cont2 {
+        container cont3 {
+            leaf leafname3 {
+                type leafref {
+                    path "../../pointToStringLeaf";
+                }
+            }
+        }
+        leaf pointToStringLeaf {
+            type leafref {
+                path "../stringleaf";
+            }
+        }
+
+        leaf point-to-identityrefleaf {
+            type leafref {
+                path "../identityrefleaf";
+            }
+        }
+
+        leaf identityrefleaf {
+            type identityref {
+              base lt:test-identity-base;
+            }
+        }
+
+        leaf stringleaf {
+            type string;
+        }
+        leaf absname {
+            type leafref {
+                path "/lt:interface/lt:simpleValue";
+            }
+        }
+        leaf relname {
+            type leafref {
+                path "../../lt:interface/lt:simpleValue";
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/schema/augment_choice_hell.yang b/yang/yang-data-codec-xml/src/test/resources/schema/augment_choice_hell.yang
new file mode 100644 (file)
index 0000000..d386d48
--- /dev/null
@@ -0,0 +1,143 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:test";
+    prefix "test";
+
+    organization "Cisco Systems, Inc.";
+
+    revision "2014-03-13" {
+        description
+            "Initial revision";
+    }
+
+
+    container container {
+        choice ch2{}
+        choice ch3{
+            case c3 {
+                leaf c3Leaf {
+                    type string;
+                }
+            }
+        }
+    }
+
+    augment "/container/" {
+        leaf augLeaf {
+            type string;
+        }
+    }
+
+    augment "/container/" {
+        choice ch{}
+    }
+
+    augment "/container/ch/" {
+        case c1 {
+            leaf c1Leaf {
+                type string;
+            }
+        }
+
+        leaf c12 {
+            type string;
+        }
+    }
+    augment "/container/ch/c1/" {
+        leaf c1Leaf_AnotherAugment {
+            type string;
+        }
+
+        choice deepChoice{}
+    }
+
+    augment "/container/ch3/" {
+        case c32 {
+            leaf c32Leaf {
+                type string;
+            }
+        }
+
+        leaf c34LeafS {
+            type string;
+        }
+    }
+
+
+    augment "/container/ch/c1/deepChoice/" {
+        case deepCase1 {
+            leaf deepLeafc1 {
+                type string;
+            }
+        }
+        case deepCase2 {
+            leaf deepLeafc2 {
+                type string;
+            }
+        }
+    }
+
+    augment "/container/ch2/" {
+        case c2 {
+            leaf c2Leaf {
+                type string;
+            }
+
+            choice c2DeepChoice {
+                case c2DeepChoiceCase1 {
+                    leaf c2DeepChoiceCase1Leaf1 {
+                        type string;
+                    }
+                }
+                case c2DeepChoiceCase2 {
+                    leaf c2DeepChoiceCase1Leaf2 {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+    augment "/container/ch2/" {
+        leaf c22Leaf {
+            type string;
+        }
+    }
+
+    augment "/container" {
+        /*ext:augment-identifier top-choice-augment1;*/
+        choice augment-choice1 {
+            case case1 {
+                container case1-container {
+                    leaf case1-leaf {
+                        type string;
+                    }
+                }
+            }
+
+            case case2 {
+                container case2-container {
+                    leaf case2-leaf {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+    augment "/container/augment-choice1/case1" {
+        /*ext:augment-identifier top-choice-augment2;*/
+        choice augment-choice2 {
+            case case11 {
+                container case11-choice-case-container {
+                    leaf case11-choice-case-leaf {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/schema/augment_choice_hell_ok.xml b/yang/yang-data-codec-xml/src/test/resources/schema/augment_choice_hell_ok.xml
new file mode 100644 (file)
index 0000000..3f2b982
--- /dev/null
@@ -0,0 +1,14 @@
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+    <c2Leaf>2</c2Leaf>
+    <c2DeepChoiceCase1Leaf2>2</c2DeepChoiceCase1Leaf2>
+    <!--<c2DeepChoiceCase1Leaf1>2</c2DeepChoiceCase1Leaf1>-->
+
+    <c3Leaf>3</c3Leaf>
+
+    <augLeaf>augment</augLeaf>
+
+    <c1Leaf>1</c1Leaf>
+    <c1Leaf_AnotherAugment>1</c1Leaf_AnotherAugment>
+    <deepLeafc1>1</deepLeafc1>
+    <!--<deepLeafc2>1</deepLeafc2>-->
+</container>
diff --git a/yang/yang-data-codec-xml/src/test/resources/schema/augment_choice_hell_ok2.xml b/yang/yang-data-codec-xml/src/test/resources/schema/augment_choice_hell_ok2.xml
new file mode 100644 (file)
index 0000000..7d6a20f
--- /dev/null
@@ -0,0 +1,11 @@
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+    <c22Leaf>2</c22Leaf>
+
+    <c34LeafS>3</c34LeafS>
+
+    <augLeaf>augment</augLeaf>
+
+    <c1Leaf>1</c1Leaf>
+    <c1Leaf_AnotherAugment>1</c1Leaf_AnotherAugment>
+    <deepLeafc2>1</deepLeafc2>
+</container>
diff --git a/yang/yang-data-codec-xml/src/test/resources/schema/augment_choice_hell_ok3.xml b/yang/yang-data-codec-xml/src/test/resources/schema/augment_choice_hell_ok3.xml
new file mode 100644 (file)
index 0000000..738a12f
--- /dev/null
@@ -0,0 +1,6 @@
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+    <case11-choice-case-container>
+        <case11-choice-case-leaf>leaf-value</case11-choice-case-leaf>
+    </case11-choice-case-container>
+</container>
+
diff --git a/yang/yang-data-codec-xml/src/test/resources/schema/rpc-test-model.yang b/yang/yang-data-codec-xml/src/test/resources/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-codec-xml/src/test/resources/schema/rpc-test-payload1.xml b/yang/yang-data-codec-xml/src/test/resources/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-codec-xml/src/test/resources/schema/rpc-test-payload2.xml b/yang/yang-data-codec-xml/src/test/resources/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
diff --git a/yang/yang-data-codec-xml/src/test/resources/schema/simple.xml b/yang/yang-data-codec-xml/src/test/resources/schema/simple.xml
new file mode 100644 (file)
index 0000000..4bcaf05
--- /dev/null
@@ -0,0 +1,72 @@
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+    <boolean>true</boolean>
+    <innerContainer>
+        <uint16>44</uint16>
+    </innerContainer>
+    <leafList>a</leafList>
+    <leafList>b</leafList>
+
+    <list>
+        <uint32InList>1</uint32InList>
+        <containerInList name="inContainer">
+            <uint32>32</uint32>
+            <uint16>16</uint16>
+            <identityr xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:b</identityr>
+            <anyX xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">direct Value</anyX>
+            <uint16-ref>16</uint16-ref>
+            <instance-id xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">/prefix:container/prefix:list[prefix:uint32InList="1"]</instance-id>
+        </containerInList>
+        <stringAugmentedToList>augmentInList</stringAugmentedToList>
+        <stringAugmentedToListInCase1>augmentInListCase1</stringAugmentedToListInCase1>
+    </list>
+    <list>
+        <uint32InList>2</uint32InList>
+        <containerInList>
+            <uint32>32</uint32>
+            <uint16>16</uint16>
+            <identityr>b</identityr>
+            <anyX>
+                <container xmlns="randomNamespace" xmlns:pref="prefixed:namespace">
+                    <pref:boolean>true</pref:boolean>
+                    <innerContainer xmlns:p="prefixed:namespace">
+                        <uint16 xmlns:pref2="prefixed:namespace">pref2:44</uint16>
+                    </innerContainer>
+                </container>
+                <container2 xmlns="randomNamespace2"/>
+            </anyX>
+        </containerInList>
+        <stringAugmentedToListInCase2>augmentInListCase2</stringAugmentedToListInCase2>
+    </list>
+    <list>
+        <uint32InList>4</uint32InList>
+        <containerInList>
+            <anyX>
+                <netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
+                    <schemas>
+                        <schema>
+                            <identifier>module</identifier>
+                            <version>2012-12-12</version>
+                            <format xmlns:x="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">x:yang</format>
+                        </schema>
+                    </schemas>
+                </netconf-state>
+            </anyX>
+        </containerInList>
+    </list>
+    <list>
+        <uint32InList>3</uint32InList>
+    </list>
+
+    <augmentString1>choice1Case1</augmentString1>
+    <augmentInt1>41</augmentInt1>
+    <stringInAugmentedCaseInAugmentedChoiceFromAugment>deep</stringInAugmentedCaseInAugmentedChoiceFromAugment>
+
+    <augmentContainer>
+        <augmentStringInaugmentContainer>choice2Case1</augmentStringInaugmentContainer>
+    </augmentContainer>
+
+    <augmentUint32>999</augmentUint32>
+
+
+
+</container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/schema/simple2.xml b/yang/yang-data-codec-xml/src/test/resources/schema/simple2.xml
new file mode 100644 (file)
index 0000000..26a343a
--- /dev/null
@@ -0,0 +1,42 @@
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+    <augmentString1>choice1Case1</augmentString1>
+    <augmentInt1>41</augmentInt1>
+    <stringInAugmentedCaseInAugmentedChoice2>deep</stringInAugmentedCaseInAugmentedChoice2>
+
+    <augmentedList>
+        <augmentStringInaugmentList>aug1</augmentStringInaugmentList>
+    </augmentedList>
+
+    <augmentedList>
+        <augmentStringInaugmentList>aug2</augmentStringInaugmentList>
+        <augmentedContainerInAugmentedListInAugmentedCase>
+            <leafInAugmentedContainerInAugmentedListInAugmentedCase>
+                66
+            </leafInAugmentedContainerInAugmentedListInAugmentedCase>
+        </augmentedContainerInAugmentedListInAugmentedCase>
+
+        <augmentedListInAugmentedListInAugmentedCase>
+            <leafInAugmentedListInAugmentedListInAugmentedCase>
+                661
+            </leafInAugmentedListInAugmentedListInAugmentedCase>
+            <leafInAugmentedListInAugmentedListInAugmentedCase>
+                662
+            </leafInAugmentedListInAugmentedListInAugmentedCase>
+        </augmentedListInAugmentedListInAugmentedCase>
+
+        <augmentedListInAugmentedListInAugmentedCase>
+            <leafInAugmentedListInAugmentedListInAugmentedCase>
+                6621
+            </leafInAugmentedListInAugmentedListInAugmentedCase>
+        </augmentedListInAugmentedListInAugmentedCase>
+    </augmentedList>
+
+    <augmentedList>
+        <augmentStringInaugmentList>aug3</augmentStringInaugmentList>
+        <augmentedContainerInAugmentedListInAugmentedCase/>
+
+        <augmentedListInAugmentedListInAugmentedCase/>
+    </augmentedList>
+
+</container>
+
diff --git a/yang/yang-data-codec-xml/src/test/resources/schema/simple_xml_with_attributes.xml b/yang/yang-data-codec-xml/src/test/resources/schema/simple_xml_with_attributes.xml
new file mode 100644 (file)
index 0000000..740bed8
--- /dev/null
@@ -0,0 +1,11 @@
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test" name="test" xmlns:foo="http://www.foo.com/" foo:baz="baz">
+
+    <list list="on list entry">
+        <uint32InList name="test" foo:baz="baz">3</uint32InList>
+    </list>
+
+    <boolean xmlns:leaf="test:namespace:in:leaf" leaf:a="b">false</boolean>
+
+    <leafList foo:b="b">a</leafList>
+
+</container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/schema/test.yang b/yang/yang-data-codec-xml/src/test/resources/schema/test.yang
new file mode 100644 (file)
index 0000000..0b108ab
--- /dev/null
@@ -0,0 +1,243 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:test";
+    prefix "test";
+
+    organization "Cisco Systems, Inc.";
+
+    revision "2014-03-13" {
+        description
+            "Initial revision";
+    }
+
+    identity a {}
+
+    identity b {
+        base "test:a";
+    }
+
+    grouping listGroup {
+        list list {
+            key "uint32InList";
+
+            leaf uint32InList {
+                type uint32;
+            }
+
+            container containerInList{
+                leaf uint32 {
+                    type uint32;
+                }
+                leaf uint16 {
+                    type uint16;
+                }
+
+                leaf identityr {
+                    type identityref {
+                        base "test:a";
+                     }
+                }
+
+                leaf uint16-ref {
+                    type leafref {
+                        path "../uint16";
+                     }
+                }
+
+                leaf instance-id {
+                    type instance-identifier;
+                }
+
+                anyxml anyX;
+            }
+        }
+     }
+
+     grouping innerContainerGrouping {
+        container innerContainer {
+            leaf uint16 {
+                type uint16;
+            }
+
+            container innerInnerContainer {
+
+                leaf uint16 {
+                    type uint16;
+                }
+
+                leaf uint32 {
+                    type uint32;
+                }
+            }
+        }
+     }
+
+    container container {
+        leaf uint32 {
+            type uint32;
+        }
+
+        leaf decimal64 {
+            type decimal64 {
+                fraction-digits 2;
+            }
+        }
+
+        leaf boolean {
+            type boolean;
+        }
+
+        leaf binary {
+            type binary;
+        }
+
+        leaf string {
+            type string;
+        }
+
+        uses listGroup {
+            augment "list/" {
+                leaf stringAugmentedToList{
+                    type string;
+                }
+
+                choice choiceInList {
+                    case caseInList1 {
+                        leaf stringAugmentedToListInCase1 {
+                            type string;
+                        }
+                    }
+                    case caseInList2 {
+                        leaf stringAugmentedToListInCase2 {
+                            type string;
+                        }
+                    }
+                }
+            }
+        }
+
+        list directList {
+            leaf stringInDirectList {
+                type string;
+            }
+        }
+
+        uses innerContainerGrouping;
+
+        choice choice{}
+        choice choice2{}
+
+        leaf-list leafList {
+            type string;
+        }
+
+        leaf identityRef {
+            type identityref {
+                base test-identity;
+            }
+        }
+    }
+
+    augment "/container/" {
+        leaf augmentUint32 {
+            type uint32;
+        }
+    }
+
+    augment "/container/directList/" {
+        leaf augmentedString {
+            type uint32;
+        }
+    }
+
+    augment "/container/choice/" {
+        case test-identity-augment {
+            when "/container/identityRef = 'test-identity'";
+            leaf augmentString1 {
+                type string;
+            }
+
+            leaf augmentInt1 {
+                type uint32;
+            }
+        }
+        case test-identity-augment2 {
+            when "/container/identityRef = 'test-identity2'";
+            leaf augmentString2 {
+                type string;
+            }
+
+            leaf augmentInt2 {
+                type uint32;
+            }
+        }
+    }
+
+    augment "/container/choice/test-identity-augment/" {
+
+        choice augmentedChoiceInCase {
+
+            case augmentedCaseInAugmentedChoice {
+                leaf stringInAugmentedCaseInAugmentedChoice {
+                    type string;
+                }
+            }
+
+            case augmentedCaseInAugmentedChoice2 {
+                leaf stringInAugmentedCaseInAugmentedChoice2 {
+                    type string;
+                }
+            }
+        }
+    }
+
+    augment "/container/choice/test-identity-augment/augmentedChoiceInCase/" {
+        case augmentedCaseInAugmentedChoiceFromAugment {
+            leaf stringInAugmentedCaseInAugmentedChoiceFromAugment {
+                type string;
+            }
+        }
+    }
+
+    augment "/container/choice2/" {
+        case test-identity-augment {
+            when "/container/identityRef = 'test-identity'";
+            container augmentContainer {
+                leaf augmentStringInaugmentContainer {
+                    type string;
+                }
+            }
+        }
+        case test-identity-augment2 {
+            when "/container/identityRef = 'test-identity2'";
+            list augmentedList {
+                leaf augmentStringInaugmentList {
+                    type string;
+                }
+            }
+        }
+    }
+
+
+    augment "/container/choice2/test-identity-augment2/augmentedList/" {
+
+        container augmentedContainerInAugmentedListInAugmentedCase {
+            leaf-list leafInAugmentedContainerInAugmentedListInAugmentedCase {
+                type uint32;
+            }
+        }
+
+        list augmentedListInAugmentedListInAugmentedCase {
+            leaf-list leafInAugmentedListInAugmentedListInAugmentedCase {
+                    type uint32;
+                }
+        }
+    }
+
+    identity test-identity {}
+    identity test-identity2 {
+        base test-identity;
+    }
+
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/strict-parsing-mode-test/foo.xml b/yang/yang-data-codec-xml/src/test/resources/strict-parsing-mode-test/foo.xml
new file mode 100644 (file)
index 0000000..3f27120
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<top-level-container xmlns="foo">
+    <unknown-container-a>
+        <unknown-container-b>
+            <unknown-container-c1>
+                <unknown-leaf-a>value-a</unknown-leaf-a>
+                <unknown-leaf-b>value-b</unknown-leaf-b>
+            </unknown-container-c1>
+            <unknown-container-c2>
+            </unknown-container-c2>
+        </unknown-container-b>
+    </unknown-container-a>
+    <unknown-leaf>value</unknown-leaf>
+    <inner-container>
+        <unknown-empty-node></unknown-empty-node>
+    </inner-container>
+</top-level-container>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/strict-parsing-mode-test/foo.yang b/yang/yang-data-codec-xml/src/test/resources/strict-parsing-mode-test/foo.yang
new file mode 100644 (file)
index 0000000..f341760
--- /dev/null
@@ -0,0 +1,8 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    container top-level-container {
+        container inner-container {}
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/test-anydata.xml b/yang/yang-data-codec-xml/src/test/resources/test-anydata.xml
new file mode 100644 (file)
index 0000000..ea194d6
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<foo xmlns="test-anydata">
+    <bgp xmlns="http://example.com/ns/bgp"
+         xmlns:or="urn:ietf:params:xml:ns:yang:ietf-origin"
+         or:origin="or:intended">
+        <peer>
+            <name>2001:db8::2:3</name>
+            <local-port or:origin="or:system">60794</local-port>
+            <state>established</state>
+        </peer>
+    </bgp>
+</foo>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/test-anydata.yang b/yang/yang-data-codec-xml/src/test/resources/test-anydata.yang
new file mode 100644 (file)
index 0000000..0655955
--- /dev/null
@@ -0,0 +1,16 @@
+module test-anydata {
+    yang-version 1.1;
+    namespace test-anydata;
+    prefix ta;
+
+    anydata foo;
+
+    container cont {
+        anydata cont-any;
+
+        leaf cont-leaf {
+            type string;
+        }
+    }
+}
+
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools821/foo-invalid.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools821/foo-invalid.xml
new file mode 100644 (file)
index 0000000..ff556ac
--- /dev/null
@@ -0,0 +1,11 @@
+<root xmlns="urn:opendaylight:params:xml:ns:yang:foo">
+    <foo>
+        <name>foo1</name>
+    </foo>
+    <bar>
+        <name>bar1</name>
+        <container-in-list>
+            <ref-from-aug>foo2</ref-from-aug>
+        </container-in-list>
+    </bar>
+</root>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools821/foo-ref-in-container-invalid.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools821/foo-ref-in-container-invalid.xml
new file mode 100644 (file)
index 0000000..362d10e
--- /dev/null
@@ -0,0 +1,11 @@
+<root xmlns="urn:opendaylight:params:xml:ns:yang:foo">
+    <foo>
+        <name>foo1</name>
+    </foo>
+    <bar>
+        <name>bar1</name>
+        <container-from-aug>
+            <ref-in-container>foo2</ref-in-container>
+        </container-from-aug>
+    </bar>
+</root>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools821/foo-ref-in-container-valid.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools821/foo-ref-in-container-valid.xml
new file mode 100644 (file)
index 0000000..93ed61b
--- /dev/null
@@ -0,0 +1,11 @@
+<root xmlns="urn:opendaylight:params:xml:ns:yang:foo">
+    <foo>
+        <name>foo1</name>
+    </foo>
+    <bar>
+        <name>bar1</name>
+        <container-from-aug>
+            <ref-in-container>foo1</ref-in-container>
+        </container-from-aug>
+    </bar>
+</root>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools821/foo-valid.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools821/foo-valid.xml
new file mode 100644 (file)
index 0000000..5c56fb1
--- /dev/null
@@ -0,0 +1,11 @@
+<root xmlns="urn:opendaylight:params:xml:ns:yang:foo">
+    <foo>
+        <name>foo1</name>
+    </foo>
+    <bar>
+        <name>bar1</name>
+        <container-in-list>
+            <ref-from-aug>foo1</ref-from-aug>
+        </container-in-list>
+    </bar>
+</root>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools821/foo.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools821/foo.yang
new file mode 100644 (file)
index 0000000..da84593
--- /dev/null
@@ -0,0 +1,41 @@
+module foo {
+    namespace "urn:opendaylight:params:xml:ns:yang:foo";
+    prefix foo;
+
+    revision 2018-07-18;
+
+    container root {
+        list foo {
+            key name;
+            leaf name {
+                type string;
+            }
+        }
+        list bar {
+            key name;
+            leaf name {
+                type string;
+            }
+            container container-in-list {
+            }
+        }
+    }
+
+    augment /root/bar/container-in-list {
+        leaf ref-from-aug {
+            type leafref {
+                path "/root/foo/name";
+            }
+        }
+    }
+
+    augment /root/bar/ {
+        container container-from-aug {
+            leaf ref-in-container {
+                type leafref {
+                    path "/root/foo/name";
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/bar.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools891/bar.yang
new file mode 100644 (file)
index 0000000..2dac68e
--- /dev/null
@@ -0,0 +1,22 @@
+module bar {
+    namespace "urn:opendaylight:params:xml:ns:yang:bar";
+    prefix bar;
+
+    revision 2018-07-27;
+
+    grouping grouping-with-list {
+        list list-in-grouping {
+            key "name";
+            leaf name {
+                type leafref {
+                    path "../container-in-list/name";
+                }
+            }
+            container container-in-list {
+                leaf name {
+                    type string;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/baz-top.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools891/baz-top.xml
new file mode 100644 (file)
index 0000000..762cd8e
--- /dev/null
@@ -0,0 +1,5 @@
+<top-container xmlns="urn:opendaylight:params:xml:ns:yang:baz">
+    <list-in-container>
+        <name>name1</name>
+    </list-in-container>
+</top-container>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/baz.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools891/baz.yang
new file mode 100644 (file)
index 0000000..8ee5d00
--- /dev/null
@@ -0,0 +1,23 @@
+module baz {
+    namespace "urn:opendaylight:params:xml:ns:yang:baz";
+    prefix baz;
+
+    revision 2018-07-27;
+
+    grouping grouping-with-leafref {
+        leaf ref {
+            type leafref {
+                path "/baz-top/list-in-container/name";
+            }
+        }
+    }
+
+    container baz-top {
+        list list-in-container {
+            key "name";
+            leaf name {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/foo.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools891/foo.yang
new file mode 100644 (file)
index 0000000..35207e5
--- /dev/null
@@ -0,0 +1,18 @@
+module foo {
+    namespace "urn:opendaylight:params:xml:ns:yang:foo";
+    prefix foo;
+
+    import bar {
+        prefix bar;
+    }
+    import baz {
+        prefix baz;
+    }
+
+    revision 2018-07-27;
+
+    container foo-top {
+        uses bar:grouping-with-list;
+        uses baz:grouping-with-leafref;
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-invalid.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-invalid.xml
new file mode 100644 (file)
index 0000000..563bce5
--- /dev/null
@@ -0,0 +1,3 @@
+<root xmlns="urn:opendaylight:params:xml:ns:yang:foo">
+    <ref>name3</ref>
+</root>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-valid.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-leafref-valid.xml
new file mode 100644 (file)
index 0000000..12aa972
--- /dev/null
@@ -0,0 +1,3 @@
+<root xmlns="urn:opendaylight:params:xml:ns:yang:foo">
+    <ref>name1</ref>
+</root>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-invalid.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-invalid.xml
new file mode 100644 (file)
index 0000000..e4ab91b
--- /dev/null
@@ -0,0 +1,8 @@
+<foo-top xmlns="urn:opendaylight:params:xml:ns:yang:foo">
+    <list-in-grouping>
+        <name>name1</name>
+        <container-in-list>
+            <name>name2</name>
+        </container-in-list>
+    </list-in-grouping>
+</foo-top>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-valid.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools891/grouping-with-list-valid.xml
new file mode 100644 (file)
index 0000000..b0412ba
--- /dev/null
@@ -0,0 +1,8 @@
+<foo-top xmlns="urn:opendaylight:params:xml:ns:yang:foo">
+    <list-in-grouping>
+        <name>name1</name>
+        <container-in-list>
+            <name>name1</name>
+        </container-in-list>
+    </list-in-grouping>
+</foo-top>
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools892/bgp-test-extensions.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools892/bgp-test-extensions.yang
new file mode 100644 (file)
index 0000000..42415d9
--- /dev/null
@@ -0,0 +1,27 @@
+module bgp-test-extensions {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:bgp:test:extensions";
+    prefix "odl-oc-ext";
+
+    import test-bgp-types { prefix bgp-types; }
+    import test-network-instance { prefix netinst; }
+    import test-bgp { prefix test-bgp; }
+
+    revision 2018-08-14;
+
+    augment /netinst:network-instances/netinst:network-instance/netinst:protocols/netinst:protocol {
+        uses test-bgp:bgp-top {
+            augment bgp/neighbors/neighbor/afi-safis/afi-safi {
+                uses test-bgp:bgp-neighbor-add-paths_config;
+            }
+
+            augment bgp/neighbors/neighbor/config {
+                description
+                    "Augmentation to allow association of a neighbor with a
+                     peer-group";
+                uses test-bgp:bgp-neighbor-peer-group_config;
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools892/neighbour.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools892/neighbour.xml
new file mode 100644 (file)
index 0000000..690c41d
--- /dev/null
@@ -0,0 +1,38 @@
+<network-instances xmlns="urn:opendaylight:params:xml:ns:yang:test:network:instance">
+    <network-instance>
+        <name>global-bgp</name>
+        <config>
+            <name>global-bgp</name>
+        </config>
+        <protocols>
+            <protocol>
+                <identifier xmlns:x="urn:opendaylight:params:xml:ns:yang:test:policy:types">x:BGP</identifier>
+                <name>test-bgp-instance</name>
+                <config>
+                    <name>test-bgp-instance</name>
+                    <identifier xmlns:x="urn:opendaylight:params:xml:ns:yang:test:policy:types">x:BGP</identifier>
+                </config>
+                <bgp xmlns="urn:opendaylight:params:xml:ns:yang:bgp:test:extensions">
+                    <neighbors>
+                        <neighbor>
+                            <neighbor-address>10.25.1.9</neighbor-address>
+                            <config>
+                                <peer-group>application-peers</peer-group>
+                            </config>
+                            <afi-safis>
+                                <afi-safi>
+                                    <afi-safi-name xmlns:x="urn:opendaylight:params:xml:ns:yang:test:bgp:types">x:IPV4-UNICAST</afi-safi-name>
+                                    <config>
+                                        <afi-safi-name xmlns:x="urn:opendaylight:params:xml:ns:yang:test:bgp:types">x:IPV4-UNICAST</afi-safi-name>
+                                    </config>
+                                    <receive>true</receive>
+                                    <send-max>0</send-max>
+                                </afi-safi>
+                            </afi-safis>
+                        </neighbor>
+                    </neighbors>
+                </bgp>
+            </protocol>
+        </protocols>
+    </network-instance>
+</network-instances>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools892/peer-groups.xml b/yang/yang-data-codec-xml/src/test/resources/yangtools892/peer-groups.xml
new file mode 100644 (file)
index 0000000..07c4e55
--- /dev/null
@@ -0,0 +1,7 @@
+<bgp xmlns="urn:opendaylight:params:xml:ns:yang:test:bgp">
+    <peer-groups>
+        <peer-group>
+            <peer-group-name>application-peers</peer-group-name>
+        </peer-group>
+    </peer-groups>
+</bgp>
\ No newline at end of file
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools892/test-bgp-multiprotocol.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools892/test-bgp-multiprotocol.yang
new file mode 100644 (file)
index 0000000..41a2749
--- /dev/null
@@ -0,0 +1,33 @@
+module test-bgp-multiprotocol {
+    yang-version "1";
+    namespace "urn:opendaylight:params:xml:ns:yang:test:bgp:multiprotocol";
+    prefix "bgp-mp";
+
+    import test-bgp-types { prefix bgp-types; }
+
+    revision "2018-08-14";
+
+    grouping bgp-afi-safi_config {
+        leaf afi-safi-name {
+            type identityref {
+                base bgp-types:afi-safi-type;
+            }
+        }
+    }
+
+    grouping bgp-common-afi-safi-list {
+        list afi-safi {
+            key "afi-safi-name";
+
+            leaf afi-safi-name {
+                type leafref {
+                    path "../config/afi-safi-name";
+                }
+            }
+
+            container config {
+                uses bgp-afi-safi_config;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools892/test-bgp-types.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools892/test-bgp-types.yang
new file mode 100644 (file)
index 0000000..ae06008
--- /dev/null
@@ -0,0 +1,23 @@
+module test-bgp-types {
+    yang-version "1";
+    namespace "urn:opendaylight:params:xml:ns:yang:test:bgp:types";
+    prefix "bgp-types";
+
+    revision "2018-08-14";
+
+    typedef peer-type {
+        type enumeration {
+            enum INTERNAL {
+            }
+            enum EXTERNAL {
+            }
+        }
+    }
+
+    identity afi-safi-type {
+    }
+
+    identity IPV4-UNICAST {
+        base afi-safi-type;
+    }
+}
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools892/test-bgp.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools892/test-bgp.yang
new file mode 100644 (file)
index 0000000..af9f5d1
--- /dev/null
@@ -0,0 +1,111 @@
+module test-bgp {
+    yang-version "1";
+    namespace "urn:opendaylight:params:xml:ns:yang:test:bgp";
+    prefix "bgp";
+
+    import test-bgp-multiprotocol { prefix bgp-mp; }
+    import test-bgp-types { prefix bgp-types; }
+
+    revision "2018-08-14";
+
+    grouping bgp-neighbor_config {
+        leaf peer-type {
+            type bgp-types:peer-type;
+        }
+    }
+
+    grouping bgp-neighbor-add-paths_config {
+        leaf receive {
+            type boolean;
+            default false;
+        }
+
+        leaf send-max {
+            type uint8;
+        }
+    }
+
+    grouping bgp-neighbor-peer-group_config {
+        leaf peer-group {
+            type leafref {
+                path "/bgp/peer-groups/peer-group/peer-group-name";
+            }
+        }
+    }
+
+    grouping bgp-neighbors {
+        list neighbor {
+            key "neighbor-address";
+
+            leaf neighbor-address {
+                type string;
+            }
+
+            uses bgp-neighbor-group;
+        }
+    }
+
+    grouping bgp-peer-group {
+        list peer-group {
+            key "peer-group-name";
+
+            leaf peer-group-name {
+                type string;
+            }
+
+            uses bgp-neighbor-group;
+
+        }
+    }
+
+    grouping bgp-neighbor-group {
+        container config {
+            uses bgp-neighbor_config;
+        }
+
+        container afi-safis {
+            uses bgp-mp:bgp-common-afi-safi-list;
+        }
+    }
+
+    grouping bgp-neighbor-neighbor-address_config {
+        leaf neighbor-address {
+            type string;
+        }
+    }
+
+    grouping bgp-peer-group-peer-group-name_config {
+        leaf peer-group-name {
+            type string;
+        }
+    }
+
+    augment /bgp/neighbors/neighbor/config {
+        uses bgp-neighbor-peer-group_config;
+    }
+
+    augment /bgp/neighbors/neighbor/config {
+        uses bgp-neighbor-neighbor-address_config;
+    }
+
+    augment /bgp/peer-groups/peer-group/config {
+        uses bgp-peer-group-peer-group-name_config;
+    }
+
+    grouping bgp-top {
+        container bgp {
+            presence "Container for BGP protocol hierarchy";
+
+            container neighbors {
+                uses bgp-neighbors;
+            }
+
+            container peer-groups {
+                uses bgp-peer-group;
+            }
+        }
+    }
+
+    uses bgp-top;
+}
+
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools892/test-network-instance.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools892/test-network-instance.yang
new file mode 100644 (file)
index 0000000..70947c4
--- /dev/null
@@ -0,0 +1,70 @@
+module test-network-instance {
+    yang-version "1";
+    namespace "urn:opendaylight:params:xml:ns:yang:test:network:instance";
+    prefix netinst;
+
+    import test-policy-types { prefix "pt"; }
+
+    revision 2018-08-14;
+
+    grouping network-instance-top {
+        container network-instances {
+            list network-instance {
+                key "name";
+
+                leaf name {
+                    type leafref {
+                        path "../config/name";
+                    }
+                }
+
+                container config {
+                    uses network-instance-config;
+                }
+
+                container protocols {
+                    list protocol {
+                        key "identifier name";
+
+                        leaf identifier {
+                            type leafref {
+                                path "../config/identifier";
+                            }
+                        }
+
+                        leaf name {
+                            type leafref {
+                                path "../config/name";
+                            }
+                        }
+
+                        container config {
+                            uses protocols-config;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    grouping network-instance-config {
+        leaf name {
+            type string;
+        }
+    }
+
+    grouping protocols-config {
+        leaf identifier {
+            type identityref {
+                base "pt:install-protocol-type";
+            }
+        }
+
+        leaf name {
+            type string;
+        }
+    }
+
+    uses network-instance-top;
+}
+
diff --git a/yang/yang-data-codec-xml/src/test/resources/yangtools892/test-policy-types.yang b/yang/yang-data-codec-xml/src/test/resources/yangtools892/test-policy-types.yang
new file mode 100644 (file)
index 0000000..6aff5b2
--- /dev/null
@@ -0,0 +1,14 @@
+module test-policy-types {
+    yang-version "1";
+    namespace "urn:opendaylight:params:xml:ns:yang:test:policy:types";
+    prefix "ptypes";
+
+    revision "2018-08-14";
+
+    identity install-protocol-type {
+    }
+
+    identity BGP {
+        base install-protocol-type;
+    }
+}
diff --git a/yang/yang-data-impl/pom.xml b/yang/yang-data-impl/pom.xml
new file mode 100644 (file)
index 0000000..bf45058
--- /dev/null
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-data-impl</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <properties>
+        <!-- FIXME: antlr-generated code does not pass SB -->
+        <odlparent.spotbugs.enforce>false</odlparent.spotbugs.enforce>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.antlr</groupId>
+                <artifactId>antlr4-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>antlr4</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <visitor>true</visitor>
+                    <listener>true</listener>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.data.impl</Automatic-Module-Name>
+                        <Export-Package>
+                            {local-packages},
+                            org.opendaylight.yangtools.yang.data.impl.*,
+                            ;-split-package:=error
+                        </Export-Package>
+                        <Bundle-Activator>org.opendaylight.yangtools.yang.data.impl.osgi.Activator</Bundle-Activator>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-data-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-data-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>antlr4-runtime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.kohsuke.metainf-services</groupId>
+            <artifactId>metainf-services</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.xmlunit</groupId>
+            <artifactId>xmlunit-legacy</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-test-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/yang/yang-data-impl/src/main/antlr4/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathLexer.g4 b/yang/yang-data-impl/src/main/antlr4/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathLexer.g4
new file mode 100644 (file)
index 0000000..3d63ee9
--- /dev/null
@@ -0,0 +1,16 @@
+lexer grammar LeafRefPathLexer;
+
+COLON : ':' ;
+SLASH : '/' ;
+DOTS : '..' ;
+EQUAL : '=' ;
+LEFT_SQUARE_BRACKET : '[' ;
+RIGHT_SQUARE_BRACKET : ']' ;
+LEFT_PARENTHESIS : '(' ;
+RIGHT_PARENTHESIS : ')' ;
+
+CURRENT_KEYWORD : 'current';
+
+SEP: [ \n\r\t]+ ;
+IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_\-.]*;
+
diff --git a/yang/yang-data-impl/src/main/antlr4/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParser.g4 b/yang/yang-data-impl/src/main/antlr4/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParser.g4
new file mode 100644 (file)
index 0000000..ec60a4c
--- /dev/null
@@ -0,0 +1,30 @@
+parser grammar LeafRefPathParser;
+
+options {
+    tokenVocab = LeafRefPathLexer;
+}
+
+path_arg : absolute_path | relative_path;
+
+absolute_path : (SLASH node_identifier (path_predicate)*)+;
+
+relative_path : (DOTS SLASH)* descendant_path;
+
+descendant_path : node_identifier ((path_predicate)* absolute_path)?;
+
+path_predicate : LEFT_SQUARE_BRACKET SEP? path_equality_expr SEP? RIGHT_SQUARE_BRACKET;
+
+path_equality_expr : node_identifier SEP? EQUAL SEP? path_key_expr;
+
+path_key_expr : current_function_invocation SEP? SLASH SEP? rel_path_keyexpr;
+
+rel_path_keyexpr : (DOTS SEP? SLASH SEP?)* (node_identifier SEP? SLASH SEP?)* node_identifier;
+
+node_identifier : (prefix COLON)? identifier;
+
+current_function_invocation : CURRENT_KEYWORD SEP? LEFT_PARENTHESIS SEP? RIGHT_PARENTHESIS;
+
+prefix : identifier;
+
+identifier: IDENTIFIER | CURRENT_KEYWORD;
+
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/AbstractDataStringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/AbstractDataStringCodec.java
new file mode 100644 (file)
index 0000000..e6ed483
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.codec;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.AbstractIllegalArgumentCodec;
+
+// FIXME: 5.0.0: yang-data-api is tied to yang-model-api, hence it should be opinionated to export exceptions
+//               encapsulating YANG-based error information.
+@Beta
+public abstract class AbstractDataStringCodec<T> extends AbstractIllegalArgumentCodec<String, T>
+        implements DataStringCodec<T> {
+
+    // FIXME: should we provide default getInputClass() implementation?
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/AbstractIntegerStringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/AbstractIntegerStringCodec.java
new file mode 100644 (file)
index 0000000..a6afa2d
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.CharMatcher;
+import com.google.common.collect.RangeSet;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+
+/**
+ * Do not use this class outside of yangtools, its presence does not fall into the API stability contract.
+ */
+@Beta
+public abstract class AbstractIntegerStringCodec<N extends Number & Comparable<N>,
+        T extends RangeRestrictedTypeDefinition<T, N>> extends TypeDefinitionAwareCodec<N, T> {
+
+    private static final Pattern INT_PATTERN = Pattern.compile("[+-]?[1-9][0-9]*$");
+    private static final Pattern HEX_PATTERN = Pattern.compile("[+-]?0[xX][0-9a-fA-F]+");
+    private static final Pattern OCT_PATTERN = Pattern.compile("[+-]?0[1-7][0-7]*$");
+
+    // For up to two characters, this is very fast
+    private static final CharMatcher X_MATCHER = CharMatcher.anyOf("xX");
+
+    // FIXME: inline this
+    private static final String INCORRECT_LEXICAL_REPRESENTATION =
+            "Incorrect lexical representation of integer value: %s."
+                    + "\nAn integer value can be defined as: "
+                    + "\n  - a decimal number,"
+                    + "\n  - a hexadecimal number (prefix 0x)," + "%n  - an octal number (prefix 0)."
+                    + "\nSigned values are allowed. Spaces between digits are NOT allowed.";
+
+    private final RangeSet<N> rangeConstraints;
+
+    AbstractIntegerStringCodec(final T typeDefinition, final Optional<RangeConstraint<N>> constraint,
+            final Class<N> outputClass) {
+        super(requireNonNull(typeDefinition), outputClass);
+        rangeConstraints = constraint.map(RangeConstraint::getAllowedRanges).orElse(null);
+    }
+
+    public static @NonNull AbstractIntegerStringCodec<Byte, Int8TypeDefinition> from(final Int8TypeDefinition type) {
+        return new Int8StringCodec(type);
+    }
+
+    public static @NonNull AbstractIntegerStringCodec<Short, Int16TypeDefinition> from(final Int16TypeDefinition type) {
+        return new Int16StringCodec(type);
+    }
+
+    public static @NonNull AbstractIntegerStringCodec<Integer, Int32TypeDefinition> from(
+            final Int32TypeDefinition type) {
+        return new Int32StringCodec(type);
+    }
+
+    public static @NonNull AbstractIntegerStringCodec<Long, Int64TypeDefinition> from(final Int64TypeDefinition type) {
+        return new Int64StringCodec(type);
+    }
+
+    public static @NonNull AbstractIntegerStringCodec<Uint8, Uint8TypeDefinition> from(final Uint8TypeDefinition type) {
+        return new Uint8StringCodec(type);
+    }
+
+    public static @NonNull AbstractIntegerStringCodec<Uint16, Uint16TypeDefinition> from(
+            final Uint16TypeDefinition type) {
+        return new Uint16StringCodec(type);
+    }
+
+    public static @NonNull AbstractIntegerStringCodec<Uint32, Uint32TypeDefinition> from(
+            final Uint32TypeDefinition type) {
+        return new Uint32StringCodec(type);
+    }
+
+    public static @NonNull AbstractIntegerStringCodec<Uint64, Uint64TypeDefinition> from(
+            final Uint64TypeDefinition type) {
+        return new Uint64StringCodec(type);
+    }
+
+    @Override
+    protected final N deserializeImpl(final String product) {
+        final int base = provideBase(product);
+        final String stringRepresentation = base != 16 ? product : X_MATCHER.removeFrom(product);
+        final N deserialized = verifyNotNull(deserialize(stringRepresentation, base));
+        if (rangeConstraints != null) {
+            checkArgument(rangeConstraints.contains(deserialized), "Value '%s'  is not in required ranges %s",
+                deserialized, rangeConstraints);
+        }
+        return deserialized;
+    }
+
+    @Override
+    protected final String serializeImpl(final N input) {
+        return input.toString();
+    }
+
+    /**
+     * Deserializes value from supplied string representation is supplied radix. See
+     * {@link Integer#parseInt(String, int)} for in-depth description about string and radix relationship.
+     *
+     * @param stringRepresentation String representation
+     * @param radix numeric base.
+     * @return Deserialized value.
+     */
+    protected abstract @NonNull N deserialize(@NonNull String stringRepresentation, int radix);
+
+    protected static <N extends Number & Comparable<N>> Optional<RangeConstraint<N>> extractRange(
+            final RangeRestrictedTypeDefinition<?, N> type) {
+        return type == null ? Optional.empty() : type.getRangeConstraint();
+    }
+
+    private static int provideBase(final String integer) {
+        if (integer.length() == 1 && integer.charAt(0) == '0') {
+            return 10;
+        } else if (INT_PATTERN.matcher(integer).matches()) {
+            return 10;
+        } else if (HEX_PATTERN.matcher(integer).matches()) {
+            return 16;
+        } else if (OCT_PATTERN.matcher(integer).matches()) {
+            return 8;
+        } else {
+            throw new NumberFormatException(String.format(INCORRECT_LEXICAL_REPRESENTATION, integer));
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/BinaryStringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/BinaryStringCodec.java
new file mode 100644 (file)
index 0000000..491f016
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Base64;
+import javax.xml.bind.DatatypeConverter;
+import org.opendaylight.yangtools.yang.data.api.codec.BinaryCodec;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+
+/**
+ * Do not use this class outside of yangtools, its presence does not fall into the API stability contract.
+ */
+@Beta
+public abstract class BinaryStringCodec extends TypeDefinitionAwareCodec<byte[], BinaryTypeDefinition>
+        implements BinaryCodec<String> {
+    private static final class Restricted extends BinaryStringCodec {
+        private final LengthConstraint lengthConstraint;
+
+        Restricted(final BinaryTypeDefinition typeDef, final LengthConstraint lengthConstraint) {
+            super(typeDef);
+            this.lengthConstraint = requireNonNull(lengthConstraint);
+        }
+
+        @Override
+        void validate(final byte[] value) {
+            // FIXME: throw an exception capturing the constraint violation
+            checkArgument(lengthConstraint.getAllowedRanges().contains(value.length),
+                "Value length %s does not match constraint %s", value.length, lengthConstraint);
+        }
+    }
+
+    private static final class Unrestricted extends BinaryStringCodec {
+        Unrestricted(final BinaryTypeDefinition typeDef) {
+            super(typeDef);
+        }
+
+        @Override
+        void validate(final byte[] value) {
+            // No-op
+        }
+    }
+
+    BinaryStringCodec(final BinaryTypeDefinition typeDef) {
+        super(requireNonNull(typeDef), byte[].class);
+    }
+
+    public static BinaryStringCodec from(final BinaryTypeDefinition type) {
+        final java.util.Optional<LengthConstraint> optConstraint = type.getLengthConstraint();
+        return optConstraint.isPresent() ? new Restricted(type, optConstraint.get()) : new Unrestricted(type);
+    }
+
+    @Override
+    public final byte[] deserializeImpl(final String product) {
+        final byte[] ret = DatatypeConverter.parseBase64Binary(product);
+        validate(ret);
+        return ret;
+    }
+
+    @Override
+    protected final String serializeImpl(final byte[] data) {
+        return Base64.getEncoder().encodeToString(data);
+    }
+
+    abstract void validate(byte[] value);
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/BitsStringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/BitsStringCodec.java
new file mode 100644 (file)
index 0000000..37d3944
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.codec.BitsCodec;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+
+/**
+ * Do not use this class outside of yangtools, its presence does not fall into the API stability contract.
+ */
+@Beta
+public final class BitsStringCodec extends TypeDefinitionAwareCodec<Set<String>, BitsTypeDefinition>
+        implements BitsCodec<String> {
+
+    private static final Joiner JOINER = Joiner.on(" ").skipNulls();
+    private static final Splitter SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
+
+    private final ImmutableSet<String> validBits;
+
+    @SuppressWarnings("unchecked")
+    private BitsStringCodec(final @NonNull BitsTypeDefinition typeDef) {
+        super(typeDef, (Class<Set<String>>) (Class<?>) Set.class);
+        validBits = ImmutableSet.copyOf(Collections2.transform(typeDef.getBits(), Bit::getName));
+    }
+
+    public static BitsStringCodec from(final BitsTypeDefinition type) {
+        return new BitsStringCodec(requireNonNull(type));
+    }
+
+    @Override
+    protected @NonNull Set<String> deserializeImpl(@NonNull final String product) {
+        final Set<String> strings = ImmutableSet.copyOf(SPLITTER.split(product));
+
+        // Normalize strings to schema first, retaining definition order
+        final List<String> sorted = new ArrayList<>(strings.size());
+        for (final String bit : validBits) {
+            if (strings.contains(bit)) {
+                sorted.add(bit);
+            }
+        }
+
+        // Check sizes, if the normalized set does not match non-normalized size, non-normalized strings contain
+        // an invalid bit.
+        if (sorted.size() != strings.size()) {
+            for (final String bit : strings) {
+                checkArgument(validBits.contains(bit), "Invalid value '%s' for bits type. Allowed values are: %s", bit,
+                    validBits);
+            }
+        }
+
+        // In case all valid bits have been specified, retain the set we have created for this codec
+        return sorted.size() == validBits.size() ? validBits : ImmutableSet.copyOf(sorted);
+    }
+
+    @Override
+    protected String serializeImpl(final Set<String> input) {
+        return JOINER.join(input);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/BooleanStringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/BooleanStringCodec.java
new file mode 100644 (file)
index 0000000..9dcf2a1
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.codec.BooleanCodec;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+
+/**
+ * Do not use this class outside of yangtools, its presence does not fall into the API stability contract.
+ */
+@Beta
+public final class BooleanStringCodec extends TypeDefinitionAwareCodec<Boolean, BooleanTypeDefinition>
+        implements BooleanCodec<String> {
+    private BooleanStringCodec(final @NonNull BooleanTypeDefinition typeDef) {
+        super(typeDef, Boolean.class);
+    }
+
+    public static @NonNull BooleanStringCodec from(final BooleanTypeDefinition normalizedType) {
+        return new BooleanStringCodec(requireNonNull(normalizedType));
+    }
+
+    @Override
+    protected Boolean deserializeImpl(final String product) {
+        // FIXME: should forbid "TRUE" ?
+        if ("true".equalsIgnoreCase(product)) {
+            return Boolean.TRUE;
+        } else if ("false".equalsIgnoreCase(product)) {
+            return Boolean.FALSE;
+        } else {
+            throw new IllegalArgumentException("Invalid value '" + product + "' for boolean type. Allowed values are "
+                    + "'true' and 'false'");
+        }
+    }
+
+    @Override
+    protected String serializeImpl(final Boolean input) {
+        return input.toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/CompiledPatternContext.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/CompiledPatternContext.java
new file mode 100644 (file)
index 0000000..3806bf3
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import java.util.Optional;
+import java.util.regex.Pattern;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+
+class CompiledPatternContext {
+
+    private final Pattern pattern;
+    private final String errorMessage;
+    private final String regEx;
+    private final boolean invert;
+
+    CompiledPatternContext(final PatternConstraint yangConstraint) {
+        pattern = Pattern.compile(yangConstraint.getJavaPatternString());
+        errorMessage = yangConstraint.getErrorMessage().orElse(null);
+        regEx = errorMessage == null ? yangConstraint.getRegularExpressionString() : null;
+
+        final Optional<ModifierKind> optModifier = yangConstraint.getModifier();
+        if (optModifier.isPresent()) {
+            final ModifierKind modifier = optModifier.get();
+            switch (modifier) {
+                case INVERT_MATCH:
+                    invert = true;
+                    break;
+                default:
+                    throw new IllegalStateException("Unhandled modifier " + modifier);
+            }
+        } else {
+            invert = false;
+        }
+    }
+
+    void validate(final String str) {
+        if (pattern.matcher(str).matches() == invert) {
+            if (errorMessage != null) {
+                throw new IllegalArgumentException(errorMessage);
+            }
+
+            throw new IllegalArgumentException("Value '" + str + "' " + (invert ? "matches" : "does not match")
+                    + " regular expression '" + regEx + "'");
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/DataStringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/DataStringCodec.java
new file mode 100644 (file)
index 0000000..0724ac1
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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.codec;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+
+// FIXME: 5.0.0: yang-data-api is tied to yang-model-api, hence it should be opinionated to export exceptions
+//               encapsulating YANG-based error information.
+public interface DataStringCodec<T> extends IllegalArgumentCodec<String, T> {
+
+    @NonNull Class<T> getInputClass();
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/DecimalStringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/DecimalStringCodec.java
new file mode 100644 (file)
index 0000000..946e333
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.math.BigDecimal;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.codec.DecimalCodec;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+
+/**
+ * Do not use this class outside of yangtools, its presence does not fall into the API stability contract.
+ */
+@Beta
+public final class DecimalStringCodec extends TypeDefinitionAwareCodec<BigDecimal, DecimalTypeDefinition>
+        implements DecimalCodec<String> {
+    private DecimalStringCodec(final DecimalTypeDefinition typeDef) {
+        super(typeDef, BigDecimal.class);
+    }
+
+    public static @NonNull DecimalStringCodec from(final DecimalTypeDefinition type) {
+        return new DecimalStringCodec(requireNonNull(type));
+    }
+
+    @Override
+    protected BigDecimal deserializeImpl(final String product) {
+        // FIXME: run value validation
+        return new BigDecimal(product);
+    }
+
+    @Override
+    protected @NonNull String serializeImpl(@NonNull final BigDecimal input) {
+        return input.toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/DeserializationException.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/DeserializationException.java
new file mode 100644 (file)
index 0000000..f86a47c
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.codec;
+
+public class DeserializationException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public DeserializationException() {
+    }
+
+    public DeserializationException(final String message) {
+        super(message);
+    }
+
+    public DeserializationException(final Throwable cause) {
+        super(cause);
+    }
+
+    public DeserializationException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+    public DeserializationException(final String message, final Throwable cause, final boolean enableSuppression,
+            final boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/EmptyStringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/EmptyStringCodec.java
new file mode 100644 (file)
index 0000000..3810ce9
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.data.api.codec.EmptyCodec;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+
+final class EmptyStringCodec extends TypeDefinitionAwareCodec<Empty, EmptyTypeDefinition>
+        implements EmptyCodec<String> {
+    static final EmptyStringCodec INSTANCE = new EmptyStringCodec();
+
+    private EmptyStringCodec() {
+        super(null, Empty.class);
+    }
+
+    @Override
+    protected Empty deserializeImpl(final String product) {
+        checkArgument(product.isEmpty(), "The value must be empty");
+        return Empty.getInstance();
+    }
+
+    @Override
+    protected @NonNull String serializeImpl(final Empty input) {
+        return "";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/EnumStringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/EnumStringCodec.java
new file mode 100644 (file)
index 0000000..950acd1
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Functions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.util.stream.Collectors;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.codec.EnumCodec;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+
+/**
+ * Do not use this class outside of yangtools, its presence does not fall into the API stability contract.
+ */
+@Beta
+public final class EnumStringCodec extends TypeDefinitionAwareCodec<String, EnumTypeDefinition>
+        implements EnumCodec<String> {
+    private final ImmutableMap<String, String> values;
+
+    private EnumStringCodec(final @NonNull EnumTypeDefinition typeDef) {
+        super(typeDef, String.class);
+
+        values = Maps.uniqueIndex(typeDef.getValues().stream()
+            // Intern the String to get wide reuse
+            .map(pair -> pair.getName().intern())
+            .collect(Collectors.toList()), Functions.identity());
+    }
+
+    public static EnumStringCodec from(final EnumTypeDefinition normalizedType) {
+        return new EnumStringCodec(requireNonNull(normalizedType));
+    }
+
+    @Override
+    protected String deserializeImpl(final String product) {
+        // Lookup the serialized string in the values. Returned string is the interned instance, which we want
+        // to use as the result.
+        final String result = values.get(product);
+        checkArgument(result != null, "Invalid value '%s' for enum type. Allowed values are: %s", product,
+                values.keySet());
+        return result;
+    }
+
+    @Override
+    protected String serializeImpl(final String input) {
+        return input;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Int16StringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Int16StringCodec.java
new file mode 100644 (file)
index 0000000..2c11f77
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import org.opendaylight.yangtools.yang.data.api.codec.Int16Codec;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+
+final class Int16StringCodec extends AbstractIntegerStringCodec<Short, Int16TypeDefinition>
+        implements Int16Codec<String> {
+    Int16StringCodec(final Int16TypeDefinition typeDef) {
+        super(typeDef, extractRange(typeDef), Short.class);
+    }
+
+    @Override
+    protected Short deserialize(final String stringRepresentation, final int base) {
+        return Short.valueOf(stringRepresentation, base);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Int32StringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Int32StringCodec.java
new file mode 100644 (file)
index 0000000..04419ce
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import org.opendaylight.yangtools.yang.data.api.codec.Int32Codec;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+
+final class Int32StringCodec extends AbstractIntegerStringCodec<Integer, Int32TypeDefinition>
+        implements Int32Codec<String> {
+    Int32StringCodec(final Int32TypeDefinition typeDef) {
+        super(typeDef, extractRange(typeDef), Integer.class);
+    }
+
+    @Override
+    protected Integer deserialize(final String stringRepresentation, final int base) {
+        return Integer.valueOf(stringRepresentation, base);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Int64StringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Int64StringCodec.java
new file mode 100644 (file)
index 0000000..16f728a
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import org.opendaylight.yangtools.yang.data.api.codec.Int64Codec;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+
+final class Int64StringCodec extends AbstractIntegerStringCodec<Long, Int64TypeDefinition>
+        implements Int64Codec<String> {
+    Int64StringCodec(final Int64TypeDefinition typeDef) {
+        super(typeDef, extractRange(typeDef), Long.class);
+    }
+
+    @Override
+    protected Long deserialize(final String stringRepresentation, final int base) {
+        return Long.valueOf(stringRepresentation, base);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Int8StringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Int8StringCodec.java
new file mode 100644 (file)
index 0000000..08f572f
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import org.opendaylight.yangtools.yang.data.api.codec.Int8Codec;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+
+final class Int8StringCodec extends AbstractIntegerStringCodec<Byte, Int8TypeDefinition> implements Int8Codec<String> {
+    Int8StringCodec(final Int8TypeDefinition typeDef) {
+        super(typeDef, extractRange(typeDef), Byte.class);
+    }
+
+    @Override
+    protected Byte deserialize(final String stringRepresentation, final int base) {
+        return Byte.valueOf(stringRepresentation, base);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/SchemaTracker.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/SchemaTracker.java
new file mode 100644 (file)
index 0000000..40e86f3
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * 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.codec;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Iterables;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.common.QName;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.DocumentedNode.WithStatus;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+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.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for tracking the underlying state of the underlying
+ * schema node.
+ */
+@Beta
+public final class SchemaTracker {
+    private static final Logger LOG = LoggerFactory.getLogger(SchemaTracker.class);
+    private final Deque<WithStatus> schemaStack = new ArrayDeque<>();
+    private final DataNodeContainer root;
+
+    private SchemaTracker(final DataNodeContainer root) {
+        this.root = requireNonNull(root);
+    }
+
+    /**
+     * Create a new writer with the specified node as its root.
+     *
+     * @param root Root node
+     * @return A new {@link NormalizedNodeStreamWriter}
+     */
+    public static @NonNull SchemaTracker create(final DataNodeContainer root) {
+        return new SchemaTracker(root);
+    }
+
+    /**
+     * Create a new writer with the specified context and rooted in the specified schema path.
+     *
+     * @param context Associated {@link SchemaContext}
+     * @param path schema path
+     * @return A new {@link NormalizedNodeStreamWriter}
+     */
+    public static @NonNull SchemaTracker create(final SchemaContext context, final SchemaPath path) {
+        final Collection<SchemaNode> schemaNodes = SchemaUtils.findParentSchemaNodesOnPath(context, path);
+        checkArgument(!schemaNodes.isEmpty(), "Unable to find schema node for supplied schema path: %s", path);
+        if (schemaNodes.size() > 1) {
+            LOG.warn("More possible schema nodes {} for supplied schema path {}", schemaNodes, path);
+        }
+        final Optional<DataNodeContainer> current = schemaNodes.stream()
+                .filter(node -> node instanceof DataNodeContainer).map(DataNodeContainer.class::cast)
+                .findFirst();
+        checkArgument(current.isPresent(),
+                "Schema path must point to container or list or an rpc input/output. Supplied path %s pointed to: %s",
+                path, current);
+        return new SchemaTracker(current.get());
+    }
+
+    public Object getParent() {
+        if (schemaStack.isEmpty()) {
+            return root;
+        }
+        return schemaStack.peek();
+    }
+
+    private SchemaNode getSchema(final PathArgument name) {
+        final Object parent = getParent();
+        SchemaNode schema = null;
+        final QName qname = name.getNodeType();
+        if (parent instanceof DataNodeContainer) {
+            schema = ((DataNodeContainer)parent).getDataChildByName(qname);
+            if (schema == null) {
+                if (parent instanceof GroupingDefinition) {
+                    schema = (GroupingDefinition) parent;
+                } else if (parent instanceof NotificationDefinition) {
+                    schema = (NotificationDefinition) parent;
+                }
+            }
+        } else if (parent instanceof ChoiceSchemaNode) {
+            schema = findChildInCases((ChoiceSchemaNode) parent, qname);
+        } else {
+            throw new IllegalStateException("Unsupported schema type " + parent.getClass() + " on stack.");
+        }
+
+        checkArgument(schema != null, "Could not find schema for node %s in %s", qname, parent);
+        return schema;
+    }
+
+    private static SchemaNode findChildInCases(final ChoiceSchemaNode parent, final QName qname) {
+        for (final CaseSchemaNode caze : parent.getCases().values()) {
+            final Optional<DataSchemaNode> potential = caze.findDataChildByName(qname);
+            if (potential.isPresent()) {
+                return potential.get();
+            }
+        }
+        return null;
+    }
+
+    private static SchemaNode findCaseByChild(final ChoiceSchemaNode parent, final QName qname) {
+        for (final CaseSchemaNode caze : parent.getCases().values()) {
+            final Optional<DataSchemaNode> potential = caze.findDataChildByName(qname);
+            if (potential.isPresent()) {
+                return caze;
+            }
+        }
+        return null;
+    }
+
+    public void startList(final PathArgument name) {
+        final SchemaNode schema = getSchema(name);
+        checkArgument(schema instanceof ListSchemaNode, "Node %s is not a list", schema.getPath());
+        schemaStack.push(schema);
+    }
+
+    public void startListItem(final PathArgument name) throws IOException {
+        final Object schema = getParent();
+        checkArgument(schema instanceof ListSchemaNode, "List item is not appropriate");
+        schemaStack.push((ListSchemaNode) schema);
+    }
+
+    public LeafSchemaNode leafNode(final NodeIdentifier name) throws IOException {
+        final SchemaNode schema = getSchema(name);
+
+        checkArgument(schema instanceof LeafSchemaNode, "Node %s is not a leaf", schema.getPath());
+        return (LeafSchemaNode) schema;
+    }
+
+    public void startLeafNode(final NodeIdentifier name) throws IOException {
+        schemaStack.push(leafNode(name));
+    }
+
+    public LeafListSchemaNode startLeafSet(final NodeIdentifier name) {
+        final SchemaNode schema = getSchema(name);
+
+        checkArgument(schema instanceof LeafListSchemaNode, "Node %s is not a leaf-list", schema.getPath());
+        schemaStack.push(schema);
+        return (LeafListSchemaNode)schema;
+    }
+
+    public LeafListSchemaNode leafSetEntryNode(final QName qname) {
+        final Object parent = getParent();
+        if (parent instanceof LeafListSchemaNode) {
+            return (LeafListSchemaNode) parent;
+        }
+
+        final SchemaNode child = SchemaUtils.findDataChildSchemaByQName((SchemaNode) parent, qname);
+        checkArgument(child instanceof LeafListSchemaNode,
+            "Node %s is neither a leaf-list nor currently in a leaf-list", child.getPath());
+        return (LeafListSchemaNode) child;
+    }
+
+    public void startLeafSetEntryNode(final NodeWithValue<?> name) {
+        schemaStack.push(leafSetEntryNode(name.getNodeType()));
+    }
+
+    public ChoiceSchemaNode startChoiceNode(final NodeIdentifier name) {
+        LOG.debug("Enter choice {}", name);
+        final SchemaNode schema = getSchema(name);
+
+        checkArgument(schema instanceof ChoiceSchemaNode, "Node %s is not a choice", schema.getPath());
+        schemaStack.push(schema);
+        return (ChoiceSchemaNode)schema;
+    }
+
+    public SchemaNode startContainerNode(final NodeIdentifier name) {
+        LOG.debug("Enter container {}", name);
+        final SchemaNode schema = getSchema(name);
+
+        boolean isAllowed = schema instanceof ContainerSchemaNode;
+        isAllowed |= schema instanceof NotificationDefinition;
+
+        checkArgument(isAllowed, "Node %s is not a container nor a notification", schema.getPath());
+        schemaStack.push(schema);
+
+        return schema;
+    }
+
+    public SchemaNode startYangModeledAnyXmlNode(final NodeIdentifier name) {
+        LOG.debug("Enter yang modeled anyXml {}", name);
+        final SchemaNode schema = getSchema(name);
+
+        checkArgument(schema instanceof YangModeledAnyxmlSchemaNode, "Node %s is not an yang modeled anyXml.",
+            schema.getPath());
+
+        schemaStack.push(((YangModeledAnyxmlSchemaNode) schema).getSchemaOfAnyXmlData());
+
+        return schema;
+    }
+
+    public AugmentationSchemaNode startAugmentationNode(final AugmentationIdentifier identifier) {
+        LOG.debug("Enter augmentation {}", identifier);
+        Object parent = getParent();
+
+        checkArgument(parent instanceof AugmentationTarget, "Augmentation not allowed under %s", parent);
+        if (parent instanceof ChoiceSchemaNode) {
+            final QName name = Iterables.get(identifier.getPossibleChildNames(), 0);
+            parent = findCaseByChild((ChoiceSchemaNode) parent, name);
+        }
+        checkArgument(parent instanceof DataNodeContainer, "Augmentation allowed only in DataNodeContainer", parent);
+        final AugmentationSchemaNode schema = SchemaUtils.findSchemaForAugment((AugmentationTarget) parent,
+            identifier.getPossibleChildNames());
+        final AugmentationSchemaNode resolvedSchema = EffectiveAugmentationSchema.create(schema,
+            (DataNodeContainer) parent);
+        schemaStack.push(resolvedSchema);
+        return resolvedSchema;
+    }
+
+    public AnyxmlSchemaNode anyxmlNode(final NodeIdentifier name) {
+        final SchemaNode schema = getSchema(name);
+        checkArgument(schema instanceof AnyxmlSchemaNode, "Node %s is not anyxml", schema.getPath());
+        return (AnyxmlSchemaNode)schema;
+    }
+
+    public void startAnyxmlNode(final NodeIdentifier name) {
+        schemaStack.push(anyxmlNode(name));
+    }
+
+    public AnydataSchemaNode anydataNode(final NodeIdentifier name) {
+        final SchemaNode schema = getSchema(name);
+        checkArgument(schema instanceof AnydataSchemaNode, "Node %s is not anydata", schema.getPath());
+        return (AnydataSchemaNode)schema;
+    }
+
+    public void startAnydataNode(final NodeIdentifier name) {
+        schemaStack.push(anydataNode(name));
+    }
+
+    public Object endNode() {
+        return schemaStack.pop();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/StringPatternCheckingCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/StringPatternCheckingCodec.java
new file mode 100644 (file)
index 0000000..8fb0075
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import java.util.List;
+import java.util.regex.PatternSyntaxException;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class StringPatternCheckingCodec extends StringStringCodec {
+    private static final Logger LOG = LoggerFactory.getLogger(StringPatternCheckingCodec.class);
+
+    private final ImmutableList<CompiledPatternContext> patterns;
+
+    StringPatternCheckingCodec(final StringTypeDefinition typeDef) {
+        super(typeDef);
+
+        final List<PatternConstraint> constraints = typeDef.getPatternConstraints();
+        final Builder<CompiledPatternContext> builder = ImmutableList.builderWithExpectedSize(constraints.size());
+        for (final PatternConstraint yangPattern : typeDef.getPatternConstraints()) {
+            try {
+                builder.add(new CompiledPatternContext(yangPattern));
+            } catch (final PatternSyntaxException e) {
+                LOG.debug("Unable to compile {} pattern, excluding it from validation.", yangPattern, e);
+            }
+        }
+        patterns = builder.build();
+    }
+
+    @Override
+    void validate(final String str) {
+        super.validate(str);
+        for (final CompiledPatternContext pattern : patterns) {
+            pattern.validate(str);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/StringStringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/StringStringCodec.java
new file mode 100644 (file)
index 0000000..e7c3020
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.data.api.codec.StringCodec;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+
+/**
+ * Do not use this class outside of yangtools, its presence does not fall into the API stability contract.
+ */
+@Beta
+public class StringStringCodec extends TypeDefinitionAwareCodec<String, StringTypeDefinition>
+        implements StringCodec<String> {
+    private final LengthConstraint lengthConstraint;
+
+    StringStringCodec(final StringTypeDefinition typeDef) {
+        super(requireNonNull(typeDef), String.class);
+        lengthConstraint = typeDef.getLengthConstraint().orElse(null);
+    }
+
+    public static StringStringCodec from(final StringTypeDefinition normalizedType) {
+        return normalizedType.getPatternConstraints().isEmpty() ? new StringStringCodec(normalizedType)
+                : new StringPatternCheckingCodec(normalizedType);
+    }
+
+    @Override
+    protected final String deserializeImpl(final String stringRepresentation) {
+        validate(stringRepresentation);
+        return stringRepresentation;
+    }
+
+    @Override
+    protected final String serializeImpl(final String data) {
+        return data;
+    }
+
+    void validate(final String str) {
+        if (lengthConstraint != null) {
+            checkArgument(lengthConstraint.getAllowedRanges().contains(str.length()),
+                    "String '%s' does not match allowed length constraint %s", lengthConstraint);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/TypeDefinitionAwareCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/TypeDefinitionAwareCodec.java
new file mode 100644 (file)
index 0000000..b563bff
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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.codec;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+
+public abstract class TypeDefinitionAwareCodec<J, T extends TypeDefinition<T>> extends AbstractDataStringCodec<J> {
+    private final @NonNull Class<J> inputClass;
+    private final @Nullable T typeDefinition;
+
+    // FIXME: reverse arguments
+    protected TypeDefinitionAwareCodec(final @Nullable T typeDefinition, final Class<J> outputClass) {
+        this.typeDefinition = typeDefinition;
+        this.inputClass = requireNonNull(outputClass);
+    }
+
+    @Override
+    public Class<J> getInputClass() {
+        return inputClass;
+    }
+
+    public Optional<T> getTypeDefinition() {
+        return Optional.ofNullable(typeDefinition);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static TypeDefinitionAwareCodec<Object, ?> from(final TypeDefinition<?> typeDefinition) {
+        return (TypeDefinitionAwareCodec<Object, ?>) fromType(typeDefinition);
+    }
+
+    // FIXME: do we want an Optional or a throws instead of @Nullable here?
+    public static @Nullable TypeDefinitionAwareCodec<?, ?> fromType(final TypeDefinition<?> typeDefinition) {
+        if (typeDefinition instanceof BinaryTypeDefinition) {
+            return BinaryStringCodec.from((BinaryTypeDefinition)typeDefinition);
+        } else if (typeDefinition instanceof BitsTypeDefinition) {
+            return BitsStringCodec.from((BitsTypeDefinition)typeDefinition);
+        } else if (typeDefinition instanceof BooleanTypeDefinition) {
+            return BooleanStringCodec.from((BooleanTypeDefinition)typeDefinition);
+        } else if (typeDefinition instanceof DecimalTypeDefinition) {
+            return  DecimalStringCodec.from((DecimalTypeDefinition)typeDefinition);
+        } else if (typeDefinition instanceof EmptyTypeDefinition) {
+            return EmptyStringCodec.INSTANCE;
+        } else if (typeDefinition instanceof EnumTypeDefinition) {
+            return EnumStringCodec.from((EnumTypeDefinition)typeDefinition);
+        } else if (typeDefinition instanceof Int8TypeDefinition) {
+            return AbstractIntegerStringCodec.from((Int8TypeDefinition) typeDefinition);
+        } else if (typeDefinition instanceof Int16TypeDefinition) {
+            return AbstractIntegerStringCodec.from((Int16TypeDefinition) typeDefinition);
+        } else if (typeDefinition instanceof Int32TypeDefinition) {
+            return AbstractIntegerStringCodec.from((Int32TypeDefinition) typeDefinition);
+        } else if (typeDefinition instanceof Int64TypeDefinition) {
+            return AbstractIntegerStringCodec.from((Int64TypeDefinition) typeDefinition);
+        } else if (typeDefinition instanceof StringTypeDefinition) {
+            return StringStringCodec.from((StringTypeDefinition)typeDefinition);
+        } else if (typeDefinition instanceof UnionTypeDefinition) {
+            return UnionStringCodec.from((UnionTypeDefinition)typeDefinition);
+        } else if (typeDefinition instanceof Uint8TypeDefinition) {
+            return AbstractIntegerStringCodec.from((Uint8TypeDefinition) typeDefinition);
+        } else if (typeDefinition instanceof Uint16TypeDefinition) {
+            return AbstractIntegerStringCodec.from((Uint16TypeDefinition) typeDefinition);
+        } else if (typeDefinition instanceof Uint32TypeDefinition) {
+            return AbstractIntegerStringCodec.from((Uint32TypeDefinition) typeDefinition);
+        } else if (typeDefinition instanceof Uint64TypeDefinition) {
+            return AbstractIntegerStringCodec.from((Uint64TypeDefinition) typeDefinition);
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Uint16StringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Uint16StringCodec.java
new file mode 100644 (file)
index 0000000..0867874
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.data.api.codec.Uint16Codec;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+
+final class Uint16StringCodec extends AbstractIntegerStringCodec<Uint16, Uint16TypeDefinition> implements
+        Uint16Codec<String> {
+    Uint16StringCodec(final Uint16TypeDefinition typeDef) {
+        super(typeDef, extractRange(typeDef), Uint16.class);
+    }
+
+    @Override
+    protected Uint16 deserialize(final String stringRepresentation, final int base) {
+        return Uint16.valueOf(stringRepresentation, base);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Uint32StringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Uint32StringCodec.java
new file mode 100644 (file)
index 0000000..7ca8be4
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.data.api.codec.Uint32Codec;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+
+final class Uint32StringCodec extends AbstractIntegerStringCodec<Uint32, Uint32TypeDefinition> implements
+        Uint32Codec<String> {
+    Uint32StringCodec(final Uint32TypeDefinition typeDef) {
+        super(typeDef, extractRange(typeDef), Uint32.class);
+    }
+
+    @Override
+    protected Uint32 deserialize(final String stringRepresentation, final int base) {
+        return Uint32.valueOf(stringRepresentation, base);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Uint64StringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Uint64StringCodec.java
new file mode 100644 (file)
index 0000000..813d760
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.data.api.codec.Uint64Codec;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+
+final class Uint64StringCodec extends AbstractIntegerStringCodec<Uint64, Uint64TypeDefinition> implements
+        Uint64Codec<String> {
+    Uint64StringCodec(final Uint64TypeDefinition typeDef) {
+        super(typeDef, extractRange(typeDef), Uint64.class);
+    }
+
+    @Override
+    protected Uint64 deserialize(final String stringRepresentation, final int base) {
+        return Uint64.valueOf(stringRepresentation, base);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Uint8StringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/Uint8StringCodec.java
new file mode 100644 (file)
index 0000000..21f3cc4
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.data.api.codec.Uint8Codec;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+
+final class Uint8StringCodec extends AbstractIntegerStringCodec<Uint8, Uint8TypeDefinition> implements
+        Uint8Codec<String> {
+    Uint8StringCodec(final Uint8TypeDefinition typeDef) {
+        super(typeDef, extractRange(typeDef), Uint8.class);
+    }
+
+    @Override
+    protected Uint8 deserialize(final String stringRepresentation, final int base) {
+        return Uint8.valueOf(stringRepresentation, base);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/UnionStringCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/UnionStringCodec.java
new file mode 100644 (file)
index 0000000..3053b1a
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 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.codec;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Base64;
+import org.opendaylight.yangtools.yang.data.api.codec.UnionCodec;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class UnionStringCodec extends TypeDefinitionAwareCodec<Object, UnionTypeDefinition>
+        implements UnionCodec<String> {
+    private static final Logger LOG = LoggerFactory.getLogger(UnionStringCodec.class);
+
+    UnionStringCodec(final UnionTypeDefinition typeDef) {
+        super(requireNonNull(typeDef), Object.class);
+    }
+
+    static TypeDefinitionAwareCodec<?, UnionTypeDefinition> from(final UnionTypeDefinition normalizedType) {
+        return new UnionStringCodec(normalizedType);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    protected Object deserializeImpl(final String stringRepresentation) {
+        for (final TypeDefinition<?> type : getTypeDefinition().get().getTypes()) {
+            final TypeDefinitionAwareCodec<Object, ?> typeAwareCodec = from(type);
+            if (typeAwareCodec == null) {
+                /*
+                 * This is a type for which we have no codec (eg identity ref) so we'll say it's
+                 * valid
+                 */
+                return stringRepresentation;
+            }
+
+            try {
+                return typeAwareCodec.deserialize(stringRepresentation);
+            } catch (final Exception e) {
+                LOG.debug("Value {} did not matched representation for {}",stringRepresentation,type,e);
+                // invalid - try the next union type.
+            }
+        }
+
+        throw new IllegalArgumentException("Invalid value \"" + stringRepresentation + "\" for union type.");
+    }
+
+    @Override
+    protected String serializeImpl(final Object data) {
+        return data instanceof byte[] ? Base64.getEncoder().encodeToString((byte[]) data) : data.toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/ValueWithQName.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/ValueWithQName.java
new file mode 100644 (file)
index 0000000..83298d8
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.codec;
+
+import java.util.AbstractMap.SimpleImmutableEntry;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class ValueWithQName<V> extends SimpleImmutableEntry<QName, V> {
+    private static final long serialVersionUID = 1L;
+
+    public ValueWithQName(final QName qname, final V value) {
+        super(qname, value);
+    }
+
+    public QName getQname() {
+        return getKey();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/AbstractQNameWithPredicate.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/AbstractQNameWithPredicate.java
new file mode 100644 (file)
index 0000000..3c76548
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.leafref;
+
+import java.io.Serializable;
+import java.util.Objects;
+import java.util.Optional;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+
+abstract class AbstractQNameWithPredicate implements Immutable, Serializable, QNameWithPredicate {
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof QNameWithPredicate)) {
+            return false;
+        }
+        final QNameWithPredicate other = (QNameWithPredicate) obj;
+        // FIXME: check also predicates ...
+        return Objects.equals(getLocalName(), other.getLocalName())
+                && Objects.equals(getModuleQname(), other.getModuleQname());
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(getModuleQname(), getLocalName());
+    }
+
+    @Override
+    public final String toString() {
+        final StringBuilder sb = new StringBuilder();
+
+        final QNameModule moduleQname = getModuleQname();
+        if (moduleQname != null) {
+            sb.append('(').append(moduleQname.getNamespace());
+            final Optional<Revision> rev = moduleQname.getRevision();
+            if (rev.isPresent()) {
+                sb.append("?revision=").append(rev.get());
+            }
+            sb.append(')');
+        }
+
+        sb.append(getLocalName());
+        getQNamePredicates().forEach(sb::append);
+        return sb.toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefContext.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefContext.java
new file mode 100644 (file)
index 0000000..2f0dae8
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.AbstractSchemaContextProvider;
+
+// FIXME: 5.0.0 hide this class
+public final class LeafRefContext extends AbstractSchemaContextProvider {
+
+    private final QName currentNodeQName;
+    private final SchemaPath currentNodePath;
+    private final Module module;
+
+    private final LeafRefPath leafRefTargetPath;
+    private final LeafRefPath absoluteLeafRefTargetPath;
+    private final String leafRefTargetPathString;
+
+    private final boolean isReferencedBy;
+    private final boolean isReferencing;
+
+    private final ImmutableMap<QName, LeafRefContext> referencingChilds;
+    private final ImmutableMap<QName, LeafRefContext> referencedByChilds;
+    private final ImmutableMap<QName, LeafRefContext> referencedByLeafRefCtx;
+
+    // FIXME: this looks like it's related to absoluteLeafRefTargetPath, but the original use in LeafRefValidation
+    //        fast path did not make it clear. Analyze the relationship between this field and
+    //        absoluteLeafRefTargetPath.
+    private volatile LeafRefPath leafRefNodePath = null;
+
+    LeafRefContext(final LeafRefContextBuilder leafRefContextBuilder) {
+        super(leafRefContextBuilder.getSchemaContext());
+        this.currentNodeQName = leafRefContextBuilder.getCurrentNodeQName();
+        this.currentNodePath = leafRefContextBuilder.getCurrentNodePath();
+        this.leafRefTargetPath = leafRefContextBuilder.getLeafRefTargetPath();
+        this.absoluteLeafRefTargetPath = leafRefContextBuilder.getAbsoluteLeafRefTargetPath();
+        this.leafRefTargetPathString = leafRefContextBuilder.getLeafRefTargetPathString();
+        this.isReferencedBy = leafRefContextBuilder.isReferencedBy();
+        this.isReferencing = leafRefContextBuilder.isReferencing();
+        this.referencingChilds = ImmutableMap.copyOf(leafRefContextBuilder.getReferencingChilds());
+        this.referencedByChilds = ImmutableMap.copyOf(leafRefContextBuilder.getReferencedByChilds());
+        this.referencedByLeafRefCtx = ImmutableMap.copyOf(leafRefContextBuilder.getAllReferencedByLeafRefCtxs());
+        this.module = leafRefContextBuilder.getLeafRefContextModule();
+    }
+
+    public static LeafRefContext create(final SchemaContext ctx) {
+        try {
+            return new LeafRefContextTreeBuilder(ctx).buildLeafRefContextTree();
+        } catch (LeafRefYangSyntaxErrorException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    public boolean hasLeafRefContextChild() {
+        return hasReferencedChild() || hasReferencingChild();
+    }
+
+    public boolean hasReferencedChild() {
+        return !referencedByChilds.isEmpty();
+    }
+
+    public boolean hasReferencingChild() {
+        return !referencingChilds.isEmpty();
+    }
+
+    public boolean isReferenced() {
+        return isReferencedBy;
+    }
+
+    public boolean isReferencing() {
+        return isReferencing;
+    }
+
+    public LeafRefContext getReferencingChildByName(final QName name) {
+        return referencingChilds.get(name);
+    }
+
+    public Map<QName, LeafRefContext> getReferencingChilds() {
+        return referencingChilds;
+    }
+
+    public LeafRefContext getReferencedChildByName(final QName name) {
+        return referencedByChilds.get(name);
+    }
+
+    public Map<QName, LeafRefContext> getReferencedByChilds() {
+        return referencedByChilds;
+    }
+
+    public SchemaPath getCurrentNodePath() {
+        return currentNodePath;
+    }
+
+    public LeafRefPath getLeafRefTargetPath() {
+        return leafRefTargetPath;
+    }
+
+    public String getLeafRefTargetPathString() {
+        return leafRefTargetPathString;
+    }
+
+    public QName getNodeName() {
+        return currentNodeQName;
+    }
+
+    public LeafRefPath getAbsoluteLeafRefTargetPath() {
+        return absoluteLeafRefTargetPath;
+    }
+
+    public Module getLeafRefContextModule() {
+        return module;
+    }
+
+    public LeafRefContext getReferencedByLeafRefCtxByName(final QName qname) {
+        return referencedByLeafRefCtx.get(qname);
+    }
+
+    public Map<QName, LeafRefContext> getAllReferencedByLeafRefCtxs() {
+        return referencedByLeafRefCtx;
+    }
+
+    LeafRefPath getLeafRefNodePath() {
+        LeafRefPath ret = leafRefNodePath;
+        if (ret == null) {
+            synchronized (this) {
+                ret = leafRefNodePath;
+                if (ret == null) {
+                    ret = leafRefNodePath = LeafRefUtils.schemaPathToLeafRefPath(currentNodePath, module);
+                }
+            }
+        }
+        return ret;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefContextBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefContextBuilder.java
new file mode 100644 (file)
index 0000000..47e9a61
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+final class LeafRefContextBuilder implements Builder<LeafRefContext> {
+
+    private final Map<QName, LeafRefContext> referencingChildren = new HashMap<>();
+    private final Map<QName, LeafRefContext> referencedByChildren = new HashMap<>();
+    private final Map<QName, LeafRefContext> referencedByLeafRefCtx = new HashMap<>();
+
+    private final QName currentNodeQName;
+    private final SchemaPath currentNodePath;
+    private final SchemaContext schemaContext;
+
+    private LeafRefPath leafRefTargetPath = null;
+    private LeafRefPath absoluteLeafRefTargetPath = null;
+    private String leafRefTargetPathString = "";
+
+    private boolean isReferencedBy = false;
+    private boolean isReferencing = false;
+
+    LeafRefContextBuilder(final QName currentNodeQName, final SchemaPath currentNodePath,
+        final SchemaContext schemaContext) {
+        this.currentNodeQName = requireNonNull(currentNodeQName);
+        this.currentNodePath = requireNonNull(currentNodePath);
+        // FIXME: requireNonNull
+        this.schemaContext = schemaContext;
+    }
+
+    @Override
+    public LeafRefContext build() {
+        final LeafRefContext leafRefContext = new LeafRefContext(this);
+
+        // LeafRefContext has made a copy of these
+        referencingChildren.clear();
+        referencedByChildren.clear();
+        referencedByLeafRefCtx.clear();
+
+        return leafRefContext;
+    }
+
+    boolean isReferencedBy() {
+        return isReferencedBy;
+    }
+
+    void setReferencedBy(final boolean referencedBy) {
+        this.isReferencedBy = referencedBy;
+    }
+
+    boolean isReferencing() {
+        return isReferencing;
+    }
+
+    void setReferencing(final boolean referencing) {
+        this.isReferencing = referencing;
+    }
+
+    void addReferencingChild(final LeafRefContext child, final QName childQName) {
+        referencingChildren.put(childQName, child);
+    }
+
+    Map<QName, LeafRefContext> getReferencingChilds() {
+        return referencingChildren;
+    }
+
+    void addReferencedByChild(final LeafRefContext child, final QName childQName) {
+        referencedByChildren.put(childQName, child);
+    }
+
+    Map<QName, LeafRefContext> getReferencedByChilds() {
+        return referencedByChildren;
+    }
+
+    SchemaPath getCurrentNodePath() {
+        return currentNodePath;
+    }
+
+    LeafRefPath getLeafRefTargetPath() {
+        return leafRefTargetPath;
+    }
+
+    void setLeafRefTargetPath(final LeafRefPath leafRefPath) {
+        this.leafRefTargetPath = leafRefPath;
+    }
+
+    String getLeafRefTargetPathString() {
+        return leafRefTargetPathString;
+    }
+
+    void setLeafRefTargetPathString(final String leafRefPathString) {
+        this.leafRefTargetPathString = leafRefPathString;
+    }
+
+    QName getCurrentNodeQName() {
+        return currentNodeQName;
+    }
+
+    SchemaContext getSchemaContext() {
+        return schemaContext;
+    }
+
+    LeafRefPath getAbsoluteLeafRefTargetPath() {
+        if (isReferencing && absoluteLeafRefTargetPath == null) {
+            if (leafRefTargetPath.isAbsolute()) {
+                absoluteLeafRefTargetPath = leafRefTargetPath;
+            } else {
+                absoluteLeafRefTargetPath = LeafRefUtils.createAbsoluteLeafRefPath(leafRefTargetPath,
+                    currentNodePath, getLeafRefContextModule());
+            }
+        }
+
+        return absoluteLeafRefTargetPath;
+    }
+
+    Module getLeafRefContextModule() {
+        final Iterator<QName> it = currentNodePath.getPathFromRoot().iterator();
+        final QNameModule qnameModule = it.hasNext() ? it.next().getModule() : currentNodeQName.getModule();
+        return schemaContext.findModule(qnameModule).orElse(null);
+    }
+
+    void addReferencedByLeafRefCtx(final QName qname, final LeafRefContext leafRef) {
+        referencedByLeafRefCtx.put(qname, leafRef);
+    }
+
+    LeafRefContext getReferencedByLeafRefCtxByName(final QName qname) {
+        return referencedByLeafRefCtx.get(qname);
+    }
+
+    Map<QName, LeafRefContext> getAllReferencedByLeafRefCtxs() {
+        return referencedByLeafRefCtx;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefContextTreeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefContextTreeBuilder.java
new file mode 100644 (file)
index 0000000..19271b9
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+
+final class LeafRefContextTreeBuilder {
+    private final List<LeafRefContext> leafRefs = new LinkedList<>();
+    private final SchemaContext schemaContext;
+
+    LeafRefContextTreeBuilder(final SchemaContext schemaContext) {
+        this.schemaContext = schemaContext;
+    }
+
+    LeafRefContext buildLeafRefContextTree() throws LeafRefYangSyntaxErrorException {
+        final LeafRefContextBuilder rootBuilder = new LeafRefContextBuilder(schemaContext.getQName(),
+            schemaContext.getPath(), schemaContext);
+
+        final Set<Module> modules = schemaContext.getModules();
+        for (final Module module : modules) {
+            for (final DataSchemaNode childNode : module.getChildNodes()) {
+                final LeafRefContext childLeafRefContext = buildLeafRefContextReferencingTree(childNode, module);
+                if (childLeafRefContext.hasReferencingChild() || childLeafRefContext.isReferencing()) {
+                    rootBuilder.addReferencingChild(childLeafRefContext, childLeafRefContext.getNodeName());
+                }
+            }
+        }
+
+        for (final Module module : modules) {
+            final Collection<DataSchemaNode> childNodes = module.getChildNodes();
+            for (final DataSchemaNode childNode : childNodes) {
+                final LeafRefContext childLeafRefContext = buildLeafRefContextReferencedByTree(childNode, module);
+
+                if (childLeafRefContext.hasReferencedChild() || childLeafRefContext.isReferenced()) {
+                    rootBuilder.addReferencedByChild(childLeafRefContext, childLeafRefContext.getNodeName());
+                }
+            }
+        }
+
+        // FIXME: it might be useful to merge these subtrees (i.e. referencing
+        // and referencedBy subtree)
+
+        return rootBuilder.build();
+    }
+
+    private LeafRefContext buildLeafRefContextReferencingTree(final DataSchemaNode node, final Module currentModule)
+            throws LeafRefYangSyntaxErrorException {
+        final LeafRefContextBuilder currentLeafRefContextBuilder = new LeafRefContextBuilder(node.getQName(),
+            node.getPath(), schemaContext);
+
+        if (node instanceof DataNodeContainer) {
+            for (final DataSchemaNode childNode : ((DataNodeContainer) node).getChildNodes()) {
+                final LeafRefContext childLeafRefContext = buildLeafRefContextReferencingTree(childNode, currentModule);
+                if (childLeafRefContext.hasReferencingChild() || childLeafRefContext.isReferencing()) {
+                    currentLeafRefContextBuilder.addReferencingChild(childLeafRefContext,
+                        childLeafRefContext.getNodeName());
+                }
+            }
+        } else if (node instanceof ChoiceSchemaNode) {
+            // :FIXME choice without case
+            for (final CaseSchemaNode caseNode : ((ChoiceSchemaNode) node).getCases().values()) {
+                final LeafRefContext childLeafRefContext = buildLeafRefContextReferencingTree(caseNode, currentModule);
+                if (childLeafRefContext.hasReferencingChild() || childLeafRefContext.isReferencing()) {
+                    currentLeafRefContextBuilder.addReferencingChild(childLeafRefContext,
+                        childLeafRefContext.getNodeName());
+                }
+            }
+
+        } else if (node instanceof TypedDataSchemaNode) {
+            final TypeDefinition<?> type = ((TypedDataSchemaNode) node).getType();
+
+            // FIXME: fix case when type is e.g. typedef -> typedef -> leafref
+            if (type instanceof LeafrefTypeDefinition) {
+                final LeafrefTypeDefinition leafrefType = (LeafrefTypeDefinition) type;
+                final String leafRefPathString = leafrefType.getPathStatement().getOriginalString();
+                final LeafRefPathParserImpl leafRefPathParser = new LeafRefPathParserImpl(schemaContext,
+                        checkNotNull(getBaseTypeModule(leafrefType), "Unable to find base module for leafref %s", node),
+                        node);
+                final LeafRefPath leafRefPath = leafRefPathParser.parseLeafRefPath(leafRefPathString);
+
+                currentLeafRefContextBuilder.setLeafRefTargetPathString(leafRefPathString);
+                currentLeafRefContextBuilder.setReferencing(true);
+                currentLeafRefContextBuilder.setLeafRefTargetPath(leafRefPath);
+
+                final LeafRefContext currentLeafRefContext = currentLeafRefContextBuilder.build();
+                leafRefs.add(currentLeafRefContext);
+                return currentLeafRefContext;
+            }
+        }
+
+        return currentLeafRefContextBuilder.build();
+    }
+
+    private Module getBaseTypeModule(final LeafrefTypeDefinition leafrefType) {
+        /*
+         * Find the first definition of supplied leafref type and return the
+         * module which contains this definition.
+         */
+        LeafrefTypeDefinition baseLeafRefType = leafrefType;
+        while (baseLeafRefType.getBaseType() != null) {
+            baseLeafRefType = baseLeafRefType.getBaseType();
+        }
+        return schemaContext.findModule(baseLeafRefType.getQName().getModule()).orElse(null);
+    }
+
+    private LeafRefContext buildLeafRefContextReferencedByTree(final DataSchemaNode node, final Module currentModule)
+            throws LeafRefYangSyntaxErrorException {
+        final LeafRefContextBuilder currentLeafRefContextBuilder = new LeafRefContextBuilder(node.getQName(),
+            node.getPath(), schemaContext);
+        if (node instanceof DataNodeContainer) {
+            for (final DataSchemaNode childNode : ((DataNodeContainer) node).getChildNodes()) {
+                final LeafRefContext childLeafRefContext = buildLeafRefContextReferencedByTree(childNode,
+                    currentModule);
+                if (childLeafRefContext.hasReferencedChild() || childLeafRefContext.isReferenced()) {
+                    currentLeafRefContextBuilder.addReferencedByChild(childLeafRefContext,
+                        childLeafRefContext.getNodeName());
+                }
+            }
+        } else if (node instanceof ChoiceSchemaNode) {
+            for (final CaseSchemaNode caseNode : ((ChoiceSchemaNode) node).getCases().values()) {
+                final LeafRefContext childLeafRefContext = buildLeafRefContextReferencedByTree(caseNode, currentModule);
+                if (childLeafRefContext.hasReferencedChild() || childLeafRefContext.isReferenced()) {
+                    currentLeafRefContextBuilder.addReferencedByChild(childLeafRefContext,
+                        childLeafRefContext.getNodeName());
+                }
+            }
+        } else if (node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode) {
+            final List<LeafRefContext> foundLeafRefs = getLeafRefsFor(node, currentModule);
+            if (!foundLeafRefs.isEmpty()) {
+                currentLeafRefContextBuilder.setReferencedBy(true);
+                for (final LeafRefContext leafRef : foundLeafRefs) {
+                    currentLeafRefContextBuilder.addReferencedByLeafRefCtx(leafRef.getNodeName(), leafRef);
+                }
+            }
+        }
+
+        return currentLeafRefContextBuilder.build();
+    }
+
+    private List<LeafRefContext> getLeafRefsFor(final DataSchemaNode node, final Module module) {
+        final LeafRefPath nodeXPath = LeafRefUtils.schemaPathToLeafRefPath(node.getPath(), module);
+        final List<LeafRefContext> foundLeafRefs = new LinkedList<>();
+        for (final LeafRefContext leafref : leafRefs) {
+            final LeafRefPath leafRefTargetPath = leafref.getAbsoluteLeafRefTargetPath();
+            if (leafRefTargetPath.equals(nodeXPath)) {
+                foundLeafRefs.add(leafref);
+            }
+        }
+
+        return foundLeafRefs;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefContextUtils.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefContextUtils.java
new file mode 100644 (file)
index 0000000..fd45b9d
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public final class LeafRefContextUtils {
+
+    private LeafRefContextUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static LeafRefContext getLeafRefReferencingContext(final SchemaNode node, final LeafRefContext root) {
+        final SchemaPath schemaPath = node.getPath();
+        return getLeafRefReferencingContext(schemaPath, root);
+    }
+
+    public static LeafRefContext getLeafRefReferencingContext(
+            final SchemaPath schemaPath, final LeafRefContext root) {
+        final Iterable<QName> pathFromRoot = schemaPath.getPathFromRoot();
+        return getLeafRefReferencingContext(pathFromRoot, root);
+    }
+
+    public static LeafRefContext getLeafRefReferencingContext(final Iterable<QName> pathFromRoot, LeafRefContext root) {
+        LeafRefContext leafRefCtx = null;
+        final Iterator<QName> iterator = pathFromRoot.iterator();
+        while (iterator.hasNext() && root != null) {
+            final QName qname = iterator.next();
+            leafRefCtx = root.getReferencingChildByName(qname);
+            if (iterator.hasNext()) {
+                root = leafRefCtx;
+            }
+        }
+
+        return leafRefCtx;
+    }
+
+    public static LeafRefContext getLeafRefReferencedByContext(final SchemaNode node, final LeafRefContext root) {
+        final SchemaPath schemaPath = node.getPath();
+        return getLeafRefReferencedByContext(schemaPath, root);
+    }
+
+    public static LeafRefContext getLeafRefReferencedByContext(
+            final SchemaPath schemaPath, final LeafRefContext root) {
+        final Iterable<QName> pathFromRoot = schemaPath.getPathFromRoot();
+        return getLeafRefReferencedByContext(pathFromRoot, root);
+    }
+
+    public static LeafRefContext getLeafRefReferencedByContext(final Iterable<QName> pathFromRoot,
+            LeafRefContext root) {
+
+        LeafRefContext leafRefCtx = null;
+        final Iterator<QName> iterator = pathFromRoot.iterator();
+        while (iterator.hasNext() && root != null) {
+            final QName qname = iterator.next();
+            leafRefCtx = root.getReferencedChildByName(qname);
+            if (iterator.hasNext()) {
+                root = leafRefCtx;
+            }
+        }
+
+        return leafRefCtx;
+    }
+
+    public static boolean isLeafRef(final SchemaNode node, final LeafRefContext root) {
+        if (node == null || root == null) {
+            return false;
+        }
+
+        final LeafRefContext leafRefReferencingContext = getLeafRefReferencingContext(node, root);
+        if (leafRefReferencingContext == null) {
+            return false;
+        }
+
+        return leafRefReferencingContext.isReferencing();
+    }
+
+    public static boolean hasLeafRefChild(final SchemaNode node, final LeafRefContext root) {
+        if (node == null || root == null) {
+            return false;
+        }
+
+        final LeafRefContext leafRefReferencingContext = getLeafRefReferencingContext(node, root);
+        if (leafRefReferencingContext == null) {
+            return false;
+        }
+
+        return leafRefReferencingContext.hasReferencingChild();
+    }
+
+    public static boolean isReferencedByLeafRef(final SchemaNode node, final LeafRefContext root) {
+        if (node == null || root == null) {
+            return false;
+        }
+
+        final LeafRefContext leafRefReferencedByContext = getLeafRefReferencedByContext(node, root);
+        if (leafRefReferencedByContext == null) {
+            return false;
+        }
+
+        return leafRefReferencedByContext.isReferenced();
+    }
+
+    public static boolean hasChildReferencedByLeafRef(final SchemaNode node, final LeafRefContext root) {
+        if (node == null || root == null) {
+            return false;
+        }
+
+        final LeafRefContext leafRefReferencedByContext = getLeafRefReferencedByContext(node, root);
+        if (leafRefReferencedByContext == null) {
+            return false;
+        }
+
+        return leafRefReferencedByContext.hasReferencedChild();
+    }
+
+    public static List<LeafRefContext> findAllLeafRefChilds(final SchemaNode node, final LeafRefContext root) {
+        return findAllLeafRefChilds(node.getPath(), root);
+    }
+
+    public static List<LeafRefContext> findAllLeafRefChilds(final SchemaPath schemaPath, final LeafRefContext root) {
+        return findAllLeafRefChilds(schemaPath.getPathFromRoot(), root);
+    }
+
+    public static List<LeafRefContext> findAllLeafRefChilds(final Iterable<QName> pathFromRoot,
+            final LeafRefContext root) {
+        final LeafRefContext leafRefReferencingContext = getLeafRefReferencingContext(pathFromRoot, root);
+        final List<LeafRefContext> allLeafRefsChilds = findAllLeafRefChilds(leafRefReferencingContext);
+
+        return allLeafRefsChilds;
+    }
+
+    public static List<LeafRefContext> findAllLeafRefChilds(final LeafRefContext parent) {
+        final LinkedList<LeafRefContext> leafRefChilds = new LinkedList<>();
+        if (parent == null) {
+            return leafRefChilds;
+        }
+
+        if (parent.isReferencing()) {
+            leafRefChilds.add(parent);
+            return leafRefChilds;
+        }
+
+        final Set<Entry<QName, LeafRefContext>> childs = parent.getReferencingChilds().entrySet();
+        for (final Entry<QName, LeafRefContext> child : childs) {
+            leafRefChilds.addAll(findAllLeafRefChilds(child.getValue()));
+        }
+        return leafRefChilds;
+    }
+
+    public static List<LeafRefContext> findAllChildsReferencedByLeafRef(final SchemaNode node,
+            final LeafRefContext root) {
+        return findAllChildsReferencedByLeafRef(node.getPath(), root);
+    }
+
+    public static List<LeafRefContext> findAllChildsReferencedByLeafRef(final SchemaPath schemaPath,
+            final LeafRefContext root) {
+        return findAllChildsReferencedByLeafRef(schemaPath.getPathFromRoot(), root);
+    }
+
+    public static List<LeafRefContext> findAllChildsReferencedByLeafRef(final Iterable<QName> pathFromRoot,
+            final LeafRefContext root) {
+
+        final LeafRefContext leafRefReferencedByContext = getLeafRefReferencedByContext(pathFromRoot, root);
+        final List<LeafRefContext> allChildsReferencedByLeafRef =
+                findAllChildsReferencedByLeafRef(leafRefReferencedByContext);
+
+        return allChildsReferencedByLeafRef;
+    }
+
+    public static List<LeafRefContext> findAllChildsReferencedByLeafRef(final LeafRefContext parent) {
+        final LinkedList<LeafRefContext> childsReferencedByLeafRef = new LinkedList<>();
+        if (parent == null) {
+            return childsReferencedByLeafRef;
+        }
+
+        if (parent.isReferenced()) {
+            childsReferencedByLeafRef.add(parent);
+            return childsReferencedByLeafRef;
+        }
+
+        final Set<Entry<QName, LeafRefContext>> childs = parent.getReferencedByChilds().entrySet();
+        for (final Entry<QName, LeafRefContext> child : childs) {
+            childsReferencedByLeafRef.addAll(findAllChildsReferencedByLeafRef(child.getValue()));
+        }
+        return childsReferencedByLeafRef;
+    }
+
+    public static Map<QName, LeafRefContext> getAllLeafRefsReferencingThisNode(
+            final SchemaNode node, final LeafRefContext root) {
+        return getAllLeafRefsReferencingThisNode(node.getPath(), root);
+    }
+
+    public static Map<QName, LeafRefContext> getAllLeafRefsReferencingThisNode(final SchemaPath path,
+            final LeafRefContext root) {
+        return getAllLeafRefsReferencingThisNode(path.getPathFromRoot(), root);
+    }
+
+    public static Map<QName, LeafRefContext> getAllLeafRefsReferencingThisNode(final Iterable<QName> pathFromRoot,
+            final LeafRefContext root) {
+
+        final LeafRefContext leafRefReferencedByContext = getLeafRefReferencedByContext(pathFromRoot, root);
+        if (leafRefReferencedByContext == null) {
+            return new HashMap<>();
+        }
+
+        return leafRefReferencedByContext.getAllReferencedByLeafRefCtxs();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefDataValidationFailedException.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefDataValidationFailedException.java
new file mode 100644 (file)
index 0000000..79465e1
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+public class LeafRefDataValidationFailedException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+
+    private final int errorsCount;
+
+    public LeafRefDataValidationFailedException(final String message, final int errorsCount) {
+        super(message);
+        this.errorsCount = errorsCount;
+    }
+
+    public LeafRefDataValidationFailedException(final String message) {
+        this(message, 1);
+    }
+
+    public LeafRefDataValidationFailedException(final String message, final Throwable cause) {
+        super(message, cause);
+        errorsCount = 1;
+    }
+
+    public int getValidationsErrorsCount() {
+        return errorsCount;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPath.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPath.java
new file mode 100644 (file)
index 0000000..1b475e4
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+public abstract class LeafRefPath implements Immutable {
+    /**
+     * An absolute LeafRefPath.
+     */
+    private static final class AbsoluteLeafRefPath extends LeafRefPath {
+        private AbsoluteLeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
+            super(parent, qname);
+        }
+
+        @Override
+        public boolean isAbsolute() {
+            return true;
+        }
+
+        @Override
+        protected LeafRefPath createInstance(final LeafRefPath newParent, final QNameWithPredicate newQname) {
+            return new AbsoluteLeafRefPath(newParent, newQname);
+        }
+    }
+
+    /**
+     * A relative LeafRefPath.
+     */
+    private static final class RelativeLeafRefPath extends LeafRefPath {
+        private RelativeLeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
+            super(parent, qname);
+        }
+
+        @Override
+        public boolean isAbsolute() {
+            return false;
+        }
+
+        @Override
+        protected LeafRefPath createInstance(final LeafRefPath newParent, final QNameWithPredicate newQname) {
+            return new RelativeLeafRefPath(newParent, newQname);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<LeafRefPath, ImmutableList> LEGACYPATH_UPDATER =
+        AtomicReferenceFieldUpdater.newUpdater(LeafRefPath.class, ImmutableList.class, "legacyPath");
+
+    /**
+     * Shared instance of the conceptual root schema node.
+     */
+    public static final LeafRefPath ROOT = new AbsoluteLeafRefPath(null, null);
+
+    /**
+     * Shared instance of the "same" relative schema node.
+     */
+    public static final LeafRefPath SAME = new RelativeLeafRefPath(null, null);
+
+    /**
+     * Parent path.
+     */
+    private final LeafRefPath parent;
+
+    /**
+     * This component.
+     */
+    private final QNameWithPredicate qname;
+
+    /**
+     * Cached hash code. We can use this since we are immutable.
+     */
+    private final int hash;
+
+    /**
+     * Cached legacy path, filled-in when {@link #getPathFromRoot()} or {@link #getPathTowardsRoot()} is invoked.
+     */
+    private volatile ImmutableList<QNameWithPredicate> legacyPath;
+
+    private ImmutableList<QNameWithPredicate> getLegacyPath() {
+        ImmutableList<QNameWithPredicate> ret = legacyPath;
+        if (ret == null) {
+            ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse();
+            LEGACYPATH_UPDATER.lazySet(this, ret);
+        }
+
+        return ret;
+    }
+
+    protected LeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
+        this.parent = parent;
+        this.qname = qname;
+
+        int hc = Objects.hashCode(parent);
+        if (qname != null) {
+            hc = hc * 31 + qname.hashCode();
+        }
+
+        hash = hc;
+    }
+
+    /**
+     * Constructs new instance of this class with the concrete path.
+     *
+     * @param path list of QNameWithPredicate instances which specifies exact path to the module node
+     * @param absolute boolean value which specifies if the path is absolute or relative
+     * @return A LeafRefPath instance.
+     */
+    public static LeafRefPath create(final Iterable<QNameWithPredicate> path, final boolean absolute) {
+        final LeafRefPath parent = absolute ? ROOT : SAME;
+        return parent.createChild(path);
+    }
+
+    /**
+     * Constructs new instance of this class with the concrete path.
+     *
+     * @param absolute boolean value which specifies if the path is absolute or relative
+     * @param path one or more QNameWithPredicate instances which specifies exact path to the module node
+     * @return A LeafRefPath instance.
+     */
+    public static LeafRefPath create(final boolean absolute, final QNameWithPredicate... path) {
+        return create(Arrays.asList(path), absolute);
+    }
+
+    /**
+     * Create a new instance.
+     *
+     * @param newParent Parent LeafRefPath
+     * @param newQname next path element
+     * @return A new LeafRefPath instance
+     */
+    protected abstract LeafRefPath createInstance(LeafRefPath newParent, QNameWithPredicate newQname);
+
+    /**
+     * Create a child path based on concatenation of this path and a relative path.
+     *
+     * @param relative Relative path
+     * @return A new child path
+     */
+    public LeafRefPath createChild(final Iterable<QNameWithPredicate> relative) {
+        if (Iterables.isEmpty(relative)) {
+            return this;
+        }
+
+        LeafRefPath newParent = this;
+        for (QNameWithPredicate relativeQname : relative) {
+            newParent = newParent.createInstance(newParent, relativeQname);
+        }
+
+        return newParent;
+    }
+
+    /**
+     * Create a child path based on concatenation of this path and a relative path.
+     *
+     * @param relative Relative LeafRefPath
+     * @return A new child path
+     */
+    public LeafRefPath createChild(final LeafRefPath relative) {
+        checkArgument(!relative.isAbsolute(), "Child creation requires relative path");
+
+        LeafRefPath newParent = this;
+        for (QNameWithPredicate relativeQname : relative.getPathFromRoot()) {
+            newParent = newParent.createInstance(newParent, relativeQname);
+        }
+
+        return newParent;
+    }
+
+    /**
+     * Create a child path based on concatenation of this path and additional path elements.
+     *
+     * @param elements Relative LeafRefPath elements
+     * @return A new child path
+     */
+    public LeafRefPath createChild(final QNameWithPredicate... elements) {
+        return createChild(Arrays.asList(elements));
+    }
+
+    /**
+     * Returns the list of nodes which need to be traversed to get from the starting point (root for absolute
+     * LeafRefPaths) to the node represented by this object.
+     *
+     * @return list of {@code qname} instances which represents path from the root to the schema node.
+     */
+    public Iterable<QNameWithPredicate> getPathFromRoot() {
+        return getLegacyPath();
+    }
+
+    /**
+     * Returns the list of nodes which need to be traversed to get from this node to the starting point (root
+     * for absolute LeafRefPaths).
+     *
+     * @return list of {@code qname} instances which represents path from the schema node towards the root.
+     */
+    public Iterable<QNameWithPredicate> getPathTowardsRoot() {
+        return () -> new Iterator<QNameWithPredicate>() {
+            private LeafRefPath current = LeafRefPath.this;
+
+            @Override
+            public boolean hasNext() {
+                return current.parent != null;
+            }
+
+            @Override
+            public QNameWithPredicate next() {
+                if (current.parent == null) {
+                    throw new NoSuchElementException("No more elements available");
+                }
+
+                final QNameWithPredicate ret = current.qname;
+                current = current.parent;
+                return ret;
+            }
+        };
+    }
+
+    /**
+     * Returns the immediate parent LeafRefPath.
+     *
+     * @return Parent path, null if this LeafRefPath is already toplevel.
+     */
+    public LeafRefPath getParent() {
+        return parent;
+    }
+
+    /**
+     * Get the last component of this path.
+     *
+     * @return The last component of this path.
+     */
+    public final QNameWithPredicate getLastComponent() {
+        return qname;
+    }
+
+    /**
+     * Describes whether schema path is|isn't absolute.
+     *
+     * @return boolean value which is {@code true} if schema path is  absolute.
+     */
+    public abstract boolean isAbsolute();
+
+    @Override
+    public final int hashCode() {
+        return hash;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final LeafRefPath other = (LeafRefPath) obj;
+        return Objects.equals(qname, other.qname) && Objects.equals(parent, other.parent);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(isAbsolute() ? "Absolute path:" : "Relative path:");
+
+        for (QNameWithPredicate qnameWithPredicate : getPathFromRoot()) {
+            sb.append('/').append(qnameWithPredicate);
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathErrorListener.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathErrorListener.java
new file mode 100644 (file)
index 0000000..5947649
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class LeafRefPathErrorListener extends BaseErrorListener {
+    private static final Logger LOG = LoggerFactory.getLogger(LeafRefPathErrorListener.class);
+
+    private final List<LeafRefPathSyntaxErrorException> exceptions = new ArrayList<>(1);
+    private final Module module;
+
+    LeafRefPathErrorListener(final Module module) {
+        this.module = module;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public void syntaxError(final Recognizer<?, ?> recognizer, final Object offendingSymbol, final int line,
+            final int charPositionInLine, final String msg, final RecognitionException e) {
+        LOG.debug("Syntax error in module {} at {}:{}: {}", module.getName(), line, charPositionInLine, msg, e);
+
+        exceptions.add(new LeafRefPathSyntaxErrorException(module.getName(), line, charPositionInLine, msg, e));
+    }
+
+    public void validate() throws LeafRefPathSyntaxErrorException {
+        if (exceptions.isEmpty()) {
+            return;
+        }
+
+        // Single exception: just throw it
+        if (exceptions.size() == 1) {
+            throw exceptions.get(0);
+        }
+
+        final StringBuilder sb = new StringBuilder();
+        String exceptionModule = null;
+        boolean first = true;
+        for (final LeafRefPathSyntaxErrorException e : exceptions) {
+            if (exceptionModule == null) {
+                exceptionModule = e.getModule();
+            }
+            if (first) {
+                first = false;
+            } else {
+                sb.append('\n');
+            }
+
+            sb.append(e.getFormattedMessage());
+        }
+
+        throw new LeafRefPathSyntaxErrorException(exceptionModule, 0, 0, sb.toString());
+    }
+
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParserImpl.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParserImpl.java
new file mode 100644 (file)
index 0000000..d781984
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Path_argContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+final class LeafRefPathParserImpl {
+    private final SchemaContext schemaContext;
+    private final Module module;
+    private final SchemaNode node;
+
+    LeafRefPathParserImpl(final SchemaContext schemaContext, final Module currentModule, final SchemaNode currentNode) {
+        this.schemaContext = schemaContext;
+        this.module = currentModule;
+        this.node = currentNode;
+    }
+
+    LeafRefPath parseLeafRefPath(final String path) throws LeafRefYangSyntaxErrorException {
+        final Path_argContext pathCtx = parseLeafRefPathSource(path);
+
+        final ParseTreeWalker walker = new ParseTreeWalker();
+        final LeafRefPathParserListenerImpl leafRefPathParserListenerImpl = new LeafRefPathParserListenerImpl(
+            schemaContext, module, node);
+        walker.walk(leafRefPathParserListenerImpl, pathCtx);
+
+        return leafRefPathParserListenerImpl.getLeafRefPath();
+    }
+
+    private Path_argContext parseLeafRefPathSource(final String path) throws LeafRefYangSyntaxErrorException {
+        final LeafRefPathLexer lexer = new LeafRefPathLexer(CharStreams.fromString(path));
+        final LeafRefPathParser parser = new LeafRefPathParser(new CommonTokenStream(lexer));
+
+        final LeafRefPathErrorListener errorListener = new LeafRefPathErrorListener(module);
+        lexer.removeErrorListeners();
+        lexer.addErrorListener(errorListener);
+        parser.removeErrorListeners();
+        parser.addErrorListener(errorListener);
+
+        final Path_argContext result = parser.path_arg();
+        errorListener.validate();
+        return result;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParserListenerImpl.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathParserListenerImpl.java
new file mode 100644 (file)
index 0000000..078abd2
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import org.antlr.v4.runtime.tree.TerminalNode;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.IdentifierContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Node_identifierContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Path_argContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Path_equality_exprContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Path_predicateContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.PrefixContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Rel_path_keyexprContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Relative_pathContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+final class LeafRefPathParserListenerImpl extends LeafRefPathParserBaseListener {
+
+    private final List<QNameWithPredicateBuilder> leafRefPathQnameList = new ArrayList<>();
+    private final SchemaContext schemaContext;
+    private final Module module;
+    private final Module leafrefModule;
+    // FIXME: use for identifier path completion
+    private final SchemaNode node;
+
+    private ParsingState currentParsingState = ParsingState.LEAF_REF_PATH;
+    private List<QNameWithPredicateBuilder> predicatePathKeyQnameList;
+    private QNameWithPredicateBuilder currentLeafRefPathQName;
+    private QNamePredicateBuilder currentPredicate;
+    private QNameModule currentQnameModule;
+    private String currentQNameLocalName;
+    private LeafRefPath leafRefPath;
+    private boolean relativePath = false;
+
+    private enum ParsingState {
+        LEAF_REF_PATH, PATH_PREDICATE, PREDICATE_PATH_EQUALITY_EXPR, PATH_KEY_EXPR
+    }
+
+    LeafRefPathParserListenerImpl(final SchemaContext schemaContext, final Module leafrefModule,
+            final SchemaNode currentNode) {
+        this.schemaContext = schemaContext;
+        this.module = schemaContext.findModule(currentNode.getQName().getModule()).get();
+        this.leafrefModule = leafrefModule;
+        this.node = currentNode;
+    }
+
+    @Override
+    public void enterPath_predicate(final Path_predicateContext ctx) {
+        currentParsingState = ParsingState.PATH_PREDICATE;
+        currentPredicate = new QNamePredicateBuilder();
+    }
+
+    @Override
+    public void exitPath_predicate(final Path_predicateContext ctx) {
+        currentLeafRefPathQName.addQNamePredicate(currentPredicate.build());
+        currentPredicate = null;
+        currentParsingState = ParsingState.LEAF_REF_PATH;
+    }
+
+
+    @Override
+    public void enterRel_path_keyexpr(final Rel_path_keyexprContext ctx) {
+        currentParsingState = ParsingState.PATH_KEY_EXPR;
+
+        final List<TerminalNode> dots = ctx.DOTS();
+        predicatePathKeyQnameList = new ArrayList<>(dots.size());
+        for (int i = 0; i < dots.size(); ++i) {
+            predicatePathKeyQnameList.add(QNameWithPredicateBuilder.UP_PARENT_BUILDER);
+        }
+    }
+
+    @Override
+    public void exitRel_path_keyexpr(final Rel_path_keyexprContext ctx) {
+        final LeafRefPath pathKeyExpression = LeafRefPath.create(Lists.transform(predicatePathKeyQnameList,
+            QNameWithPredicateBuilder::build), false);
+        currentPredicate.setPathKeyExpression(pathKeyExpression);
+
+        currentParsingState = ParsingState.PREDICATE_PATH_EQUALITY_EXPR;
+    }
+
+    @Override
+    public void enterRelative_path(final Relative_pathContext ctx) {
+        relativePath = true;
+        final List<TerminalNode> dots = ctx.DOTS();
+        for (int i = 0; i < dots.size(); ++i) {
+            leafRefPathQnameList.add(QNameWithPredicateBuilder.UP_PARENT_BUILDER);
+        }
+    }
+
+    @Override
+    public void enterPath_equality_expr(final Path_equality_exprContext ctx) {
+        currentParsingState = ParsingState.PREDICATE_PATH_EQUALITY_EXPR;
+    }
+
+    @Override
+    public void exitPath_equality_expr(final Path_equality_exprContext ctx) {
+        currentParsingState = ParsingState.PATH_PREDICATE;
+    }
+
+    @Override
+    public void enterPrefix(final PrefixContext ctx) {
+        final String prefix = ctx.getText();
+        if (!leafrefModule.getPrefix().equals(prefix)) {
+            final Optional<QNameModule> qnameModuleOpt = getQNameModuleForImportPrefix(leafrefModule, prefix);
+            checkArgument(qnameModuleOpt.isPresent(), "No module import for prefix: %s in module: %s", prefix,
+                leafrefModule.getName());
+            currentQnameModule = qnameModuleOpt.get();
+        } else {
+            currentQnameModule = leafrefModule.getQNameModule();
+        }
+    }
+
+    @Override
+    public void exitPath_arg(final Path_argContext ctx) {
+        leafRefPath = LeafRefPath.create(Lists.transform(leafRefPathQnameList, QNameWithPredicateBuilder::build),
+            !relativePath);
+    }
+
+    @Override
+    public void enterIdentifier(final IdentifierContext ctx) {
+        currentQNameLocalName = ctx.getText();
+    }
+
+    @Override
+    public void exitNode_identifier(final Node_identifierContext ctx) {
+        if (currentQnameModule == null) {
+            if (relativePath) {
+                currentQnameModule = module.getQNameModule();
+            } else {
+                currentQnameModule = leafrefModule.getQNameModule();
+            }
+        }
+
+        if (currentParsingState == ParsingState.PREDICATE_PATH_EQUALITY_EXPR) {
+            currentPredicate.setIdentifier(QName.create(currentQnameModule, currentQNameLocalName));
+        } else {
+            final QNameWithPredicateBuilder qnameBuilder = new QNameWithPredicateBuilder(currentQnameModule,
+                currentQNameLocalName);
+
+            if (currentParsingState == ParsingState.PATH_KEY_EXPR) {
+                predicatePathKeyQnameList.add(qnameBuilder);
+            } else if (currentParsingState == ParsingState.LEAF_REF_PATH) {
+                currentLeafRefPathQName = qnameBuilder;
+                leafRefPathQnameList.add(qnameBuilder);
+            }
+        }
+        currentQnameModule = null;
+        currentQNameLocalName = null;
+    }
+
+    public LeafRefPath getLeafRefPath() {
+        return leafRefPath;
+    }
+
+    private Optional<QNameModule> getQNameModuleForImportPrefix(final Module targetModule, final String prefix) {
+        final ModuleImport moduleImport = getModuleImport(targetModule, prefix);
+        if (moduleImport == null) {
+            return Optional.empty();
+        }
+
+        final String moduleName = moduleImport.getModuleName();
+        final Optional<Revision> revision = moduleImport.getRevision();
+        return schemaContext.findModule(moduleName, revision).map(Module::getQNameModule);
+    }
+
+    private static ModuleImport getModuleImport(final Module targetModule, final String prefix) {
+        return targetModule.getImports().stream()
+            .filter(imp -> prefix.equals(imp.getPrefix())).findFirst().orElse(null);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathSyntaxErrorException.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefPathSyntaxErrorException.java
new file mode 100644 (file)
index 0000000..6f2450a
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+public class LeafRefPathSyntaxErrorException extends LeafRefYangSyntaxErrorException {
+
+    private static final long serialVersionUID = 1L;
+
+    public LeafRefPathSyntaxErrorException(final String module, final int line, final int charPositionInLine,
+            final String message) {
+        super(module, line, charPositionInLine, message, null);
+    }
+
+    public LeafRefPathSyntaxErrorException(final String module, final int line, final int charPositionInLine,
+            final String message, final Throwable cause) {
+        super(module,line,charPositionInLine,message,cause);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefUtils.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefUtils.java
new file mode 100644 (file)
index 0000000..6d0d22e
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.LinkedList;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public final class LeafRefUtils {
+    private LeafRefUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Create an absolute leafref path.
+     *
+     * @param leafRefPath leafRefPath
+     * @param contextNodeSchemaPath contextNodeSchemaPath
+     * @param module module
+     * @return LeafRefPath object
+     */
+    public static LeafRefPath createAbsoluteLeafRefPath(
+            final LeafRefPath leafRefPath, final SchemaPath contextNodeSchemaPath,
+            final Module module) {
+        if (leafRefPath.isAbsolute()) {
+            return leafRefPath;
+        }
+
+        final Deque<QNameWithPredicate> absoluteLeafRefTargetPathList = schemaPathToXPathQNames(
+                contextNodeSchemaPath, module);
+        final Iterator<QNameWithPredicate> leafRefTgtPathFromRootIterator = leafRefPath.getPathFromRoot().iterator();
+
+        while (leafRefTgtPathFromRootIterator.hasNext()) {
+            final QNameWithPredicate qname = leafRefTgtPathFromRootIterator.next();
+            if (qname.equals(QNameWithPredicate.UP_PARENT)) {
+                absoluteLeafRefTargetPathList.removeLast();
+            } else {
+                absoluteLeafRefTargetPathList.add(qname);
+            }
+        }
+
+        return LeafRefPath.create(absoluteLeafRefTargetPathList, true);
+    }
+
+    private static Deque<QNameWithPredicate> schemaPathToXPathQNames(final SchemaPath nodePath, final Module module) {
+        final Deque<QNameWithPredicate> xpath = new LinkedList<>();
+        final Iterator<QName> nodePathIterator = nodePath.getPathFromRoot().iterator();
+
+        DataNodeContainer currenDataNodeContainer = module;
+        while (nodePathIterator.hasNext()) {
+            final QName qname = nodePathIterator.next();
+            final DataSchemaNode child = currenDataNodeContainer.getDataChildByName(qname);
+
+            if (child instanceof DataNodeContainer) {
+                if (!(child instanceof CaseSchemaNode)) {
+                    xpath.add(new SimpleQNameWithPredicate(qname));
+                }
+                currenDataNodeContainer = (DataNodeContainer) child;
+            } else if (child instanceof ChoiceSchemaNode) {
+                if (nodePathIterator.hasNext()) {
+                    currenDataNodeContainer = ((ChoiceSchemaNode) child).getCases().get(nodePathIterator.next());
+                } else {
+                    break;
+                }
+            } else if (child instanceof LeafSchemaNode || child instanceof LeafListSchemaNode) {
+                xpath.add(new SimpleQNameWithPredicate(qname));
+                break;
+            } else if (child == null) {
+                throw new IllegalArgumentException("No child " + qname + " found in node container "
+                        + currenDataNodeContainer + " in module " + module.getName());
+            } else {
+                throw new IllegalStateException("Illegal schema node type in the path: " + child.getClass());
+            }
+        }
+
+        return xpath;
+    }
+
+    public static LeafRefPath schemaPathToLeafRefPath(final SchemaPath nodePath, final Module module) {
+        return LeafRefPath.create(schemaPathToXPathQNames(nodePath, module), true);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefValidation.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefValidation.java
new file mode 100644 (file)
index 0000000..5a1ce76
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.opendaylight.yangtools.yang.common.QName;
+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.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+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.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ValueNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class LeafRefValidation {
+    private static final Logger LOG = LoggerFactory.getLogger(LeafRefValidation.class);
+    private static final String FAILED = " -> FAILED";
+    private static final String SUCCESS = " -> OK";
+
+    private final Set<LeafRefContext> validatedLeafRefCtx = new HashSet<>();
+    private final List<String> errorsMessages = new ArrayList<>();
+    private final NormalizedNode<?, ?> root;
+
+    private LeafRefValidation(final NormalizedNode<?, ?> root) {
+        this.root = root;
+    }
+
+    public static void validate(final DataTreeCandidate tree, final LeafRefContext rootLeafRefCtx)
+            throws LeafRefDataValidationFailedException {
+        final Optional<NormalizedNode<?, ?>> root = tree.getRootNode().getDataAfter();
+        if (root.isPresent()) {
+            new LeafRefValidation(root.get()).validateChildren(rootLeafRefCtx, tree.getRootNode().getChildNodes());
+        }
+    }
+
+    private void validateChildren(final LeafRefContext rootLeafRefCtx, final Collection<DataTreeCandidateNode> children)
+            throws LeafRefDataValidationFailedException {
+        for (final DataTreeCandidateNode dataTreeCandidateNode : children) {
+            if (dataTreeCandidateNode.getModificationType() != ModificationType.UNMODIFIED) {
+                final PathArgument identifier = dataTreeCandidateNode.getIdentifier();
+                final QName childQName = identifier.getNodeType();
+
+                final LeafRefContext referencedByCtx = rootLeafRefCtx.getReferencedChildByName(childQName);
+                final LeafRefContext referencingCtx = rootLeafRefCtx.getReferencingChildByName(childQName);
+                if (referencedByCtx != null || referencingCtx != null) {
+                    validateNode(dataTreeCandidateNode, referencedByCtx, referencingCtx,
+                        YangInstanceIdentifier.create(identifier));
+                }
+            }
+        }
+
+        if (!errorsMessages.isEmpty()) {
+            final StringBuilder message = new StringBuilder();
+            int errCount = 0;
+            for (final String errorMessage : errorsMessages) {
+                message.append(errorMessage);
+                errCount++;
+            }
+            throw new LeafRefDataValidationFailedException(message.toString(), errCount);
+        }
+    }
+
+    private void validateNode(final DataTreeCandidateNode node, final LeafRefContext referencedByCtx,
+        final LeafRefContext referencingCtx, final YangInstanceIdentifier current) {
+
+        if (node.getModificationType() == ModificationType.WRITE && node.getDataAfter().isPresent()) {
+            validateNodeData(node.getDataAfter().get(), referencedByCtx, referencingCtx, node.getModificationType(),
+                current);
+            return;
+        }
+
+        if (node.getModificationType() == ModificationType.DELETE && referencedByCtx != null) {
+            validateNodeData(node.getDataBefore().get(), referencedByCtx, null, node.getModificationType(), current);
+            return;
+        }
+
+        for (final DataTreeCandidateNode childNode : node.getChildNodes()) {
+            if (childNode.getModificationType() != ModificationType.UNMODIFIED) {
+                final LeafRefContext childReferencedByCtx = getReferencedByCtxChild(referencedByCtx, childNode);
+                final LeafRefContext childReferencingCtx = getReferencingCtxChild(referencingCtx, childNode);
+
+                if (childReferencedByCtx != null || childReferencingCtx != null) {
+                    validateNode(childNode, childReferencedByCtx,childReferencingCtx,
+                        current.node(childNode.getIdentifier()));
+                }
+            }
+        }
+    }
+
+    private static LeafRefContext getReferencingCtxChild(final LeafRefContext referencingCtx,
+            final DataTreeCandidateNode childNode) {
+        if (referencingCtx == null) {
+            return null;
+        }
+
+        final QName childQName = childNode.getIdentifier().getNodeType();
+        LeafRefContext childReferencingCtx = referencingCtx.getReferencingChildByName(childQName);
+        if (childReferencingCtx == null) {
+            final NormalizedNode<?, ?> data = childNode.getDataAfter().get();
+            if (data instanceof MapEntryNode || data instanceof UnkeyedListEntryNode) {
+                childReferencingCtx = referencingCtx;
+            }
+        }
+
+        return childReferencingCtx;
+    }
+
+    private static LeafRefContext getReferencedByCtxChild(final LeafRefContext referencedByCtx,
+            final DataTreeCandidateNode childNode) {
+        if (referencedByCtx == null) {
+            return null;
+        }
+
+        final QName childQName = childNode.getIdentifier().getNodeType();
+        LeafRefContext childReferencedByCtx = referencedByCtx.getReferencedChildByName(childQName);
+        if (childReferencedByCtx == null) {
+            final NormalizedNode<?, ?> data = childNode.getDataAfter().get();
+            if (data instanceof MapEntryNode || data instanceof UnkeyedListEntryNode) {
+                childReferencedByCtx = referencedByCtx;
+            }
+        }
+
+        return childReferencedByCtx;
+    }
+
+    private void validateNodeData(final NormalizedNode<?, ?> node, final LeafRefContext referencedByCtx,
+            final LeafRefContext referencingCtx, final ModificationType modificationType,
+            final YangInstanceIdentifier current) {
+        if (node instanceof LeafNode) {
+            validateLeafNodeData((LeafNode<?>) node, referencedByCtx, referencingCtx, modificationType, current);
+        } else if (node instanceof LeafSetNode) {
+            validateLeafSetNodeData((LeafSetNode<?>) node, referencedByCtx, referencingCtx, modificationType, current);
+        } else if (node instanceof ChoiceNode) {
+            validateChoiceNodeData((ChoiceNode) node, referencedByCtx, referencingCtx, modificationType, current);
+        } else if (node instanceof DataContainerNode) {
+            validateDataContainerNodeData((DataContainerNode<?>) node, referencedByCtx, referencingCtx,
+                modificationType, current);
+        } else if (node instanceof MapNode) {
+            validateMapNodeData((MapNode) node, referencedByCtx, referencingCtx, modificationType, current);
+        }
+        // FIXME: check UnkeyedListNode case
+    }
+
+    private void validateLeafNodeData(final LeafNode<?> node, final LeafRefContext referencedByCtx,
+            final LeafRefContext referencingCtx, final ModificationType modificationType,
+            final YangInstanceIdentifier current) {
+        if (referencedByCtx != null && referencedByCtx.isReferenced()) {
+            validateLeafRefTargetNodeData(node, referencedByCtx, modificationType);
+        }
+        if (referencingCtx != null && referencingCtx.isReferencing()) {
+            validateLeafRefNodeData(node, referencingCtx, modificationType, current);
+        }
+    }
+
+    private void validateLeafSetNodeData(final LeafSetNode<?> node, final LeafRefContext referencedByCtx,
+            final LeafRefContext referencingCtx, final ModificationType modificationType,
+            final YangInstanceIdentifier current) {
+        if (referencedByCtx != null || referencingCtx != null) {
+            for (final NormalizedNode<?, ?> leafSetEntry : node.getValue()) {
+                if (referencedByCtx != null && referencedByCtx.isReferenced()) {
+                    validateLeafRefTargetNodeData(leafSetEntry, referencedByCtx, modificationType);
+                }
+                if (referencingCtx != null && referencingCtx.isReferencing()) {
+                    validateLeafRefNodeData(leafSetEntry, referencingCtx, modificationType, current);
+                }
+            }
+        }
+    }
+
+    private void validateChoiceNodeData(final ChoiceNode node, final LeafRefContext referencedByCtx,
+            final LeafRefContext referencingCtx, final ModificationType modificationType,
+            final YangInstanceIdentifier current) {
+        for (final DataContainerChild<?, ?> child : node.getValue()) {
+            final QName qname = child.getNodeType();
+            final LeafRefContext childReferencedByCtx = referencedByCtx == null ? null
+                    : findReferencedByCtxUnderChoice(referencedByCtx, qname);
+            final LeafRefContext childReferencingCtx = referencingCtx == null ? null
+                    : findReferencingCtxUnderChoice(referencingCtx, qname);
+            if (childReferencedByCtx != null || childReferencingCtx != null) {
+                validateNodeData(child, childReferencedByCtx, childReferencingCtx, modificationType,
+                    current.node(child.getIdentifier()));
+            }
+        }
+    }
+
+    private void validateDataContainerNodeData(final DataContainerNode<?> node, final LeafRefContext referencedByCtx,
+            final LeafRefContext referencingCtx, final ModificationType modificationType,
+            final YangInstanceIdentifier current) {
+        for (final DataContainerChild<?, ?> child : node.getValue()) {
+            if (child instanceof AugmentationNode) {
+                validateNodeData(child, referencedByCtx, referencingCtx, modificationType, current.node(
+                    child.getIdentifier()));
+                return;
+            }
+
+            validateChildNodeData(child, referencedByCtx, referencingCtx, modificationType, current);
+        }
+    }
+
+    private void validateMapNodeData(final MapNode node, final LeafRefContext referencedByCtx,
+            final LeafRefContext referencingCtx, final ModificationType modificationType,
+            final YangInstanceIdentifier current) {
+        for (final MapEntryNode mapEntry : node.getValue()) {
+            final YangInstanceIdentifier mapEntryIdentifier = current.node(mapEntry.getIdentifier());
+            for (final DataContainerChild<?, ?> child : mapEntry.getValue()) {
+                if (child instanceof AugmentationNode) {
+                    validateNodeData(child, referencedByCtx, referencingCtx, modificationType, current.node(
+                        child.getIdentifier()));
+                    return;
+                }
+
+                validateChildNodeData(child, referencedByCtx, referencingCtx, modificationType, mapEntryIdentifier);
+            }
+        }
+    }
+
+    private void validateChildNodeData(final DataContainerChild<?, ?> child, final LeafRefContext referencedByCtx,
+            final LeafRefContext referencingCtx, final ModificationType modificationType,
+            final YangInstanceIdentifier current) {
+        final QName qname = child.getNodeType();
+        final LeafRefContext childReferencedByCtx = referencedByCtx == null ? null
+                : referencedByCtx.getReferencedChildByName(qname);
+        final LeafRefContext childReferencingCtx = referencingCtx == null ? null
+                : referencingCtx.getReferencingChildByName(qname);
+        if (childReferencedByCtx != null || childReferencingCtx != null) {
+            validateNodeData(child, childReferencedByCtx, childReferencingCtx, modificationType, current.node(
+                child.getIdentifier()));
+        }
+    }
+
+    private static LeafRefContext findReferencingCtxUnderChoice(final LeafRefContext referencingCtx,
+            final QName qname) {
+        for (final LeafRefContext child : referencingCtx.getReferencingChilds().values()) {
+            final LeafRefContext referencingChildByName = child.getReferencingChildByName(qname);
+            if (referencingChildByName != null) {
+                return referencingChildByName;
+            }
+        }
+        return null;
+    }
+
+    private static LeafRefContext findReferencedByCtxUnderChoice(final LeafRefContext referencedByCtx,
+            final QName qname) {
+        for (final LeafRefContext child : referencedByCtx.getReferencedByChilds().values()) {
+            final LeafRefContext referencedByChildByName = child.getReferencedChildByName(qname);
+            if (referencedByChildByName != null) {
+                return referencedByChildByName;
+            }
+        }
+        return null;
+    }
+
+    private void validateLeafRefTargetNodeData(final NormalizedNode<?, ?> leaf, final LeafRefContext
+            referencedByCtx, final ModificationType modificationType) {
+        if (!validatedLeafRefCtx.add(referencedByCtx)) {
+            LOG.trace("Operation [{}] validate data of leafref TARGET node: name[{}] = value[{}] -> SKIP: Already "
+                    + "validated", modificationType, referencedByCtx.getNodeName(), leaf.getValue());
+            return;
+        }
+
+        LOG.trace("Operation [{}] validate data of leafref TARGET node: name[{}] = value[{}]", modificationType,
+            referencedByCtx.getNodeName(), leaf.getValue());
+        final Set<LeafRefContext> leafRefs = referencedByCtx.getAllReferencedByLeafRefCtxs().values().stream()
+                .filter(LeafRefContext::isReferencing).collect(Collectors.toSet());
+        if (leafRefs.isEmpty()) {
+            return;
+        }
+
+        final Set<Object> leafRefTargetNodeValues = extractRootValues(referencedByCtx);
+        leafRefs.forEach(leafRefContext -> {
+            extractRootValues(leafRefContext).forEach(leafRefsValue -> {
+                if (leafRefTargetNodeValues.contains(leafRefsValue)) {
+                    LOG.trace("Valid leafref value [{}] {}", leafRefsValue, SUCCESS);
+                    return;
+                }
+
+                LOG.debug("Invalid leafref value [{}] allowed values {} by validation of leafref TARGET node: {} path "
+                        + "of invalid LEAFREF node: {} leafRef target path: {} {}", leafRefsValue,
+                        leafRefTargetNodeValues, leaf.getNodeType(), leafRefContext.getCurrentNodePath(),
+                        leafRefContext.getAbsoluteLeafRefTargetPath(), FAILED);
+                errorsMessages.add(String.format("Invalid leafref value [%s] allowed values %s by validation of leafref"
+                        + " TARGET node: %s path of invalid LEAFREF node: %s leafRef target path: %s %s", leafRefsValue,
+                        leafRefTargetNodeValues, leaf.getNodeType(), leafRefContext.getCurrentNodePath(),
+                        leafRefContext.getAbsoluteLeafRefTargetPath(),
+                        FAILED));
+            });
+        });
+    }
+
+    private Set<Object> extractRootValues(final LeafRefContext context) {
+        return computeValues(root, createPath(context.getLeafRefNodePath()), null);
+    }
+
+    private void validateLeafRefNodeData(final NormalizedNode<?, ?> leaf, final LeafRefContext referencingCtx,
+            final ModificationType modificationType, final YangInstanceIdentifier current) {
+        final Set<Object> values = computeValues(root, createPath(referencingCtx.getAbsoluteLeafRefTargetPath()),
+            current);
+        if (values.contains(leaf.getValue())) {
+            LOG.debug("Operation [{}] validate data of LEAFREF node: name[{}] = value[{}] {}", modificationType,
+                referencingCtx.getNodeName(), leaf.getValue(), SUCCESS);
+            return;
+        }
+
+        LOG.debug("Operation [{}] validate data of LEAFREF node: name[{}] = value[{}] {}", modificationType,
+            referencingCtx.getNodeName(), leaf.getValue(), FAILED);
+        LOG.debug("Invalid leafref value [{}] allowed values {} of LEAFREF node: {} leafRef target path: {}",
+            leaf.getValue(), values, leaf.getNodeType(), referencingCtx.getAbsoluteLeafRefTargetPath());
+        errorsMessages.add(String.format("Invalid leafref value [%s] allowed values %s of LEAFREF node: %s leafRef "
+                + "target path: %s", leaf.getValue(), values, leaf.getNodeType(),
+                referencingCtx.getAbsoluteLeafRefTargetPath()));
+    }
+
+    private Set<Object> computeValues(final NormalizedNode<?, ?> node, final Deque<QNameWithPredicate> path,
+            final YangInstanceIdentifier current) {
+        final HashSet<Object> values = new HashSet<>();
+        addValues(values, node, ImmutableList.of(), path, current);
+        return values;
+    }
+
+    private void addValues(final Set<Object> values, final NormalizedNode<?, ?> node,
+            final List<QNamePredicate> nodePredicates, final Deque<QNameWithPredicate> path,
+            final YangInstanceIdentifier current) {
+        if (node instanceof ValueNode) {
+            values.add(node.getValue());
+            return;
+        }
+        if (node instanceof LeafSetNode<?>) {
+            for (final NormalizedNode<?, ?> entry : ((LeafSetNode<?>) node).getValue()) {
+                values.add(entry.getValue());
+            }
+            return;
+        }
+
+        final QNameWithPredicate next = path.peek();
+        if (next == null) {
+            return;
+        }
+
+        final PathArgument pathArgument = new NodeIdentifier(next.getQName());
+        if (node instanceof DataContainerNode) {
+            processChildNode(values, (DataContainerNode<?>) node, pathArgument, next.getQNamePredicates(), path,
+                current);
+        } else if (node instanceof MapNode) {
+            Stream<MapEntryNode> entries = ((MapNode) node).getValue().stream();
+            if (!nodePredicates.isEmpty() && current != null) {
+                entries = entries.filter(createMapEntryPredicate(nodePredicates, current));
+            }
+
+            entries.forEach(entry -> processChildNode(values, entry, pathArgument, next.getQNamePredicates(), path,
+                current));
+        }
+    }
+
+    private void processChildNode(final Set<Object> values, final DataContainerNode<?> parent,
+            final PathArgument arg, final List<QNamePredicate> nodePredicates, final Deque<QNameWithPredicate> path,
+            final YangInstanceIdentifier current) {
+        final Optional<DataContainerChild<?, ?>> child = parent.getChild(arg);
+        if (!child.isPresent()) {
+            // FIXME: YANGTOOLS-901. We have SchemaContext nearby, hence we should be able to cache how to get
+            //        to the leaf with with specified QName, without having to iterate through Choices/Augmentations.
+            //        That perhaps means we should not have QNameWithPredicates, but NodeIdentifierWithPredicates as
+            //        the path specification.
+            for (final DataContainerChild<?, ?> mixin : parent.getValue()) {
+                if (mixin instanceof AugmentationNode || mixin instanceof ChoiceNode) {
+                    addValues(values, mixin, nodePredicates, path, current);
+                }
+            }
+        } else {
+            addNextValues(values, child.get(), nodePredicates, path, current);
+        }
+    }
+
+    private Predicate<MapEntryNode> createMapEntryPredicate(final List<QNamePredicate> nodePredicates,
+            final YangInstanceIdentifier current) {
+        final Map<QName, Set<?>> keyValues = new HashMap<>();
+        for (QNamePredicate predicate : nodePredicates) {
+            keyValues.put(predicate.getIdentifier(), getPathKeyExpressionValues(predicate.getPathKeyExpression(),
+                current));
+        }
+
+        return mapEntry -> {
+            for (final Entry<QName, Object> entryKeyValue : mapEntry.getIdentifier().entrySet()) {
+                final Set<?> allowedValues = keyValues.get(entryKeyValue.getKey());
+                if (allowedValues != null && !allowedValues.contains(entryKeyValue.getValue())) {
+                    return false;
+                }
+            }
+            return true;
+        };
+    }
+
+    private void addNextValues(final Set<Object> values, final NormalizedNode<?, ?> node,
+            final List<QNamePredicate> nodePredicates, final Deque<QNameWithPredicate> path,
+            final YangInstanceIdentifier current) {
+        final QNameWithPredicate element = path.pop();
+        try {
+            addValues(values, node, nodePredicates, path, current);
+        } finally {
+            path.push(element);
+        }
+    }
+
+    private Set<?> getPathKeyExpressionValues(final LeafRefPath predicatePathKeyExpression,
+            final YangInstanceIdentifier current) {
+        return findParentNode(Optional.of(root), current).map(parent -> {
+            final Deque<QNameWithPredicate> path = createPath(predicatePathKeyExpression);
+            path.pollFirst();
+            return computeValues(parent, path, null);
+        }).orElse(ImmutableSet.of());
+    }
+
+    private static Optional<NormalizedNode<?, ?>> findParentNode(
+            final Optional<NormalizedNode<?, ?>> root, final YangInstanceIdentifier path) {
+        Optional<NormalizedNode<?, ?>> currentNode = root;
+        final Iterator<PathArgument> pathIterator = path.getPathArguments().iterator();
+        while (pathIterator.hasNext()) {
+            final PathArgument childPathArgument = pathIterator.next();
+            if (pathIterator.hasNext() && currentNode.isPresent()) {
+                currentNode = NormalizedNodes.getDirectChild(currentNode.get(), childPathArgument);
+            } else {
+                return currentNode;
+            }
+        }
+        return Optional.empty();
+    }
+
+    private static Deque<QNameWithPredicate> createPath(final LeafRefPath path) {
+        final Deque<QNameWithPredicate> ret = new ArrayDeque<>();
+        path.getPathTowardsRoot().forEach(ret::push);
+        return ret;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefYangSyntaxErrorException.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/LeafRefYangSyntaxErrorException.java
new file mode 100644 (file)
index 0000000..5177852
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.leafref;
+
+import static java.util.Objects.requireNonNull;
+
+public class LeafRefYangSyntaxErrorException extends Exception {
+    private static final long serialVersionUID = 1L;
+    private final String module;
+    private final int line;
+    private final int charPositionInLine;
+
+    public LeafRefYangSyntaxErrorException(final String module, final int line, final int charPositionInLine,
+            final String message) {
+        this(module, line, charPositionInLine, message, null);
+    }
+
+    public LeafRefYangSyntaxErrorException(final String module, final int line, final int charPositionInLine,
+            final String message, final Throwable cause) {
+        super(requireNonNull(message), cause);
+        this.module = module;
+        this.line = line;
+        this.charPositionInLine = charPositionInLine;
+    }
+
+    public String getModule() {
+        return module;
+    }
+
+    public int getLine() {
+        return line;
+    }
+
+    public int getCharPositionInLine() {
+        return charPositionInLine;
+    }
+
+    public String getFormattedMessage() {
+        final StringBuilder sb = new StringBuilder(getMessage());
+        if (module != null) {
+            sb.append(" in module ");
+            sb.append(module);
+        }
+        if (line != 0) {
+            sb.append(" on line ");
+            sb.append(line);
+            if (charPositionInLine != 0) {
+                sb.append(" character ");
+                sb.append(charPositionInLine);
+            }
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getName() + ": " + getFormattedMessage();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNamePredicate.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNamePredicate.java
new file mode 100644 (file)
index 0000000..a019bc9
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface QNamePredicate extends Identifiable<QName> {
+
+    LeafRefPath getPathKeyExpression();
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNamePredicateBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNamePredicateBuilder.java
new file mode 100644 (file)
index 0000000..6d5f859
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.common.QName;
+
+class QNamePredicateBuilder implements Builder<QNamePredicate> {
+
+    private QName identifier;
+    private LeafRefPath pathKeyExpression;
+
+    QNamePredicateBuilder() {
+    }
+
+    QNamePredicateBuilder(final QName identifier, final LeafRefPath pathKeyExpression) {
+        this.identifier = identifier;
+        this.pathKeyExpression = pathKeyExpression;
+    }
+
+    public QName getIdentifier() {
+        return identifier;
+    }
+
+    public void setIdentifier(final QName identifier) {
+        this.identifier = identifier;
+    }
+
+    public LeafRefPath getPathKeyExpression() {
+        return pathKeyExpression;
+    }
+
+    public void setPathKeyExpression(final LeafRefPath pathKeyExpression) {
+        this.pathKeyExpression = pathKeyExpression;
+    }
+
+    @Override
+    public QNamePredicate build() {
+        return new QNamePredicateImpl(identifier, pathKeyExpression);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append('[');
+
+        sb.append(identifier);
+        sb.append("=current()");
+
+        for (final QNameWithPredicate qname : pathKeyExpression.getPathFromRoot()) {
+            sb.append('/').append(qname);
+        }
+
+        sb.append(']');
+        return sb.toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNamePredicateImpl.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNamePredicateImpl.java
new file mode 100644 (file)
index 0000000..73e7bcd
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.Serializable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+class QNamePredicateImpl implements Immutable, Serializable, QNamePredicate {
+    private static final long serialVersionUID = 1L;
+
+    private final QName identifier;
+    private final LeafRefPath pathKeyExpression;
+
+    QNamePredicateImpl(final QName identifier, final LeafRefPath pathKeyExpression) {
+        this.identifier = requireNonNull(identifier, "QNamePredicate: identifier should not be null");
+        this.pathKeyExpression = requireNonNull(pathKeyExpression,
+            "QNamePredicate: pathKeyExpression should not be null");
+    }
+
+    @Override
+    public QName getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public LeafRefPath getPathKeyExpression() {
+        return pathKeyExpression;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append('[').append(identifier).append("=current()");
+
+        for (final QNameWithPredicate qname : pathKeyExpression.getPathFromRoot()) {
+            sb.append('/').append(qname);
+        }
+
+        return sb.append(']').toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNameWithPredicate.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNameWithPredicate.java
new file mode 100644 (file)
index 0000000..470f60a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import java.util.List;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+public interface QNameWithPredicate {
+
+    QNameWithPredicate UP_PARENT = new QNameWithPredicateBuilder(null, "..").build();
+
+    QNameWithPredicate ROOT = new QNameWithPredicateBuilder(null, "").build();
+
+    List<QNamePredicate> getQNamePredicates();
+
+    QNameModule getModuleQname();
+
+    String getLocalName();
+
+    QName getQName();
+
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNameWithPredicateBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNameWithPredicateBuilder.java
new file mode 100644 (file)
index 0000000..4b44884
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+class QNameWithPredicateBuilder implements Builder<QNameWithPredicate> {
+
+    private final List<QNamePredicate> qnamePredicates = new ArrayList<>();
+    private QNameModule moduleQname;
+    private String localName;
+
+    static final QNameWithPredicateBuilder UP_PARENT_BUILDER = new QNameWithPredicateBuilder(null, "..") {
+        @Override
+        public QNameWithPredicate build() {
+            return QNameWithPredicate.UP_PARENT;
+        }
+    };
+
+    QNameWithPredicateBuilder(final QNameModule moduleQname, final String localName) {
+        this.moduleQname = moduleQname;
+        this.localName = localName;
+    }
+
+    @Override
+    public QNameWithPredicate build() {
+        if (qnamePredicates.isEmpty() && moduleQname != null && localName != null) {
+            return new SimpleQNameWithPredicate(QName.create(moduleQname, localName));
+        }
+
+        final QNameWithPredicateImpl qNameWithPredicateImpl = new QNameWithPredicateImpl(moduleQname, localName,
+            qnamePredicates);
+
+        // QNameWithPredicateImpl has taken a copy
+        qnamePredicates.clear();
+        return qNameWithPredicateImpl;
+    }
+
+    public List<QNamePredicate> getQNamePredicates() {
+        return qnamePredicates;
+    }
+
+    public void addQNamePredicate(final QNamePredicate qnamePredicate) {
+        qnamePredicates.add(qnamePredicate);
+    }
+
+    public QNameModule getModuleQname() {
+        return moduleQname;
+    }
+
+    public void setModuleQname(final QNameModule moduleQname) {
+        this.moduleQname = moduleQname;
+    }
+
+    public String getLocalName() {
+        return localName;
+    }
+
+    public void setLocalName(final String localName) {
+        this.localName = localName;
+    }
+
+    // FIXME: check also predicates ...
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof QNameWithPredicateBuilder)) {
+            return false;
+        }
+        final QNameWithPredicateBuilder other = (QNameWithPredicateBuilder) obj;
+        return Objects.equals(localName, other.localName) &&  moduleQname.equals(other.moduleQname);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = moduleQname != null ? moduleQname.hashCode() : 0;
+        result = 31 * result + Objects.hashCode(localName);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+
+        if (moduleQname != null) {
+            sb.append('(').append(moduleQname.getNamespace());
+            sb.append("?revision=").append(moduleQname.getRevision());
+            sb.append(')');
+        }
+
+        sb.append(localName);
+
+        for (final QNamePredicate predicate : qnamePredicates) {
+            sb.append(predicate);
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNameWithPredicateImpl.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/QNameWithPredicateImpl.java
new file mode 100644 (file)
index 0000000..390fe34
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015 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.leafref;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+final class QNameWithPredicateImpl extends AbstractQNameWithPredicate {
+
+    private static final long serialVersionUID = 1L;
+
+    private final ImmutableList<QNamePredicate> qnamePredicates;
+    private final QNameModule moduleQname;
+    private final String localName;
+
+    QNameWithPredicateImpl(final QNameModule moduleQname, final String localName,
+            final List<QNamePredicate> qnamePredicates) {
+        this.moduleQname = moduleQname;
+        this.localName = localName;
+        this.qnamePredicates = ImmutableList.copyOf(qnamePredicates);
+    }
+
+    @Override
+    public List<QNamePredicate> getQNamePredicates() {
+        return qnamePredicates;
+    }
+
+    @Override
+    public QNameModule getModuleQname() {
+        return moduleQname;
+    }
+
+    @Override
+    public String getLocalName() {
+        return localName;
+    }
+
+    @Override
+    public QName getQName() {
+        return QName.create(moduleQname, localName);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/SimpleQNameWithPredicate.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/leafref/SimpleQNameWithPredicate.java
new file mode 100644 (file)
index 0000000..1e414df
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.leafref;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+public class SimpleQNameWithPredicate extends AbstractQNameWithPredicate {
+    private static final long serialVersionUID = 1L;
+
+    private final QName qname;
+
+    SimpleQNameWithPredicate(final QName qname) {
+        this.qname = requireNonNull(qname);
+    }
+
+    @Override
+    public List<QNamePredicate> getQNamePredicates() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public QNameModule getModuleQname() {
+        return qname.getModule();
+    }
+
+    @Override
+    public String getLocalName() {
+        return qname.getLocalName();
+    }
+
+    @Override
+    public QName getQName() {
+        return qname;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/osgi/Activator.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/osgi/Activator.java
new file mode 100644 (file)
index 0000000..183e286
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.osgi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeFactory;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * YANG data implementation activator. Publishes a {@link DataTreeFactory} implementation on bundle start.
+ *
+ * @author Robert Varga
+ */
+public final class Activator implements BundleActivator {
+    private ServiceRegistration<@NonNull DataTreeFactory> registration;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        registration = context.registerService(DataTreeFactory.class, new InMemoryDataTreeFactory(), null);
+    }
+
+    @Override
+    public void stop(final BundleContext context) throws Exception {
+        if (registration != null) {
+            registration.unregister();
+            registration = null;
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/osgi/package-info.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/osgi/package-info.java
new file mode 100644 (file)
index 0000000..b184727
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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
+ */
+/**
+ * OSGi-specific YANG data implementation code. Contains activators and similar.
+ */
+package org.opendaylight.yangtools.yang.data.impl.osgi;
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/AbstractMountPointChild.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/AbstractMountPointChild.java
new file mode 100644 (file)
index 0000000..9db8799
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.annotations.Beta;
+import java.io.IOException;
+import org.opendaylight.yangtools.rcf8528.data.util.EmptyMountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Utility base class for implementing {@link MountPointChild} classes.
+ */
+@Beta
+public abstract class AbstractMountPointChild implements MountPointChild {
+    @Override
+    public final NormalizedNode<?, ?> normalizeTo(final SchemaContext schemaContext) throws IOException {
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        writeTo(streamWriter, new EmptyMountPointContext(schemaContext));
+        return result.getResult();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/AbstractNormalizableAnydata.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/AbstractNormalizableAnydata.java
new file mode 100644 (file)
index 0000000..e3967d0
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.rfc7952.data.util.ImmutableMetadataNormalizedAnydata;
+import org.opendaylight.yangtools.yang.data.api.schema.AnydataNormalizationException;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizableAnydata;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.util.ImmutableNormalizedAnydata;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Abstract base class for implementing the NormalizableAnydata interface. This class provides the binding to
+ * NormalizedNodeStreamWriter.
+ */
+@Beta
+@NonNullByDefault
+public abstract class AbstractNormalizableAnydata implements NormalizableAnydata {
+    @Override
+    public final ImmutableNormalizedAnydata normalizeTo(final SchemaContext schemaContext,
+            final DataSchemaNode contextNode) throws AnydataNormalizationException {
+        final NormalizedNodeMetadataResult result = new NormalizedNodeMetadataResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        try {
+            writeTo(streamWriter, schemaContext, contextNode);
+        } catch (IOException e) {
+            throw new AnydataNormalizationException("Failed to normalize anydata", e);
+        }
+
+        return ImmutableMetadataNormalizedAnydata.ofOptional(schemaContext, contextNode, result.getResult(),
+            result.getMetadata());
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    protected abstract ToStringHelper addToStringAttributes(ToStringHelper helper);
+
+    protected abstract void writeTo(NormalizedNodeStreamWriter streamWriter, SchemaContext schemaContext,
+            DataSchemaNode contextNode) throws IOException;
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/Builders.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/Builders.java
new file mode 100644 (file)
index 0000000..8b81e42
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * 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;
+
+import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode;
+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.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAnyXmlNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAnyXmlNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAnydataNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedLeafSetNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableYangModeledAnyXmlNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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;
+
+public final class Builders {
+
+    private Builders() {
+        throw new UnsupportedOperationException("Utilities class should not be instantiated");
+    }
+
+    public static <T> NormalizedNodeBuilder<NodeIdentifier, T, LeafNode<T>> leafBuilder() {
+        return ImmutableLeafNodeBuilder.create();
+    }
+
+    public static <T> NormalizedNodeBuilder<NodeIdentifier, T, LeafNode<T>> leafBuilder(
+            final LeafSchemaNode schema) {
+        return ImmutableLeafNodeSchemaAwareBuilder.create(schema);
+    }
+
+    public static <T> NormalizedNodeBuilder<NodeWithValue, T, LeafSetEntryNode<T>> leafSetEntryBuilder() {
+        return ImmutableLeafSetEntryNodeBuilder.create();
+    }
+
+    public static <T> NormalizedNodeBuilder<NodeWithValue, T, LeafSetEntryNode<T>> leafSetEntryBuilder(
+            final LeafListSchemaNode schema) {
+        return ImmutableLeafSetEntryNodeSchemaAwareBuilder.create(schema);
+    }
+
+    public static NormalizedNodeBuilder<NodeIdentifier, DOMSource, DOMSourceAnyxmlNode> anyXmlBuilder() {
+        return ImmutableAnyXmlNodeBuilder.create();
+    }
+
+    public static NormalizedNodeBuilder<NodeIdentifier, DOMSource, DOMSourceAnyxmlNode> anyXmlBuilder(
+            final AnyxmlSchemaNode schema) {
+        return ImmutableAnyXmlNodeSchemaAwareBuilder.create(schema);
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifier, YangModeledAnyXmlNode> yangModeledAnyXmlBuilder(
+            final YangModeledAnyxmlSchemaNode schema) {
+        return ImmutableYangModeledAnyXmlNodeBuilder.create(schema);
+    }
+
+    public static <T> NormalizedNodeBuilder<NodeIdentifier, T, AnydataNode<T>> anydataBuilder(
+            final Class<T> objectModel) {
+        return ImmutableAnydataNodeBuilder.create(objectModel);
+    }
+
+    public static <T> ListNodeBuilder<T, LeafSetEntryNode<T>> orderedLeafSetBuilder() {
+        return ImmutableOrderedLeafSetNodeBuilder.create();
+    }
+
+    public static <T> ListNodeBuilder<T, LeafSetEntryNode<T>> orderedLeafSetBuilder(final LeafListSchemaNode schema) {
+        return ImmutableOrderedLeafSetNodeSchemaAwareBuilder.create(schema);
+    }
+
+    public static <T> ListNodeBuilder<T, LeafSetEntryNode<T>> leafSetBuilder() {
+        return ImmutableLeafSetNodeBuilder.create();
+    }
+
+    public static <T> ListNodeBuilder<T, LeafSetEntryNode<T>> leafSetBuilder(final LeafSetNode<T> node) {
+        return ImmutableLeafSetNodeBuilder.create(node);
+    }
+
+    public static <T> ListNodeBuilder<T, LeafSetEntryNode<T>> leafSetBuilder(final LeafListSchemaNode schema) {
+        return ImmutableLeafSetNodeSchemaAwareBuilder.create(schema);
+    }
+
+    public static <T> ListNodeBuilder<T, LeafSetEntryNode<T>> leafSetBuilder(final LeafListSchemaNode schema,
+            final LeafSetNode<T> node) {
+        return ImmutableLeafSetNodeSchemaAwareBuilder.create(schema, node);
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder() {
+        return ImmutableContainerNodeBuilder.create();
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder(
+            final ContainerNode node) {
+        return ImmutableContainerNodeBuilder.create(node);
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder(
+            final ContainerSchemaNode schema) {
+        return ImmutableContainerNodeSchemaAwareBuilder.create(schema);
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder(
+            final ContainerSchemaNode schema, final ContainerNode node) {
+        return ImmutableContainerNodeSchemaAwareBuilder.create(schema, node);
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder() {
+        return ImmutableMapEntryNodeBuilder.create();
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder(
+            final MapEntryNode mapEntryNode) {
+        return ImmutableMapEntryNodeBuilder.create(mapEntryNode);
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder(
+            final ListSchemaNode schema) {
+        return ImmutableMapEntryNodeSchemaAwareBuilder.create(schema);
+    }
+
+    public static CollectionNodeBuilder<MapEntryNode, OrderedMapNode> orderedMapBuilder() {
+        return ImmutableOrderedMapNodeBuilder.create();
+    }
+
+    public static CollectionNodeBuilder<MapEntryNode, OrderedMapNode> orderedMapBuilder(final ListSchemaNode schema) {
+        return ImmutableOrderedMapNodeSchemaAwareBuilder.create(schema);
+    }
+
+    public static CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> unkeyedListBuilder() {
+        return ImmutableUnkeyedListNodeBuilder.create();
+    }
+
+    public static CollectionNodeBuilder<MapEntryNode, MapNode> mapBuilder() {
+        return ImmutableMapNodeBuilder.create();
+    }
+
+    public static CollectionNodeBuilder<MapEntryNode, MapNode> mapBuilder(final MapNode node) {
+        return ImmutableMapNodeBuilder.create(node);
+    }
+
+    public static CollectionNodeBuilder<MapEntryNode, MapNode> mapBuilder(final ListSchemaNode schema) {
+        return ImmutableMapNodeSchemaAwareBuilder.create(schema);
+    }
+
+    public static CollectionNodeBuilder<MapEntryNode, MapNode> mapBuilder(final ListSchemaNode schema,
+            final MapNode node) {
+        return ImmutableMapNodeSchemaAwareBuilder.create(schema, node);
+    }
+
+    public static DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> augmentationBuilder() {
+        return ImmutableAugmentationNodeBuilder.create();
+    }
+
+    public static DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> augmentationBuilder(
+            final AugmentationSchemaNode schema) {
+        return ImmutableAugmentationNodeSchemaAwareBuilder.create(schema);
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> choiceBuilder() {
+        return ImmutableChoiceNodeBuilder.create();
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> choiceBuilder(final ChoiceSchemaNode schema) {
+        return ImmutableChoiceNodeSchemaAwareBuilder.create(schema);
+    }
+
+    public static DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> unkeyedListEntryBuilder() {
+        return ImmutableUnkeyedListEntryNodeBuilder.create();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableMetadataNormalizedNodeStreamWriter.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableMetadataNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..ee3c4eb
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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 static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadata;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.rfc7952.data.util.ImmutableNormalizedMetadata;
+import org.opendaylight.yangtools.rfc7952.data.util.ImmutableNormalizedMetadata.Builder;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriterExtension;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+
+/**
+ * A {@link NormalizedMetadata}-aware {@link ImmutableMetadataNormalizedNodeStreamWriter}. It advertizes the
+ * {@link StreamWriterMetadataExtension} extension.
+ */
+@Beta
+public class ImmutableMetadataNormalizedNodeStreamWriter extends ImmutableNormalizedNodeStreamWriter
+        implements StreamWriterMetadataExtension {
+    /**
+     * Snapshot of currently-open data- and metadatastate.
+     */
+    public static final class State {
+        final Builder metaBuilder;
+        final NormalizedNodeBuilder dataBuilder;
+
+        State(final NormalizedNodeBuilder dataBuilder, final Builder metadataBuilder) {
+            this.dataBuilder = requireNonNull(dataBuilder);
+            this.metaBuilder = requireNonNull(metadataBuilder);
+        }
+
+        public NormalizedNodeBuilder getDataBuilder() {
+            return dataBuilder;
+        }
+
+        public Builder getMetaBuilder() {
+            return metaBuilder;
+        }
+    }
+
+    private final Deque<Builder> builders = new ArrayDeque<>();
+    private final NormalizedNodeMetadataResult result;
+
+    protected ImmutableMetadataNormalizedNodeStreamWriter(final State state) {
+        super(state.getDataBuilder());
+        builders.push(state.getMetaBuilder());
+        result = null;
+    }
+
+    protected ImmutableMetadataNormalizedNodeStreamWriter(final NormalizedNodeMetadataResult result) {
+        super(result);
+        this.result = requireNonNull(result);
+    }
+
+    @Override
+    public final ClassToInstanceMap<NormalizedNodeStreamWriterExtension> getExtensions() {
+        return ImmutableClassToInstanceMap.of(StreamWriterMetadataExtension.class, this);
+    }
+
+    @Override
+    public final void metadata(final ImmutableMap<QName, Object> metadata) throws IOException {
+        final Builder current = builders.peek();
+        checkState(current != null, "Attempted to emit metadata when no metadata is open");
+        current.withAnnotations(metadata);
+    }
+
+    /**
+     * Remove the currently-open builders for data and metadata from the stack.
+     *
+     * @return Builder state.
+     */
+    protected final @NonNull State popState() {
+        return new State(popBuilder(), builders.pop());
+    }
+
+    @Override
+    @SuppressWarnings("rawtypes")
+    final void enter(final PathArgument identifier, final NormalizedNodeBuilder next) {
+        super.enter(identifier, next);
+        builders.push(ImmutableNormalizedMetadata.builder().withIdentifier(identifier));
+    }
+
+    @Override
+    public final void endNode() {
+        super.endNode();
+        final ImmutableNormalizedMetadata metadata = builders.pop().build();
+        final Builder current = builders.peek();
+        if (current != null) {
+            if (!metadata.getAnnotations().isEmpty() || !metadata.getChildren().isEmpty()) {
+                current.withChild(metadata);
+            }
+        } else {
+            // All done
+            result.setResult(metadata);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableMountPointNormalizedNodeStreamWriter.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableMountPointNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..ca43383
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.annotations.Beta;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import java.io.IOException;
+import org.opendaylight.yangtools.rcf8528.data.util.ImmutableMountPointNode;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.rfc8528.data.api.StreamWriterMountPointExtension;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.ForwardingNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriterExtension;
+
+// FIXME: document usage of this
+@Beta
+public abstract class ImmutableMountPointNormalizedNodeStreamWriter extends ImmutableNormalizedNodeStreamWriter
+        implements StreamWriterMountPointExtension {
+    protected ImmutableMountPointNormalizedNodeStreamWriter(final NormalizedNodeResult result) {
+        super(result);
+    }
+
+    @Override
+    public final ClassToInstanceMap<NormalizedNodeStreamWriterExtension> getExtensions() {
+        return ImmutableClassToInstanceMap.of(StreamWriterMountPointExtension.class, this);
+    }
+
+    @Override
+    public final NormalizedNodeStreamWriter startMountPoint(final MountPointIdentifier mountId,
+            final MountPointContext mountCtx) {
+        final NormalizedNodeResult mountResult = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter mountDelegate = ImmutableNormalizedNodeStreamWriter.from(mountResult);
+
+        return new ForwardingNormalizedNodeStreamWriter() {
+            @Override
+            protected NormalizedNodeStreamWriter delegate() {
+                return mountDelegate;
+            }
+
+            @Override
+            public void close() throws IOException {
+                super.close();
+
+                final NormalizedNode<?, ?> data = mountResult.getResult();
+                if (!(data instanceof ContainerNode)) {
+                    throw new IOException("Unhandled mount data " + data);
+                }
+
+                writeChild(ImmutableMountPointNode.of(mountId, mountCtx, (ContainerNode) data));
+            }
+        };
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java
new file mode 100644 (file)
index 0000000..2831040
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * 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 static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Iterator;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+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.impl.ImmutableChoiceNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public final class ImmutableNodes {
+    private static final NodeIdentifier SCHEMACONTEXT_NAME = NodeIdentifier.create(SchemaContext.NAME);
+
+    private ImmutableNodes() {
+        throw new UnsupportedOperationException("Utilities class should not be instantiated");
+    }
+
+    public static @NonNull CollectionNodeBuilder<MapEntryNode, MapNode> mapNodeBuilder() {
+        return ImmutableMapNodeBuilder.create();
+    }
+
+    public static @NonNull CollectionNodeBuilder<MapEntryNode, MapNode> mapNodeBuilder(final QName name) {
+        return mapNodeBuilder(NodeIdentifier.create(name));
+    }
+
+    public static @NonNull CollectionNodeBuilder<MapEntryNode, MapNode> mapNodeBuilder(final NodeIdentifier name) {
+        return ImmutableMapNodeBuilder.create().withNodeIdentifier(name);
+    }
+
+    /**
+     * Create an immutable map node.
+     *
+     * @param name QName which will be used as node identifier
+     * @return An unordered Map node
+     */
+    public static @NonNull MapNode mapNode(final QName name) {
+        return mapNode(NodeIdentifier.create(name));
+    }
+
+    /**
+     * Create an immutable map node.
+     *
+     * @param name QName which will be used as node identifier
+     * @return An unordered Map node
+     */
+    public static @NonNull MapNode mapNode(final NodeIdentifier name) {
+        return mapNodeBuilder(name).build();
+    }
+
+    /**
+     * Create immutable ordered map node.
+     *
+     * @param name QName which will be used as node identifier
+     * @return An ordered Map node
+     */
+    public static @NonNull OrderedMapNode orderedMapNode(final QName name) {
+        return orderedMapNode(NodeIdentifier.create(name));
+    }
+
+    /**
+     * Create immutable ordered map node.
+     *
+     * @param name Node identifier
+     * @return An ordered Map node
+     */
+    public static @NonNull OrderedMapNode orderedMapNode(final NodeIdentifier name) {
+        return ImmutableOrderedMapNodeBuilder.create().withNodeIdentifier(name).build();
+    }
+
+    /**
+     * Construct immutable leaf node.
+     *
+     * @param name Identifier of leaf node
+     * @param value Value of leaf node
+     * @param <T> Type of leaf node value
+     * @return Leaf node with supplied identifier and value
+     */
+    public static <T> @NonNull LeafNode<T> leafNode(final NodeIdentifier name, final T value) {
+        return ImmutableLeafNodeBuilder.createNode(name, value);
+    }
+
+    /**
+     * Construct immutable leaf node.
+     *
+     * @param name QName which will be used as node identifier
+     * @param value Value of leaf node.
+     * @param <T> Type of leaf node value
+     * @return Leaf node with supplied identifier and value
+     */
+    public static <T> @NonNull LeafNode<T> leafNode(final QName name, final T value) {
+        return leafNode(NodeIdentifier.create(name), value);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder(
+            final QName nodeName, final QName keyName, final Object keyValue) {
+        return ImmutableMapEntryNodeBuilder.create()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(nodeName, keyName, keyValue))
+                .withChild(leafNode(keyName, keyValue));
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder() {
+        return ImmutableMapEntryNodeBuilder.create();
+    }
+
+    public static @NonNull MapEntryNode mapEntry(final QName nodeName, final QName keyName, final Object keyValue) {
+        return mapEntryBuilder(nodeName, keyName, keyValue).build();
+    }
+
+    /**
+     * Create an immutable container node.
+     *
+     * @param name QName which will be used as node identifier
+     * @return A container node
+     */
+    public static @NonNull ContainerNode containerNode(final QName name) {
+        return containerNode(NodeIdentifier.create(name));
+    }
+
+    /**
+     * Create an immutable container node.
+     *
+     * @param name Node identifier
+     * @return A container node
+     */
+    public static @NonNull ContainerNode containerNode(final NodeIdentifier name) {
+        return ImmutableContainerNodeBuilder.create().withNodeIdentifier(name).build();
+    }
+
+    /**
+     * Create an immutable choice node.
+     *
+     * @param name QName which will be used as node identifier
+     * @return A choice node
+     */
+    public static @NonNull ChoiceNode choiceNode(final QName name) {
+        return choiceNode(NodeIdentifier.create(name));
+    }
+
+    /**
+     * Create an immutable choice node.
+     *
+     * @param name Node identifier
+     * @return A choice node
+     */
+    public static @NonNull ChoiceNode choiceNode(final NodeIdentifier name) {
+        return ImmutableChoiceNodeBuilder.create().withNodeIdentifier(name).build();
+    }
+
+    /**
+     * Create an immutable list node.
+     *
+     * @param name QName which will be used as node identifier
+     * @return An unkeyed list node
+     */
+    public static @NonNull UnkeyedListNode listNode(final QName name) {
+        return listNode(NodeIdentifier.create(name));
+    }
+
+    /**
+     * Create an immutable list node.
+     *
+     * @param name Node identifier
+     * @return An unkeyed list node
+     */
+    public static @NonNull UnkeyedListNode listNode(final NodeIdentifier name) {
+        return ImmutableUnkeyedListNodeBuilder.create().withNodeIdentifier(name).build();
+    }
+
+    /**
+     * Convert YangInstanceIdentifier into a normalized node structure.
+     *
+     * @param ctx schema context to used during serialization
+     * @param id instance identifier to convert to node structure starting from root
+     * @return serialized normalized node for provided instance Id
+     */
+    public static @NonNull NormalizedNode<?, ?> fromInstanceId(final SchemaContext ctx,
+            final YangInstanceIdentifier id) {
+        return fromInstanceId(ctx, id, Optional.empty());
+    }
+
+    /**
+     * Convert YangInstanceIdentifier into a normalized node structure.
+     *
+     * @param ctx schema context to used during serialization
+     * @param id instance identifier to convert to node structure starting from root
+     * @param deepestElement pre-built deepest child that will be inserted at the last path argument of provided
+     *                       instance identifier
+     * @return serialized normalized node for provided instance Id with overridden last child.
+     */
+    public static @NonNull NormalizedNode<?, ?> fromInstanceId(final SchemaContext ctx, final YangInstanceIdentifier id,
+            final NormalizedNode<?, ?> deepestElement) {
+        return fromInstanceId(ctx, id, Optional.of(deepestElement));
+    }
+
+    /**
+     * Convert YangInstanceIdentifier into a normalized node structure.
+     *
+     * @param ctx schema context to used during serialization
+     * @param id instance identifier to convert to node structure starting from root
+     * @param deepestElement pre-built deepest child that will be inserted at the last path argument of provided
+     *                       instance identifier
+     * @return serialized normalized node for provided instance Id with (optionally) overridden last child
+     *         and (optionally) marked with specific operation attribute.
+     */
+    public static @NonNull NormalizedNode<?, ?> fromInstanceId(final SchemaContext ctx, final YangInstanceIdentifier id,
+            final Optional<NormalizedNode<?, ?>> deepestElement) {
+        final PathArgument topLevelElement;
+        final InstanceIdToNodes<?> instanceIdToNodes;
+        final Iterator<PathArgument> it = id.getPathArguments().iterator();
+        if (it.hasNext()) {
+            topLevelElement = it.next();
+            final DataSchemaNode dataChildByName = ctx.getDataChildByName(topLevelElement.getNodeType());
+            checkNotNull(dataChildByName,
+                "Cannot find %s node in schema context. Instance identifier has to start from root", topLevelElement);
+            instanceIdToNodes = InstanceIdToNodes.fromSchemaAndQNameChecked(ctx, topLevelElement.getNodeType());
+        } else {
+            topLevelElement = SCHEMACONTEXT_NAME;
+            instanceIdToNodes = InstanceIdToNodes.fromDataSchemaNode(ctx);
+        }
+
+        return instanceIdToNodes.create(topLevelElement, it, deepestElement);
+    }
+}
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..d8b9dc4
--- /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.impl.schema;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+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.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAnyXmlNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAnydataNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableYangModeledAnyXmlNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * 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.
+ *
+ * <p>
+ * Other mode of operation is using {@link #from(NormalizedNodeContainerBuilder)}, where all created nodes will be
+ * written to this builder.
+ *
+ * <p>
+ * This class is not final for purposes of customization, normal users should not need to subclass it.
+ */
+public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+    @SuppressWarnings("rawtypes")
+    private final Deque<NormalizedNodeBuilder> builders = new ArrayDeque<>();
+
+    private DataSchemaNode nextSchema;
+
+    @SuppressWarnings("rawtypes")
+    protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeBuilder topLevelBuilder) {
+        builders.push(topLevelBuilder);
+    }
+
+    protected ImmutableNormalizedNodeStreamWriter(final NormalizedNodeResult result) {
+        this(new NormalizedNodeResultBuilder(result));
+    }
+
+    /**
+     * 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 @NonNull 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 the 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 written.
+     *
+     * @param result {@link NormalizedNodeResult} object which will hold result value.
+     * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
+     */
+    public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeResult result) {
+        return result instanceof NormalizedNodeMetadataResult ? from((NormalizedNodeMetadataResult) result)
+                : new ImmutableNormalizedNodeStreamWriter(result);
+    }
+
+    /**
+     * 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 the 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 written.
+     *
+     * @param result {@link NormalizedNodeResult} object which will hold result value.
+     * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
+     */
+    public static @NonNull NormalizedNodeStreamWriter from(final NormalizedNodeMetadataResult result) {
+        return new ImmutableMetadataNormalizedNodeStreamWriter(result);
+    }
+
+    @Override
+    public void startLeafNode(final NodeIdentifier name) {
+        checkDataNodeContainer();
+        enter(name, leafNodeBuilder(nextSchema));
+        nextSchema = null;
+    }
+
+    @Override
+    public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
+        checkDataNodeContainer();
+        enter(name, UNKNOWN_SIZE == childSizeHint ? InterningLeafSetNodeBuilder.create(nextSchema)
+                : InterningLeafSetNodeBuilder.create(nextSchema, childSizeHint));
+    }
+
+    @Override
+    public void startLeafSetEntryNode(final NodeWithValue<?> name) {
+        final NormalizedNodeBuilder<?, ?, ?> current = current();
+        checkArgument(current instanceof ImmutableLeafSetNodeBuilder
+            || current instanceof ImmutableOrderedLeafSetNodeBuilder || current instanceof NormalizedNodeResultBuilder,
+            "LeafSetEntryNode is not valid for parent %s", current);
+        enter(name, leafsetEntryNodeBuilder());
+        nextSchema = null;
+    }
+
+    @Override
+    public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
+        checkDataNodeContainer();
+        enter(name, Builders.orderedLeafSetBuilder());
+    }
+
+    @Override
+    public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) {
+        checkDataNodeContainer();
+        if (DOMSource.class.isAssignableFrom(objectModel)) {
+            enter(name, ImmutableAnyXmlNodeBuilder.create());
+            nextSchema = null;
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void startContainerNode(final NodeIdentifier name, final int childSizeHint) {
+        checkDataNodeContainer();
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableContainerNodeBuilder.create()
+                : ImmutableContainerNodeBuilder.create(childSizeHint));
+    }
+
+    @Override
+    public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) {
+        checkDataNodeContainer();
+
+        checkArgument(nextSchema instanceof YangModeledAnyxmlSchemaNode,
+                "Schema of this node should be instance of YangModeledAnyxmlSchemaNode");
+        final YangModeledAnyxmlSchemaNode anyxmlSchema = (YangModeledAnyxmlSchemaNode) nextSchema;
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableYangModeledAnyXmlNodeBuilder.create(anyxmlSchema)
+                : ImmutableYangModeledAnyXmlNodeBuilder.create(anyxmlSchema, childSizeHint));
+    }
+
+    @Override
+    public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
+        checkDataNodeContainer();
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListNodeBuilder.create()
+                : ImmutableUnkeyedListNodeBuilder.create(childSizeHint));
+    }
+
+    @Override
+    public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) {
+        final NormalizedNodeBuilder<?, ?, ?> current = current();
+        checkArgument(current instanceof ImmutableUnkeyedListNodeBuilder
+            || current instanceof NormalizedNodeResultBuilder);
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableUnkeyedListEntryNodeBuilder.create()
+                : ImmutableUnkeyedListEntryNodeBuilder.create(childSizeHint));
+    }
+
+    @Override
+    public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
+        checkDataNodeContainer();
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableMapNodeBuilder.create()
+                : ImmutableMapNodeBuilder.create(childSizeHint));
+    }
+
+    @Override
+    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) {
+        final NormalizedNodeBuilder<?, ?, ?> current = current();
+        checkArgument(current instanceof ImmutableMapNodeBuilder || current instanceof ImmutableOrderedMapNodeBuilder
+            || current instanceof NormalizedNodeResultBuilder);
+
+        enter(identifier, UNKNOWN_SIZE == childSizeHint ? ImmutableMapEntryNodeBuilder.create()
+                : ImmutableMapEntryNodeBuilder.create(childSizeHint));
+    }
+
+    @Override
+    public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
+        checkDataNodeContainer();
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableOrderedMapNodeBuilder.create()
+                : ImmutableOrderedMapNodeBuilder.create(childSizeHint));
+    }
+
+    @Override
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
+        checkDataNodeContainer();
+        enter(name, UNKNOWN_SIZE == childSizeHint ? ImmutableChoiceNodeBuilder.create()
+                : ImmutableChoiceNodeBuilder.create(childSizeHint));
+    }
+
+    @Override
+    public void startAugmentationNode(final AugmentationIdentifier identifier) {
+        checkDataNodeContainer();
+        checkArgument(!(current() instanceof ImmutableAugmentationNodeBuilder));
+        enter(identifier, Builders.augmentationBuilder());
+    }
+
+    @Override
+    public void flush() {
+        // no-op
+    }
+
+    @Override
+    public void close() {
+        // no-op
+    }
+
+    @Override
+    public void nextDataSchemaNode(final DataSchemaNode schema) {
+        nextSchema = requireNonNull(schema);
+    }
+
+    @Override
+    public void scalarValue(final Object value) {
+        currentScalar().withValue(value);
+    }
+
+    @Override
+    public void domSourceValue(final DOMSource value) {
+        currentScalar().withValue(value);
+    }
+
+    @Override
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void endNode() {
+        final NormalizedNodeBuilder finishedBuilder = builders.poll();
+        checkState(finishedBuilder != null, "Node which should be closed does not exists.");
+        final NormalizedNode<PathArgument, ?> product = finishedBuilder.build();
+        nextSchema = null;
+
+        writeChild(product);
+    }
+
+    @Override
+    public boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        checkDataNodeContainer();
+        enter(name, ImmutableAnydataNodeBuilder.create(objectModel));
+        // We support all object models
+        return true;
+    }
+
+    /**
+     * Add a child not to the currently-open builder.
+     *
+     * @param child A new child
+     * @throws NullPointerException if {@code child} is null
+     * @throws IllegalStateException if there is no open builder
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    protected final void writeChild(final NormalizedNode<?, ?> child) {
+        final NormalizedNodeContainerBuilder current = currentContainer();
+        checkState(current != null, "Reached top level node, which could not be closed in this writer.");
+        current.addChild(requireNonNull(child));
+    }
+
+    // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
+    @SuppressWarnings("rawtypes")
+    void enter(final PathArgument identifier, final NormalizedNodeBuilder next) {
+        builders.push(next.withNodeIdentifier(identifier));
+        nextSchema = null;
+    }
+
+    // Exposed for ImmutableMetadataNormalizedNodeStreamWriter
+    protected final NormalizedNodeBuilder popBuilder() {
+        return builders.pop();
+    }
+
+    final void reset(final NormalizedNodeResultBuilder builder) {
+        nextSchema = null;
+        builders.clear();
+        builders.push(builder);
+    }
+
+    private <T> ImmutableLeafNodeBuilder<T> leafNodeBuilder(final DataSchemaNode schema) {
+        final InterningLeafNodeBuilder<T> interning = InterningLeafNodeBuilder.forSchema(schema);
+        return interning != null ? interning : leafNodeBuilder();
+    }
+
+    <T> ImmutableLeafNodeBuilder<T> leafNodeBuilder() {
+        return new ImmutableLeafNodeBuilder<>();
+    }
+
+    <T> ImmutableLeafSetEntryNodeBuilder<T> leafsetEntryNodeBuilder() {
+        return ImmutableLeafSetEntryNodeBuilder.create();
+    }
+
+    private void checkDataNodeContainer() {
+        @SuppressWarnings("rawtypes")
+        final NormalizedNodeContainerBuilder current = currentContainer();
+        if (!(current instanceof NormalizedNodeResultBuilder)) {
+            checkArgument(current instanceof DataContainerNodeBuilder<?, ?>, "Invalid nesting of data.");
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    private NormalizedNodeBuilder current() {
+        return builders.peek();
+    }
+
+    @SuppressWarnings("rawtypes")
+    private NormalizedNodeContainerBuilder currentContainer() {
+        final NormalizedNodeBuilder current = current();
+        if (current == null) {
+            return null;
+        }
+        checkState(current instanceof NormalizedNodeContainerBuilder, "%s is not a node container", current);
+        return (NormalizedNodeContainerBuilder) current;
+    }
+
+    @SuppressWarnings("rawtypes")
+    private NormalizedNodeBuilder currentScalar() {
+        final NormalizedNodeBuilder current = current();
+        checkState(!(current instanceof NormalizedNodeContainerBuilder), "Unexpected node container %s", current);
+        return current;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToCompositeNodes.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToCompositeNodes.java
new file mode 100644 (file)
index 0000000..f95ff07
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2015 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 static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.yangtools.yang.common.QName;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+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.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
+
+/**
+ * Base strategy for converting an instance identifier into a normalized node structure for container-like types.
+ */
+abstract class InstanceIdToCompositeNodes<T extends PathArgument> extends InstanceIdToNodes<T> {
+    InstanceIdToCompositeNodes(final T identifier) {
+        super(identifier);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    final NormalizedNode<?, ?> create(final PathArgument first, final Iterator<PathArgument> others,
+            final Optional<NormalizedNode<?, ?>> lastChild) {
+        if (!isMixin()) {
+            final QName type = getIdentifier().getNodeType();
+            if (type != null) {
+                final QName firstType = first.getNodeType();
+                checkArgument(type.equals(firstType), "Node QName must be %s was %s", type, firstType);
+            }
+        }
+
+        @SuppressWarnings("rawtypes")
+        final NormalizedNodeContainerBuilder builder = createBuilder(first);
+
+        if (others.hasNext()) {
+            final PathArgument childPath = others.next();
+            final InstanceIdToNodes<?> childOp = getChildOperation(childPath);
+            builder.addChild(childOp.create(childPath, others, lastChild));
+        } else if (lastChild.isPresent()) {
+            builder.withValue(ImmutableList.copyOf((Collection<?>) lastChild.get().getValue()));
+        }
+
+        return builder.build();
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private InstanceIdToNodes<?> getChildOperation(final PathArgument childPath) {
+        final InstanceIdToNodes<?> childOp;
+        try {
+            childOp = getChild(childPath);
+        } catch (final RuntimeException e) {
+            throw new IllegalArgumentException(String.format("Failed to process child node %s", childPath), e);
+        }
+        checkArgument(childOp != null, "Node %s is not allowed inside %s", childPath, getIdentifier());
+        return childOp;
+    }
+
+    abstract NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(PathArgument compositeNode);
+
+    abstract static class DataContainerNormalizationOperation<T extends PathArgument>
+            extends InstanceIdToCompositeNodes<T> {
+
+        private final Map<PathArgument, InstanceIdToNodes<?>> byArg = new ConcurrentHashMap<>();
+        private final DataNodeContainer schema;
+
+        DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) {
+            super(identifier);
+            this.schema = schema;
+        }
+
+        @Override
+        final InstanceIdToNodes<?> getChild(final PathArgument child) {
+            InstanceIdToNodes<?> potential = byArg.get(child);
+            if (potential != null) {
+                return potential;
+            }
+            potential = fromLocalSchema(child);
+            return register(potential);
+        }
+
+        private InstanceIdToNodes<?> fromLocalSchema(final PathArgument child) {
+            if (child instanceof AugmentationIdentifier) {
+                return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
+                        .iterator().next());
+            }
+            return fromSchemaAndQNameChecked(schema, child.getNodeType());
+        }
+
+        private InstanceIdToNodes<?> register(final InstanceIdToNodes<?> potential) {
+            if (potential != null) {
+                byArg.put(potential.getIdentifier(), potential);
+            }
+            return potential;
+        }
+    }
+
+    static final class ListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
+        ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
+            super(identifier, schema);
+        }
+
+        @Override
+        DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> createBuilder(
+                final PathArgument currentArg) {
+            final NodeIdentifierWithPredicates arg = (NodeIdentifierWithPredicates) currentArg;
+            final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
+                    .mapEntryBuilder().withNodeIdentifier(arg);
+            for (final Entry<QName, Object> keyValue : arg.entrySet()) {
+                builder.addChild(Builders.leafBuilder()
+                        .withNodeIdentifier(NodeIdentifier.create(keyValue.getKey())).withValue(keyValue.getValue())
+                        .build());
+            }
+            return builder;
+        }
+
+        @Override
+        boolean isMixin() {
+            return false;
+        }
+    }
+
+    static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
+        UnkeyedListItemNormalization(final ListSchemaNode schema) {
+            super(NodeIdentifier.create(schema.getQName()), schema);
+        }
+
+        @Override
+        DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> createBuilder(
+                final PathArgument compositeNode) {
+            return Builders.unkeyedListEntryBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        boolean isMixin() {
+            return false;
+        }
+    }
+
+    static final class ContainerTransformation extends DataContainerNormalizationOperation<NodeIdentifier> {
+        ContainerTransformation(final ContainerSchemaNode schema) {
+            super(NodeIdentifier.create(schema.getQName()), schema);
+        }
+
+        @Override
+        DataContainerNodeBuilder<NodeIdentifier, ContainerNode> createBuilder(final PathArgument compositeNode) {
+            return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        boolean isMixin() {
+            return false;
+        }
+    }
+
+    static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
+        OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+            super(potential);
+        }
+
+        @Override
+        ListNodeBuilder<?, ?> createBuilder(final PathArgument compositeNode) {
+            return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+
+    static class UnorderedLeafListMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
+        private final InstanceIdToNodes<?> innerOp;
+
+        UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+            super(NodeIdentifier.create(potential.getQName()));
+            innerOp = new InstanceIdToSimpleNodes.LeafListEntryNormalization(potential);
+        }
+
+        @Override
+        ListNodeBuilder<?, ?> createBuilder(final PathArgument compositeNode) {
+            return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        final InstanceIdToNodes<?> getChild(final PathArgument child) {
+            return child instanceof NodeWithValue ? innerOp : null;
+        }
+
+        @Override
+        final boolean isMixin() {
+            return true;
+        }
+    }
+
+    static final class AugmentationNormalization extends DataContainerNormalizationOperation<AugmentationIdentifier> {
+        AugmentationNormalization(final AugmentationSchemaNode augmentation, final DataNodeContainer schema) {
+            super(DataSchemaContextNode.augmentationIdentifierFrom(augmentation),
+                    EffectiveAugmentationSchema.create(augmentation, schema));
+        }
+
+        @Override
+        DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> createBuilder(
+                final PathArgument compositeNode) {
+            return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        boolean isMixin() {
+            return true;
+        }
+    }
+
+    static class UnorderedMapMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
+        private final ListItemNormalization innerNode;
+
+        UnorderedMapMixinNormalization(final ListSchemaNode list) {
+            super(NodeIdentifier.create(list.getQName()));
+            this.innerNode = new ListItemNormalization(NodeIdentifierWithPredicates.of(list.getQName()), list);
+        }
+
+        @Override
+        CollectionNodeBuilder<MapEntryNode, ? extends MapNode> createBuilder(final PathArgument compositeNode) {
+            return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        final InstanceIdToNodes<?> getChild(final PathArgument child) {
+            return child.getNodeType().equals(getIdentifier().getNodeType()) ? innerNode : null;
+        }
+
+        @Override
+        final boolean isMixin() {
+            return true;
+        }
+    }
+
+    static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
+        OrderedMapMixinNormalization(final ListSchemaNode list) {
+            super(list);
+        }
+
+        @Override
+        CollectionNodeBuilder<MapEntryNode, OrderedMapNode> createBuilder(final PathArgument compositeNode) {
+            return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+
+    static final class ChoiceNodeNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
+        private final ImmutableMap<PathArgument, InstanceIdToNodes<?>> byArg;
+
+        ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
+            super(NodeIdentifier.create(schema.getQName()));
+            final ImmutableMap.Builder<PathArgument, InstanceIdToNodes<?>> byArgBuilder = ImmutableMap.builder();
+
+            for (final CaseSchemaNode caze : schema.getCases().values()) {
+                for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
+                    final InstanceIdToNodes<?> childOp = fromDataSchemaNode(cazeChild);
+                    byArgBuilder.put(childOp.getIdentifier(), childOp);
+                }
+            }
+            byArg = byArgBuilder.build();
+        }
+
+        @Override
+        InstanceIdToNodes<?> getChild(final PathArgument child) {
+            return byArg.get(child);
+        }
+
+        @Override
+        DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> createBuilder(final PathArgument compositeNode) {
+            return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        boolean isMixin() {
+            return true;
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToNodes.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToNodes.java
new file mode 100644 (file)
index 0000000..43c13bb
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2015 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 static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.Iterables;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+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.schema.AnydataNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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;
+
+/**
+ * Base strategy for converting an instance identifier into a normalized node structure.
+ * Use provided static methods for generic YangInstanceIdentifier -> NormalizedNode translation in ImmutableNodes.
+ */
+abstract class InstanceIdToNodes<T extends PathArgument> extends AbstractIdentifiable<T> {
+    InstanceIdToNodes(final T identifier) {
+        super(identifier);
+    }
+
+    /**
+     * Build a strategy for the next path argument.
+     *
+     * @param child child identifier
+     * @return transformation strategy for a specific child
+     */
+    abstract InstanceIdToNodes<?> getChild(PathArgument child);
+
+    /**
+     * Convert instance identifier into a NormalizedNode structure.
+     *
+     * @param instanceId Instance identifier to transform into NormalizedNodes
+     * @param deepestChild Optional normalized node to be inserted as the last child
+     * @param operation Optional modify operation to be set on the last child
+     * @return NormalizedNode structure corresponding to submitted instance ID
+     */
+    abstract @NonNull NormalizedNode<?, ?> create(PathArgument first, Iterator<PathArgument> others,
+            Optional<NormalizedNode<?, ?>> deepestChild);
+
+    abstract boolean isMixin();
+
+    private static final class UnkeyedListMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> {
+        private final UnkeyedListItemNormalization innerNode;
+
+        UnkeyedListMixinNormalization(final ListSchemaNode list) {
+            super(NodeIdentifier.create(list.getQName()));
+            this.innerNode = new UnkeyedListItemNormalization(list);
+        }
+
+        @Override
+        CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> createBuilder(final PathArgument compositeNode) {
+            return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        InstanceIdToNodes<?> getChild(final PathArgument child) {
+            return child.getNodeType().equals(getIdentifier().getNodeType()) ? innerNode : null;
+        }
+
+        @Override
+        boolean isMixin() {
+            return true;
+        }
+    }
+
+    private abstract static class AbstractOpaqueNormalization extends InstanceIdToNodes<NodeIdentifier> {
+        AbstractOpaqueNormalization(final DataSchemaNode schema) {
+            super(NodeIdentifier.create(schema.getQName()));
+        }
+
+        @Override
+        final InstanceIdToNodes<?> getChild(final PathArgument child) {
+            return null;
+        }
+
+        @Override
+        final boolean isMixin() {
+            return false;
+        }
+    }
+
+    private static final class AnydataNormalization extends AbstractOpaqueNormalization {
+        AnydataNormalization(final AnydataSchemaNode schema) {
+            super(schema);
+        }
+
+        @Override
+        NormalizedNode<?, ?> create(final PathArgument first, final Iterator<PathArgument> others,
+                final Optional<NormalizedNode<?, ?>> deepestChild) {
+            checkState(deepestChild.isPresent(), "Cannot instantiate anydata node without a value");
+            final NormalizedNode<?, ?> child = deepestChild.get();
+            checkState(child instanceof AnydataNode, "Invalid child %s", child);
+            return createAnydata((AnydataNode<?>) child);
+        }
+
+        private <T> AnydataNode<T> createAnydata(final AnydataNode<T> child) {
+            return Builders.anydataBuilder(child.getValueObjectModel()).withValue(child.getValue())
+            .withNodeIdentifier(getIdentifier()).build();
+        }
+    }
+
+    private static final class AnyXmlNormalization extends AbstractOpaqueNormalization {
+        AnyXmlNormalization(final AnyxmlSchemaNode schema) {
+            super(schema);
+        }
+
+        @Override
+        NormalizedNode<?, ?> create(final PathArgument first, final Iterator<PathArgument> others,
+                final Optional<NormalizedNode<?, ?>> deepestChild) {
+            final NormalizedNodeBuilder<NodeIdentifier, DOMSource, DOMSourceAnyxmlNode> builder =
+                    Builders.anyXmlBuilder()
+                    .withNodeIdentifier(getIdentifier());
+            if (deepestChild.isPresent()) {
+                final NormalizedNode<?, ?> child = deepestChild.get();
+                checkState(child instanceof DOMSourceAnyxmlNode, "Invalid child %s", child);
+                builder.withValue(((DOMSourceAnyxmlNode) child).getValue());
+            }
+
+            return builder.build();
+        }
+    }
+
+    private static Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent, final QName child) {
+        final Optional<DataSchemaNode> potential = parent.findDataChildByName(child);
+        return potential.isPresent() ? potential : Optional.ofNullable(
+            findChoice(Iterables.filter(parent.getChildNodes(), ChoiceSchemaNode.class), child));
+    }
+
+    static InstanceIdToNodes<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) {
+        final Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
+        checkArgument(potential.isPresent(),
+                "Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema,
+                schema.getChildNodes());
+
+        final DataSchemaNode result = potential.get();
+        // We try to look up if this node was added by augmentation
+        if (schema instanceof DataSchemaNode && result.isAugmenting()) {
+            return fromAugmentation(schema, (AugmentationTarget) schema, result);
+        }
+        return fromDataSchemaNode(result);
+    }
+
+    private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
+        for (final ChoiceSchemaNode choice : choices) {
+            for (final CaseSchemaNode caze : choice.getCases().values()) {
+                if (findChildSchemaNode(caze, child).isPresent()) {
+                    return choice;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a SchemaPathUtil for provided child node
+     * <p/>
+     * If supplied child is added by Augmentation this operation returns
+     * a SchemaPathUtil for augmentation,
+     * otherwise returns a SchemaPathUtil for child as
+     * call for {@link #fromDataSchemaNode(org.opendaylight.yangtools.yang.model.api.DataSchemaNode)}.
+     */
+    private static InstanceIdToNodes<?> fromAugmentation(final DataNodeContainer parent,
+            final AugmentationTarget parentAug, final DataSchemaNode child) {
+        for (final AugmentationSchemaNode aug : parentAug.getAvailableAugmentations()) {
+            final Optional<DataSchemaNode> potential = aug.findDataChildByName(child.getQName());
+            if (potential.isPresent()) {
+                return new InstanceIdToCompositeNodes.AugmentationNormalization(aug, parent);
+            }
+        }
+        return fromDataSchemaNode(child);
+    }
+
+    static InstanceIdToNodes<?> fromDataSchemaNode(final DataSchemaNode potential) {
+        if (potential instanceof ContainerSchemaNode) {
+            return new InstanceIdToCompositeNodes.ContainerTransformation((ContainerSchemaNode) potential);
+        } else if (potential instanceof ListSchemaNode) {
+            return fromListSchemaNode((ListSchemaNode) potential);
+        } else if (potential instanceof LeafSchemaNode) {
+            return new InstanceIdToSimpleNodes.LeafNormalization((LeafSchemaNode) potential);
+        } else if (potential instanceof ChoiceSchemaNode) {
+            return new InstanceIdToCompositeNodes.ChoiceNodeNormalization((ChoiceSchemaNode) potential);
+        } else if (potential instanceof LeafListSchemaNode) {
+            return fromLeafListSchemaNode((LeafListSchemaNode) potential);
+        } else if (potential instanceof AnydataSchemaNode) {
+            return new AnydataNormalization((AnydataSchemaNode) potential);
+        } else if (potential instanceof AnyxmlSchemaNode) {
+            return new AnyXmlNormalization((AnyxmlSchemaNode) potential);
+        }
+        return null;
+    }
+
+    private static InstanceIdToNodes<?> fromListSchemaNode(final ListSchemaNode potential) {
+        final List<QName> keyDefinition = potential.getKeyDefinition();
+        if (keyDefinition == null || keyDefinition.isEmpty()) {
+            return new UnkeyedListMixinNormalization(potential);
+        }
+        return potential.isUserOrdered() ? new InstanceIdToCompositeNodes.OrderedMapMixinNormalization(potential)
+                : new InstanceIdToCompositeNodes.UnorderedMapMixinNormalization(potential);
+    }
+
+    private static InstanceIdToNodes<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
+        return potential.isUserOrdered() ? new InstanceIdToCompositeNodes.OrderedLeafListMixinNormalization(potential)
+                : new InstanceIdToCompositeNodes.UnorderedLeafListMixinNormalization(potential);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToSimpleNodes.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToSimpleNodes.java
new file mode 100644 (file)
index 0000000..f4f4d17
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015 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 static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.Iterator;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+
+/**
+* Base strategy for converting an instance identifier into a normalized node structure for leaf and leaf-list types.
+*/
+abstract class InstanceIdToSimpleNodes<T extends PathArgument> extends InstanceIdToNodes<T> {
+
+    InstanceIdToSimpleNodes(final T identifier) {
+        super(identifier);
+    }
+
+    @Override
+    final NormalizedNode<?, ?> create(final PathArgument first, final Iterator<PathArgument> others,
+            final Optional<NormalizedNode<?, ?>> deepestChild) {
+        final NormalizedNodeBuilder<? extends PathArgument, Object,
+                ? extends NormalizedNode<? extends PathArgument, Object>> builder = getBuilder(first);
+
+        if (deepestChild.isPresent()) {
+            builder.withValue(deepestChild.get().getValue());
+        }
+
+        return builder.build();
+    }
+
+    @Override
+    final InstanceIdToNodes<?> getChild(final PathArgument child) {
+        return null;
+    }
+
+    @Override
+    final boolean isMixin() {
+        return false;
+    }
+
+    abstract NormalizedNodeBuilder<? extends PathArgument, Object,
+            ? extends NormalizedNode<? extends PathArgument, Object>> getBuilder(PathArgument node);
+
+    static final class LeafNormalization extends InstanceIdToSimpleNodes<NodeIdentifier> {
+        LeafNormalization(final LeafSchemaNode potential) {
+            super(new NodeIdentifier(potential.getQName()));
+        }
+
+        @Override
+        NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> getBuilder(final PathArgument node) {
+            return Builders.leafBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+
+    static final class LeafListEntryNormalization extends InstanceIdToSimpleNodes<NodeWithValue> {
+        LeafListEntryNormalization(final LeafListSchemaNode potential) {
+            super(new NodeWithValue<>(potential.getQName(), null));
+        }
+
+        @Override
+        NormalizedNodeBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> getBuilder(final PathArgument node) {
+            checkArgument(node instanceof NodeWithValue);
+            return Builders.leafSetEntryBuilder().withNodeIdentifier((NodeWithValue<?>) node)
+                    .withValue(((NodeWithValue<?>) node).getValue());
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InterningLeafNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InterningLeafNodeBuilder.java
new file mode 100644 (file)
index 0000000..f50ba27
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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 static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.Interner;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
+import org.opendaylight.yangtools.yang.data.util.LeafInterner;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+
+final class InterningLeafNodeBuilder<T> extends ImmutableLeafNodeBuilder<T> {
+    private final Interner<LeafNode<T>> interner;
+
+    private InterningLeafNodeBuilder(final Interner<LeafNode<T>> interner) {
+        this.interner = requireNonNull(interner);
+    }
+
+    static <T> @Nullable InterningLeafNodeBuilder<T> forSchema(final @Nullable DataSchemaNode schema) {
+        if (schema instanceof LeafSchemaNode) {
+            final Optional<Interner<LeafNode<T>>> interner = LeafInterner.forSchema((LeafSchemaNode)schema);
+            if (interner.isPresent()) {
+                return new InterningLeafNodeBuilder<>(interner.get());
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public LeafNode<T> build() {
+        return interner.intern(super.build());
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InterningLeafSetNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InterningLeafSetNodeBuilder.java
new file mode 100644 (file)
index 0000000..e55b103
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015 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 static java.util.Objects.requireNonNull;
+
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.util.LeafsetEntryInterner;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+final class InterningLeafSetNodeBuilder<T> extends ImmutableLeafSetNodeBuilder<T> {
+    private final LeafsetEntryInterner interner;
+
+    private InterningLeafSetNodeBuilder(final LeafsetEntryInterner interner) {
+        this.interner = requireNonNull(interner);
+    }
+
+    private InterningLeafSetNodeBuilder(final LeafsetEntryInterner interner, final int sizeHint) {
+        super(sizeHint);
+        this.interner = requireNonNull(interner);
+    }
+
+    private static LeafsetEntryInterner getInterner(final DataSchemaNode schema) {
+        return schema instanceof LeafSetNode ? LeafsetEntryInterner.forSchema((LeafListSchemaNode) schema) : null;
+    }
+
+    static <T> ListNodeBuilder<T, LeafSetEntryNode<T>> create(final DataSchemaNode schema) {
+        final LeafsetEntryInterner interner = getInterner(schema);
+        if (interner != null) {
+            return new InterningLeafSetNodeBuilder<>(interner);
+        }
+
+        return ImmutableLeafSetNodeBuilder.create();
+    }
+
+    static <T> ListNodeBuilder<T, LeafSetEntryNode<T>> create(final DataSchemaNode schema, final int sizeHint) {
+        final LeafsetEntryInterner interner = getInterner(schema);
+        if (interner != null) {
+            return new InterningLeafSetNodeBuilder<>(interner, sizeHint);
+        }
+
+        return ImmutableLeafSetNodeBuilder.create(sizeHint);
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withChild(final LeafSetEntryNode<T> child) {
+        return super.withChild(interner.intern(child));
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeMetadataResult.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeMetadataResult.java
new file mode 100644 (file)
index 0000000..84424ed
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.annotations.Beta;
+import java.util.Optional;
+import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadata;
+
+/**
+ * A NormalizedNodeResult which also holds NormalizedMetadata structure.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public class NormalizedNodeMetadataResult extends NormalizedNodeResult {
+    private NormalizedMetadata metadata;
+
+    public final Optional<NormalizedMetadata> getMetadata() {
+        return Optional.ofNullable(metadata);
+    }
+
+    @SuppressWarnings("checkstyle:hiddenField")
+    void setResult(final NormalizedMetadata metadata) {
+        this.metadata = metadata;
+    }
+
+    @Override
+    void reset() {
+        metadata = null;
+        super.reset();
+    }
+}
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..6751515
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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 org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Client allocated result holder for {@link ImmutableNormalizedNodeStreamWriter} which produces instance
+ * of NormalizedNode.
+ *
+ * <p>
+ * Client may supply result holder to {@link ImmutableNormalizedNodeStreamWriter} which will be once updated when
+ * the result is available.
+ *
+ * <p>
+ * 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;
+    }
+
+    public boolean isFinished() {
+        return finished;
+    }
+
+    void setResult(final NormalizedNode<?, ?> result) {
+        if (finished) {
+            throw new ResultAlreadySetException("Normalized Node result was already set.", this.result);
+        }
+        this.finished = true;
+        this.result = result;
+    }
+
+    void reset() {
+        result = null;
+        finished = false;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeResultBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeResultBuilder.java
new file mode 100644 (file)
index 0000000..c4617db
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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 static java.util.Objects.requireNonNull;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+
+@SuppressWarnings("rawtypes")
+final class NormalizedNodeResultBuilder implements NormalizedNodeContainerBuilder {
+    private final @NonNull NormalizedNodeResult result;
+
+    NormalizedNodeResultBuilder() {
+        this.result = new NormalizedNodeResult();
+    }
+
+    NormalizedNodeResultBuilder(final NormalizedNodeResult result) {
+        this.result = requireNonNull(result);
+    }
+
+    @NonNull NormalizedNodeResult result() {
+        return result;
+    }
+
+    @Override
+    public NormalizedNodeBuilder withValue(final Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public NormalizedNodeContainerBuilder withValue(final Collection 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 addChild(final NormalizedNode child) {
+        result.setResult(child);
+        return this;
+    }
+
+    @Override
+    public NormalizedNodeContainerBuilder removeChild(final PathArgument key) {
+        throw new UnsupportedOperationException();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ResultAlreadySetException.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ResultAlreadySetException.java
new file mode 100644 (file)
index 0000000..483a86c
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 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 org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class ResultAlreadySetException extends IllegalStateException {
+    private static final long serialVersionUID = 1L;
+
+    private final NormalizedNode<?, ?> resultData;
+
+    public ResultAlreadySetException(final String message, final NormalizedNode<?, ?> resultData) {
+        this(message, resultData, null);
+    }
+
+    public ResultAlreadySetException(final String message, final NormalizedNode<?, ?> resultData,
+            final Throwable cause) {
+        super(message, cause);
+        this.resultData = resultData;
+    }
+
+    public NormalizedNode<?, ?> getResultData() {
+        return resultData;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ReusableImmutableNormalizedNodeStreamWriter.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ReusableImmutableNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..fc49467
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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 static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.ReusableStreamReceiver;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
+
+/**
+ * A reusable variant of {@link ImmutableNormalizedNodeStreamWriter}. It can be reset into its base state and used for
+ * multiple streaming sessions.
+ */
+@Beta
+public final class ReusableImmutableNormalizedNodeStreamWriter extends ImmutableNormalizedNodeStreamWriter
+        implements ReusableStreamReceiver {
+    private final NormalizedNodeResultBuilder builder;
+
+    private final ImmutableLeafSetEntryNodeBuilder<?> leafsetEntryBuilder = ImmutableLeafSetEntryNodeBuilder.create();
+    private final ImmutableLeafNodeBuilder<?> leafNodeBuilder = new ImmutableLeafNodeBuilder<>();
+
+    private ReusableImmutableNormalizedNodeStreamWriter(final NormalizedNodeResultBuilder builder) {
+        super(builder);
+        this.builder = requireNonNull(builder);
+    }
+
+    public static @NonNull ReusableImmutableNormalizedNodeStreamWriter create() {
+        return new ReusableImmutableNormalizedNodeStreamWriter(new NormalizedNodeResultBuilder());
+    }
+
+    @Override
+    public void reset() {
+        builder.result().reset();
+        reset(builder);
+    }
+
+    @Override
+    public NormalizedNode<?, ?> getResult() {
+        return builder.result().getResult();
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    <T> ImmutableLeafNodeBuilder<T> leafNodeBuilder() {
+        return (ImmutableLeafNodeBuilder<T>) leafNodeBuilder;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    <T> ImmutableLeafSetEntryNodeBuilder<T> leafsetEntryNodeBuilder() {
+        return (ImmutableLeafSetEntryNodeBuilder<T>) leafsetEntryBuilder;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaOrderedNormalizedNodeWriter.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaOrderedNormalizedNodeWriter.java
new file mode 100644 (file)
index 0000000..143b9d4
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2016 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 com.google.common.collect.ArrayListMultimap;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is an iterator over a {@link NormalizedNode}. Unlike {@link NormalizedNodeWriter}, this iterates over elements
+ * in the order as they are defined in YANG file.
+ */
+public class SchemaOrderedNormalizedNodeWriter extends NormalizedNodeWriter {
+    private static final Logger LOG = LoggerFactory.getLogger(SchemaOrderedNormalizedNodeWriter.class);
+    private final SchemaContext schemaContext;
+    private final SchemaNode root;
+
+    private SchemaNode currentSchemaNode;
+
+    /**
+     * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
+     *
+     * @param writer
+     *            Back-end writer
+     * @param schemaContext
+     *            Schema context
+     * @param path
+     *            path
+     */
+    public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext,
+            final SchemaPath path) {
+        super(writer);
+        this.schemaContext = schemaContext;
+        final Collection<SchemaNode> schemaNodes = SchemaUtils.findParentSchemaNodesOnPath(schemaContext, path);
+        Preconditions.checkArgument(!schemaNodes.isEmpty(), "Unable to find schema node for supplied schema path: %s",
+                path);
+        if (schemaNodes.size() > 1) {
+            LOG.warn("More possible schema nodes {} for supplied schema path {}", schemaNodes, path);
+        }
+        this.root = schemaNodes.iterator().next();
+    }
+
+    @Override
+    public SchemaOrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
+        if (Objects.equals(root, schemaContext)) {
+            currentSchemaNode = schemaContext.getDataChildByName(node.getNodeType());
+        } else {
+            currentSchemaNode = root;
+        }
+        return write(node, currentSchemaNode);
+    }
+
+    /**
+     * Iterate over the provided collection and emit write
+     * events to the encapsulated {@link NormalizedNodeStreamWriter}.
+     *
+     * @param nodes nodes
+     * @return NormalizedNodeWriter this
+     * @throws IOException when thrown from the backing writer.
+     */
+    public SchemaOrderedNormalizedNodeWriter write(final Collection<DataContainerChild<?,?>> nodes) throws IOException {
+        currentSchemaNode = root;
+        if (writeChildren(nodes, currentSchemaNode, false)) {
+            return this;
+        }
+
+        throw new IllegalStateException("It wasn't possible to serialize nodes " + nodes);
+    }
+
+    private SchemaOrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node, final SchemaNode dataSchemaNode)
+            throws IOException {
+
+        //Set current schemaNode
+        try (SchemaNodeSetter sns = new SchemaNodeSetter(dataSchemaNode)) {
+            if (node == null) {
+                return this;
+            }
+
+            if (wasProcessedAsCompositeNode(node)) {
+                return this;
+            }
+
+            if (wasProcessAsSimpleNode(node)) {
+                return this;
+            }
+        }
+
+        throw new IllegalStateException("It wasn't possible to serialize node " + node);
+    }
+
+    private void write(final List<NormalizedNode<?, ?>> nodes, final SchemaNode dataSchemaNode) throws IOException {
+        for (final NormalizedNode<?, ?> node : nodes) {
+            write(node, dataSchemaNode);
+        }
+    }
+
+    @Override
+    protected boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children) throws IOException {
+        return writeChildren(children, currentSchemaNode, true);
+    }
+
+    private boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children,
+            final SchemaNode parentSchemaNode, final boolean endParent) throws IOException {
+        //Augmentations cannot be gotten with node.getChild so create our own structure with augmentations resolved
+        final ArrayListMultimap<QName, NormalizedNode<?, ?>> qNameToNodes = ArrayListMultimap.create();
+        for (final NormalizedNode<?, ?> child : children) {
+            if (child instanceof AugmentationNode) {
+                qNameToNodes.putAll(resolveAugmentations(child));
+            } else {
+                qNameToNodes.put(child.getNodeType(), child);
+            }
+        }
+
+        if (parentSchemaNode instanceof DataNodeContainer) {
+            if (parentSchemaNode instanceof ListSchemaNode && qNameToNodes.containsKey(parentSchemaNode.getQName())) {
+                write(qNameToNodes.get(parentSchemaNode.getQName()), parentSchemaNode);
+            } else {
+                for (final DataSchemaNode schemaNode : ((DataNodeContainer) parentSchemaNode).getChildNodes()) {
+                    write(qNameToNodes.get(schemaNode.getQName()), schemaNode);
+                }
+            }
+        } else if (parentSchemaNode instanceof ChoiceSchemaNode) {
+            for (final CaseSchemaNode ccNode : ((ChoiceSchemaNode) parentSchemaNode).getCases().values()) {
+                for (final DataSchemaNode dsn : ccNode.getChildNodes()) {
+                    if (qNameToNodes.containsKey(dsn.getQName())) {
+                        write(qNameToNodes.get(dsn.getQName()), dsn);
+                    }
+                }
+            }
+        } else {
+            for (final NormalizedNode<?, ?> child : children) {
+                writeLeaf(child);
+            }
+        }
+        if (endParent) {
+            getWriter().endNode();
+        }
+        return true;
+    }
+
+    private SchemaOrderedNormalizedNodeWriter writeLeaf(final NormalizedNode<?, ?> node) throws IOException {
+        if (wasProcessAsSimpleNode(node)) {
+            return this;
+        }
+
+        throw new IllegalStateException("It wasn't possible to serialize node " + node);
+    }
+
+    private ArrayListMultimap<QName, NormalizedNode<?, ?>> resolveAugmentations(final NormalizedNode<?, ?> child) {
+        final ArrayListMultimap<QName, NormalizedNode<?, ?>> resolvedAugs = ArrayListMultimap.create();
+        for (final NormalizedNode<?, ?> node : ((AugmentationNode) child).getValue()) {
+            if (node instanceof AugmentationNode) {
+                resolvedAugs.putAll(resolveAugmentations(node));
+            } else {
+                resolvedAugs.put(node.getNodeType(), node);
+            }
+        }
+        return resolvedAugs;
+    }
+
+    private final class SchemaNodeSetter implements AutoCloseable {
+
+        private final SchemaNode previousSchemaNode;
+
+        /**
+         * Sets current schema node new value and store old value for later restore.
+         */
+        SchemaNodeSetter(final SchemaNode schemaNode) {
+            previousSchemaNode = SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode;
+            SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode = schemaNode;
+        }
+
+        /**
+         * Restore previous schema node.
+         */
+        @Override
+        public void close() {
+            SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode = previousSchemaNode;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaUtils.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaUtils.java
new file mode 100644 (file)
index 0000000..9747829
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ * 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;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public final class SchemaUtils {
+    private SchemaUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Find the first schema with specified QName.
+     *
+     * @param qname schema node to find
+     * @param dataSchemaNode Iterable of schemaNodes to look through
+     * @return schema node with newest revision or absent if no schema node with matching qname is found
+     */
+    public static Optional<DataSchemaNode> findFirstSchema(final QName qname,
+            final Iterable<DataSchemaNode> dataSchemaNode) {
+        DataSchemaNode schema = null;
+        if (dataSchemaNode != null && qname != null) {
+            for (final DataSchemaNode dsn : dataSchemaNode) {
+                if (qname.isEqualWithoutRevision(dsn.getQName())) {
+                    if (schema == null || Revision.compare(schema.getQName().getRevision(),
+                        dsn.getQName().getRevision()) < 0) {
+                        schema = dsn;
+                    }
+                } else if (dsn instanceof ChoiceSchemaNode) {
+                    for (final CaseSchemaNode choiceCase : ((ChoiceSchemaNode) dsn).getCases().values()) {
+                        final Optional<DataSchemaNode> dataChildByName = choiceCase.findDataChildByName(qname);
+                        if (dataChildByName.isPresent()) {
+                            return dataChildByName;
+                        }
+                        final Optional<DataSchemaNode> foundDsn = findFirstSchema(qname, choiceCase.getChildNodes());
+                        if (foundDsn.isPresent()) {
+                            return foundDsn;
+                        }
+                    }
+                }
+            }
+        }
+        return Optional.ofNullable(schema);
+    }
+
+    /**
+     * Find child schema node identified by its QName within a provided schema node.
+     *
+     * @param schema schema for parent node - search root
+     * @param qname qname(with or without a revision) of a child node to be found in the parent schema
+     * @return found schema node
+     * @throws java.lang.IllegalStateException if the child was not found in parent schema node
+     */
+    public static DataSchemaNode findSchemaForChild(final DataNodeContainer schema, final QName qname) {
+        // Try to find child schema node directly, but use a fallback that compares QNames without revisions
+        // and auto-expands choices
+        final Optional<DataSchemaNode> dataChildByName = schema.findDataChildByName(qname);
+        return dataChildByName.isPresent() ? dataChildByName.get()
+                : findSchemaForChild(schema, qname, schema.getChildNodes());
+    }
+
+    public static @Nullable DataSchemaNode findSchemaForChild(final DataNodeContainer schema, final QName qname,
+            final boolean strictMode) {
+        if (strictMode) {
+            return findSchemaForChild(schema, qname);
+        }
+
+        final Optional<DataSchemaNode> childSchemaOptional = findFirstSchema(qname, schema.getChildNodes());
+        if (!childSchemaOptional.isPresent()) {
+            return null;
+        }
+        return childSchemaOptional.get();
+    }
+
+    public static DataSchemaNode findSchemaForChild(final DataNodeContainer schema, final QName qname,
+            final Iterable<DataSchemaNode> childNodes) {
+        final Optional<DataSchemaNode> childSchema = findFirstSchema(qname, childNodes);
+        checkState(childSchema.isPresent(), "Unknown child(ren) node(s) detected, identified by: %s, in: %s", qname,
+            schema);
+        return childSchema.get();
+    }
+
+    public static DataSchemaNode findSchemaForChild(final ChoiceSchemaNode schema, final QName childPartialQName) {
+        for (final CaseSchemaNode choiceCaseNode : schema.getCases().values()) {
+            final Optional<DataSchemaNode> childSchema = findFirstSchema(childPartialQName,
+                choiceCaseNode.getChildNodes());
+            if (childSchema.isPresent()) {
+                return childSchema.get();
+            }
+        }
+
+
+        throw new IllegalStateException(String.format("Unknown child(ren) node(s) detected, identified by: %s, in: %s",
+                childPartialQName, schema));
+    }
+
+    public static AugmentationSchemaNode findSchemaForAugment(final AugmentationTarget schema,
+            final Set<QName> qnames) {
+        final Optional<AugmentationSchemaNode> schemaForAugment = findAugment(schema, qnames);
+        checkState(schemaForAugment.isPresent(), "Unknown augmentation node detected, identified by: %s, in: %s",
+            qnames, schema);
+        return schemaForAugment.get();
+    }
+
+    public static AugmentationSchemaNode findSchemaForAugment(final ChoiceSchemaNode schema, final Set<QName> qnames) {
+        for (final CaseSchemaNode choiceCaseNode : schema.getCases().values()) {
+            final Optional<AugmentationSchemaNode> schemaForAugment = findAugment(choiceCaseNode, qnames);
+            if (schemaForAugment.isPresent()) {
+                return schemaForAugment.get();
+            }
+        }
+
+        throw new IllegalStateException(String.format("Unknown augmentation node detected, identified by: %s, in: %s",
+            qnames, schema));
+    }
+
+    private static Optional<AugmentationSchemaNode> findAugment(final AugmentationTarget schema,
+            final Set<QName> qnames) {
+        for (final AugmentationSchemaNode augment : schema.getAvailableAugmentations()) {
+            final Set<QName> qNamesFromAugment = ImmutableSet.copyOf(Collections2.transform(augment.getChildNodes(),
+                        DataSchemaNode::getQName));
+            if (qnames.equals(qNamesFromAugment)) {
+                return Optional.of(augment);
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    /**
+     * Recursively find all child nodes that come from choices.
+     *
+     * @param schema schema
+     * @return Map with all child nodes, to their most top augmentation
+     */
+    public static Map<QName, ChoiceSchemaNode> mapChildElementsFromChoices(final DataNodeContainer schema) {
+        return mapChildElementsFromChoices(schema, schema.getChildNodes());
+    }
+
+    private static Map<QName, ChoiceSchemaNode> mapChildElementsFromChoices(final DataNodeContainer schema,
+            final Iterable<DataSchemaNode> childNodes) {
+        final Map<QName, ChoiceSchemaNode> mappedChoices = new LinkedHashMap<>();
+
+        for (final DataSchemaNode childSchema : childNodes) {
+            if (childSchema instanceof ChoiceSchemaNode) {
+
+                if (isFromAugment(schema, childSchema)) {
+                    continue;
+                }
+
+                for (final CaseSchemaNode choiceCaseNode : ((ChoiceSchemaNode) childSchema).getCases().values()) {
+                    for (final QName qname : getChildNodesRecursive(choiceCaseNode)) {
+                        mappedChoices.put(qname, (ChoiceSchemaNode) childSchema);
+                    }
+                }
+            }
+        }
+
+        return mappedChoices;
+    }
+
+    private static boolean isFromAugment(final DataNodeContainer schema, final DataSchemaNode childSchema) {
+        if (!(schema instanceof AugmentationTarget)) {
+            return false;
+        }
+
+        for (final AugmentationSchemaNode augmentation : ((AugmentationTarget) schema).getAvailableAugmentations()) {
+            if (augmentation.findDataChildByName(childSchema.getQName()).isPresent()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Recursively find all child nodes that come from augmentations.
+     *
+     * @param schema schema
+     * @return Map with all child nodes, to their most top augmentation
+     */
+    public static Map<QName, AugmentationSchemaNode> mapChildElementsFromAugments(final AugmentationTarget schema) {
+
+        final Map<QName, AugmentationSchemaNode> childNodesToAugmentation = new LinkedHashMap<>();
+
+        // Find QNames of augmented child nodes
+        final Map<QName, AugmentationSchemaNode> augments = new HashMap<>();
+        for (final AugmentationSchemaNode augmentationSchema : schema.getAvailableAugmentations()) {
+            for (final DataSchemaNode dataSchemaNode : augmentationSchema.getChildNodes()) {
+                augments.put(dataSchemaNode.getQName(), augmentationSchema);
+            }
+        }
+
+        // Augmented nodes have to be looked up directly in augmentationTarget
+        // because nodes from augment do not contain nodes from other augmentations
+        if (schema instanceof DataNodeContainer) {
+
+            for (final DataSchemaNode child : ((DataNodeContainer) schema).getChildNodes()) {
+                // If is not augmented child, continue
+                if (!augments.containsKey(child.getQName())) {
+                    continue;
+                }
+
+                final AugmentationSchemaNode mostTopAugmentation = augments.get(child.getQName());
+
+                // recursively add all child nodes in case of augment, case and choice
+                if (child instanceof AugmentationSchemaNode || child instanceof CaseSchemaNode) {
+                    for (final QName qname : getChildNodesRecursive((DataNodeContainer) child)) {
+                        childNodesToAugmentation.put(qname, mostTopAugmentation);
+                    }
+                } else if (child instanceof ChoiceSchemaNode) {
+                    for (final CaseSchemaNode choiceCaseNode : ((ChoiceSchemaNode) child).getCases().values()) {
+                        for (final QName qname : getChildNodesRecursive(choiceCaseNode)) {
+                            childNodesToAugmentation.put(qname, mostTopAugmentation);
+                        }
+                    }
+                } else {
+                    childNodesToAugmentation.put(child.getQName(), mostTopAugmentation);
+                }
+            }
+        }
+
+        // Choice Node has to map child nodes from all its cases
+        if (schema instanceof ChoiceSchemaNode) {
+            for (final CaseSchemaNode choiceCaseNode : ((ChoiceSchemaNode) schema).getCases().values()) {
+                if (!augments.containsKey(choiceCaseNode.getQName())) {
+                    continue;
+                }
+
+                for (final QName qname : getChildNodesRecursive(choiceCaseNode)) {
+                    childNodesToAugmentation.put(qname, augments.get(choiceCaseNode.getQName()));
+                }
+            }
+        }
+
+        return childNodesToAugmentation;
+    }
+
+    /**
+     * Recursively list all child nodes. In case of choice, augment and cases, step in.
+     *
+     * @param nodeContainer node container
+     * @return set of QNames
+     */
+    public static Set<QName> getChildNodesRecursive(final DataNodeContainer nodeContainer) {
+        final Set<QName> allChildNodes = new HashSet<>();
+
+        for (final DataSchemaNode childSchema : nodeContainer.getChildNodes()) {
+            if (childSchema instanceof ChoiceSchemaNode) {
+                for (final CaseSchemaNode choiceCaseNode : ((ChoiceSchemaNode) childSchema).getCases().values()) {
+                    allChildNodes.addAll(getChildNodesRecursive(choiceCaseNode));
+                }
+            } else if (childSchema instanceof AugmentationSchemaNode || childSchema instanceof CaseSchemaNode) {
+                allChildNodes.addAll(getChildNodesRecursive((DataNodeContainer) childSchema));
+            } else {
+                allChildNodes.add(childSchema.getQName());
+            }
+        }
+
+        return allChildNodes;
+    }
+
+    /**
+     * Retrieves real schemas for augmented child node.
+     *
+     * <p>
+     * Schema of the same child node from augment, and directly from target is not the same.
+     * Schema of child node from augment is incomplete, therefore its useless for XML/NormalizedNode translation.
+     *
+     * @param targetSchema target schema
+     * @param augmentSchema augment schema
+     * @return set of nodes
+     */
+    public static Set<DataSchemaNode> getRealSchemasForAugment(final AugmentationTarget targetSchema,
+            final AugmentationSchemaNode augmentSchema) {
+        if (!targetSchema.getAvailableAugmentations().contains(augmentSchema)) {
+            return ImmutableSet.of();
+        }
+        if (targetSchema instanceof DataNodeContainer) {
+            return getRealSchemasForAugment((DataNodeContainer)targetSchema, augmentSchema);
+        }
+        final Set<DataSchemaNode> realChildNodes = new HashSet<>();
+        if (targetSchema instanceof ChoiceSchemaNode) {
+            for (final DataSchemaNode dataSchemaNode : augmentSchema.getChildNodes()) {
+                for (final CaseSchemaNode choiceCaseNode : ((ChoiceSchemaNode) targetSchema).getCases().values()) {
+                    if (getChildNodesRecursive(choiceCaseNode).contains(dataSchemaNode.getQName())) {
+                        realChildNodes.add(choiceCaseNode.getDataChildByName(dataSchemaNode.getQName()));
+                    }
+                }
+            }
+        }
+
+        return realChildNodes;
+    }
+
+    public static Set<DataSchemaNode> getRealSchemasForAugment(final DataNodeContainer targetSchema,
+            final AugmentationSchemaNode augmentSchema) {
+        final Set<DataSchemaNode> realChildNodes = new HashSet<>();
+        for (final DataSchemaNode dataSchemaNode : augmentSchema.getChildNodes()) {
+            final DataSchemaNode realChild = targetSchema.getDataChildByName(dataSchemaNode.getQName());
+            realChildNodes.add(realChild);
+        }
+        return realChildNodes;
+    }
+
+    public static Optional<CaseSchemaNode> detectCase(final ChoiceSchemaNode schema,
+            final DataContainerChild<?, ?> child) {
+        for (final CaseSchemaNode choiceCaseNode : schema.getCases().values()) {
+            if (child instanceof AugmentationNode
+                    && belongsToCaseAugment(choiceCaseNode, (AugmentationIdentifier) child.getIdentifier())) {
+                return Optional.of(choiceCaseNode);
+            } else if (choiceCaseNode.findDataChildByName(child.getNodeType()).isPresent()) {
+                return Optional.of(choiceCaseNode);
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    public static boolean belongsToCaseAugment(final CaseSchemaNode caseNode,
+            final AugmentationIdentifier childToProcess) {
+        for (final AugmentationSchemaNode augmentationSchema : caseNode.getAvailableAugmentations()) {
+
+            final Set<QName> currentAugmentChildNodes = new HashSet<>();
+            for (final DataSchemaNode dataSchemaNode : augmentationSchema.getChildNodes()) {
+                currentAugmentChildNodes.add(dataSchemaNode.getQName());
+            }
+
+            if (childToProcess.getPossibleChildNames().equals(currentAugmentChildNodes)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such
+     * node is found then it is returned, else null.
+     *
+     * @param parent parent node
+     * @param child child node
+     * @return augmentation schema
+     */
+    public static AugmentationSchemaNode findCorrespondingAugment(final DataSchemaNode parent,
+            final DataSchemaNode child) {
+        if (!(parent instanceof AugmentationTarget) || parent instanceof ChoiceSchemaNode) {
+            return null;
+        }
+
+        for (final AugmentationSchemaNode augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
+            final Optional<DataSchemaNode> childInAugmentation = augmentation.findDataChildByName(child.getQName());
+            if (childInAugmentation.isPresent()) {
+                return augmentation;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Finds schema node for given path in schema context. This method performs
+     * lookup in the namespace of all leafs, leaf-lists, lists, containers,
+     * choices, rpcs, actions, notifications, anydatas, and anyxmls according to
+     * Rfc6050/Rfc7950 section 6.2.1.
+     *
+     * @param schemaContext
+     *            schema context
+     * @param path
+     *            path
+     * @return schema node on path
+     */
+    public static SchemaNode findDataParentSchemaOnPath(final SchemaContext schemaContext, final SchemaPath path) {
+        SchemaNode current = requireNonNull(schemaContext);
+        for (final QName qname : path.getPathFromRoot()) {
+            current = findDataChildSchemaByQName(current, qname);
+        }
+        return current;
+    }
+
+    /**
+     * Find child data schema node identified by its QName within a provided schema node. This method performs lookup
+     * in the namespace of all leafs, leaf-lists, lists, containers, choices, rpcs, actions, notifications, anydatas
+     * and anyxmls according to RFC6050/RFC7950 section 6.2.1.
+     *
+     * @param node
+     *            schema node
+     * @param qname
+     *            QName
+     * @return data child schema node
+     * @throws IllegalArgumentException
+     *             if the schema node does not allow children
+     */
+    public static @Nullable SchemaNode findDataChildSchemaByQName(final SchemaNode node, final QName qname) {
+        if (node instanceof DataNodeContainer) {
+            SchemaNode child = ((DataNodeContainer) node).getDataChildByName(qname);
+            if (child == null && node instanceof SchemaContext) {
+                child = tryFind(((SchemaContext) node).getOperations(), qname).orElse(null);
+            }
+            if (child == null && node instanceof NotificationNodeContainer) {
+                child = tryFind(((NotificationNodeContainer) node).getNotifications(), qname).orElse(null);
+            }
+            if (child == null && node instanceof ActionNodeContainer) {
+                child = tryFind(((ActionNodeContainer) node).getActions(), qname).orElse(null);
+            }
+
+            return child;
+        }
+        if (node instanceof ChoiceSchemaNode) {
+            return ((ChoiceSchemaNode) node).getCases().get(qname);
+        }
+        if (node instanceof OperationDefinition) {
+            switch (qname.getLocalName()) {
+                case "input":
+                    return ((OperationDefinition) node).getInput();
+                case "output":
+                    return ((OperationDefinition) node).getOutput();
+                default:
+                    return null;
+            }
+        }
+
+        throw new IllegalArgumentException(String.format("Schema node %s does not allow children.", node));
+    }
+
+    /**
+     * Finds schema node for given path in schema context. This method performs lookup in both the namespace
+     * of groupings and the namespace of all leafs, leaf-lists, lists, containers, choices, rpcs, actions,
+     * notifications, anydatas and anyxmls according to Rfc6050/Rfc7950 section 6.2.1.
+     *
+     * <p>
+     * This method returns collection of SchemaNodes, because name conflicts can occur between the namespace
+     * of groupings and namespace of data nodes. This method finds and collects all schema nodes that matches supplied
+     * SchemaPath and returns them all as collection of schema nodes.
+     *
+     * @param schemaContext
+     *            schema context
+     * @param path
+     *            path
+     * @return collection of schema nodes on path
+     */
+    public static Collection<SchemaNode> findParentSchemaNodesOnPath(final SchemaContext schemaContext,
+            final SchemaPath path) {
+        final Collection<SchemaNode> currentNodes = new ArrayList<>();
+        final Collection<SchemaNode> childNodes = new ArrayList<>();
+        currentNodes.add(requireNonNull(schemaContext));
+        for (final QName qname : path.getPathFromRoot()) {
+            for (final SchemaNode current : currentNodes) {
+                childNodes.addAll(findChildSchemaNodesByQName(current, qname));
+            }
+            currentNodes.clear();
+            currentNodes.addAll(childNodes);
+            childNodes.clear();
+        }
+
+        return currentNodes;
+    }
+
+    /**
+     * Find child schema node identified by its QName within a provided schema node. This method performs lookup in both
+     * the namespace of groupings and the namespace of all leafs, leaf-lists, lists, containers, choices, rpcs,
+     * actions, notifications, anydatas and anyxmls according to RFC6050/RFC7950 section 6.2.1.
+     *
+     * <p>
+     * This method returns collection of SchemaNodes, because name conflicts can occur between the namespace
+     * of groupings and namespace of data nodes. This method finds and collects all schema nodes with supplied QName
+     * and returns them all as collection of schema nodes.
+     *
+     * @param node
+     *            schema node
+     * @param qname
+     *            QName
+     * @return collection of child schema nodes
+     * @throws IllegalArgumentException
+     *             if the schema node does not allow children
+     */
+    public static Collection<SchemaNode> findChildSchemaNodesByQName(final SchemaNode node, final QName qname) {
+        final List<SchemaNode> childNodes = new ArrayList<>();
+        final SchemaNode dataNode = findDataChildSchemaByQName(node, qname);
+        if (dataNode != null) {
+            childNodes.add(dataNode);
+        }
+        if (node instanceof DataNodeContainer) {
+            tryFind(((DataNodeContainer) node).getGroupings(), qname).ifPresent(childNodes::add);
+        }
+        return childNodes.isEmpty() ? ImmutableList.of() : ImmutableList.copyOf(childNodes);
+    }
+
+    private static <T extends SchemaNode> Optional<T> tryFind(final Collection<T> nodes, final QName qname) {
+        return nodes.stream().filter(node -> qname.equals(node.getQName())).findFirst();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/CollectionNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/CollectionNodeBuilder.java
new file mode 100644 (file)
index 0000000..2a149ab
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.builder.api;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+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.schema.NormalizedNode;
+
+public interface CollectionNodeBuilder<V extends NormalizedNode<?, ?>, R extends NormalizedNode<NodeIdentifier, ?>>
+        extends NormalizedNodeContainerBuilder<NodeIdentifier, PathArgument, V, R> {
+
+    @Override
+    CollectionNodeBuilder<V, R> withValue(Collection<V> value);
+
+    @Override
+    CollectionNodeBuilder<V, R> withNodeIdentifier(NodeIdentifier nodeIdentifier);
+
+    @NonNull CollectionNodeBuilder<V, R> withChild(V child);
+
+    @NonNull CollectionNodeBuilder<V, R> withoutChild(PathArgument key);
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/DataContainerNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/DataContainerNodeBuilder.java
new file mode 100644 (file)
index 0000000..6b3730a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.builder.api;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+
+public interface DataContainerNodeBuilder<I extends PathArgument, R extends DataContainerNode<I>>
+        extends NormalizedNodeContainerBuilder<I, PathArgument, DataContainerChild<? extends PathArgument, ?>, R> {
+
+    @Override
+    DataContainerNodeBuilder<I, R> withValue(Collection<DataContainerChild<? extends PathArgument, ?>> value);
+
+    @Override
+    DataContainerNodeBuilder<I, R> withNodeIdentifier(I nodeIdentifier);
+
+    @NonNull DataContainerNodeBuilder<I, R> withChild(DataContainerChild<?, ?> child);
+
+    @NonNull DataContainerNodeBuilder<I, R> withoutChild(PathArgument key);
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/ListNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/ListNodeBuilder.java
new file mode 100644 (file)
index 0000000..0563fbe
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.builder.api;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+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.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+
+public interface ListNodeBuilder<T, V>
+        extends CollectionNodeBuilder<LeafSetEntryNode<T>, LeafSetNode<T>> {
+
+    @Override
+    ListNodeBuilder<T, V> withNodeIdentifier(NodeIdentifier nodeIdentifier);
+
+    @Override
+    ListNodeBuilder<T, V> withValue(Collection<LeafSetEntryNode<T>> value);
+
+    @Override
+    ListNodeBuilder<T, V> withChild(LeafSetEntryNode<T> child);
+
+    @Override
+    ListNodeBuilder<T, V> withoutChild(PathArgument key);
+
+    @NonNull ListNodeBuilder<T, V> withChildValue(T child);
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/NormalizedNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/NormalizedNodeBuilder.java
new file mode 100644 (file)
index 0000000..e4d528a
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.builder.api;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface NormalizedNodeBuilder<I extends PathArgument, V, R extends NormalizedNode<I, ?>> extends Builder<R> {
+
+    @NonNull NormalizedNodeBuilder<I, V, R> withValue(V value);
+
+    @NonNull NormalizedNodeBuilder<I, V, R> withNodeIdentifier(I nodeIdentifier);
+
+    @Override
+    R build();
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/NormalizedNodeContainerBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/api/NormalizedNodeContainerBuilder.java
new file mode 100644 (file)
index 0000000..b85f678
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.builder.api;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface NormalizedNodeContainerBuilder<K extends PathArgument, CK extends PathArgument,
+        CV extends NormalizedNode<? extends CK, ?>, P extends NormalizedNode<K, ?>>
+        extends NormalizedNodeBuilder<K, Collection<CV>, P> {
+
+    @Override
+    NormalizedNodeContainerBuilder<K, CK, CV, P> withNodeIdentifier(K nodeIdentifier);
+
+    @Override
+    NormalizedNodeContainerBuilder<K, CK, CV, P> withValue(Collection<CV> value);
+
+    @NonNull NormalizedNodeContainerBuilder<K, CK, CV, P> addChild(CV child);
+
+    @NonNull NormalizedNodeContainerBuilder<K, CK, CV, P> removeChild(CK key);
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/AbstractImmutableDataContainerNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/AbstractImmutableDataContainerNodeBuilder.java
new file mode 100644 (file)
index 0000000..dc8cf1e
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * 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.builder.impl;
+
+import com.google.common.collect.Maps;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.opendaylight.yangtools.util.ModifiableMapPhase;
+import org.opendaylight.yangtools.util.UnmodifiableMapPhase;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.CloneableMap;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.LazyLeafOperations;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractImmutableDataContainerNodeBuilder<I extends PathArgument, R extends DataContainerNode<I>>
+        implements DataContainerNodeBuilder<I, R> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractImmutableDataContainerNodeBuilder.class);
+    private static final int DEFAULT_CAPACITY = 4;
+
+    // This is a run-time constant, i.e. it is set at class initialization time. We expect JIT to notice this and
+    // perform DCE based on the value, so that the static newHashMap() methods end up not containing the branch at all.
+    private static final boolean USE_LINKEDHASHMAP;
+
+    static {
+        USE_LINKEDHASHMAP = Boolean.getBoolean(
+            "org.opendaylight.yangtools.yang.data.impl.schema.builder.retain-child-order");
+        if (USE_LINKEDHASHMAP) {
+            LOG.info("Initial immutable DataContainerNodes are retaining child insertion order");
+        }
+    }
+
+    private Map<PathArgument, Object> value;
+    private I nodeIdentifier;
+
+    /*
+     * Tracks whether the builder is dirty, e.g. whether the value map has been used
+     * to construct a child. If it has, we detect this condition before any further
+     * modification and create a new value map with same contents. This way we do not
+     * force a map copy if the builder is not reused.
+     */
+    private boolean dirty;
+
+    protected AbstractImmutableDataContainerNodeBuilder() {
+        this.value = newHashMap();
+        this.dirty = false;
+    }
+
+    protected AbstractImmutableDataContainerNodeBuilder(final int sizeHint) {
+        if (sizeHint >= 0) {
+            this.value = newHashMap(sizeHint);
+        } else {
+            this.value = newHashMap();
+        }
+        this.dirty = false;
+    }
+
+    protected AbstractImmutableDataContainerNodeBuilder(final AbstractImmutableDataContainerNode<I> node) {
+        this.nodeIdentifier = node.getIdentifier();
+
+        /*
+         * This quite awkward. What we actually want to be saying here is: give me
+         * a copy-on-write view of your children. The API involved in that could be
+         * a bit hairy, so we do the next best thing and rely on the fact that the
+         * returned object implements a specific interface, which leaks the functionality
+         * we need.
+         */
+        this.value = node.getChildren();
+        this.dirty = true;
+    }
+
+    protected final I getNodeIdentifier() {
+        return nodeIdentifier;
+    }
+
+    protected final DataContainerChild<? extends PathArgument, ?> getChild(final PathArgument child) {
+        return LazyLeafOperations.getChild(value, child);
+    }
+
+    protected final Map<PathArgument, Object> buildValue() {
+        if (value instanceof ModifiableMapPhase) {
+            return ((ModifiableMapPhase<PathArgument, Object>)value).toUnmodifiableMap();
+        }
+
+        dirty = true;
+        return value;
+    }
+
+    private void checkDirty() {
+        if (dirty) {
+            if (value instanceof UnmodifiableMapPhase) {
+                value = ((UnmodifiableMapPhase<PathArgument, Object>) value).toModifiableMap();
+            } else if (value instanceof CloneableMap) {
+                value = ((CloneableMap<PathArgument, Object>) value).createMutableClone();
+            } else {
+                value = newHashMap(value);
+            }
+            dirty = false;
+        }
+    }
+
+    @Override
+    public DataContainerNodeBuilder<I, R> withValue(
+            final Collection<DataContainerChild<? extends PathArgument, ?>> withValue) {
+        // TODO Replace or putAll ?
+        for (final DataContainerChild<? extends PathArgument, ?> dataContainerChild : withValue) {
+            withChild(dataContainerChild);
+        }
+        return this;
+    }
+
+    @Override
+    public DataContainerNodeBuilder<I, R> withChild(final DataContainerChild<?, ?> child) {
+        checkDirty();
+        LazyLeafOperations.putChild(value, child);
+        return this;
+    }
+
+    @Override
+    public DataContainerNodeBuilder<I, R> withoutChild(final PathArgument key) {
+        checkDirty();
+        this.value.remove(key);
+        return this;
+    }
+
+    @Override
+    public DataContainerNodeBuilder<I, R> withNodeIdentifier(final I withNodeIdentifier) {
+        this.nodeIdentifier = withNodeIdentifier;
+        return this;
+    }
+
+    @Override
+    public DataContainerNodeBuilder<I, R> addChild(
+            final DataContainerChild<? extends PathArgument, ?> child) {
+        return withChild(child);
+    }
+
+    @Override
+    public NormalizedNodeContainerBuilder<I, PathArgument, DataContainerChild<? extends PathArgument, ?>, R>
+            removeChild(final PathArgument key) {
+        return withoutChild(key);
+    }
+
+    // Static utility methods providing dispatch to proper HashMap implementation.
+    private static <K, V> HashMap<K, V> newHashMap() {
+        return USE_LINKEDHASHMAP ? new LinkedHashMap<>(DEFAULT_CAPACITY) : new HashMap<>(DEFAULT_CAPACITY);
+    }
+
+    private static <K, V> HashMap<K, V> newHashMap(final int size) {
+        return USE_LINKEDHASHMAP ? Maps.newLinkedHashMapWithExpectedSize(size) : Maps.newHashMapWithExpectedSize(size);
+    }
+
+    private static <K, V> HashMap<K, V> newHashMap(final Map<K, V> map) {
+        return USE_LINKEDHASHMAP ? new LinkedHashMap<>(map) : new HashMap<>(map);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/AbstractImmutableNormalizedNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/AbstractImmutableNormalizedNodeBuilder.java
new file mode 100644 (file)
index 0000000..338b4c7
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.builder.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+
+abstract class AbstractImmutableNormalizedNodeBuilder<I extends PathArgument, V, R extends NormalizedNode<I, ?>>
+        implements NormalizedNodeBuilder<I, V, R> {
+    private I nodeIdentifier;
+    private V value;
+
+    protected final I getNodeIdentifier() {
+        checkState(nodeIdentifier != null, "Identifier has not been set");
+        return nodeIdentifier;
+    }
+
+    protected final V getValue() {
+        checkState(value != null, "Value has not been set");
+        return value;
+    }
+
+    @Override
+    public NormalizedNodeBuilder<I, V, R> withValue(final V withValue) {
+        this.value = requireNonNull(withValue);
+        return this;
+    }
+
+    @Override
+    public NormalizedNodeBuilder<I, V, R> withNodeIdentifier(final I withNodeIdentifier) {
+        this.nodeIdentifier = requireNonNull(withNodeIdentifier);
+        return this;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAnyXmlNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAnyXmlNodeBuilder.java
new file mode 100644 (file)
index 0000000..18aa815
--- /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.builder.impl;
+
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedSimpleValueNode;
+
+public class ImmutableAnyXmlNodeBuilder
+        extends AbstractImmutableNormalizedNodeBuilder<NodeIdentifier, DOMSource, DOMSourceAnyxmlNode> {
+
+    public static @NonNull NormalizedNodeBuilder<NodeIdentifier, DOMSource, DOMSourceAnyxmlNode> create() {
+        return new ImmutableAnyXmlNodeBuilder();
+    }
+
+    @Override
+    public DOMSourceAnyxmlNode build() {
+        return new ImmutableXmlNode(getNodeIdentifier(), getValue());
+    }
+
+    private static final class ImmutableXmlNode
+            extends AbstractImmutableNormalizedSimpleValueNode<NodeIdentifier, DOMSource>
+            implements DOMSourceAnyxmlNode {
+
+        ImmutableXmlNode(final NodeIdentifier nodeIdentifier, final DOMSource value) {
+            super(nodeIdentifier, value);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAnyXmlNodeSchemaAwareBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAnyXmlNodeSchemaAwareBuilder.java
new file mode 100644 (file)
index 0000000..c48e3e5
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.builder.impl;
+
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+
+public final class ImmutableAnyXmlNodeSchemaAwareBuilder extends ImmutableAnyXmlNodeBuilder {
+
+    private ImmutableAnyXmlNodeSchemaAwareBuilder(final AnyxmlSchemaNode schema) {
+        super.withNodeIdentifier(NodeIdentifier.create(schema.getQName()));
+    }
+
+    public static @NonNull NormalizedNodeBuilder<NodeIdentifier, DOMSource, DOMSourceAnyxmlNode> create(
+            final AnyxmlSchemaNode schema) {
+        return new ImmutableAnyXmlNodeSchemaAwareBuilder(schema);
+    }
+
+    @Override
+    public NormalizedNodeBuilder<NodeIdentifier, DOMSource, DOMSourceAnyxmlNode> withValue(final DOMSource withValue) {
+        return super.withValue(withValue);
+    }
+
+    @Override
+    public NormalizedNodeBuilder<NodeIdentifier, DOMSource, DOMSourceAnyxmlNode> withNodeIdentifier(
+            final NodeIdentifier withNodeIdentifier) {
+        throw new UnsupportedOperationException("Node identifier created from schema");
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAnydataNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAnydataNodeBuilder.java
new file mode 100644 (file)
index 0000000..2d45aae
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.builder.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedSimpleValueNode;
+
+@Beta
+public class ImmutableAnydataNodeBuilder<V>
+        extends AbstractImmutableNormalizedNodeBuilder<NodeIdentifier, V, AnydataNode<V>> {
+    private final Class<V> objectModel;
+
+    ImmutableAnydataNodeBuilder(final Class<V> objectModel) {
+        this.objectModel = requireNonNull(objectModel);
+    }
+
+    public static <V> @NonNull NormalizedNodeBuilder<NodeIdentifier, V, AnydataNode<V>> create(
+            final Class<V> objectModel) {
+        return new ImmutableAnydataNodeBuilder<>(objectModel);
+    }
+
+    @Override
+    public AnydataNode<V> build() {
+        return new ImmutableAnydataNode<>(getNodeIdentifier(), getValue(), objectModel);
+    }
+
+    private static final class ImmutableAnydataNode<V>
+            extends AbstractImmutableNormalizedSimpleValueNode<NodeIdentifier, V> implements AnydataNode<V> {
+        private final @NonNull Class<V> objectModel;
+
+        protected ImmutableAnydataNode(final NodeIdentifier nodeIdentifier, final V value, final Class<V> objectModel) {
+            super(nodeIdentifier, value);
+            this.objectModel = requireNonNull(objectModel);
+        }
+
+        @Override
+        public Class<V> getValueObjectModel() {
+            return objectModel;
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAugmentationNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAugmentationNodeBuilder.java
new file mode 100644 (file)
index 0000000..912cdc4
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.builder.impl;
+
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataValidationException;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
+
+public class ImmutableAugmentationNodeBuilder
+        extends AbstractImmutableDataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> {
+
+    protected ImmutableAugmentationNodeBuilder() {
+    }
+
+    protected ImmutableAugmentationNodeBuilder(final int sizeHint) {
+        super(sizeHint);
+    }
+
+    public ImmutableAugmentationNodeBuilder(final ImmutableAugmentationNode node) {
+        super(node);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> create() {
+        return new ImmutableAugmentationNodeBuilder();
+    }
+
+    public static @NonNull DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> create(
+            final int sizeHint) {
+        return new ImmutableAugmentationNodeBuilder(sizeHint);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> create(
+            final AugmentationNode node) {
+        if (!(node instanceof ImmutableAugmentationNode)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+
+        return new ImmutableAugmentationNodeBuilder((ImmutableAugmentationNode)node);
+    }
+
+    @Override
+    public DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> withChild(
+            final DataContainerChild<?, ?> child) {
+        // Check nested augments
+        DataValidationException.checkLegalData(!(child instanceof AugmentationNode),
+                "Unable to add: %s, as a child for: %s, Nested augmentations are not permitted", child.getNodeType(),
+                getNodeIdentifier() == null ? this : getNodeIdentifier());
+
+        return super.withChild(child);
+    }
+
+    @Override
+    public DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> withoutChild(final PathArgument key) {
+        return super.withoutChild(key);
+    }
+
+    @Override
+    public AugmentationNode build() {
+        return new ImmutableAugmentationNode(getNodeIdentifier(), buildValue());
+    }
+
+    private static final class ImmutableAugmentationNode
+            extends AbstractImmutableDataContainerNode<AugmentationIdentifier> implements AugmentationNode {
+
+        ImmutableAugmentationNode(final AugmentationIdentifier nodeIdentifier,
+                final Map<PathArgument, Object> children) {
+            super(children, nodeIdentifier);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAugmentationNodeSchemaAwareBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableAugmentationNodeSchemaAwareBuilder.java
new file mode 100644 (file)
index 0000000..eea80cb
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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.builder.impl;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataNodeContainerValidator;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+
+public class ImmutableAugmentationNodeSchemaAwareBuilder extends ImmutableAugmentationNodeBuilder {
+
+    private final DataNodeContainerValidator validator;
+
+    protected ImmutableAugmentationNodeSchemaAwareBuilder(final AugmentationSchemaNode schema) {
+        this.validator = new DataNodeContainerValidator(schema);
+        super.withNodeIdentifier(DataSchemaContextNode.augmentationIdentifierFrom(schema));
+    }
+
+    @Override
+    public DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> withNodeIdentifier(
+            final AugmentationIdentifier withNodeIdentifier) {
+        throw new UnsupportedOperationException("Node identifier created from schema");
+    }
+
+    @Override
+    public DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> withChild(
+            final DataContainerChild<?, ?> child) {
+        return super.withChild(validator.validateChild(child));
+    }
+
+    public static @NonNull DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> create(
+            final AugmentationSchemaNode schema) {
+        return new ImmutableAugmentationNodeSchemaAwareBuilder(schema);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableChoiceNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableChoiceNodeBuilder.java
new file mode 100644 (file)
index 0000000..80be092
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.builder.impl;
+
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+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.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
+
+public class ImmutableChoiceNodeBuilder extends AbstractImmutableDataContainerNodeBuilder<NodeIdentifier, ChoiceNode> {
+
+    protected ImmutableChoiceNodeBuilder() {
+
+    }
+
+    protected ImmutableChoiceNodeBuilder(final int sizeHint) {
+        super(sizeHint);
+    }
+
+    protected ImmutableChoiceNodeBuilder(final ImmutableChoiceNode node) {
+        super(node);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> create() {
+        return new ImmutableChoiceNodeBuilder();
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> create(final int sizeHint) {
+        return new ImmutableChoiceNodeBuilder(sizeHint);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> create(final ChoiceNode node) {
+        if (!(node instanceof ImmutableChoiceNode)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+
+        return new ImmutableChoiceNodeBuilder((ImmutableChoiceNode)node);
+    }
+
+    @Override
+    public ChoiceNode build() {
+        return new ImmutableChoiceNode(getNodeIdentifier(), buildValue());
+    }
+
+    private static final class ImmutableChoiceNode extends AbstractImmutableDataContainerNode<NodeIdentifier>
+            implements ChoiceNode {
+
+        ImmutableChoiceNode(final NodeIdentifier nodeIdentifier, final Map<PathArgument, Object> children) {
+            super(children, nodeIdentifier);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableChoiceNodeSchemaAwareBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableChoiceNodeSchemaAwareBuilder.java
new file mode 100644 (file)
index 0000000..2b56fb1
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.builder.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataNodeContainerValidator;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataValidationException;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+
+public class ImmutableChoiceNodeSchemaAwareBuilder extends ImmutableChoiceNodeBuilder {
+
+    private final ChoiceSchemaNode schema;
+    private DataNodeContainerValidator validator;
+
+    protected ImmutableChoiceNodeSchemaAwareBuilder(final ChoiceSchemaNode schema) {
+        this.schema = requireNonNull(schema, "Schema was null");
+        super.withNodeIdentifier(NodeIdentifier.create(schema.getQName()));
+    }
+
+    @Override
+    public DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> withNodeIdentifier(
+            final NodeIdentifier withNodeIdentifier) {
+        throw new UnsupportedOperationException("Node identifier created from schema");
+    }
+
+    @Override
+    public DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> withChild(final DataContainerChild<?, ?> child) {
+        if (validator == null) {
+            Optional<CaseSchemaNode> detectedCaseOpt = SchemaUtils.detectCase(schema, child);
+            DataValidationException.checkLegalChild(detectedCaseOpt.isPresent(), child.getIdentifier(), schema);
+            validator = new DataNodeContainerValidator(detectedCaseOpt.get());
+        }
+
+        return super.withChild(validator.validateChild(child));
+    }
+
+    @Override
+    public ChoiceNode build() {
+        // TODO validate when statement
+        return super.build();
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> create(final ChoiceSchemaNode schema) {
+        return new ImmutableChoiceNodeSchemaAwareBuilder(schema);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableContainerNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableContainerNodeBuilder.java
new file mode 100644 (file)
index 0000000..caa1e7d
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.builder.impl;
+
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+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.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
+
+public class ImmutableContainerNodeBuilder
+        extends AbstractImmutableDataContainerNodeBuilder<NodeIdentifier, ContainerNode> {
+
+    protected ImmutableContainerNodeBuilder() {
+
+    }
+
+    protected ImmutableContainerNodeBuilder(final int sizeHint) {
+        super(sizeHint);
+    }
+
+    protected ImmutableContainerNodeBuilder(final ImmutableContainerNode node) {
+        super(node);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, ContainerNode> create() {
+        return new ImmutableContainerNodeBuilder();
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, ContainerNode> create(final int sizeHint) {
+        return new ImmutableContainerNodeBuilder(sizeHint);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, ContainerNode> create(
+            final ContainerNode node) {
+        if (!(node instanceof ImmutableContainerNode)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+        return new ImmutableContainerNodeBuilder((ImmutableContainerNode) node);
+    }
+
+    @Override
+    public ContainerNode build() {
+        return new ImmutableContainerNode(getNodeIdentifier(), buildValue());
+    }
+
+    protected static final class ImmutableContainerNode extends AbstractImmutableDataContainerNode<NodeIdentifier>
+            implements ContainerNode {
+
+        ImmutableContainerNode(final NodeIdentifier nodeIdentifier, final Map<PathArgument, Object> children) {
+            super(children, nodeIdentifier);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableContainerNodeSchemaAwareBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableContainerNodeSchemaAwareBuilder.java
new file mode 100644 (file)
index 0000000..9f25600
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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.builder.impl;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataNodeContainerValidator;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+
+public final class ImmutableContainerNodeSchemaAwareBuilder extends ImmutableContainerNodeBuilder {
+    private final DataNodeContainerValidator validator;
+
+    private ImmutableContainerNodeSchemaAwareBuilder(final ContainerSchemaNode schema) {
+        this.validator = new DataNodeContainerValidator(schema);
+        super.withNodeIdentifier(NodeIdentifier.create(schema.getQName()));
+    }
+
+    private ImmutableContainerNodeSchemaAwareBuilder(final ContainerSchemaNode schema,
+            final ImmutableContainerNode node) {
+        super(node);
+        this.validator = new DataNodeContainerValidator(schema);
+        super.withNodeIdentifier(NodeIdentifier.create(schema.getQName()));
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, ContainerNode> create(
+            final ContainerSchemaNode schema) {
+        return new ImmutableContainerNodeSchemaAwareBuilder(schema);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, ContainerNode> create(
+            final ContainerSchemaNode schema, final ContainerNode node) {
+        if (!(node instanceof ImmutableContainerNode)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+        return new ImmutableContainerNodeSchemaAwareBuilder(schema, (ImmutableContainerNode)node);
+    }
+
+    @Override
+    public DataContainerNodeBuilder<NodeIdentifier, ContainerNode> withNodeIdentifier(
+            final NodeIdentifier withNodeIdentifier) {
+        throw new UnsupportedOperationException("Node identifier created from schema");
+    }
+
+    @Override
+    public DataContainerNodeBuilder<NodeIdentifier, ContainerNode> withChild(final DataContainerChild<?, ?> child) {
+        validator.validateChild(child.getIdentifier());
+        return super.withChild(child);
+    }
+
+    @Override
+    public ContainerNode build() {
+        // TODO check when statements... somewhere
+        return super.build();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafNodeBuilder.java
new file mode 100644 (file)
index 0000000..96c2413
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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.builder.impl;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedSimpleValueNode;
+
+public class ImmutableLeafNodeBuilder<T>
+        extends AbstractImmutableNormalizedNodeBuilder<NodeIdentifier, T, LeafNode<T>> {
+
+    public static <T> @NonNull NormalizedNodeBuilder<NodeIdentifier, T, LeafNode<T>> create() {
+        return new ImmutableLeafNodeBuilder<>();
+    }
+
+    @Beta
+    @SuppressWarnings("unchecked")
+    public static <T> @NonNull LeafNode<T> createNode(final NodeIdentifier identifier, final T value) {
+        if (value instanceof byte[]) {
+            return (LeafNode<T>) new ImmutableBinaryLeafNode(identifier, (byte[]) value);
+        }
+        return new ImmutableLeafNode<>(identifier, value);
+    }
+
+    @Override
+    public LeafNode<T> build() {
+        return createNode(getNodeIdentifier(), getValue());
+    }
+
+    private static final class ImmutableLeafNode<T>
+            extends AbstractImmutableNormalizedSimpleValueNode<NodeIdentifier, T> implements LeafNode<T> {
+        ImmutableLeafNode(final NodeIdentifier nodeIdentifier, final T value) {
+            super(nodeIdentifier, value);
+        }
+    }
+
+    private static final class ImmutableBinaryLeafNode
+            extends AbstractImmutableNormalizedSimpleValueNode<NodeIdentifier, byte[]> implements LeafNode<byte[]> {
+        ImmutableBinaryLeafNode(final NodeIdentifier nodeIdentifier, final byte[] value) {
+            super(nodeIdentifier, value);
+        }
+
+        @Override
+        protected byte[] wrapValue(final byte[] valueToWrap) {
+            return valueToWrap.clone();
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafNodeSchemaAwareBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafNodeSchemaAwareBuilder.java
new file mode 100644 (file)
index 0000000..a460ae4
--- /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.builder.impl;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+
+public final class ImmutableLeafNodeSchemaAwareBuilder<T> extends ImmutableLeafNodeBuilder<T> {
+    private ImmutableLeafNodeSchemaAwareBuilder(final LeafSchemaNode schema) {
+        super.withNodeIdentifier(NodeIdentifier.create(schema.getQName()));
+    }
+
+    public static <T> @NonNull NormalizedNodeBuilder<NodeIdentifier, T, LeafNode<T>> create(
+            final LeafSchemaNode schema) {
+        return new ImmutableLeafNodeSchemaAwareBuilder<>(schema);
+    }
+
+    @Override
+    public NormalizedNodeBuilder<NodeIdentifier, T, LeafNode<T>> withValue(final T withValue) {
+        // TODO: check value type
+        return super.withValue(withValue);
+    }
+
+    @Override
+    public NormalizedNodeBuilder<NodeIdentifier, T, LeafNode<T>> withNodeIdentifier(
+            final NodeIdentifier withNodeIdentifier) {
+        throw new UnsupportedOperationException("Node identifier created from schema");
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetEntryNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetEntryNodeBuilder.java
new file mode 100644 (file)
index 0000000..f0068ec
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.builder.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedSimpleValueNode;
+
+public class ImmutableLeafSetEntryNodeBuilder<T>
+        extends AbstractImmutableNormalizedNodeBuilder<NodeWithValue, T, LeafSetEntryNode<T>> {
+
+    public static <T> @NonNull ImmutableLeafSetEntryNodeBuilder<T> create() {
+        return new ImmutableLeafSetEntryNodeBuilder<>();
+    }
+
+    @Override
+    public LeafSetEntryNode<T> build() {
+        return new ImmutableLeafSetEntryNode<>(getNodeIdentifier(), getValue());
+    }
+
+    private static final class ImmutableLeafSetEntryNode<T>
+            extends AbstractImmutableNormalizedSimpleValueNode<NodeWithValue, T> implements LeafSetEntryNode<T> {
+
+        ImmutableLeafSetEntryNode(final NodeWithValue nodeIdentifier, final T value) {
+            super(nodeIdentifier, value);
+            checkArgument(Objects.deepEquals(nodeIdentifier.getValue(), value),
+                    "Node identifier contains different value: %s than value itself: %s", nodeIdentifier, value);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetEntryNodeSchemaAwareBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetEntryNodeSchemaAwareBuilder.java
new file mode 100644 (file)
index 0000000..e388aca
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.builder.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+public final class ImmutableLeafSetEntryNodeSchemaAwareBuilder<T> extends ImmutableLeafSetEntryNodeBuilder<T> {
+    private final LeafListSchemaNode schema;
+
+    private ImmutableLeafSetEntryNodeSchemaAwareBuilder(final LeafListSchemaNode schema) {
+        this.schema = requireNonNull(schema);
+    }
+
+    public static <T> @NonNull NormalizedNodeBuilder<NodeWithValue, T, LeafSetEntryNode<T>> create(
+            final LeafListSchemaNode schema) {
+        return new ImmutableLeafSetEntryNodeSchemaAwareBuilder<>(schema);
+    }
+
+    @Override
+    public NormalizedNodeBuilder<NodeWithValue, T, LeafSetEntryNode<T>> withValue(final T withValue) {
+        super.withNodeIdentifier(new NodeWithValue<>(schema.getQName(), withValue));
+        // TODO check value type using TypeProvider ?
+        return super.withValue(withValue);
+    }
+
+    @Override
+    public NormalizedNodeBuilder<NodeWithValue, T, LeafSetEntryNode<T>> withNodeIdentifier(
+            final NodeWithValue withNodeIdentifier) {
+        throw new UnsupportedOperationException("Node identifier created from schema");
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetNodeBuilder.java
new file mode 100644 (file)
index 0000000..46d8361
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * 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.builder.impl;
+
+import com.google.common.collect.Maps;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.MapAdaptor;
+import org.opendaylight.yangtools.util.UnmodifiableCollection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedValueNode;
+
+public class ImmutableLeafSetNodeBuilder<T> implements ListNodeBuilder<T, LeafSetEntryNode<T>> {
+    private static final int DEFAULT_CAPACITY = 4;
+
+    private final Map<NodeWithValue, LeafSetEntryNode<T>> value;
+    private NodeIdentifier nodeIdentifier;
+
+    protected ImmutableLeafSetNodeBuilder() {
+        value = new HashMap<>(DEFAULT_CAPACITY);
+    }
+
+    protected ImmutableLeafSetNodeBuilder(final int sizeHint) {
+        if (sizeHint >= 0) {
+            value = Maps.newHashMapWithExpectedSize(sizeHint);
+        } else {
+            value = new HashMap<>(DEFAULT_CAPACITY);
+        }
+    }
+
+    protected ImmutableLeafSetNodeBuilder(final ImmutableLeafSetNode<T> node) {
+        nodeIdentifier = node.getIdentifier();
+        value = MapAdaptor.getDefaultInstance().takeSnapshot(node.children);
+    }
+
+    public static <T> @NonNull ListNodeBuilder<T, LeafSetEntryNode<T>> create() {
+        return new ImmutableLeafSetNodeBuilder<>();
+    }
+
+    public static <T> @NonNull ListNodeBuilder<T, LeafSetEntryNode<T>> create(final int sizeHint) {
+        return new ImmutableLeafSetNodeBuilder<>(sizeHint);
+    }
+
+    public static <T> @NonNull ListNodeBuilder<T, LeafSetEntryNode<T>> create(final LeafSetNode<T> node) {
+        if (!(node instanceof ImmutableLeafSetNode<?>)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+
+        return new ImmutableLeafSetNodeBuilder<>((ImmutableLeafSetNode<T>) node);
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withChild(final LeafSetEntryNode<T> child) {
+        this.value.put(child.getIdentifier(), child);
+        return this;
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withoutChild(final PathArgument key) {
+        this.value.remove(key);
+        return this;
+    }
+
+    @Override
+    public LeafSetNode<T> build() {
+        return new ImmutableLeafSetNode<>(nodeIdentifier, MapAdaptor.getDefaultInstance().optimize(value));
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withNodeIdentifier(final NodeIdentifier withNodeIdentifier) {
+        this.nodeIdentifier = withNodeIdentifier;
+        return this;
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withValue(final Collection<LeafSetEntryNode<T>> withValue) {
+        for (final LeafSetEntryNode<T> leafSetEntry : withValue) {
+            withChild(leafSetEntry);
+        }
+        return this;
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withChildValue(final T childValue) {
+        return withChild(ImmutableLeafSetEntryNodeBuilder.<T>create()
+            .withNodeIdentifier(new NodeWithValue<>(nodeIdentifier.getNodeType(), childValue))
+            .withValue(childValue).build());
+    }
+
+    protected static final class ImmutableLeafSetNode<T> extends
+            AbstractImmutableNormalizedValueNode<NodeIdentifier, Collection<LeafSetEntryNode<T>>> implements
+            LeafSetNode<T> {
+
+        private final Map<NodeWithValue, LeafSetEntryNode<T>> children;
+
+        ImmutableLeafSetNode(final NodeIdentifier nodeIdentifier,
+                final Map<NodeWithValue, LeafSetEntryNode<T>> children) {
+            super(nodeIdentifier, UnmodifiableCollection.create(children.values()));
+            this.children = children;
+        }
+
+        @Override
+        public Optional<LeafSetEntryNode<T>> getChild(final NodeWithValue child) {
+            return Optional.ofNullable(children.get(child));
+        }
+
+        @Override
+        protected int valueHashCode() {
+            return children.hashCode();
+        }
+
+        @Override
+        protected boolean valueEquals(final AbstractImmutableNormalizedNode<?, ?> other) {
+            return children.equals(((ImmutableLeafSetNode<?>) other).children);
+        }
+    }
+
+    @Override
+    public NormalizedNodeContainerBuilder<NodeIdentifier, PathArgument, LeafSetEntryNode<T>, LeafSetNode<T>> addChild(
+            final LeafSetEntryNode<T> child) {
+        return withChild(child);
+    }
+
+    @Override
+    public NormalizedNodeContainerBuilder<NodeIdentifier, PathArgument, LeafSetEntryNode<T>, LeafSetNode<T>>
+            removeChild(final PathArgument key) {
+        return withoutChild(key);
+    }
+
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetNodeSchemaAwareBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetNodeSchemaAwareBuilder.java
new file mode 100644 (file)
index 0000000..b913c7f
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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.builder.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collections;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataValidationException;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+public final class ImmutableLeafSetNodeSchemaAwareBuilder<T> extends ImmutableLeafSetNodeBuilder<T> {
+
+    private final LeafListSchemaNode schema;
+
+    private ImmutableLeafSetNodeSchemaAwareBuilder(final LeafListSchemaNode schema) {
+        this.schema = requireNonNull(schema);
+        super.withNodeIdentifier(new NodeIdentifier(schema.getQName()));
+    }
+
+    public ImmutableLeafSetNodeSchemaAwareBuilder(final LeafListSchemaNode schema, final ImmutableLeafSetNode<T> node) {
+        super(node);
+        this.schema = requireNonNull(schema);
+        // FIXME: Preconditions.checkArgument(schema.getQName().equals(node.getIdentifier()));
+        super.withNodeIdentifier(new NodeIdentifier(schema.getQName()));
+    }
+
+    public static <T> @NonNull ListNodeBuilder<T, LeafSetEntryNode<T>> create(final LeafListSchemaNode schema) {
+        return new ImmutableLeafSetNodeSchemaAwareBuilder<>(schema);
+    }
+
+    public static <T> @NonNull ListNodeBuilder<T, LeafSetEntryNode<T>> create(final LeafListSchemaNode schema,
+            final LeafSetNode<T> node) {
+        if (!(node instanceof ImmutableLeafSetNode<?>)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+
+        return new ImmutableLeafSetNodeSchemaAwareBuilder<>(schema, (ImmutableLeafSetNode<T>) node);
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withChildValue(final T childValue) {
+        // TODO check value type
+        return super.withChildValue(childValue);
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withChild(final LeafSetEntryNode<T> child) {
+        checkArgument(schema.getQName().equals(child.getNodeType()),
+                "Incompatible node type, should be: %s, is: %s", schema.getQName(), child.getNodeType());
+        // TODO check value type using TypeProvider ?
+        DataValidationException.checkLegalChild(schema.getQName().equals(child.getNodeType()), child.getIdentifier(),
+            schema, Collections.singleton(schema.getQName()));
+        return super.withChild(child);
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withNodeIdentifier(final NodeIdentifier withNodeIdentifier) {
+        throw new UnsupportedOperationException("Node identifier created from schema");
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapEntryNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapEntryNodeBuilder.java
new file mode 100644 (file)
index 0000000..33fd8a8
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * 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.builder.impl;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataValidationException;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ImmutableMapEntryNodeBuilder
+        extends AbstractImmutableDataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> {
+    private static final Logger LOG = LoggerFactory.getLogger(ImmutableMapEntryNodeBuilder.class);
+    protected final Map<QName, PathArgument> childrenQNamesToPaths;
+
+    protected ImmutableMapEntryNodeBuilder() {
+        this.childrenQNamesToPaths = new LinkedHashMap<>();
+    }
+
+    protected ImmutableMapEntryNodeBuilder(final int sizeHint) {
+        super(sizeHint);
+        this.childrenQNamesToPaths = new LinkedHashMap<>(sizeHint);
+    }
+
+    protected ImmutableMapEntryNodeBuilder(final ImmutableMapEntryNode node) {
+        super(node);
+        this.childrenQNamesToPaths = new LinkedHashMap<>();
+        fillQnames(node.getValue(), childrenQNamesToPaths);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> create() {
+        return new ImmutableMapEntryNodeBuilder();
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> create(
+            final int sizeHint) {
+        return new ImmutableMapEntryNodeBuilder(sizeHint);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> create(
+            final MapEntryNode node) {
+        if (!(node instanceof ImmutableMapEntryNode)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+
+        return new ImmutableMapEntryNodeBuilder((ImmutableMapEntryNode)node);
+    }
+
+    private static void fillQnames(final Iterable<DataContainerChild<? extends PathArgument, ?>> iterable,
+            final Map<QName, PathArgument> out) {
+        for (final DataContainerChild<? extends PathArgument, ?> childId : iterable) {
+            final PathArgument identifier = childId.getIdentifier();
+
+            // Augmentation nodes cannot be keys, and do not have to be present in childrenQNamesToPaths map
+            if (isAugment(identifier)) {
+                continue;
+            }
+
+            out.put(childId.getNodeType(), identifier);
+        }
+    }
+
+    @Override
+    public DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> withValue(
+            final Collection<DataContainerChild<? extends PathArgument, ?>> withValue) {
+        fillQnames(withValue, childrenQNamesToPaths);
+        return super.withValue(withValue);
+    }
+
+    private static boolean isAugment(final PathArgument identifier) {
+        return identifier instanceof AugmentationIdentifier;
+    }
+
+    @Override
+    public DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> withChild(
+            final DataContainerChild<?, ?> child) {
+        // Augmentation nodes cannot be keys, and do not have to be present in childrenQNamesToPaths map
+        if (!isAugment(child.getIdentifier())) {
+            childrenQNamesToPaths.put(child.getNodeType(), child.getIdentifier());
+        }
+
+        return super.withChild(child);
+    }
+
+    @Override
+    public MapEntryNode build() {
+        for (final Entry<QName, Object> key : getNodeIdentifier().entrySet()) {
+            final DataContainerChild<?, ?> childNode = getChild(childrenQNamesToPaths.get(key.getKey()));
+
+            // We have enough information to fill-in missing leaf nodes, so let's do that
+            if (childNode == null) {
+                LeafNode<Object> leaf = ImmutableNodes.leafNode(key.getKey(), key.getValue());
+                LOG.debug("Adding leaf {} implied by key {}", leaf, key);
+                withChild(leaf);
+            } else {
+                DataValidationException.checkListKey(getNodeIdentifier(), key.getKey(), key.getValue(),
+                    childNode.getValue());
+            }
+        }
+
+        return new ImmutableMapEntryNode(getNodeIdentifier(), buildValue());
+    }
+
+    private static final class ImmutableMapEntryNode
+            extends AbstractImmutableDataContainerNode<NodeIdentifierWithPredicates> implements MapEntryNode {
+
+        ImmutableMapEntryNode(final NodeIdentifierWithPredicates nodeIdentifier,
+                final Map<PathArgument, Object> children) {
+            super(children, nodeIdentifier);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapEntryNodeSchemaAwareBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapEntryNodeSchemaAwareBuilder.java
new file mode 100644 (file)
index 0000000..557b7a5
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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.builder.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.ImmutableMapTemplate;
+import org.opendaylight.yangtools.yang.common.QName;
+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.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataNodeContainerValidator;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataValidationException.IllegalListKeyException;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+public final class ImmutableMapEntryNodeSchemaAwareBuilder extends ImmutableMapEntryNodeBuilder {
+
+    private final ListSchemaNode schema;
+    private final DataNodeContainerValidator validator;
+
+    ImmutableMapEntryNodeSchemaAwareBuilder(final ListSchemaNode schema) {
+        this.schema = requireNonNull(schema);
+        this.validator = new DataNodeContainerValidator(schema);
+    }
+
+    @Override
+    public ImmutableMapEntryNodeBuilder withNodeIdentifier(final NodeIdentifierWithPredicates withNodeIdentifier) {
+        throw new UnsupportedOperationException("Node identifier created from schema");
+    }
+
+    @Override
+    public DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> withChild(
+            final DataContainerChild<?, ?> child) {
+        validator.validateChild(child.getIdentifier());
+        return super.withChild(child);
+    }
+
+    @Override
+    public MapEntryNode build() {
+        super.withNodeIdentifier(constructNodeIdentifier());
+        return super.build();
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> create(
+            final ListSchemaNode schema) {
+        return new ImmutableMapEntryNodeSchemaAwareBuilder(schema);
+    }
+
+    /**
+     * Build map entry node identifier from schema and provided children.
+     */
+    private NodeIdentifierWithPredicates constructNodeIdentifier() {
+        final Map<QName, Object> predicates;
+        final Collection<QName> keys = schema.getKeyDefinition();
+        if (!keys.isEmpty()) {
+            predicates = keyDefToPredicates(keys);
+        } else if (!childrenQNamesToPaths.isEmpty()) {
+            predicates = childrenToPredicates();
+        } else {
+            predicates = ImmutableMap.of();
+        }
+        return NodeIdentifierWithPredicates.of(schema.getQName(), predicates);
+    }
+
+    private Map<QName, Object> childrenToPredicates() {
+        final Object[] values = new Object[childrenQNamesToPaths.size()];
+        int offset = 0;
+        for (Entry<QName, PathArgument> entry : childrenQNamesToPaths.entrySet()) {
+            values[offset++] = nonnullKeyValue(entry.getKey(), getChild(entry.getValue())).getValue();
+        }
+        return ImmutableMapTemplate.ordered(childrenQNamesToPaths.keySet()).instantiateWithValues(values);
+    }
+
+    private Map<QName, Object> keyDefToPredicates(final Collection<QName> keys) {
+        final Object[] values = new Object[keys.size()];
+        int offset = 0;
+        for (QName key : keys) {
+            values[offset++] = nonnullKeyValue(key, getChild(childrenQNamesToPaths.get(key))).getValue();
+        }
+        return ImmutableMapTemplate.ordered(keys).instantiateWithValues(values);
+    }
+
+    private DataContainerChild<?, ?> nonnullKeyValue(final QName key, final DataContainerChild<?, ?> value) {
+        if (value != null) {
+            return value;
+        }
+        throw new IllegalListKeyException("Key value not present for key: %s, in: %s values %s", key, schema.getQName(),
+            buildValue());
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapNodeBuilder.java
new file mode 100644 (file)
index 0000000..0254680
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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.builder.impl;
+
+import com.google.common.collect.Maps;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.MapAdaptor;
+import org.opendaylight.yangtools.util.UnmodifiableCollection;
+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.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedNode;
+
+public class ImmutableMapNodeBuilder implements CollectionNodeBuilder<MapEntryNode, MapNode> {
+    private static final int DEFAULT_CAPACITY = 4;
+
+    private final Map<NodeIdentifierWithPredicates, MapEntryNode> value;
+    private NodeIdentifier nodeIdentifier;
+
+    protected ImmutableMapNodeBuilder() {
+        this.value = new HashMap<>(DEFAULT_CAPACITY);
+    }
+
+    protected ImmutableMapNodeBuilder(final int sizeHint) {
+        if (sizeHint >= 0) {
+            this.value = Maps.newHashMapWithExpectedSize(sizeHint);
+        } else {
+            this.value = new HashMap<>(DEFAULT_CAPACITY);
+        }
+    }
+
+    protected ImmutableMapNodeBuilder(final ImmutableMapNode node) {
+        this.nodeIdentifier = node.getIdentifier();
+        this.value = MapAdaptor.getDefaultInstance().takeSnapshot(node.children);
+    }
+
+    public static @NonNull CollectionNodeBuilder<MapEntryNode, MapNode> create() {
+        return new ImmutableMapNodeBuilder();
+    }
+
+    public static @NonNull CollectionNodeBuilder<MapEntryNode, MapNode> create(final int sizeHint) {
+        return new ImmutableMapNodeBuilder(sizeHint);
+    }
+
+    public static CollectionNodeBuilder<MapEntryNode, MapNode> create(final MapNode node) {
+        if (!(node instanceof ImmutableMapNode)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+
+        return new ImmutableMapNodeBuilder((ImmutableMapNode) node);
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, MapNode> withChild(final MapEntryNode child) {
+        this.value.put(child.getIdentifier(), child);
+        return this;
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, MapNode> withoutChild(final PathArgument key) {
+        this.value.remove(key);
+        return this;
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, MapNode> withValue(final Collection<MapEntryNode> withValue) {
+        // TODO replace or putAll ?
+        for (final MapEntryNode mapEntryNode : withValue) {
+            withChild(mapEntryNode);
+        }
+
+        return this;
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, MapNode> withNodeIdentifier(final NodeIdentifier withNodeIdentifier) {
+        this.nodeIdentifier = withNodeIdentifier;
+        return this;
+    }
+
+    @Override
+    public MapNode build() {
+        return new ImmutableMapNode(nodeIdentifier, MapAdaptor.getDefaultInstance().optimize(value));
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, MapNode> addChild(
+            final MapEntryNode child) {
+        return withChild(child);
+    }
+
+
+    @Override
+    public NormalizedNodeContainerBuilder<NodeIdentifier, PathArgument, MapEntryNode, MapNode> removeChild(
+            final PathArgument key) {
+        return withoutChild(key);
+    }
+
+    protected static final class ImmutableMapNode
+            extends AbstractImmutableNormalizedNode<NodeIdentifier, Collection<MapEntryNode>> implements MapNode {
+
+        private final Map<NodeIdentifierWithPredicates, MapEntryNode> children;
+
+        ImmutableMapNode(final NodeIdentifier nodeIdentifier,
+                         final Map<NodeIdentifierWithPredicates, MapEntryNode> children) {
+            super(nodeIdentifier);
+            this.children = children;
+        }
+
+        @Override
+        public Optional<MapEntryNode> getChild(final NodeIdentifierWithPredicates child) {
+            return Optional.ofNullable(children.get(child));
+        }
+
+        @Override
+        public Collection<MapEntryNode> getValue() {
+            return UnmodifiableCollection.create(children.values());
+        }
+
+        @Override
+        protected int valueHashCode() {
+            return children.hashCode();
+        }
+
+        @Override
+        protected boolean valueEquals(final AbstractImmutableNormalizedNode<?, ?> other) {
+            return children.equals(((ImmutableMapNode) other).children);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapNodeSchemaAwareBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapNodeSchemaAwareBuilder.java
new file mode 100644 (file)
index 0000000..d747a4a
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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.builder.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collections;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+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.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataValidationException;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+public class ImmutableMapNodeSchemaAwareBuilder extends ImmutableMapNodeBuilder {
+    private final ListSchemaNode schema;
+
+    protected ImmutableMapNodeSchemaAwareBuilder(final ListSchemaNode schema) {
+        this.schema = requireNonNull(schema);
+        super.withNodeIdentifier(NodeIdentifier.create(schema.getQName()));
+    }
+
+    protected ImmutableMapNodeSchemaAwareBuilder(final ListSchemaNode schema, final ImmutableMapNode node) {
+        super(node);
+        this.schema = requireNonNull(schema);
+        super.withNodeIdentifier(NodeIdentifier.create(schema.getQName()));
+    }
+
+    public static @NonNull CollectionNodeBuilder<MapEntryNode, MapNode> create(final ListSchemaNode schema) {
+        return new ImmutableMapNodeSchemaAwareBuilder(schema);
+    }
+
+    public static @NonNull CollectionNodeBuilder<MapEntryNode, MapNode> create(final ListSchemaNode schema,
+            final MapNode node) {
+        if (!(node instanceof ImmutableMapNode)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+
+        return new ImmutableMapNodeSchemaAwareBuilder(schema, (ImmutableMapNode) node);
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, MapNode> withChild(final MapEntryNode child) {
+        DataValidationException.checkLegalChild(schema.getQName().equals(child.getNodeType()), child.getIdentifier(),
+            schema, Collections.singleton(schema.getQName()));
+        return super.withChild(child);
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, MapNode> withNodeIdentifier(final NodeIdentifier withNodeIdentifier) {
+        throw new UnsupportedOperationException("Node identifier created from schema");
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedLeafSetNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedLeafSetNodeBuilder.java
new file mode 100644 (file)
index 0000000..b58936a
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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.builder.impl;
+
+import com.google.common.collect.Iterables;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.UnmodifiableCollection;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedNode;
+
+public class ImmutableOrderedLeafSetNodeBuilder<T> implements ListNodeBuilder<T, LeafSetEntryNode<T>> {
+    private Map<NodeWithValue, LeafSetEntryNode<T>> value;
+    private NodeIdentifier nodeIdentifier;
+    private boolean dirty;
+
+    protected ImmutableOrderedLeafSetNodeBuilder() {
+        value = new LinkedHashMap<>();
+        dirty = false;
+    }
+
+    protected ImmutableOrderedLeafSetNodeBuilder(final ImmutableOrderedLeafSetNode<T> node) {
+        nodeIdentifier = node.getIdentifier();
+        value = node.getChildren();
+        dirty = true;
+    }
+
+    public static <T> @NonNull ListNodeBuilder<T, LeafSetEntryNode<T>> create() {
+        return new ImmutableOrderedLeafSetNodeBuilder<>();
+    }
+
+    public static <T> @NonNull ListNodeBuilder<T, LeafSetEntryNode<T>> create(final LeafSetNode<T> node) {
+        if (!(node instanceof ImmutableOrderedLeafSetNode<?>)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+
+        return new ImmutableOrderedLeafSetNodeBuilder<>((ImmutableOrderedLeafSetNode<T>) node);
+    }
+
+    private void checkDirty() {
+        if (dirty) {
+            value = new LinkedHashMap<>(value);
+            dirty = false;
+        }
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withChild(final LeafSetEntryNode<T> child) {
+        checkDirty();
+        this.value.put(child.getIdentifier(), child);
+        return this;
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withoutChild(final PathArgument key) {
+        checkDirty();
+        this.value.remove(key);
+        return this;
+    }
+
+    @Override
+    public OrderedLeafSetNode<T> build() {
+        dirty = true;
+        return new ImmutableOrderedLeafSetNode<>(nodeIdentifier, value);
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withNodeIdentifier(final NodeIdentifier withNodeIdentifier) {
+        this.nodeIdentifier = withNodeIdentifier;
+        return this;
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withValue(final Collection<LeafSetEntryNode<T>> withValue) {
+        checkDirty();
+        for (final LeafSetEntryNode<T> leafSetEntry : withValue) {
+            withChild(leafSetEntry);
+        }
+        return this;
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withChildValue(final T childValue) {
+        return withChild(ImmutableLeafSetEntryNodeBuilder.<T>create()
+            .withNodeIdentifier(new NodeWithValue<>(nodeIdentifier.getNodeType(), childValue))
+            .withValue(childValue).build());
+    }
+
+    protected static final class ImmutableOrderedLeafSetNode<T> extends
+            AbstractImmutableNormalizedNode<NodeIdentifier, Collection<LeafSetEntryNode<T>>> implements
+            OrderedLeafSetNode<T> {
+
+        private final Map<NodeWithValue, LeafSetEntryNode<T>> children;
+
+        ImmutableOrderedLeafSetNode(final NodeIdentifier nodeIdentifier,
+                final Map<NodeWithValue, LeafSetEntryNode<T>> children) {
+            super(nodeIdentifier);
+            this.children = children;
+        }
+
+        @Override
+        public Optional<LeafSetEntryNode<T>> getChild(final NodeWithValue child) {
+            return Optional.ofNullable(children.get(child));
+        }
+
+        @Override
+        public LeafSetEntryNode<T> getChild(final int position) {
+            return Iterables.get(children.values(), position);
+        }
+
+        @Override
+        protected int valueHashCode() {
+            return children.hashCode();
+        }
+
+        private Map<NodeWithValue, LeafSetEntryNode<T>> getChildren() {
+            return Collections.unmodifiableMap(children);
+        }
+
+        @Override
+        protected boolean valueEquals(final AbstractImmutableNormalizedNode<?, ?> other) {
+            return children.equals(((ImmutableOrderedLeafSetNode<?>) other).children);
+        }
+
+        @Override
+        public int getSize() {
+            return children.size();
+        }
+
+        @Override
+        public Collection<LeafSetEntryNode<T>> getValue() {
+            return UnmodifiableCollection.create(children.values());
+        }
+    }
+
+    @Override
+    public NormalizedNodeContainerBuilder<NodeIdentifier, PathArgument, LeafSetEntryNode<T>, LeafSetNode<T>> addChild(
+            final LeafSetEntryNode<T> child) {
+        return withChild(child);
+    }
+
+    @Override
+    public NormalizedNodeContainerBuilder<NodeIdentifier, PathArgument, LeafSetEntryNode<T>, LeafSetNode<T>>
+            removeChild(final PathArgument key) {
+        return withoutChild(key);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedLeafSetNodeSchemaAwareBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedLeafSetNodeSchemaAwareBuilder.java
new file mode 100644 (file)
index 0000000..bbd103c
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.builder.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collections;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataValidationException;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+public final class ImmutableOrderedLeafSetNodeSchemaAwareBuilder<T> extends ImmutableOrderedLeafSetNodeBuilder<T> {
+
+    private final LeafListSchemaNode schema;
+
+    private ImmutableOrderedLeafSetNodeSchemaAwareBuilder(final LeafListSchemaNode schema) {
+        this.schema = requireNonNull(schema);
+        super.withNodeIdentifier(new NodeIdentifier(schema.getQName()));
+    }
+
+    public ImmutableOrderedLeafSetNodeSchemaAwareBuilder(final LeafListSchemaNode schema,
+            final ImmutableOrderedLeafSetNode<T> node) {
+        super(node);
+        this.schema = requireNonNull(schema);
+        // FIXME: Preconditions.checkArgument(schema.getQName().equals(node.getIdentifier()));
+        super.withNodeIdentifier(new NodeIdentifier(schema.getQName()));
+    }
+
+    public static <T> @NonNull ListNodeBuilder<T, LeafSetEntryNode<T>> create(final LeafListSchemaNode schema) {
+        return new ImmutableOrderedLeafSetNodeSchemaAwareBuilder<>(schema);
+    }
+
+    public static <T> @NonNull ListNodeBuilder<T, LeafSetEntryNode<T>> create(final LeafListSchemaNode schema,
+            final LeafSetNode<T> node) {
+        if (!(node instanceof ImmutableOrderedLeafSetNode<?>)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+
+        return new ImmutableOrderedLeafSetNodeSchemaAwareBuilder<>(schema, (ImmutableOrderedLeafSetNode<T>) node);
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withChildValue(final T childValue) {
+        // TODO check value type
+        return super.withChildValue(childValue);
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withChild(final LeafSetEntryNode<T> child) {
+        checkArgument(schema.getQName().equals(child.getNodeType()), "Incompatible node type, should be: %s, is: %s",
+            schema.getQName(), child.getNodeType());
+        // TODO check value type using TypeProvider ?
+        DataValidationException.checkLegalChild(schema.getQName().equals(child.getNodeType()), child.getIdentifier(),
+            schema, Collections.singleton(schema.getQName()));
+        return super.withChild(child);
+    }
+
+    @Override
+    public ListNodeBuilder<T, LeafSetEntryNode<T>> withNodeIdentifier(final NodeIdentifier withNodeIdentifier) {
+        throw new UnsupportedOperationException("Node identifier created from schema");
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedMapNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedMapNodeBuilder.java
new file mode 100644 (file)
index 0000000..dcf17ea
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * 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.builder.impl;
+
+import com.google.common.collect.Iterables;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.UnmodifiableCollection;
+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.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedNode;
+
+public class ImmutableOrderedMapNodeBuilder implements CollectionNodeBuilder<MapEntryNode, OrderedMapNode> {
+    private static final int DEFAULT_CAPACITY = 4;
+
+    private Map<NodeIdentifierWithPredicates, MapEntryNode> value;
+    private NodeIdentifier nodeIdentifier;
+    private boolean dirty;
+
+    protected ImmutableOrderedMapNodeBuilder() {
+        this.value = new LinkedHashMap<>(DEFAULT_CAPACITY);
+        this.dirty = false;
+    }
+
+    protected ImmutableOrderedMapNodeBuilder(final int sizeHint) {
+        if (sizeHint >= 0) {
+            this.value = new LinkedHashMap<>(sizeHint + sizeHint / 3);
+        } else {
+            this.value = new LinkedHashMap<>(DEFAULT_CAPACITY);
+        }
+        this.dirty = false;
+    }
+
+    protected ImmutableOrderedMapNodeBuilder(final ImmutableOrderedMapNode node) {
+        this.nodeIdentifier = node.getIdentifier();
+        this.value = node.children;
+        this.dirty = true;
+    }
+
+    public static @NonNull CollectionNodeBuilder<MapEntryNode, OrderedMapNode> create() {
+        return new ImmutableOrderedMapNodeBuilder();
+    }
+
+    public static @NonNull CollectionNodeBuilder<MapEntryNode, OrderedMapNode> create(final int sizeHint) {
+        return new ImmutableOrderedMapNodeBuilder(sizeHint);
+    }
+
+    public static @NonNull CollectionNodeBuilder<MapEntryNode, OrderedMapNode> create(final MapNode node) {
+        if (!(node instanceof ImmutableOrderedMapNode)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+
+        return new ImmutableOrderedMapNodeBuilder((ImmutableOrderedMapNode) node);
+    }
+
+    private void checkDirty() {
+        if (dirty) {
+            value = new LinkedHashMap<>(value);
+            dirty = false;
+        }
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, OrderedMapNode> withChild(final MapEntryNode child) {
+        checkDirty();
+        this.value.put(child.getIdentifier(), child);
+        return this;
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, OrderedMapNode> withoutChild(final PathArgument key) {
+        checkDirty();
+        this.value.remove(key);
+        return this;
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, OrderedMapNode> withValue(final Collection<MapEntryNode> withValue) {
+        // TODO replace or putAll ?
+        for (final MapEntryNode mapEntryNode : withValue) {
+            withChild(mapEntryNode);
+        }
+
+        return this;
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, OrderedMapNode> withNodeIdentifier(
+            final NodeIdentifier withNodeIdentifier) {
+        this.nodeIdentifier = withNodeIdentifier;
+        return this;
+    }
+
+    @Override
+    public OrderedMapNode build() {
+        dirty = true;
+        return new ImmutableOrderedMapNode(nodeIdentifier, value);
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, OrderedMapNode> addChild(
+            final MapEntryNode child) {
+        return withChild(child);
+    }
+
+
+    @Override
+    public NormalizedNodeContainerBuilder<NodeIdentifier, PathArgument, MapEntryNode, OrderedMapNode> removeChild(
+            final PathArgument key) {
+        return withoutChild(key);
+    }
+
+    protected static final class ImmutableOrderedMapNode
+            extends AbstractImmutableNormalizedNode<NodeIdentifier, Collection<MapEntryNode>>
+            implements OrderedMapNode {
+
+        private final Map<NodeIdentifierWithPredicates, MapEntryNode> children;
+
+        ImmutableOrderedMapNode(final NodeIdentifier nodeIdentifier,
+                         final Map<NodeIdentifierWithPredicates, MapEntryNode> children) {
+            super(nodeIdentifier);
+            this.children = children;
+        }
+
+        @Override
+        public Optional<MapEntryNode> getChild(final NodeIdentifierWithPredicates child) {
+            return Optional.ofNullable(children.get(child));
+        }
+
+        @Override
+        public MapEntryNode getChild(final int position) {
+            return Iterables.get(children.values(), position);
+        }
+
+        @Override
+        protected int valueHashCode() {
+            return children.hashCode();
+        }
+
+        @Override
+        protected boolean valueEquals(final AbstractImmutableNormalizedNode<?, ?> other) {
+            return children.equals(((ImmutableOrderedMapNode) other).children);
+        }
+
+        @Override
+        public int getSize() {
+            return children.size();
+        }
+
+        @Override
+        public Collection<MapEntryNode> getValue() {
+            return UnmodifiableCollection.create(children.values());
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedMapNodeSchemaAwareBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedMapNodeSchemaAwareBuilder.java
new file mode 100644 (file)
index 0000000..d71f08c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.builder.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collections;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataValidationException;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+public class ImmutableOrderedMapNodeSchemaAwareBuilder extends ImmutableOrderedMapNodeBuilder {
+    private final ListSchemaNode schema;
+
+    protected ImmutableOrderedMapNodeSchemaAwareBuilder(final ListSchemaNode schema) {
+        this.schema = requireNonNull(schema);
+        super.withNodeIdentifier(NodeIdentifier.create(schema.getQName()));
+    }
+
+    protected ImmutableOrderedMapNodeSchemaAwareBuilder(final ListSchemaNode schema,
+            final ImmutableOrderedMapNode node) {
+        super(node);
+        this.schema = requireNonNull(schema);
+        super.withNodeIdentifier(NodeIdentifier.create(schema.getQName()));
+    }
+
+    public static @NonNull CollectionNodeBuilder<MapEntryNode, OrderedMapNode> create(final ListSchemaNode schema) {
+        return new ImmutableOrderedMapNodeSchemaAwareBuilder(schema);
+    }
+
+    public static @NonNull CollectionNodeBuilder<MapEntryNode, OrderedMapNode> create(final ListSchemaNode schema,
+        final MapNode node) {
+        if (!(node instanceof ImmutableOrderedMapNode)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+
+        return new ImmutableOrderedMapNodeSchemaAwareBuilder(schema, (ImmutableOrderedMapNode) node);
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, OrderedMapNode> withChild(final MapEntryNode child) {
+        DataValidationException.checkLegalChild(schema.getQName().equals(child.getNodeType()), child.getIdentifier(),
+            schema, Collections.singleton(schema.getQName()));
+        return super.withChild(child);
+    }
+
+    @Override
+    public CollectionNodeBuilder<MapEntryNode, OrderedMapNode> withNodeIdentifier(
+            final NodeIdentifier withNodeIdentifier) {
+        throw new UnsupportedOperationException("Node identifier created from schema");
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableUnkeyedListEntryNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableUnkeyedListEntryNodeBuilder.java
new file mode 100644 (file)
index 0000000..93823e8
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.builder.impl;
+
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+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.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
+
+public class ImmutableUnkeyedListEntryNodeBuilder
+        extends AbstractImmutableDataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> {
+
+    protected ImmutableUnkeyedListEntryNodeBuilder() {
+
+    }
+
+    protected ImmutableUnkeyedListEntryNodeBuilder(final int sizeHint) {
+        super(sizeHint);
+    }
+
+    protected ImmutableUnkeyedListEntryNodeBuilder(final ImmutableUnkeyedListEntryNode node) {
+        super(node);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> create() {
+        return new ImmutableUnkeyedListEntryNodeBuilder();
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> create(
+            final int sizeHint) {
+        return new ImmutableUnkeyedListEntryNodeBuilder(sizeHint);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> create(
+            final UnkeyedListEntryNode node) {
+        if (!(node instanceof ImmutableUnkeyedListEntryNode)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+        return new ImmutableUnkeyedListEntryNodeBuilder((ImmutableUnkeyedListEntryNode) node);
+    }
+
+    @Override
+    public UnkeyedListEntryNode build() {
+        return new ImmutableUnkeyedListEntryNode(getNodeIdentifier(), buildValue());
+    }
+
+    protected static final class ImmutableUnkeyedListEntryNode
+            extends AbstractImmutableDataContainerNode<NodeIdentifier> implements UnkeyedListEntryNode {
+
+        ImmutableUnkeyedListEntryNode(final NodeIdentifier nodeIdentifier, final Map<PathArgument, Object> children) {
+            super(children, nodeIdentifier);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableUnkeyedListNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableUnkeyedListNodeBuilder.java
new file mode 100644 (file)
index 0000000..81d10f7
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * 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.builder.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+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.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedValueNode;
+
+public class ImmutableUnkeyedListNodeBuilder implements CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> {
+    private List<UnkeyedListEntryNode> value;
+    private NodeIdentifier nodeIdentifier;
+    private boolean dirty;
+
+    protected ImmutableUnkeyedListNodeBuilder() {
+        this.value = new LinkedList<>();
+        this.dirty = false;
+    }
+
+    protected ImmutableUnkeyedListNodeBuilder(final ImmutableUnkeyedListNode node) {
+        this.nodeIdentifier = node.getIdentifier();
+        // FIXME: clean this up, notably reuse unmodified lists
+        this.value = new LinkedList<>();
+        Iterables.addAll(value, node.getValue());
+        this.dirty = true;
+    }
+
+    public static @NonNull CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> create() {
+        return new ImmutableUnkeyedListNodeBuilder();
+    }
+
+    public static @NonNull CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> create(final int sizeHint) {
+        return new ImmutableUnkeyedListNodeBuilder();
+    }
+
+    public static @NonNull CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> create(
+            final UnkeyedListNode node) {
+        if (!(node instanceof ImmutableUnkeyedListNode)) {
+            throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
+        }
+
+        return new ImmutableUnkeyedListNodeBuilder((ImmutableUnkeyedListNode) node);
+    }
+
+    private void checkDirty() {
+        if (dirty) {
+            value = new LinkedList<>(value);
+            dirty = false;
+        }
+    }
+
+    @Override
+    public CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> withChild(final UnkeyedListEntryNode child) {
+        checkDirty();
+        this.value.add(child);
+        return this;
+    }
+
+    @Override
+    public CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> withoutChild(
+            final PathArgument key) {
+        checkDirty();
+        throw new UnsupportedOperationException("Children does not have identifiers.");
+    }
+
+    @Override
+    public CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> withValue(
+            final Collection<UnkeyedListEntryNode> withValue) {
+        // TODO replace or putAll ?
+        for (final UnkeyedListEntryNode node : withValue) {
+            withChild(node);
+        }
+
+        return this;
+    }
+
+    @Override
+    public CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> withNodeIdentifier(
+            final NodeIdentifier withNodeIdentifier) {
+        this.nodeIdentifier = withNodeIdentifier;
+        return this;
+    }
+
+    @Override
+    public UnkeyedListNode build() {
+        dirty = true;
+        if (value.isEmpty()) {
+            return new EmptyImmutableUnkeyedListNode(nodeIdentifier);
+        }
+        return new ImmutableUnkeyedListNode(nodeIdentifier, ImmutableList.copyOf(value));
+    }
+
+    @Override
+    public CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> addChild(final UnkeyedListEntryNode child) {
+        return withChild(child);
+    }
+
+    @Override
+    public NormalizedNodeContainerBuilder<NodeIdentifier, PathArgument, UnkeyedListEntryNode, UnkeyedListNode>
+            removeChild(final PathArgument key) {
+        return withoutChild(key);
+    }
+
+    protected static final class EmptyImmutableUnkeyedListNode extends
+            AbstractImmutableNormalizedNode<NodeIdentifier, Collection<UnkeyedListEntryNode>> implements
+            UnkeyedListNode {
+        protected EmptyImmutableUnkeyedListNode(final NodeIdentifier nodeIdentifier) {
+            super(nodeIdentifier);
+        }
+
+        @Override
+        public ImmutableList<UnkeyedListEntryNode> getValue() {
+            return ImmutableList.of();
+        }
+
+        @Override
+        public UnkeyedListEntryNode getChild(final int position) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        @Override
+        public int getSize() {
+            return 0;
+        }
+
+        @Override
+        protected boolean valueEquals(final AbstractImmutableNormalizedNode<?, ?> other) {
+            return Collections.emptyList().equals(other.getValue());
+        }
+
+        @Override
+        protected int valueHashCode() {
+            return 1;
+        }
+    }
+
+    protected static final class ImmutableUnkeyedListNode extends
+            AbstractImmutableNormalizedValueNode<NodeIdentifier, Collection<UnkeyedListEntryNode>>
+            implements UnkeyedListNode {
+
+        private final ImmutableList<UnkeyedListEntryNode> children;
+
+        ImmutableUnkeyedListNode(final NodeIdentifier nodeIdentifier,
+                final ImmutableList<UnkeyedListEntryNode> children) {
+            super(nodeIdentifier, children);
+            this.children = children;
+        }
+
+        @Override
+        protected int valueHashCode() {
+            return children.hashCode();
+        }
+
+        @Override
+        protected boolean valueEquals(final AbstractImmutableNormalizedNode<?, ?> other) {
+            return children.equals(((ImmutableUnkeyedListNode) other).children);
+        }
+
+        @Override
+        public UnkeyedListEntryNode getChild(final int position) {
+            return children.get(position);
+        }
+
+        @Override
+        public int getSize() {
+            return children.size();
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableYangModeledAnyXmlNodeBuilder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableYangModeledAnyXmlNodeBuilder.java
new file mode 100644 (file)
index 0000000..ed45e08
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015 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.builder.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+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.schema.YangModeledAnyXmlNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+
+public final class ImmutableYangModeledAnyXmlNodeBuilder extends
+        AbstractImmutableDataContainerNodeBuilder<NodeIdentifier, YangModeledAnyXmlNode> {
+
+    private final ContainerSchemaNode contentSchema;
+
+    private ImmutableYangModeledAnyXmlNodeBuilder(final YangModeledAnyxmlSchemaNode yangModeledAnyXMLSchemaNode) {
+        requireNonNull(yangModeledAnyXMLSchemaNode, "Yang modeled any xml node must not be null.");
+        super.withNodeIdentifier(NodeIdentifier.create(yangModeledAnyXMLSchemaNode.getQName()));
+        this.contentSchema = yangModeledAnyXMLSchemaNode.getSchemaOfAnyXmlData();
+    }
+
+    private ImmutableYangModeledAnyXmlNodeBuilder(final YangModeledAnyxmlSchemaNode yangModeledAnyXMLSchemaNode,
+            final int sizeHint) {
+        super(sizeHint);
+        requireNonNull(yangModeledAnyXMLSchemaNode, "Yang modeled any xml node must not be null.");
+        super.withNodeIdentifier(NodeIdentifier.create(yangModeledAnyXMLSchemaNode.getQName()));
+        this.contentSchema = yangModeledAnyXMLSchemaNode.getSchemaOfAnyXmlData();
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, YangModeledAnyXmlNode> create(
+            final YangModeledAnyxmlSchemaNode yangModeledAnyXMLSchemaNode) {
+        return new ImmutableYangModeledAnyXmlNodeBuilder(yangModeledAnyXMLSchemaNode);
+    }
+
+    public static @NonNull DataContainerNodeBuilder<NodeIdentifier, YangModeledAnyXmlNode> create(
+            final YangModeledAnyxmlSchemaNode yangModeledAnyXMLSchemaNode, final int sizeHint) {
+        return new ImmutableYangModeledAnyXmlNodeBuilder(yangModeledAnyXMLSchemaNode, sizeHint);
+    }
+
+    @Override
+    public YangModeledAnyXmlNode build() {
+        return new ImmutableYangModeledAnyXmlNode(getNodeIdentifier(), buildValue(), contentSchema);
+    }
+
+    private static final class ImmutableYangModeledAnyXmlNode extends
+            AbstractImmutableDataContainerNode<NodeIdentifier> implements YangModeledAnyXmlNode {
+
+        private final @NonNull ContainerSchemaNode contentSchema;
+
+        ImmutableYangModeledAnyXmlNode(final NodeIdentifier nodeIdentifier, final Map<PathArgument, Object> value,
+                final ContainerSchemaNode contentSchema) {
+            super(value, nodeIdentifier);
+            this.contentSchema = requireNonNull(contentSchema, "Schema of yang modeled anyXml content cannot be null.");
+        }
+
+        @Override
+        public ContainerSchemaNode getSchemaOfAnyXmlData() {
+            return contentSchema;
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/valid/DataNodeContainerValidator.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/valid/DataNodeContainerValidator.java
new file mode 100644 (file)
index 0000000..554aeb7
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.builder.impl.valid;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * General validator for container like statements, e.g. container, list-entry, choice, augment
+ */
+public class DataNodeContainerValidator {
+    private final Set<AugmentationIdentifier> augments = new HashSet<>();
+    private final DataNodeContainer schema;
+    private final Set<QName> childNodes;
+
+    public DataNodeContainerValidator(final DataNodeContainer schema) {
+        this.schema = requireNonNull(schema, "Schema was null");
+        this.childNodes = getChildNodes(schema);
+
+        if (schema instanceof AugmentationTarget) {
+            for (AugmentationSchemaNode augmentation : ((AugmentationTarget) schema).getAvailableAugmentations()) {
+                augments.add(DataSchemaContextNode.augmentationIdentifierFrom(augmentation));
+            }
+        }
+    }
+
+    private boolean isKnownChild(final PathArgument child) {
+        if (child instanceof AugmentationIdentifier) {
+            return augments.contains(child);
+        }
+
+        return childNodes.contains(child.getNodeType());
+    }
+
+    public void validateChild(final PathArgument child) {
+        DataValidationException.checkLegalChild(isKnownChild(child), child, schema, childNodes, augments);
+    }
+
+    public DataContainerChild<?, ?> validateChild(final DataContainerChild<?, ?> child) {
+        validateChild(child.getIdentifier());
+        return child;
+    }
+
+    /**
+     * Map all direct child nodes. Skip augments since they have no qname. List cases since cases do not exist in
+     * NormalizedNode API.
+     */
+    private static Set<QName> getChildNodes(final DataNodeContainer nodeContainer) {
+        Set<QName> allChildNodes = new HashSet<>();
+
+        for (DataSchemaNode childSchema : nodeContainer.getChildNodes()) {
+            if (childSchema instanceof CaseSchemaNode) {
+                allChildNodes.addAll(getChildNodes((DataNodeContainer) childSchema));
+            } else if (!(childSchema instanceof AugmentationSchemaNode)) {
+                allChildNodes.add(childSchema.getQName());
+            }
+        }
+
+        return allChildNodes;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/valid/DataValidationException.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/valid/DataValidationException.java
new file mode 100644 (file)
index 0000000..0b3a7f4
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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.builder.impl.valid;
+
+import com.google.common.annotations.Beta;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+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.DataContainerChild;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+public class DataValidationException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    public DataValidationException(final String message) {
+        super(message);
+    }
+
+    public static void checkLegalChild(final boolean isLegal, final PathArgument child, final DataNodeContainer schema,
+            final Set<QName> childNodes, final Set<AugmentationIdentifier> augments) {
+        if (!isLegal) {
+            throw new IllegalChildException(child, schema, childNodes, augments);
+        }
+    }
+
+    public static void checkLegalChild(final boolean isLegal, final PathArgument child, final DataSchemaNode schema,
+            final Set<QName> childNodes) {
+        if (!isLegal) {
+            throw new IllegalChildException(child, schema, childNodes);
+        }
+    }
+
+    public static void checkLegalChild(final boolean isLegal, final PathArgument child, final ChoiceSchemaNode schema) {
+        if (!isLegal) {
+            throw new IllegalChildException(child, schema);
+        }
+    }
+
+    public static void checkLegalData(final boolean isLegal, final String messageTemplate,
+            final Object... messageAttrs) {
+        if (!isLegal) {
+            throw new DataValidationException(String.format(messageTemplate, messageAttrs));
+        }
+    }
+
+    public static void checkListKey(final NodeIdentifierWithPredicates nodeId, final QName keyQName,
+            final Object expected, final Object actual) {
+        // Objects.equals() does not deal with arrays, but is faster
+        if (!Objects.equals(expected, actual) && !Objects.deepEquals(expected, actual)) {
+            throw new IllegalListKeyException(keyQName, nodeId, actual, expected);
+        }
+    }
+
+    public static void checkListKey(final DataContainerChild<?, ?> childNode, final Map<QName, Object> keyValues,
+            final QName keyQName, final NodeIdentifierWithPredicates nodeId) {
+        checkListKey(childNode, keyQName, nodeId);
+
+        final Object expected = keyValues.get(keyQName);
+        final Object actual = childNode.getValue();
+
+        checkListKey(nodeId, keyQName, expected, actual);
+    }
+
+    public static void checkListKey(final DataContainerChild<?, ?> childNode, final QName keyQName,
+            final NodeIdentifierWithPredicates nodeId) {
+        if (childNode == null) {
+            throw new IllegalListKeyException(keyQName, nodeId);
+        }
+    }
+
+    private static final class IllegalChildException extends DataValidationException {
+        private static final long serialVersionUID = 1L;
+
+        IllegalChildException(final PathArgument child, final DataNodeContainer schema,
+                final Set<QName> childNodes, final Set<AugmentationIdentifier> augments) {
+            super(String.format("Unknown child node: %s, does not belong to: %s as a direct child. "
+                    + "Direct child nodes: %s, augmented child nodes: %s", child, schema, childNodes, augments));
+        }
+
+        IllegalChildException(final PathArgument child, final ChoiceSchemaNode schema) {
+            super(String.format("Unknown child node: %s, not detected in choice: %s", child, schema));
+        }
+
+        IllegalChildException(final PathArgument child, final DataSchemaNode schema,
+                final Set<QName> childNodes) {
+            super(String.format("Unknown child node: %s, does not belong to: %s as a child. Child nodes: %s", child,
+                schema, childNodes));
+        }
+    }
+
+    @Beta
+    public static final class IllegalListKeyException extends DataValidationException {
+        private static final long serialVersionUID = 1L;
+
+        public IllegalListKeyException(final String format, final Object... args) {
+            super(String.format(format, args));
+        }
+
+        IllegalListKeyException(final QName keyQName, final NodeIdentifierWithPredicates id) {
+            this("Key value not present for key: %s, in: %s", keyQName, id);
+        }
+
+        IllegalListKeyException(final QName keyQName, final NodeIdentifierWithPredicates id, final Object actualValue,
+                final Object expectedValue) {
+            this("Illegal value for key: %s, in: %s, actual value: %s, expected value from key: %s",
+                keyQName, id, actualValue, expectedValue);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableDataContainerNode.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableDataContainerNode.java
new file mode 100644 (file)
index 0000000..dabcc57
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.nodes;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import org.opendaylight.yangtools.util.ImmutableOffsetMap;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+
+public abstract class AbstractImmutableDataContainerNode<K extends PathArgument>
+        extends AbstractImmutableNormalizedNode<K, Collection<DataContainerChild<? extends PathArgument, ?>>>
+        implements DataContainerNode<K> {
+    private final Map<PathArgument, Object> children;
+
+    protected AbstractImmutableDataContainerNode(final Map<PathArgument, Object> children, final K nodeIdentifier) {
+        super(nodeIdentifier);
+
+        this.children = ImmutableOffsetMap.unorderedCopyOf(children);
+    }
+
+    @Override
+    public final Optional<DataContainerChild<? extends PathArgument, ?>> getChild(final PathArgument child) {
+        return LazyLeafOperations.findChild(children, child);
+    }
+
+    @Override
+    public final Collection<DataContainerChild<? extends PathArgument, ?>> getValue() {
+        return LazyLeafOperations.getValue(children);
+    }
+
+    @Override
+    protected int valueHashCode() {
+        return children.hashCode();
+    }
+
+    /**
+     * DO NOT USE THIS METHOD.
+     *
+     * <p>
+     * This is an implementation-internal API and no outside users should use it. If you do, you are asking for trouble,
+     * as the returned object is not guaranteed to conform to java.util.Map interface, nor is its contents well-defined.
+     *
+     * @return An unmodifiable view if this node's children.
+     */
+    public final Map<PathArgument, Object> getChildren() {
+        return children;
+    }
+
+    @Override
+    protected boolean valueEquals(final AbstractImmutableNormalizedNode<?, ?> other) {
+        return other instanceof AbstractImmutableDataContainerNode<?> && children.equals(
+                ((AbstractImmutableDataContainerNode<?>) other).children);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedNode.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedNode.java
new file mode 100644 (file)
index 0000000..8489825
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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.nodes;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public abstract class AbstractImmutableNormalizedNode<K extends PathArgument, V> extends AbstractIdentifiable<K>
+        implements NormalizedNode<K, V>, Immutable {
+    protected AbstractImmutableNormalizedNode(final K nodeIdentifier) {
+        super(nodeIdentifier);
+    }
+
+    @Override
+    public final QName getNodeType() {
+        return getIdentifier().getNodeType();
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return super.addToStringAttributes(toStringHelper).add("value", getValue());
+    }
+
+    protected abstract boolean valueEquals(AbstractImmutableNormalizedNode<?, ?> other);
+
+    protected abstract int valueHashCode();
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || this.getClass() != obj.getClass()) {
+            return false;
+        }
+
+        final AbstractImmutableNormalizedNode<?, ?> other = (AbstractImmutableNormalizedNode<?, ?>)obj;
+        return getIdentifier().equals(other.getIdentifier()) && valueEquals(other);
+    }
+
+    @Override
+    public final int hashCode() {
+        return 31 * getIdentifier().hashCode() + valueHashCode();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedSimpleValueNode.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedSimpleValueNode.java
new file mode 100644 (file)
index 0000000..5e4660b
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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.nodes;
+
+import java.util.Objects;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+public abstract class AbstractImmutableNormalizedSimpleValueNode<K extends PathArgument,V>
+        extends AbstractImmutableNormalizedValueNode<K, V> {
+    protected AbstractImmutableNormalizedSimpleValueNode(final K nodeIdentifier, final V value) {
+        super(nodeIdentifier, value);
+    }
+
+    @Override
+    protected int valueHashCode() {
+        final V local = value();
+        final int result = local != null ? local.hashCode() : 1;
+        // FIXME: are attributes part of hashCode/equals?
+        return result;
+    }
+
+    @Override
+    protected boolean valueEquals(final AbstractImmutableNormalizedNode<?, ?> other) {
+        // We can not call directly getValue.equals because of Empty Type
+        // RequireInstanceStatementSupport leaves which always have NULL value
+
+        // FIXME: are attributes part of hashCode/equals?
+        return Objects.deepEquals(value(), other.getValue());
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueNode.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueNode.java
new file mode 100644 (file)
index 0000000..13c31ec
--- /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.nodes;
+
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+public abstract class AbstractImmutableNormalizedValueNode<K extends PathArgument, V> extends
+        AbstractImmutableNormalizedNode<K, V> {
+
+    private final @NonNull V value;
+
+    protected AbstractImmutableNormalizedValueNode(final K nodeIdentifier, final @NonNull V value) {
+        super(nodeIdentifier);
+        this.value = requireNonNull(value);
+    }
+
+    @Override
+    public final V getValue() {
+        return wrapValue(value);
+    }
+
+    protected final @NonNull V value() {
+        return value;
+    }
+
+    protected V wrapValue(final V valueToWrap) {
+        return valueToWrap;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/CloneableMap.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/CloneableMap.java
new file mode 100644 (file)
index 0000000..ede65a4
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 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.nodes;
+
+import com.google.common.annotations.Beta;
+import java.util.Map;
+
+@Beta
+public interface CloneableMap<K, V> extends Map<K, V> {
+    /**
+     * Create a clone of this map's contents. This does not include the actual
+     * keys and values, just the internal map state.
+     *
+     * @return An isolated, writable map.
+     */
+    Map<K, V> createMutableClone();
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/LazyLeafOperations.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/LazyLeafOperations.java
new file mode 100644 (file)
index 0000000..dffa923
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.nodes;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+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.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Support utilities for dealing with Maps which would normally hold {@link DataContainerChild} values, but are modified
+ * to eliminate {@link LeafNode} instances.
+ *
+ * <p>
+ * This class holds implementation logic which controls lifecycle of {@link LeafNode}s  by providing a central policy
+ * point for how the implementation treats these nodes. There are two modes of operation:
+ * <ul>
+ *   <li>eager, which means leaf nodes are retained by their parent<li>
+ *   <li>lazy, which means leaf nodes are created whenever they are queried and no attempt is made to retain them</li>
+ * </ul>
+ *
+ * <p>
+ * Selection of the mode in effect is available through {@value #EXPENDABLE_PROP_NAME} system property.
+ */
+@Beta
+public final class LazyLeafOperations {
+    private static final Logger LOG = LoggerFactory.getLogger(LazyLeafOperations.class);
+    private static final String EXPENDABLE_PROP_NAME =
+            "org.opendaylight.yangtools.yang.data.impl.schema.nodes.lazy-leaves";
+
+    /**
+     * Global enabled run-time constant. If set to true, this class will treat {@link LeafNode} and
+     * {@link LeafSetEntryNode} as an expendable object. This constant is controlled by {@value #EXPENDABLE_PROP_NAME}
+     * system property.
+     */
+    private static final boolean EXPENDABLE;
+
+    static {
+        EXPENDABLE = Boolean.parseBoolean(System.getProperty(EXPENDABLE_PROP_NAME, "true"));
+        LOG.info("Leaf nodes are treated as {} nodes", EXPENDABLE ? "transient" : "regular");
+    }
+
+    private LazyLeafOperations() {
+
+    }
+
+    /**
+     * A boolean flag indicating whether leaf nodes are being treated as expendable.
+     *
+     * @return True if NormalizedNode implementations in this artifact are treating leaf nodes as transient, i.e. do
+     *              not retain them.
+     */
+    public static boolean isEnabled() {
+        return EXPENDABLE;
+    }
+
+    public static Optional<DataContainerChild<?, ?>> findChild(final Map<PathArgument, Object> map,
+            final PathArgument key) {
+        final Object value = map.get(key);
+        return value == null ? Optional.empty() : Optional.of(decodeChild(key, value));
+    }
+
+    public static @Nullable DataContainerChild<?, ?> getChild(final Map<PathArgument, Object> map,
+            final PathArgument key) {
+        final Object value = map.get(key);
+        return value == null ? null : decodeChild(key, value);
+    }
+
+    public static void putChild(final Map<PathArgument, Object> map, final DataContainerChild<?, ?> child) {
+        final DataContainerChild<?, ?> node = requireNonNull(child);
+        map.put(node.getIdentifier(), EXPENDABLE ? encodeExpendableChild(node) : node);
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public static @NonNull Collection<DataContainerChild<?, ?>> getValue(final Map<PathArgument, Object> map) {
+        return EXPENDABLE ? new LazyValues(map)
+                // This is an ugly cast, but it is accurate IFF all modifications are done through this class
+                : (Collection)map.values();
+    }
+
+    static @NonNull LeafNode<?> coerceLeaf(final PathArgument key, final Object value) {
+        verify(key instanceof NodeIdentifier, "Unexpected value %s for child %s", value, key);
+        return ImmutableNodes.leafNode((NodeIdentifier) key, value);
+    }
+
+    private static @Nullable DataContainerChild<?, ?> decodeChild(final PathArgument key, final @NonNull Object value) {
+        return EXPENDABLE ? decodeExpendableChild(key, value) : verifyCast(value);
+    }
+
+    private static @NonNull DataContainerChild<?, ?> decodeExpendableChild(final PathArgument key,
+            @NonNull final Object value) {
+        return value instanceof DataContainerChild ? (DataContainerChild<?, ?>) value  : coerceLeaf(key, value);
+    }
+
+    private static @NonNull Object encodeExpendableChild(final @NonNull DataContainerChild<?, ?> node) {
+        return node instanceof LeafNode ? verifyEncode(((LeafNode<?>) node).getValue()) : node;
+    }
+
+    private static @NonNull Object verifyEncode(final @NonNull Object value) {
+        verify(!(value instanceof DataContainerChild), "Unexpected leaf value %s", value);
+        return value;
+    }
+
+    private static @NonNull DataContainerChild<?, ?> verifyCast(final @NonNull Object value) {
+        verify(value instanceof DataContainerChild, "Unexpected child %s", value);
+        return (DataContainerChild<?, ?>)value;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/LazyValues.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/LazyValues.java
new file mode 100644 (file)
index 0000000..e9ad16e
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.nodes;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.AbstractCollection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+
+// This is *almost* the same as Guava's TransformedCollection. The main difference is delegation of hashCode()/equals()
+// towards the backing map. This is needed because we do not retain a reference to this object and thus
+// NormalizedNode.getValue() does not compare as equal. When invoked twice and lazy leaves are in effect. Note that
+// Collection.equals() is undefined, but the expectation from users is that we will return the same view object, which
+// equals on identity.
+final class LazyValues extends AbstractCollection<DataContainerChild<?, ?>> {
+    private final Map<PathArgument, Object> map;
+
+    LazyValues(final Map<PathArgument, Object> map) {
+        this.map = requireNonNull(map);
+    }
+
+    @Override
+    public int size() {
+        return map.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return map.isEmpty();
+    }
+
+    @Override
+    public Iterator<DataContainerChild<?, ?>> iterator() {
+        return new Iter(map.entrySet().iterator());
+    }
+
+    @Override
+    public int hashCode() {
+        return map.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return this == obj || obj instanceof LazyValues && map.equals(((LazyValues)obj).map);
+    }
+
+    private static final class Iter implements Iterator<DataContainerChild<?, ?>> {
+        private final Iterator<Entry<PathArgument, Object>> iterator;
+
+        Iter(final Iterator<Entry<PathArgument, Object>> iterator) {
+            this.iterator = requireNonNull(iterator);
+        }
+
+        @Override
+        public boolean hasNext() {
+            return iterator.hasNext();
+        }
+
+        @Override
+        public DataContainerChild<?, ?> next() {
+            final Entry<PathArgument, Object> entry = iterator.next();
+            final Object value = entry.getValue();
+            return value instanceof DataContainerChild ? (DataContainerChild<?, ?>) value
+                    : LazyLeafOperations.coerceLeaf(entry.getKey(), value);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/UnmodifiableChildrenMap.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/UnmodifiableChildrenMap.java
new file mode 100644 (file)
index 0000000..2334a5c
--- /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.data.impl.schema.nodes;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+
+/**
+ * Internal equivalent of {@link Collections}' unmodifiable Map. It does not retain
+ * keySet/entrySet references, thus lowering the memory overhead.
+ */
+final class UnmodifiableChildrenMap
+        implements CloneableMap<PathArgument, DataContainerChild<? extends PathArgument, ?>>, Serializable {
+    private static final long serialVersionUID = 1L;
+    /*
+     * Do not wrap maps which are smaller than this and instead copy them into
+     * an ImmutableMap.
+     */
+    private static final int WRAP_THRESHOLD = 9;
+    private final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> delegate;
+    private transient Collection<DataContainerChild<? extends PathArgument, ?>> values;
+
+    private UnmodifiableChildrenMap(final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> delegate) {
+        this.delegate = requireNonNull(delegate);
+    }
+
+    /**
+     * Create an unmodifiable view of a particular map. Does not perform unnecessary
+     * encapsulation if the map is known to be already unmodifiable.
+     *
+     * @param map Backing map
+     * @return Unmodifiable view
+     */
+    static Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> create(
+            final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> map) {
+        if (map instanceof UnmodifiableChildrenMap) {
+            return map;
+        }
+        if (map instanceof ImmutableMap) {
+            return map;
+        }
+        if (map.isEmpty()) {
+            return ImmutableMap.of();
+        }
+        if (map.size() < WRAP_THRESHOLD) {
+            return ImmutableMap.copyOf(map);
+        }
+
+        return new UnmodifiableChildrenMap(map);
+    }
+
+    @Override
+    public int size() {
+        return delegate.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return delegate.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(final Object key) {
+        return delegate.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(final Object value) {
+        return delegate.containsValue(value);
+    }
+
+    @Override
+    public DataContainerChild<? extends PathArgument, ?> get(final Object key) {
+        return delegate.get(key);
+    }
+
+    @Override
+    public DataContainerChild<? extends PathArgument, ?> put(final PathArgument key,
+            final DataContainerChild<? extends PathArgument, ?> value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public DataContainerChild<? extends PathArgument, ?> remove(final Object key) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public void putAll(final Map<? extends PathArgument, ? extends DataContainerChild<? extends PathArgument, ?>> m) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Set<PathArgument> keySet() {
+        return Collections.unmodifiableSet(delegate.keySet());
+    }
+
+    @Override
+    public Collection<DataContainerChild<? extends PathArgument, ?>> values() {
+        if (values == null) {
+            values = Collections.unmodifiableCollection(delegate.values());
+        }
+        return values;
+    }
+
+    @Override
+    public Set<Entry<PathArgument, DataContainerChild<? extends PathArgument, ?>>> entrySet() {
+        /*
+         * Okay, this is not as efficient as it could be -- we could save ourselves the
+         * map instantiation. The cost of that would be re-implementation of a read-only
+         * Map.Entry to ensure our delegate is never modified.
+         *
+         * Let's skip that and use whatever the JRE gives us instead.
+         */
+        return Collections.unmodifiableMap(delegate).entrySet();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return this == obj || delegate.equals(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return delegate.toString();
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> createMutableClone() {
+        if (delegate instanceof HashMap) {
+            return (Map<PathArgument, DataContainerChild<? extends PathArgument, ?>>)
+                    ((HashMap<?, ?>) delegate).clone();
+        }
+
+        return new HashMap<>(delegate);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractCursor.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractCursor.java
new file mode 100644 (file)
index 0000000..e114a08
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshotCursor;
+
+abstract class AbstractCursor<T extends AbstractCursorAware> implements DataTreeSnapshotCursor {
+    @SuppressWarnings("rawtypes")
+    private static final AtomicIntegerFieldUpdater<AbstractCursor> CLOSED_UPDATER =
+            AtomicIntegerFieldUpdater.newUpdater(AbstractCursor.class, "closed");
+    private final YangInstanceIdentifier rootPath;
+    private final T parent;
+    // closed isn't unused, it's updated by CLOSED_UPDATER but data-flow analysers can't see that
+    @SuppressWarnings("unused")
+    private volatile int closed;
+
+    AbstractCursor(final T parent, final YangInstanceIdentifier rootPath) {
+        this.rootPath = requireNonNull(rootPath);
+        this.parent = requireNonNull(parent);
+    }
+
+    final T getParent() {
+        return parent;
+    }
+
+    final YangInstanceIdentifier getRootPath() {
+        return rootPath;
+    }
+
+
+    final void ensureNotClosed() {
+        checkState(closed == 0, "Modification cursor has been closed");
+    }
+
+    @Override
+    public final void enter(final PathArgument... path) {
+        enter(Arrays.asList(path));
+    }
+
+    @Override
+    public final void exit() {
+        exit(1);
+    }
+
+    @Override
+    public final void close() {
+        if (CLOSED_UPDATER.compareAndSet(this, 0, 1)) {
+            parent.closeCursor(this);
+        }
+    }
+
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractCursorAware.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractCursorAware.java
new file mode 100644 (file)
index 0000000..3bba4fc
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import com.google.common.base.Preconditions;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractCursorAware {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractCursorAware.class);
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<AbstractCursorAware, AbstractCursor> CURSOR_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(AbstractCursorAware.class, AbstractCursor.class, "cursor");
+    private volatile AbstractCursor<?> cursor = null;
+
+    protected <T extends AbstractCursor<?>> T openCursor(final T cursorToOpen) {
+        final boolean success = CURSOR_UPDATER.compareAndSet(this, null, cursorToOpen);
+        Preconditions.checkState(success, "Modification %s has cursor attached at path %s", this,
+            this.cursor.getRootPath());
+        return cursorToOpen;
+    }
+
+    final void closeCursor(final AbstractCursor<?> cursorToClose) {
+        final boolean success = CURSOR_UPDATER.compareAndSet(this, cursorToClose, null);
+        if (!success) {
+            LOG.warn("Attempted to close cursor {} while {} is open", cursorToClose, this.cursor);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractDataTreeCandidate.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractDataTreeCandidate.java
new file mode 100644 (file)
index 0000000..bd6d6b1
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
+
+@NonNullByDefault
+abstract class AbstractDataTreeCandidate extends AbstractDataTreeTip implements DataTreeCandidateTip {
+    private final YangInstanceIdentifier rootPath;
+
+    protected AbstractDataTreeCandidate(final YangInstanceIdentifier rootPath) {
+        this.rootPath = requireNonNull(rootPath);
+    }
+
+    @Override
+    public final YangInstanceIdentifier getRootPath() {
+        return rootPath;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractDataTreeTip.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractDataTreeTip.java
new file mode 100644 (file)
index 0000000..40c08b5
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeTip;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+
+abstract class AbstractDataTreeTip implements DataTreeTip {
+    /**
+     * Return the current root node of this tip.
+     *
+     * @return Current tip root node, may not be null.
+     */
+    protected abstract @NonNull TreeNode getTipRoot();
+
+    abstract @NonNull YangInstanceIdentifier getRootPath();
+
+    @Override
+    public final void validate(final DataTreeModification modification) throws DataValidationFailedException {
+        final InMemoryDataTreeModification m = checkedCast(modification);
+        checkArgument(m.isSealed(), "Attempted to verify unsealed modification %s", m);
+
+        m.getStrategy().checkApplicable(new ModificationPath(getRootPath()), m.getRootModification(),
+            Optional.of(getTipRoot()), m.getVersion());
+    }
+
+    @Override
+    public final DataTreeCandidateTip prepare(final DataTreeModification modification) {
+        final InMemoryDataTreeModification m = checkedCast(modification);
+        checkArgument(m.isSealed(), "Attempted to prepare unsealed modification %s", m);
+
+        final ModifiedNode root = m.getRootModification();
+
+        final TreeNode currentRoot = getTipRoot();
+        if (root.getOperation() == LogicalOperation.NONE) {
+            return new NoopDataTreeCandidate(YangInstanceIdentifier.empty(), root, currentRoot);
+        }
+
+        final Optional<? extends TreeNode> newRoot = m.getStrategy().apply(m.getRootModification(),
+            Optional.of(currentRoot), m.getVersion());
+        checkState(newRoot.isPresent(), "Apply strategy failed to produce root node for modification %s", modification);
+        return new InMemoryDataTreeCandidate(YangInstanceIdentifier.empty(), root, currentRoot, newRoot.get());
+    }
+
+    private static InMemoryDataTreeModification checkedCast(final DataTreeModification mod) {
+        checkArgument(mod instanceof InMemoryDataTreeModification, "Invalid modification class %s", mod.getClass());
+        return (InMemoryDataTreeModification)mod;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractModifiedNodeBasedCandidateNode.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractModifiedNodeBasedCandidateNode.java
new file mode 100644 (file)
index 0000000..25e44a7
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2015, 2016 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.tree;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+
+abstract class AbstractModifiedNodeBasedCandidateNode implements DataTreeCandidateNode {
+    private final ModifiedNode mod;
+    private final TreeNode newMeta;
+    private final TreeNode oldMeta;
+
+    protected AbstractModifiedNodeBasedCandidateNode(final ModifiedNode mod, final TreeNode oldMeta,
+            final TreeNode newMeta) {
+        this.newMeta = newMeta;
+        this.oldMeta = oldMeta;
+        this.mod = requireNonNull(mod);
+    }
+
+    protected final ModifiedNode getMod() {
+        return mod;
+    }
+
+    protected final TreeNode getNewMeta() {
+        return newMeta;
+    }
+
+    protected final TreeNode getOldMeta() {
+        return oldMeta;
+    }
+
+    private static TreeNode childMeta(final TreeNode parent, final PathArgument id) {
+        return parent == null ? null : parent.getChild(id).orElse(null);
+    }
+
+    private static boolean canHaveChildren(final @Nullable TreeNode oldMeta, final @Nullable TreeNode newMeta) {
+        if (oldMeta != null) {
+            return oldMeta.getData() instanceof NormalizedNodeContainer;
+        }
+        if (newMeta != null) {
+            return newMeta.getData() instanceof NormalizedNodeContainer;
+        }
+        return false;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> getContainer(
+            final @Nullable TreeNode meta) {
+        return meta == null ? null : (NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>>)meta.getData();
+    }
+
+    private ChildNode childNode(final ModifiedNode childMod) {
+        final PathArgument id = childMod.getIdentifier();
+        return new ChildNode(childMod, childMeta(oldMeta, id), childMeta(newMeta, id));
+    }
+
+    @Override
+    public Collection<DataTreeCandidateNode> getChildNodes() {
+        switch (mod.getModificationType()) {
+            case APPEARED:
+            case DISAPPEARED:
+            case SUBTREE_MODIFIED:
+                return Collections2.transform(mod.getChildren(), this::childNode);
+            case UNMODIFIED:
+                // Unmodified node, but we still need to resolve potential children. canHaveChildren returns
+                // false if both arguments are null.
+                if (!canHaveChildren(oldMeta, newMeta)) {
+                    return ImmutableList.of();
+                }
+
+                return Collections2.transform(getContainer(newMeta != null ? newMeta : oldMeta).getValue(),
+                    DataTreeCandidateNodes::unmodified);
+            case DELETE:
+            case WRITE:
+                // This is unusual, the user is requesting we follow into an otherwise-terminal node.
+                // We need to fudge things based on before/after data to correctly fake the expectations.
+                if (!canHaveChildren(oldMeta, newMeta)) {
+                    return ImmutableList.of();
+                }
+                return DataTreeCandidateNodes.containerDelta(getContainer(oldMeta), getContainer(newMeta));
+            default:
+                throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
+        }
+    }
+
+    @Override
+    public ModificationType getModificationType() {
+        return verifyNotNull(mod.getModificationType(), "Node %s does not have resolved modification type", mod);
+    }
+
+    private static @NonNull Optional<NormalizedNode<?, ?>> optionalData(final TreeNode meta) {
+        return meta == null ? Optional.empty() : Optional.of(meta.getData());
+    }
+
+    @Override
+    public final Optional<NormalizedNode<?, ?>> getDataAfter() {
+        return optionalData(newMeta);
+    }
+
+    @Override
+    public final Optional<NormalizedNode<?, ?>> getDataBefore() {
+        return optionalData(oldMeta);
+    }
+
+    @Override
+    public final Optional<DataTreeCandidateNode> getModifiedChild(final PathArgument identifier) {
+        switch (mod.getModificationType()) {
+            case APPEARED:
+            case DISAPPEARED:
+            case SUBTREE_MODIFIED:
+                return mod.getChild(identifier).map(this::childNode);
+            case UNMODIFIED:
+                if (!canHaveChildren(oldMeta, newMeta)) {
+                    return Optional.empty();
+                }
+                return getContainer(newMeta != null ? newMeta : oldMeta).getChild(identifier)
+                        .map(DataTreeCandidateNodes::unmodified);
+            case DELETE:
+            case WRITE:
+                if (!canHaveChildren(oldMeta, newMeta)) {
+                    return Optional.empty();
+                }
+                return DataTreeCandidateNodes.containerDelta(getContainer(oldMeta), getContainer(newMeta), identifier);
+            default:
+                throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
+        }
+    }
+
+    private static final class ChildNode extends AbstractModifiedNodeBasedCandidateNode {
+        ChildNode(final ModifiedNode mod, final TreeNode oldMeta, final TreeNode newMeta) {
+            super(mod, oldMeta, newMeta);
+        }
+
+        @Override
+        public PathArgument getIdentifier() {
+            return getMod().getIdentifier();
+        }
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getSimpleName() + "{mod = " + this.mod + ", oldMeta = " + this.oldMeta + ", newMeta = "
+                + this.newMeta + "}";
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractNodeContainerModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractNodeContainerModificationStrategy.java
new file mode 100644 (file)
index 0000000..2d6985a
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Verify;
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.MutableTreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNodeFactory;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+
+abstract class AbstractNodeContainerModificationStrategy<T extends WithStatus>
+        extends SchemaAwareApplyOperation<T> {
+    abstract static class Invisible<T extends WithStatus> extends AbstractNodeContainerModificationStrategy<T> {
+        private final @NonNull SchemaAwareApplyOperation<T> entryStrategy;
+
+        Invisible(final NormalizedNodeContainerSupport<?, ?> support, final DataTreeConfiguration treeConfig,
+                final SchemaAwareApplyOperation<T> entryStrategy) {
+            super(support, treeConfig);
+            this.entryStrategy = requireNonNull(entryStrategy);
+        }
+
+        @Override
+        final T getSchema() {
+            return entryStrategy.getSchema();
+        }
+
+        final Optional<ModificationApplyOperation> entryStrategy() {
+            return Optional.of(entryStrategy);
+        }
+
+        @Override
+        ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            return super.addToStringAttributes(helper).add("entry", entryStrategy);
+        }
+    }
+
+    abstract static class Visible<T extends WithStatus> extends AbstractNodeContainerModificationStrategy<T> {
+        private final @NonNull T schema;
+
+        Visible(final NormalizedNodeContainerSupport<?, ?> support, final DataTreeConfiguration treeConfig,
+            final T schema) {
+            super(support, treeConfig);
+            this.schema = requireNonNull(schema);
+        }
+
+        @Override
+        final T getSchema() {
+            return schema;
+        }
+
+        @Override
+        ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            return super.addToStringAttributes(helper).add("schema", schema);
+        }
+    }
+
+    /**
+     * Fake TreeNode version used in
+     * {@link #checkTouchApplicable(ModificationPath, NodeModification, Optional, Version)}
+     * It is okay to use a global constant, as the delegate will ignore it anyway.
+     */
+    private static final Version FAKE_VERSION = Version.initial();
+
+    private final NormalizedNodeContainerSupport<?, ?> support;
+    private final boolean verifyChildrenStructure;
+
+    AbstractNodeContainerModificationStrategy(final NormalizedNodeContainerSupport<?, ?> support,
+            final DataTreeConfiguration treeConfig) {
+        this.support = requireNonNull(support);
+        this.verifyChildrenStructure = treeConfig.getTreeType() == TreeType.CONFIGURATION;
+    }
+
+    @Override
+    protected final ChildTrackingPolicy getChildPolicy() {
+        return support.childPolicy;
+    }
+
+    @Override
+    final void verifyValue(final NormalizedNode<?, ?> writtenValue) {
+        final Class<?> nodeClass = support.requiredClass;
+        checkArgument(nodeClass.isInstance(writtenValue), "Node %s is not of type %s", writtenValue, nodeClass);
+        checkArgument(writtenValue instanceof NormalizedNodeContainer);
+    }
+
+    @Override
+    final void verifyValueChildren(final NormalizedNode<?, ?> writtenValue) {
+        if (verifyChildrenStructure) {
+            final NormalizedNodeContainer<?, ?, ?> container = (NormalizedNodeContainer<?, ?, ?>) writtenValue;
+            for (final Object child : container.getValue()) {
+                checkArgument(child instanceof NormalizedNode);
+                final NormalizedNode<?, ?> castedChild = (NormalizedNode<?, ?>) child;
+                final Optional<ModificationApplyOperation> childOp = getChild(castedChild.getIdentifier());
+                if (childOp.isPresent()) {
+                    childOp.get().fullVerifyStructure(castedChild);
+                } else {
+                    throw new SchemaValidationFailedException(String.format(
+                            "Node %s is not a valid child of %s according to the schema.",
+                            castedChild.getIdentifier(), container.getIdentifier()));
+                }
+            }
+
+            optionalVerifyValueChildren(writtenValue);
+        }
+        mandatoryVerifyValueChildren(writtenValue);
+    }
+
+    /**
+     * Perform additional verification on written value's child structure, like presence of mandatory children and
+     * exclusion. The default implementation does nothing and is not invoked for non-CONFIG data trees.
+     *
+     * @param writtenValue Effective written value
+     */
+    void optionalVerifyValueChildren(final NormalizedNode<?, ?> writtenValue) {
+        // Defaults to no-op
+    }
+
+    /**
+     * Perform additional verification on written value's child structure, like presence of mandatory children.
+     * The default implementation does nothing.
+     *
+     * @param writtenValue Effective written value
+     */
+    void mandatoryVerifyValueChildren(final NormalizedNode<?, ?> writtenValue) {
+        // Defaults to no-op
+    }
+
+    @Override
+    protected final void recursivelyVerifyStructure(final NormalizedNode<?, ?> value) {
+        final NormalizedNodeContainer<?, ?, ?> container = (NormalizedNodeContainer<?, ?, ?>) value;
+        for (final Object child : container.getValue()) {
+            checkArgument(child instanceof NormalizedNode);
+            final NormalizedNode<?, ?> castedChild = (NormalizedNode<?, ?>) child;
+            final Optional<ModificationApplyOperation> childOp = getChild(castedChild.getIdentifier());
+            if (!childOp.isPresent()) {
+                throw new SchemaValidationFailedException(
+                    String.format("Node %s is not a valid child of %s according to the schema.",
+                        castedChild.getIdentifier(), container.getIdentifier()));
+            }
+
+            childOp.get().recursivelyVerifyStructure(castedChild);
+        }
+    }
+
+    @Override
+    protected TreeNode applyWrite(final ModifiedNode modification, final NormalizedNode<?, ?> newValue,
+            final Optional<? extends TreeNode> currentMeta, final Version version) {
+        final TreeNode newValueMeta = TreeNodeFactory.createTreeNode(newValue, version);
+
+        if (modification.getChildren().isEmpty()) {
+            return newValueMeta;
+        }
+
+        /*
+         * This is where things get interesting. The user has performed a write and
+         * then she applied some more modifications to it. So we need to make sense
+         * of that an apply the operations on top of the written value. We could have
+         * done it during the write, but this operation is potentially expensive, so
+         * we have left it out of the fast path.
+         *
+         * As it turns out, once we materialize the written data, we can share the
+         * code path with the subtree change. So let's create an unsealed TreeNode
+         * and run the common parts on it -- which end with the node being sealed.
+         *
+         * FIXME: this code needs to be moved out from the prepare() path and into
+         *        the read() and seal() paths. Merging of writes needs to be charged
+         *        to the code which originated this, not to the code which is
+         *        attempting to make it visible.
+         */
+        final MutableTreeNode mutable = newValueMeta.mutable();
+        mutable.setSubtreeVersion(version);
+
+        @SuppressWarnings("rawtypes")
+        final NormalizedNodeContainerBuilder dataBuilder = support.createBuilder(newValue);
+        final TreeNode result = mutateChildren(mutable, dataBuilder, version, modification.getChildren());
+
+        // We are good to go except one detail: this is a single logical write, but
+        // we have a result TreeNode which has been forced to materialized, e.g. it
+        // is larger than it needs to be. Create a new TreeNode to host the data.
+        return TreeNodeFactory.createTreeNode(result.getData(), version);
+    }
+
+    /**
+     * Applies write/remove diff operation for each modification child in modification subtree.
+     * Operation also sets the Data tree references for each Tree Node (Index Node) in meta (MutableTreeNode) structure.
+     *
+     * @param meta MutableTreeNode (IndexTreeNode)
+     * @param data DataBuilder
+     * @param nodeVersion Version of TreeNode
+     * @param modifications modification operations to apply
+     * @return Sealed immutable copy of TreeNode structure with all Data Node references set.
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private TreeNode mutateChildren(final MutableTreeNode meta, final NormalizedNodeContainerBuilder data,
+            final Version nodeVersion, final Iterable<ModifiedNode> modifications) {
+
+        for (final ModifiedNode mod : modifications) {
+            final PathArgument id = mod.getIdentifier();
+            final Optional<? extends TreeNode> cm = meta.getChild(id);
+
+            final Optional<? extends TreeNode> result = resolveChildOperation(id).apply(mod, cm, nodeVersion);
+            if (result.isPresent()) {
+                final TreeNode tn = result.get();
+                meta.addChild(tn);
+                data.addChild(tn.getData());
+            } else {
+                meta.removeChild(id);
+                data.removeChild(id);
+            }
+        }
+
+        meta.setData(data.build());
+        return meta.seal();
+    }
+
+    @Override
+    protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+        /*
+         * The node which we are merging exists. We now need to expand any child operations implied by the value. Once
+         * we do that, ModifiedNode children will look like this node were a TOUCH and we will let applyTouch() do the
+         * heavy lifting of applying the children recursively (either through here or through applyWrite().
+         */
+        final NormalizedNode<?, ?> value = modification.getWrittenValue();
+
+        Verify.verify(value instanceof NormalizedNodeContainer, "Attempted to merge non-container %s", value);
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        final Collection<NormalizedNode<?, ?>> children = ((NormalizedNodeContainer) value).getValue();
+        for (final NormalizedNode<?, ?> c : children) {
+            final PathArgument id = c.getIdentifier();
+            modification.modifyChild(id, resolveChildOperation(id), version);
+        }
+        return applyTouch(modification, currentMeta, version);
+    }
+
+    private void mergeChildrenIntoModification(final ModifiedNode modification,
+            final Collection<NormalizedNode<?, ?>> children, final Version version) {
+        for (final NormalizedNode<?, ?> c : children) {
+            final ModificationApplyOperation childOp = resolveChildOperation(c.getIdentifier());
+            final ModifiedNode childNode = modification.modifyChild(c.getIdentifier(), childOp, version);
+            childOp.mergeIntoModifiedNode(childNode, c, version);
+        }
+    }
+
+    @Override
+    final void mergeIntoModifiedNode(final ModifiedNode modification, final NormalizedNode<?, ?> value,
+            final Version version) {
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        final Collection<NormalizedNode<?, ?>> children = ((NormalizedNodeContainer)value).getValue();
+
+        switch (modification.getOperation()) {
+            case NONE:
+                // Fresh node, just record a MERGE with a value
+                recursivelyVerifyStructure(value);
+                modification.updateValue(LogicalOperation.MERGE, value);
+                return;
+            case TOUCH:
+
+                mergeChildrenIntoModification(modification, children, version);
+                // We record empty merge value, since real children merges
+                // are already expanded. This is needed to satisfy non-null for merge
+                // original merge value can not be used since it mean different
+                // order of operation - parent changes are always resolved before
+                // children ones, and having node in TOUCH means children was modified
+                // before.
+                modification.updateValue(LogicalOperation.MERGE, support.createEmptyValue(value));
+                return;
+            case MERGE:
+                // Merging into an existing node. Merge data children modifications (maybe recursively) and mark
+                // as MERGE, invalidating cached snapshot
+                mergeChildrenIntoModification(modification, children, version);
+                modification.updateOperationType(LogicalOperation.MERGE);
+                return;
+            case DELETE:
+                // Delete performs a data dependency check on existence of the node. Performing a merge on DELETE means
+                // we are really performing a write. One thing that ruins that are any child modifications. If there
+                // are any, we will perform a read() to get the current state of affairs, turn this into into a WRITE
+                // and then append any child entries.
+                if (!modification.getChildren().isEmpty()) {
+                    // Version does not matter here as we'll throw it out
+                    final Optional<? extends TreeNode> current = apply(modification, modification.getOriginal(),
+                        Version.initial());
+                    if (current.isPresent()) {
+                        modification.updateValue(LogicalOperation.WRITE, current.get().getData());
+                        mergeChildrenIntoModification(modification, children, version);
+                        return;
+                    }
+                }
+
+                modification.updateValue(LogicalOperation.WRITE, value);
+                return;
+            case WRITE:
+                // We are augmenting a previous write. We'll just walk value's children, get the corresponding
+                // ModifiedNode and run recursively on it
+                mergeChildrenIntoModification(modification, children, version);
+                modification.updateOperationType(LogicalOperation.WRITE);
+                return;
+            default:
+                throw new IllegalArgumentException("Unsupported operation " + modification.getOperation());
+        }
+    }
+
+    @Override
+    protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+        /*
+         * The user may have issued an empty merge operation. In this case we do not perform
+         * a data tree mutation, do not pass GO, and do not collect useless garbage. It
+         * also means the ModificationType is UNMODIFIED.
+         */
+        final Collection<ModifiedNode> children = modification.getChildren();
+        if (!children.isEmpty()) {
+            @SuppressWarnings("rawtypes")
+            final NormalizedNodeContainerBuilder dataBuilder = support.createBuilder(currentMeta.getData());
+            final MutableTreeNode newMeta = currentMeta.mutable();
+            newMeta.setSubtreeVersion(version);
+            final TreeNode ret = mutateChildren(newMeta, dataBuilder, version, children);
+
+            /*
+             * It is possible that the only modifications under this node were empty merges,
+             * which were turned into UNMODIFIED. If that is the case, we can turn this operation
+             * into UNMODIFIED, too, potentially cascading it up to root. This has the benefit
+             * of speeding up any users, who can skip processing child nodes.
+             *
+             * In order to do that, though, we have to check all child operations are UNMODIFIED.
+             * Let's do precisely that, stopping as soon we find a different result.
+             */
+            for (final ModifiedNode child : children) {
+                if (child.getModificationType() != ModificationType.UNMODIFIED) {
+                    modification.resolveModificationType(ModificationType.SUBTREE_MODIFIED);
+                    return ret;
+                }
+            }
+        }
+
+        // The merge operation did not have any children, or all of them turned out to be UNMODIFIED, hence do not
+        // replace the metadata node.
+        modification.resolveModificationType(ModificationType.UNMODIFIED);
+        return currentMeta;
+    }
+
+    @Override
+    protected final void checkTouchApplicable(final ModificationPath path, final NodeModification modification,
+            final Optional<? extends TreeNode> current, final Version version) throws DataValidationFailedException {
+        final TreeNode currentNode;
+        if (!current.isPresent()) {
+            currentNode = defaultTreeNode();
+            if (currentNode == null) {
+                if (!modification.getOriginal().isPresent()) {
+                    final YangInstanceIdentifier id = path.toInstanceIdentifier();
+                    throw new ModifiedNodeDoesNotExistException(id,
+                        String.format("Node %s does not exist. Cannot apply modification to its children.", id));
+                }
+
+                throw new ConflictingModificationAppliedException(path.toInstanceIdentifier(),
+                    "Node was deleted by other transaction.");
+            }
+        } else {
+            currentNode = current.get();
+        }
+
+        checkChildPreconditions(path, modification, currentNode, version);
+    }
+
+    /**
+     * Return the default tree node. Default implementation does nothing, but can be overridden to call
+     * {@link #defaultTreeNode(NormalizedNode)}.
+     *
+     * @return Default empty tree node, or null if no default is available
+     */
+    @Nullable TreeNode defaultTreeNode() {
+        // Defaults to no recovery
+        return null;
+    }
+
+    static final TreeNode defaultTreeNode(final NormalizedNode<?, ?> emptyNode) {
+        return TreeNodeFactory.createTreeNode(emptyNode, FAKE_VERSION);
+    }
+
+    @Override
+    protected final void checkMergeApplicable(final ModificationPath path, final NodeModification modification,
+            final Optional<? extends TreeNode> current, final Version version) throws DataValidationFailedException {
+        if (current.isPresent()) {
+            checkChildPreconditions(path, modification, current.get(), version);
+        }
+    }
+
+    /**
+     * Recursively check child preconditions.
+     *
+     * @param path current node path
+     * @param modification current modification
+     * @param current Current data tree node.
+     */
+    private void checkChildPreconditions(final ModificationPath path, final NodeModification modification,
+            final TreeNode current, final Version version) throws DataValidationFailedException {
+        for (final NodeModification childMod : modification.getChildren()) {
+            final PathArgument childId = childMod.getIdentifier();
+            final Optional<? extends TreeNode> childMeta = current.getChild(childId);
+
+            path.push(childId);
+            try {
+                resolveChildOperation(childId).checkApplicable(path, childMod, childMeta, version);
+            } finally {
+                path.pop();
+            }
+        }
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("support", support).add("verifyChildren", verifyChildrenStructure);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractReadyIterator.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractReadyIterator.java
new file mode 100644 (file)
index 0000000..160ce66
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+
+abstract class AbstractReadyIterator {
+    final Iterator<ModifiedNode> children;
+    final ModifiedNode node;
+    final ModificationApplyOperation op;
+
+    private AbstractReadyIterator(final ModifiedNode node, final Iterator<ModifiedNode> children,
+            final ModificationApplyOperation operation) {
+        this.children = requireNonNull(children);
+        this.node = requireNonNull(node);
+        this.op = requireNonNull(operation);
+    }
+
+    static AbstractReadyIterator create(final ModifiedNode root, final ModificationApplyOperation operation) {
+        return new RootReadyIterator(root, root.getChildren().iterator(), operation);
+    }
+
+    final AbstractReadyIterator process(final Version version) {
+        // Walk all child nodes and remove any children which have not
+        // been modified. If a child has children, we need to iterate
+        // through it via re-entering this method on the child iterator.
+        while (children.hasNext()) {
+            final ModifiedNode child = children.next();
+            final Optional<ModificationApplyOperation> childOperation = op.getChild(child.getIdentifier());
+            checkState(childOperation.isPresent(), "Schema for child %s is not present.", child.getIdentifier());
+            final Collection<ModifiedNode> grandChildren = child.getChildren();
+            final ModificationApplyOperation childOp = childOperation.get();
+
+            if (grandChildren.isEmpty()) {
+                // The child is empty, seal it
+                child.seal(childOp, version);
+                if (child.getOperation() == LogicalOperation.NONE) {
+                    children.remove();
+                }
+            } else {
+                return new NestedReadyIterator(this, child, grandChildren.iterator(), childOp);
+            }
+        }
+
+        // We are done with this node, seal it.
+        node.seal(op, version);
+
+        // Remove from parent if we have one and this is a no-op
+        if (node.getOperation() == LogicalOperation.NONE) {
+            removeFromParent();
+        }
+
+        // Sub-iteration complete, return back to parent
+        return getParent();
+    }
+
+    abstract AbstractReadyIterator getParent();
+
+    abstract void removeFromParent();
+
+    private static final class NestedReadyIterator extends AbstractReadyIterator {
+        private final AbstractReadyIterator parent;
+
+        private NestedReadyIterator(final AbstractReadyIterator parent, final ModifiedNode node,
+                final Iterator<ModifiedNode> children, final ModificationApplyOperation operation) {
+            super(node, children, operation);
+            this.parent = requireNonNull(parent);
+        }
+
+        @Override
+        AbstractReadyIterator getParent() {
+            return parent;
+        }
+
+        @Override
+        void removeFromParent() {
+            parent.children.remove();
+        }
+    }
+
+    private static final class RootReadyIterator extends AbstractReadyIterator {
+        private RootReadyIterator(final ModifiedNode node, final Iterator<ModifiedNode> children,
+                final ModificationApplyOperation operation) {
+            super(node, children, operation);
+        }
+
+        @Override
+        AbstractReadyIterator getParent() {
+            return null;
+        }
+
+        @Override
+        void removeFromParent() {
+            // No-op, since root node cannot be removed
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AugmentationModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AugmentationModificationStrategy.java
new file mode 100644 (file)
index 0000000..b1c7076
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
+
+final class AugmentationModificationStrategy
+        extends DataNodeContainerModificationStrategy<AugmentationSchemaNode> {
+    private static final NormalizedNodeContainerSupport<AugmentationIdentifier, AugmentationNode> SUPPORT =
+            new NormalizedNodeContainerSupport<>(AugmentationNode.class, ImmutableAugmentationNodeBuilder::create,
+                    ImmutableAugmentationNodeBuilder::create);
+
+    private final AugmentationNode emptyNode;
+
+    AugmentationModificationStrategy(final AugmentationSchemaNode schema, final DataNodeContainer resolved,
+            final DataTreeConfiguration treeConfig) {
+        super(SUPPORT, EffectiveAugmentationSchema.create(schema, resolved), treeConfig);
+        emptyNode = Builders.augmentationBuilder()
+                .withNodeIdentifier(DataSchemaContextNode.augmentationIdentifierFrom(schema))
+                .build();
+    }
+
+    @Override
+    Optional<? extends TreeNode> apply(final ModifiedNode modification, final Optional<? extends TreeNode> storeMeta,
+            final Version version) {
+        return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, storeMeta,
+            version);
+    }
+
+    @Override
+    TreeNode defaultTreeNode() {
+        return defaultTreeNode(emptyNode);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AutomaticLifecycleMixin.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AutomaticLifecycleMixin.java
new file mode 100644 (file)
index 0000000..466e103
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNodeFactory;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+
+/**
+ * Mixin-type support class for subclasses of {@link ModificationApplyOperation} which need to provide automatic
+ * lifecycle management.
+ */
+final class AutomaticLifecycleMixin {
+    /**
+     * This is a capture of {@link ModificationApplyOperation#apply(ModifiedNode, Optional, Version)}.
+     */
+    @FunctionalInterface
+    interface Apply {
+        Optional<? extends TreeNode> apply(ModifiedNode modification, Optional<? extends TreeNode> storeMeta,
+                Version version);
+    }
+
+    /**
+     * This is a capture of
+     * {@link SchemaAwareApplyOperation#applyWrite(ModifiedNode, NormalizedNode, Optional, Version)}.
+     */
+    @FunctionalInterface
+    interface ApplyWrite {
+        TreeNode applyWrite(ModifiedNode modification, NormalizedNode<?, ?> newValue,
+                Optional<? extends TreeNode> storeMeta, Version version);
+    }
+
+    private AutomaticLifecycleMixin() {
+
+    }
+
+    static Optional<? extends TreeNode> apply(final Apply delegate, final ApplyWrite writeDelegate,
+            final NormalizedNode<?, ?> emptyNode, final ModifiedNode modification,
+            final Optional<? extends TreeNode> storeMeta, final Version version) {
+        final Optional<? extends TreeNode> ret;
+        if (modification.getOperation() == LogicalOperation.DELETE) {
+            if (modification.getChildren().isEmpty()) {
+                return delegate.apply(modification, storeMeta, version);
+            }
+            // Delete with children, implies it really is an empty write
+            ret = Optional.of(writeDelegate.applyWrite(modification, emptyNode, storeMeta, version));
+        } else if (modification.getOperation() == LogicalOperation.TOUCH && !storeMeta.isPresent()) {
+            ret = applyTouch(delegate, emptyNode, modification, storeMeta, version);
+        } else {
+            // No special handling required here, run normal apply operation
+            ret = delegate.apply(modification, storeMeta, version);
+        }
+
+        return ret.isPresent() ? disappearResult(modification, ret.get(), storeMeta) : ret;
+    }
+
+    private static Optional<? extends TreeNode> applyTouch(final Apply delegate, final NormalizedNode<?, ?> emptyNode,
+            final ModifiedNode modification, final Optional<? extends TreeNode> storeMeta, final Version version) {
+        // Container is not present, let's take care of the 'magically appear' part of our job
+        final Optional<? extends TreeNode> ret = delegate.apply(modification, fakeMeta(emptyNode, version), version);
+
+        // If the delegate indicated SUBTREE_MODIFIED, account for the fake and report APPEARED
+        if (modification.getModificationType() == ModificationType.SUBTREE_MODIFIED) {
+            modification.resolveModificationType(ModificationType.APPEARED);
+        }
+        return ret;
+    }
+
+    private static Optional<? extends TreeNode> disappearResult(final ModifiedNode modification, final TreeNode result,
+            final Optional<? extends TreeNode> storeMeta) {
+        // Check if the result is in fact empty before pulling any tricks
+        if (!isEmpty(result)) {
+            return Optional.of(result);
+        }
+
+        // We are pulling the 'disappear' trick, but what we report can be three different things
+        final ModificationType finalType;
+        if (!storeMeta.isPresent()) {
+            // ... there was nothing in the datastore, no change
+            finalType = ModificationType.UNMODIFIED;
+        } else if (modification.getModificationType() == ModificationType.WRITE) {
+            // ... this was an empty write, possibly originally a delete
+            finalType = ModificationType.DELETE;
+        } else {
+            // ... it really disappeared
+            finalType = ModificationType.DISAPPEARED;
+        }
+        modification.resolveModificationType(finalType);
+        return Optional.empty();
+    }
+
+    private static Optional<TreeNode> fakeMeta(final NormalizedNode<?, ?> emptyNode, final Version version) {
+        return Optional.of(TreeNodeFactory.createTreeNode(emptyNode, version));
+    }
+
+    private static boolean isEmpty(final TreeNode treeNode) {
+        final NormalizedNode<?, ?> data = treeNode.getData();
+        if (data instanceof NormalizedNodeContainer) {
+            return ((NormalizedNodeContainer<?, ?, ?>) data).getValue().isEmpty();
+        }
+        if (data instanceof OrderedNodeContainer) {
+            return ((OrderedNodeContainer<?>) data).getSize() == 0;
+        }
+        throw new IllegalStateException("Unhandled data " + data);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseEnforcer.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseEnforcer.java
new file mode 100644 (file)
index 0000000..c12b677
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.Sets;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import org.opendaylight.yangtools.concepts.Immutable;
+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.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class CaseEnforcer implements Immutable {
+    private static final class EnforcingMandatory extends CaseEnforcer {
+        private final MandatoryLeafEnforcer enforcer;
+
+        EnforcingMandatory(final ImmutableMap<NodeIdentifier, DataSchemaNode> children,
+                final ImmutableMap<AugmentationIdentifier, AugmentationSchemaNode> augmentations,
+                final MandatoryLeafEnforcer enforcer) {
+            super(children, augmentations);
+            this.enforcer = requireNonNull(enforcer);
+        }
+
+        @Override
+        void enforceOnTreeNode(final NormalizedNode<?, ?> normalizedNode) {
+            enforcer.enforceOnData(normalizedNode);
+        }
+    }
+
+    private final ImmutableMap<NodeIdentifier, DataSchemaNode> children;
+    private final ImmutableMap<AugmentationIdentifier, AugmentationSchemaNode> augmentations;
+
+    CaseEnforcer(final ImmutableMap<NodeIdentifier, DataSchemaNode> children,
+            final ImmutableMap<AugmentationIdentifier, AugmentationSchemaNode> augmentations) {
+        this.children = requireNonNull(children);
+        this.augmentations = requireNonNull(augmentations);
+    }
+
+    static CaseEnforcer forTree(final CaseSchemaNode schema, final DataTreeConfiguration treeConfig) {
+        final TreeType type = treeConfig.getTreeType();
+        final Builder<NodeIdentifier, DataSchemaNode> childrenBuilder = ImmutableMap.builder();
+        final Builder<AugmentationIdentifier, AugmentationSchemaNode> augmentationsBuilder = ImmutableMap.builder();
+        if (SchemaAwareApplyOperation.belongsToTree(type, schema)) {
+            for (final DataSchemaNode child : schema.getChildNodes()) {
+                if (SchemaAwareApplyOperation.belongsToTree(type, child)) {
+                    childrenBuilder.put(NodeIdentifier.create(child.getQName()), child);
+                }
+            }
+            for (final AugmentationSchemaNode augment : schema.getAvailableAugmentations()) {
+                if (augment.getChildNodes().stream()
+                        .anyMatch(child -> SchemaAwareApplyOperation.belongsToTree(type, child))) {
+                    augmentationsBuilder.put(DataSchemaContextNode.augmentationIdentifierFrom(augment), augment);
+                }
+            }
+        }
+
+        final ImmutableMap<NodeIdentifier, DataSchemaNode> children = childrenBuilder.build();
+        if (children.isEmpty()) {
+            return null;
+        }
+        final ImmutableMap<AugmentationIdentifier, AugmentationSchemaNode> augmentations = augmentationsBuilder.build();
+        final Optional<MandatoryLeafEnforcer> enforcer = MandatoryLeafEnforcer.forContainer(schema, treeConfig);
+        return enforcer.isPresent() ? new EnforcingMandatory(children, augmentations, enforcer.get())
+                : new CaseEnforcer(children, augmentations);
+    }
+
+    final Set<Entry<NodeIdentifier, DataSchemaNode>> getChildEntries() {
+        return children.entrySet();
+    }
+
+    final Set<NodeIdentifier> getChildIdentifiers() {
+        return children.keySet();
+    }
+
+    final Set<Entry<AugmentationIdentifier, AugmentationSchemaNode>> getAugmentationEntries() {
+        return augmentations.entrySet();
+    }
+
+    final Set<AugmentationIdentifier> getAugmentationIdentifiers() {
+        return augmentations.keySet();
+    }
+
+    final Set<PathArgument> getAllChildIdentifiers() {
+        return Sets.union(children.keySet(), augmentations.keySet());
+    }
+
+    void enforceOnTreeNode(final NormalizedNode<?, ?> normalizedNode) {
+        // Default is no-op
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ChildTrackingPolicy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ChildTrackingPolicy.java
new file mode 100644 (file)
index 0000000..13256f2
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Child ordering policy. It defines how a {@link ModifiedNode} tracks its children.
+ */
+abstract class ChildTrackingPolicy {
+    private static final int DEFAULT_CHILD_COUNT = 8;
+
+    /**
+     * No child nodes are possible, ever.
+     */
+    static final ChildTrackingPolicy NONE = new ChildTrackingPolicy() {
+        @Override
+        Map<PathArgument, ModifiedNode> createMap() {
+            // We cannot use ImmutableMap, as we need a functioning Map.clear()
+            return Collections.emptyMap();
+        }
+    };
+    /**
+     * Child nodes are possible and we need to make sure that their iteration order
+     * matches the order in which they are introduced.
+     */
+    static final ChildTrackingPolicy ORDERED = new ChildTrackingPolicy() {
+        @Override
+        Map<PathArgument, ModifiedNode> createMap() {
+            return new LinkedHashMap<>(DEFAULT_CHILD_COUNT);
+        }
+    };
+    /**
+     * Child nodes are possible, but their iteration order can be undefined.
+     */
+    static final ChildTrackingPolicy UNORDERED = new ChildTrackingPolicy() {
+        @Override
+        Map<PathArgument, ModifiedNode> createMap() {
+            return new HashMap<>();
+        }
+    };
+
+    /**
+     * Instantiate a new map for all possible children.
+     *
+     * @return An empty map instance
+     */
+    abstract Map<PathArgument, ModifiedNode> createMap();
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ChoiceModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ChoiceModificationStrategy.java
new file mode 100644 (file)
index 0000000..b0d9826
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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.tree;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicates;
+import com.google.common.base.Verify;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+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.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.AbstractNodeContainerModificationStrategy.Visible;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+final class ChoiceModificationStrategy extends Visible<ChoiceSchemaNode> {
+    private static final NormalizedNodeContainerSupport<NodeIdentifier, ChoiceNode> SUPPORT =
+            new NormalizedNodeContainerSupport<>(ChoiceNode.class, ImmutableChoiceNodeBuilder::create,
+                    ImmutableChoiceNodeBuilder::create);
+
+    private final ImmutableMap<PathArgument, ModificationApplyOperation> childNodes;
+    // FIXME: enforce leaves not coming from two case statements at the same time
+    private final ImmutableMap<CaseEnforcer, Collection<CaseEnforcer>> exclusions;
+    private final ImmutableMap<PathArgument, CaseEnforcer> caseEnforcers;
+    private final @NonNull ChoiceNode emptyNode;
+
+    ChoiceModificationStrategy(final ChoiceSchemaNode schema, final DataTreeConfiguration treeConfig) {
+        super(SUPPORT, treeConfig, schema);
+
+        final Builder<PathArgument, ModificationApplyOperation> childBuilder = ImmutableMap.builder();
+        final Builder<PathArgument, CaseEnforcer> enforcerBuilder = ImmutableMap.builder();
+        for (final CaseSchemaNode caze : schema.getCases().values()) {
+            final CaseEnforcer enforcer = CaseEnforcer.forTree(caze, treeConfig);
+            if (enforcer != null) {
+                for (final Entry<NodeIdentifier, DataSchemaNode> e : enforcer.getChildEntries()) {
+                    childBuilder.put(e.getKey(), SchemaAwareApplyOperation.from(e.getValue(), treeConfig));
+                    enforcerBuilder.put(e.getKey(), enforcer);
+                }
+                for (final Entry<AugmentationIdentifier, AugmentationSchemaNode> e
+                        : enforcer.getAugmentationEntries()) {
+                    childBuilder.put(e.getKey(), new AugmentationModificationStrategy(e.getValue(), caze, treeConfig));
+                    enforcerBuilder.put(e.getKey(), enforcer);
+                }
+            }
+        }
+        childNodes = childBuilder.build();
+        caseEnforcers = enforcerBuilder.build();
+
+        final Map<CaseEnforcer, Collection<CaseEnforcer>> exclusionsBuilder = new HashMap<>();
+        for (final CaseEnforcer e : caseEnforcers.values()) {
+            exclusionsBuilder.put(e, ImmutableList.copyOf(
+                Collections2.filter(caseEnforcers.values(), Predicates.not(Predicates.equalTo(e)))));
+        }
+        exclusions = ImmutableMap.copyOf(exclusionsBuilder);
+        emptyNode = ImmutableNodes.choiceNode(schema.getQName());
+    }
+
+    @Override
+    Optional<? extends TreeNode> apply(final ModifiedNode modification, final Optional<? extends TreeNode> storeMeta,
+            final Version version) {
+        return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, storeMeta,
+            version);
+    }
+
+    @Override
+    TreeNode defaultTreeNode() {
+        return defaultTreeNode(emptyNode);
+    }
+
+    @Override
+    public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+        return Optional.ofNullable(childNodes.get(child));
+    }
+
+    @Override
+    void optionalVerifyValueChildren(final NormalizedNode<?, ?> writtenValue) {
+        enforceCases(writtenValue);
+    }
+
+    private void enforceCases(final TreeNode tree) {
+        enforceCases(tree.getData());
+    }
+
+    private void enforceCases(final NormalizedNode<?, ?> normalizedNode) {
+        Verify.verify(normalizedNode instanceof ChoiceNode);
+        final Collection<DataContainerChild<?, ?>> children = ((ChoiceNode) normalizedNode).getValue();
+        if (!children.isEmpty()) {
+            final DataContainerChild<?, ?> firstChild = children.iterator().next();
+            final CaseEnforcer enforcer = Verify.verifyNotNull(caseEnforcers.get(firstChild.getIdentifier()),
+                "Case enforcer cannot be null. Most probably, child node %s of choice node %s does not belong "
+                + "in current tree type.", firstChild.getIdentifier(), normalizedNode.getIdentifier());
+
+            // Make sure no leaves from other cases are present
+            for (final CaseEnforcer other : exclusions.get(enforcer)) {
+                for (final PathArgument id : other.getAllChildIdentifiers()) {
+                    final Optional<NormalizedNode<?, ?>> maybeChild = NormalizedNodes.getDirectChild(normalizedNode,
+                        id);
+                    Preconditions.checkArgument(!maybeChild.isPresent(),
+                        "Child %s (from case %s) implies non-presence of child %s (from case %s), which is %s",
+                        firstChild.getIdentifier(), enforcer, id, other, maybeChild.orElse(null));
+                }
+            }
+
+            // Make sure all mandatory children are present
+            enforcer.enforceOnTreeNode(normalizedNode);
+        }
+    }
+
+    @Override
+    protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+        final TreeNode ret = super.applyMerge(modification, currentMeta, version);
+        enforceCases(ret);
+        return ret;
+    }
+
+    @Override
+    protected TreeNode applyWrite(final ModifiedNode modification,  final NormalizedNode<?, ?> newValue,
+            final Optional<? extends TreeNode> currentMeta, final Version version) {
+        final TreeNode ret = super.applyWrite(modification, newValue, currentMeta, version);
+        enforceCases(ret);
+        return ret;
+    }
+
+    @Override
+    protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+        final TreeNode ret = super.applyTouch(modification, currentMeta, version);
+        enforceCases(ret);
+        return ret;
+    }
+}
+
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ContainerModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ContainerModificationStrategy.java
new file mode 100644 (file)
index 0000000..df66a95
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+
+/**
+ * General container modification strategy. This is used by {@link EnforcingMandatory} in case of presence containers
+ * with mandatory nodes, as it needs to tap into {@link SchemaAwareApplyOperation}'s operations, or subclassed
+ * for the purposes of {@link StructuralContainerModificationStrategy}'s automatic lifecycle.
+ */
+class ContainerModificationStrategy extends DataNodeContainerModificationStrategy<ContainerSchemaNode> {
+    private static final class EnforcingMandatory extends ContainerModificationStrategy {
+        private final MandatoryLeafEnforcer enforcer;
+
+        EnforcingMandatory(final ContainerSchemaNode schemaNode, final DataTreeConfiguration treeConfig,
+                final MandatoryLeafEnforcer enforcer) {
+            super(schemaNode, treeConfig);
+            this.enforcer = requireNonNull(enforcer);
+        }
+
+        @Override
+        void mandatoryVerifyValueChildren(final NormalizedNode<?, ?> writtenValue) {
+            enforcer.enforceOnData(writtenValue);
+        }
+
+        @Override
+        protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta,
+                final Version version) {
+            final TreeNode ret = super.applyMerge(modification, currentMeta, version);
+            enforcer.enforceOnTreeNode(ret);
+            return ret;
+        }
+
+        @Override
+        protected TreeNode applyWrite(final ModifiedNode modification, final NormalizedNode<?, ?> newValue,
+                final Optional<? extends TreeNode> currentMeta, final Version version) {
+            final TreeNode ret = super.applyWrite(modification, newValue, currentMeta, version);
+            enforcer.enforceOnTreeNode(ret);
+            return ret;
+        }
+
+        @Override
+        protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta,
+                final Version version) {
+            final TreeNode ret = super.applyTouch(modification, currentMeta, version);
+            enforcer.enforceOnTreeNode(ret);
+            return ret;
+        }
+    }
+
+    private static final NormalizedNodeContainerSupport<NodeIdentifier, ContainerNode> SUPPORT =
+            new NormalizedNodeContainerSupport<>(ContainerNode.class, ImmutableContainerNodeBuilder::create,
+                    ImmutableContainerNodeBuilder::create);
+
+    ContainerModificationStrategy(final ContainerSchemaNode schemaNode, final DataTreeConfiguration treeConfig) {
+        super(SUPPORT, schemaNode, treeConfig);
+    }
+
+    static ModificationApplyOperation of(final ContainerSchemaNode schema, final DataTreeConfiguration treeConfig) {
+        if (schema.isPresenceContainer()) {
+            final Optional<MandatoryLeafEnforcer> enforcer = MandatoryLeafEnforcer.forContainer(schema, treeConfig);
+            return enforcer.isPresent() ? new EnforcingMandatory(schema, treeConfig, enforcer.get())
+                    : new ContainerModificationStrategy(schema, treeConfig);
+        }
+
+        return new StructuralContainerModificationStrategy(schema, treeConfig);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataNodeContainerModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataNodeContainerModificationStrategy.java
new file mode 100644 (file)
index 0000000..00d752b
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.AbstractNodeContainerModificationStrategy.Visible;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Base strategy for applying changes to a ContainerNode, irrespective of its
+ * actual type.
+ *
+ * @param <T> Type of the container node
+ */
+class DataNodeContainerModificationStrategy<T extends DataNodeContainer & WithStatus> extends Visible<T> {
+    private static final Logger LOG = LoggerFactory.getLogger(DataNodeContainerModificationStrategy.class);
+
+    private final @NonNull DataTreeConfiguration treeConfig;
+
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<DataNodeContainerModificationStrategy, ImmutableMap> UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(DataNodeContainerModificationStrategy.class, ImmutableMap.class,
+                "children");
+    private volatile ImmutableMap<PathArgument, ModificationApplyOperation> children = ImmutableMap.of();
+
+    DataNodeContainerModificationStrategy(final NormalizedNodeContainerSupport<?, ?> support, final T schema,
+            final DataTreeConfiguration treeConfig) {
+        super(support, treeConfig, schema);
+        this.treeConfig = requireNonNull(treeConfig, "treeConfig");
+    }
+
+    @Override
+    public final Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+        final ImmutableMap<PathArgument, ModificationApplyOperation> local = children;
+        final ModificationApplyOperation existing = local.get(identifier);
+        if (existing != null) {
+            return Optional.of(existing);
+        }
+
+        final ModificationApplyOperation childOperation = resolveChild(identifier);
+        return childOperation != null ? appendChild(local, identifier, childOperation) : Optional.empty();
+    }
+
+    private ModificationApplyOperation resolveChild(final PathArgument identifier) {
+        final T schema = getSchema();
+        if (identifier instanceof AugmentationIdentifier && schema instanceof AugmentationTarget) {
+            return SchemaAwareApplyOperation.from(schema, (AugmentationTarget) schema,
+                (AugmentationIdentifier) identifier, treeConfig);
+        }
+
+        final QName qname = identifier.getNodeType();
+        final Optional<DataSchemaNode> child = schema.findDataChildByName(qname);
+        if (!child.isPresent()) {
+            LOG.trace("Child {} not present in container schema {} children {}", identifier, this,
+                schema.getChildNodes());
+            return null;
+        }
+
+        try {
+            return SchemaAwareApplyOperation.from(child.get(), treeConfig);
+        } catch (IllegalArgumentException e) {
+            LOG.trace("Failed to instantiate child {} in container schema {} children {}", identifier, this,
+                schema.getChildNodes(), e);
+            return null;
+        }
+    }
+
+    private Optional<ModificationApplyOperation> appendChild(
+            final ImmutableMap<PathArgument, ModificationApplyOperation> initial, final PathArgument identifier,
+            final ModificationApplyOperation computed) {
+
+        ImmutableMap<PathArgument, ModificationApplyOperation> previous = initial;
+        while (true) {
+            // Build up a new map based on observed snapshot and computed child
+            final Builder<PathArgument, ModificationApplyOperation> builder = ImmutableMap.builderWithExpectedSize(
+                previous.size() + 1);
+            builder.putAll(previous);
+            builder.put(identifier, computed);
+            final ImmutableMap<PathArgument, ModificationApplyOperation> updated = builder.build();
+
+            // Attempt to install the updated map
+            if (UPDATER.compareAndSet(this, previous, updated)) {
+                return Optional.of(computed);
+            }
+
+            // We have raced, acquire a new snapshot, recheck presence and retry if needed
+            previous = children;
+            final ModificationApplyOperation raced = previous.get(identifier);
+            if (raced != null) {
+                return Optional.of(raced);
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataTreeState.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataTreeState.java
new file mode 100644 (file)
index 0000000..48d8d49
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Instances of this class hold the current state of a DataTree instance.
+ * The need for encapsulation stems from atomic updates, which potentially change
+ * multiple fields in one go.
+ */
+final class DataTreeState {
+    private final LatestOperationHolder holder;
+    private final SchemaContext schemaContext;
+    private final @NonNull TreeNode root;
+
+    private DataTreeState(final TreeNode root) {
+        this.root = requireNonNull(root);
+        holder = new LatestOperationHolder();
+        schemaContext = null;
+    }
+
+    private DataTreeState(final TreeNode root, final LatestOperationHolder holder, final SchemaContext schemaContext) {
+        // It should be impossible to instantiate a new root without a SchemaContext
+        this.schemaContext = requireNonNull(schemaContext);
+        this.holder = requireNonNull(holder);
+        this.root = requireNonNull(root);
+    }
+
+    static DataTreeState createInitial(final TreeNode root) {
+        return new DataTreeState(root);
+    }
+
+    @NonNull TreeNode getRoot() {
+        return root;
+    }
+
+    InMemoryDataTreeSnapshot newSnapshot() {
+        return new InMemoryDataTreeSnapshot(schemaContext, root, holder.newSnapshot());
+    }
+
+    DataTreeState withSchemaContext(final SchemaContext newSchemaContext, final ModificationApplyOperation operation) {
+        holder.setCurrent(operation);
+        return new DataTreeState(root, holder, newSchemaContext);
+    }
+
+    DataTreeState withRoot(final TreeNode newRoot) {
+        return new DataTreeState(newRoot, holder, schemaContext);
+    }
+
+    @Override
+    public String toString() {
+        final TreeNode r = root;
+        return MoreObjects.toStringHelper(this).add("data", NormalizedNodes.toStringTree(r.getData())).toString();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTree.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTree.java
new file mode 100644 (file)
index 0000000..587664c
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+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.NormalizedNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+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.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Read-only snapshot of the data tree.
+ */
+final class InMemoryDataTree extends AbstractDataTreeTip implements DataTree {
+    private static final VarHandle STATE;
+
+    static {
+        try {
+            STATE = MethodHandles.lookup().findVarHandle(InMemoryDataTree.class, "state", DataTreeState.class);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(InMemoryDataTree.class);
+
+    private final DataTreeConfiguration treeConfig;
+    private final boolean maskMandatory;
+
+    /**
+     * Current data store state generation. All accesses need to go through {@link #STATE}
+     */
+    @SuppressWarnings("unused")
+    private volatile DataTreeState state;
+
+    InMemoryDataTree(final TreeNode rootNode, final DataTreeConfiguration treeConfig,
+            final SchemaContext schemaContext) {
+        this.treeConfig = requireNonNull(treeConfig, "treeConfig");
+        maskMandatory = true;
+        state = DataTreeState.createInitial(rootNode);
+        if (schemaContext != null) {
+            setSchemaContext(schemaContext);
+        }
+    }
+
+    InMemoryDataTree(final TreeNode rootNode, final DataTreeConfiguration treeConfig,
+            final SchemaContext schemaContext, final DataSchemaNode rootSchemaNode, final boolean maskMandatory) {
+        this.treeConfig = requireNonNull(treeConfig, "treeConfig");
+        this.maskMandatory = maskMandatory;
+
+        state = DataTreeState.createInitial(rootNode).withSchemaContext(schemaContext, getOperation(rootSchemaNode));
+    }
+
+    private ModificationApplyOperation getOperation(final DataSchemaNode rootSchemaNode) {
+        if (rootSchemaNode instanceof ContainerSchemaNode && maskMandatory) {
+            return new ContainerModificationStrategy((ContainerSchemaNode) rootSchemaNode, treeConfig);
+        }
+        if (rootSchemaNode instanceof ListSchemaNode) {
+            final PathArgument arg = treeConfig.getRootPath().getLastPathArgument();
+            if (arg instanceof NodeIdentifierWithPredicates) {
+                return maskMandatory ? new ListEntryModificationStrategy((ListSchemaNode) rootSchemaNode, treeConfig)
+                        : ListEntryModificationStrategy.of((ListSchemaNode) rootSchemaNode, treeConfig);
+            }
+        }
+
+        return SchemaAwareApplyOperation.from(rootSchemaNode, treeConfig);
+    }
+
+    @Deprecated
+    @Override
+    public void setSchemaContext(final SchemaContext newSchemaContext) {
+        internalSetSchemaContext(newSchemaContext);
+    }
+
+    @Override
+    public void setEffectiveModelContext(final EffectiveModelContext newModelContext) {
+        internalSetSchemaContext(newModelContext);
+    }
+
+    /*
+     * This method is synchronized to guard against user attempting to install
+     * multiple contexts. Otherwise it runs in a lock-free manner.
+     */
+    private synchronized void internalSetSchemaContext(final SchemaContext newSchemaContext) {
+        requireNonNull(newSchemaContext);
+
+        LOG.debug("Following schema contexts will be attempted {}", newSchemaContext);
+
+        final DataSchemaContextTree contextTree = DataSchemaContextTree.from(newSchemaContext);
+        final Optional<DataSchemaContextNode<?>> rootContextNode = contextTree.findChild(getRootPath());
+        if (!rootContextNode.isPresent()) {
+            LOG.warn("Could not find root {} in new schema context, not upgrading", getRootPath());
+            return;
+        }
+
+        final DataSchemaNode rootSchemaNode = rootContextNode.get().getDataSchemaNode();
+        if (!(rootSchemaNode instanceof DataNodeContainer)) {
+            LOG.warn("Root {} resolves to non-container type {}, not upgrading", getRootPath(), rootSchemaNode);
+            return;
+        }
+
+        final ModificationApplyOperation rootNode = getOperation(rootSchemaNode);
+        DataTreeState currentState;
+        DataTreeState newState;
+        do {
+            currentState = currentState();
+            newState = currentState.withSchemaContext(newSchemaContext, rootNode);
+            // TODO: can we lower this to compareAndSwapRelease?
+        } while (!STATE.compareAndSet(this, currentState, newState));
+    }
+
+    @Override
+    public InMemoryDataTreeSnapshot takeSnapshot() {
+        return currentState().newSnapshot();
+    }
+
+    @Override
+    public void commit(final DataTreeCandidate candidate) {
+        if (candidate instanceof NoopDataTreeCandidate) {
+            return;
+        }
+        checkArgument(candidate instanceof InMemoryDataTreeCandidate, "Invalid candidate class %s",
+            candidate.getClass());
+        final InMemoryDataTreeCandidate c = (InMemoryDataTreeCandidate)candidate;
+
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("Data Tree is {}", NormalizedNodes.toStringTree(c.getTipRoot().getData()));
+        }
+
+        final TreeNode newRoot = c.getTipRoot();
+        DataTreeState currentState;
+        DataTreeState newState;
+        do {
+            currentState = currentState();
+            final TreeNode currentRoot = currentState.getRoot();
+            LOG.debug("Updating datastore from {} to {}", currentRoot, newRoot);
+
+            final TreeNode oldRoot = c.getBeforeRoot();
+            if (oldRoot != currentRoot) {
+                final String oldStr = simpleToString(oldRoot);
+                final String currentStr = simpleToString(currentRoot);
+                throw new IllegalStateException("Store tree " + currentStr + " and candidate base " + oldStr
+                    + " differ.");
+            }
+
+            newState = currentState.withRoot(newRoot);
+            LOG.trace("Updated state from {} to {}", currentState, newState);
+            // TODO: can we lower this to compareAndSwapRelease?
+        } while (!STATE.compareAndSet(this, currentState, newState));
+    }
+
+    private static String simpleToString(final Object obj) {
+        return obj.getClass().getName() + "@" + Integer.toHexString(obj.hashCode());
+    }
+
+    private DataTreeState currentState() {
+        return (DataTreeState) STATE.getAcquire(this);
+    }
+
+    @Override
+    public YangInstanceIdentifier getRootPath() {
+        return treeConfig.getRootPath();
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("object", super.toString())
+                .add("config", treeConfig)
+                .add("state", currentState())
+                .toString();
+    }
+
+    @Override
+    protected TreeNode getTipRoot() {
+        return currentState().getRoot();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeCandidate.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeCandidate.java
new file mode 100644 (file)
index 0000000..c2f7d8c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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.tree;
+
+import com.google.common.base.MoreObjects;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+
+final class InMemoryDataTreeCandidate extends AbstractDataTreeCandidate {
+
+    private static final class RootNode extends AbstractModifiedNodeBasedCandidateNode {
+        RootNode(final ModifiedNode mod, final TreeNode oldMeta, final TreeNode newMeta) {
+            super(mod, oldMeta, newMeta);
+        }
+
+        @Override
+        public PathArgument getIdentifier() {
+            throw new IllegalStateException("Attempted to get identifier of the root node");
+        }
+    }
+
+    private final RootNode root;
+
+    InMemoryDataTreeCandidate(final YangInstanceIdentifier rootPath, final ModifiedNode modificationRoot,
+            final TreeNode beforeRoot, final TreeNode afterRoot) {
+        super(rootPath);
+        this.root = new RootNode(modificationRoot, beforeRoot, afterRoot);
+    }
+
+    @Override
+    protected TreeNode getTipRoot() {
+        return root.getNewMeta();
+    }
+
+    TreeNode getBeforeRoot() {
+        return root.getOldMeta();
+    }
+
+    @Override
+    public DataTreeCandidateNode getRootNode() {
+        return root;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("rootPath", getRootPath())
+                .add("rootNode", getRootNode()).toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeFactory.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeFactory.java
new file mode 100644 (file)
index 0000000..dd23823
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.Optional;
+import org.kohsuke.MetaInfServices;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeFactory;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNodeFactory;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+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.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * A factory for creating in-memory data trees.
+ */
+@MetaInfServices
+public final class InMemoryDataTreeFactory implements DataTreeFactory {
+    private static final NormalizedNode<?, ?> ROOT_CONTAINER = ImmutableNodes.containerNode(SchemaContext.NAME);
+
+    @Override
+    public DataTree create(final DataTreeConfiguration treeConfig) {
+        return new InMemoryDataTree(TreeNodeFactory.createTreeNode(createRoot(treeConfig.getRootPath()),
+            Version.initial()), treeConfig, null);
+    }
+
+    @Override
+    public DataTree create(final DataTreeConfiguration treeConfig, final SchemaContext initialSchemaContext) {
+        return create(treeConfig, initialSchemaContext, true);
+    }
+
+    @Override
+    public DataTree create(final DataTreeConfiguration treeConfig, final SchemaContext initialSchemaContext,
+            final NormalizedNodeContainer<?, ?, ?> initialRoot) throws DataValidationFailedException {
+        final DataTree ret = create(treeConfig, initialSchemaContext, false);
+
+        final DataTreeModification mod = ret.takeSnapshot().newModification();
+        mod.write(YangInstanceIdentifier.empty(), initialRoot);
+        mod.ready();
+
+        ret.validate(mod);
+        final DataTreeCandidate candidate = ret.prepare(mod);
+        ret.commit(candidate);
+        return ret;
+    }
+
+    private static DataTree create(final DataTreeConfiguration treeConfig, final SchemaContext initialSchemaContext,
+            final boolean maskMandatory) {
+        final DataSchemaNode rootSchemaNode = getRootSchemaNode(initialSchemaContext, treeConfig.getRootPath());
+        final NormalizedNode<?, ?> rootDataNode = createRoot((DataNodeContainer)rootSchemaNode,
+            treeConfig.getRootPath());
+        return new InMemoryDataTree(TreeNodeFactory.createTreeNode(rootDataNode, Version.initial()), treeConfig,
+            initialSchemaContext, rootSchemaNode, maskMandatory);
+    }
+
+    private static DataSchemaNode getRootSchemaNode(final SchemaContext schemaContext,
+            final YangInstanceIdentifier rootPath) {
+        final DataSchemaContextTree contextTree = DataSchemaContextTree.from(schemaContext);
+        final Optional<DataSchemaContextNode<?>> rootContextNode = contextTree.findChild(rootPath);
+        checkArgument(rootContextNode.isPresent(), "Failed to find root %s in schema context", rootPath);
+
+        final DataSchemaNode rootSchemaNode = rootContextNode.get().getDataSchemaNode();
+        checkArgument(rootSchemaNode instanceof DataNodeContainer, "Root %s resolves to non-container type %s",
+            rootPath, rootSchemaNode);
+        return rootSchemaNode;
+    }
+
+    private static NormalizedNode<?, ?> createRoot(final DataNodeContainer schemaNode,
+            final YangInstanceIdentifier path) {
+        if (path.isEmpty()) {
+            checkArgument(schemaNode instanceof ContainerSchemaNode,
+                "Conceptual tree root has to be a container, not %s", schemaNode);
+            return ROOT_CONTAINER;
+        }
+
+        final PathArgument arg = path.getLastPathArgument();
+        if (schemaNode instanceof ContainerSchemaNode) {
+            checkArgument(arg instanceof NodeIdentifier, "Mismatched container %s path %s", schemaNode, path);
+            return ImmutableContainerNodeBuilder.create().withNodeIdentifier((NodeIdentifier) arg).build();
+        } else if (schemaNode instanceof ListSchemaNode) {
+            // This can either be a top-level list or its individual entry
+            if (arg instanceof NodeIdentifierWithPredicates) {
+                return ImmutableNodes.mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) arg).build();
+            }
+            checkArgument(arg instanceof NodeIdentifier, "Mismatched list %s path %s", schemaNode, path);
+            return ImmutableNodes.mapNodeBuilder().withNodeIdentifier((NodeIdentifier) arg).build();
+        } else {
+            throw new IllegalArgumentException("Unsupported root schema " + schemaNode);
+        }
+    }
+
+    private static NormalizedNode<?, ?> createRoot(final YangInstanceIdentifier path) {
+        if (path.isEmpty()) {
+            return ROOT_CONTAINER;
+        }
+
+        final PathArgument arg = path.getLastPathArgument();
+        if (arg instanceof NodeIdentifier) {
+            return ImmutableContainerNodeBuilder.create().withNodeIdentifier((NodeIdentifier) arg).build();
+        }
+        if (arg instanceof NodeIdentifierWithPredicates) {
+            return ImmutableNodes.mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) arg).build();
+        }
+
+        // FIXME: implement augmentations and leaf-lists
+        throw new IllegalArgumentException("Unsupported root node " + arg);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModification.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModification.java
new file mode 100644 (file)
index 0000000..72dac50
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.util.Collection;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.CursorAwareDataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModificationCursor;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class InMemoryDataTreeModification extends AbstractCursorAware implements CursorAwareDataTreeModification,
+        SchemaContextProvider {
+    private static final Logger LOG = LoggerFactory.getLogger(InMemoryDataTreeModification.class);
+
+    private final RootApplyStrategy strategyTree;
+    private final InMemoryDataTreeSnapshot snapshot;
+    private final ModifiedNode rootNode;
+    private final Version version;
+
+    private static final VarHandle SEALED;
+
+    static {
+        try {
+            SEALED = MethodHandles.lookup().findVarHandle(InMemoryDataTreeModification.class, "sealed", int.class);
+        } catch (ReflectiveOperationException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    // All access needs to go through this handle
+    @SuppressWarnings("unused")
+    private volatile int sealed;
+
+    InMemoryDataTreeModification(final InMemoryDataTreeSnapshot snapshot,
+            final RootApplyStrategy resolver) {
+        this.snapshot = requireNonNull(snapshot);
+        this.strategyTree = requireNonNull(resolver).snapshot();
+        this.rootNode = ModifiedNode.createUnmodified(snapshot.getRootNode(), getStrategy().getChildPolicy());
+
+        /*
+         * We could allocate version beforehand, since Version contract
+         * states two allocated version must be always different.
+         *
+         * Preallocating version simplifies scenarios such as
+         * chaining of modifications, since version for particular
+         * node in modification and in data tree (if successfully
+         * committed) will be same and will not change.
+         */
+        this.version = snapshot.getRootNode().getSubtreeVersion().next();
+    }
+
+    ModifiedNode getRootModification() {
+        return rootNode;
+    }
+
+    ModificationApplyOperation getStrategy() {
+        final ModificationApplyOperation ret = strategyTree.delegate();
+        if (ret == null) {
+            throw new IllegalStateException("Schema Context is not available.");
+        }
+        return ret;
+    }
+
+    @Override
+    public SchemaContext getSchemaContext() {
+        return snapshot.getSchemaContext();
+    }
+
+    @Override
+    public void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        checkSealed();
+        checkIdentifierReferencesData(path, data);
+        resolveModificationFor(path).write(data);
+    }
+
+    @Override
+    public void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        checkSealed();
+        checkIdentifierReferencesData(path, data);
+        resolveModificationFor(path).merge(data, version);
+    }
+
+    @Override
+    public void delete(final YangInstanceIdentifier path) {
+        checkSealed();
+
+        resolveModificationFor(path).delete();
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> readNode(final YangInstanceIdentifier path) {
+        /*
+         * Walk the tree from the top, looking for the first node between root and
+         * the requested path which has been modified. If no such node exists,
+         * we use the node itself.
+         */
+        final Entry<YangInstanceIdentifier, ModifiedNode> entry = StoreTreeNodes.findClosestsOrFirstMatch(rootNode,
+            path, ModifiedNode.IS_TERMINAL_PREDICATE);
+        final YangInstanceIdentifier key = entry.getKey();
+        final ModifiedNode mod = entry.getValue();
+
+        final Optional<? extends TreeNode> result = resolveSnapshot(key, mod);
+        if (result.isPresent()) {
+            final NormalizedNode<?, ?> data = result.get().getData();
+            return NormalizedNodes.findNode(key, data, path);
+        }
+
+        return Optional.empty();
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private Optional<? extends TreeNode> resolveSnapshot(final YangInstanceIdentifier path,
+            final ModifiedNode modification) {
+        final Optional<? extends TreeNode> potentialSnapshot = modification.getSnapshot();
+        if (potentialSnapshot != null) {
+            return potentialSnapshot;
+        }
+
+        try {
+            return resolveModificationStrategy(path).apply(modification, modification.getOriginal(), version);
+        } catch (final Exception e) {
+            LOG.error("Could not create snapshot for {}:{}", path, modification, e);
+            throw e;
+        }
+    }
+
+    void upgradeIfPossible() {
+        if (rootNode.getOperation() == LogicalOperation.NONE) {
+            strategyTree.upgradeIfPossible();
+        }
+    }
+
+    private ModificationApplyOperation resolveModificationStrategy(final YangInstanceIdentifier path) {
+        LOG.trace("Resolving modification apply strategy for {}", path);
+
+        upgradeIfPossible();
+        return StoreTreeNodes.findNodeChecked(getStrategy(), path);
+    }
+
+    private OperationWithModification resolveModificationFor(final YangInstanceIdentifier path) {
+        upgradeIfPossible();
+
+        /*
+         * Walk the strategy and modification trees in-sync, creating modification nodes as needed.
+         *
+         * If the user has provided wrong input, we may end up with a bunch of TOUCH nodes present
+         * ending with an empty one, as we will throw the exception below. This fact could end up
+         * being a problem, as we'd have bunch of phantom operations.
+         *
+         * That is fine, as we will prune any empty TOUCH nodes in the last phase of the ready
+         * process.
+         */
+        ModificationApplyOperation operation = getStrategy();
+        ModifiedNode modification = rootNode;
+
+        int depth = 1;
+        for (final PathArgument pathArg : path.getPathArguments()) {
+            final Optional<ModificationApplyOperation> potential = operation.getChild(pathArg);
+            if (!potential.isPresent()) {
+                throw new SchemaValidationFailedException(String.format("Child %s is not present in schema tree.",
+                        path.getAncestor(depth)));
+            }
+            operation = potential.get();
+            ++depth;
+
+            modification = modification.modifyChild(pathArg, operation, version);
+        }
+
+        return OperationWithModification.from(operation, modification);
+    }
+
+    private void checkSealed() {
+        checkState(!isSealed(), "Data Tree is sealed. No further modifications allowed.");
+    }
+
+    @Override
+    public String toString() {
+        return "MutableDataTree [modification=" + rootNode + "]";
+    }
+
+    @Override
+    public InMemoryDataTreeModification newModification() {
+        checkState(isSealed(), "Attempted to chain on an unsealed modification");
+
+        if (rootNode.getOperation() == LogicalOperation.NONE) {
+            // Simple fast case: just use the underlying modification
+            return snapshot.newModification();
+        }
+
+        /*
+         * We will use preallocated version, this means returned snapshot will
+         * have same version each time this method is called.
+         */
+        final TreeNode originalSnapshotRoot = snapshot.getRootNode();
+        final Optional<? extends TreeNode> tempRoot = getStrategy().apply(rootNode, Optional.of(originalSnapshotRoot),
+            version);
+        checkState(tempRoot.isPresent(), "Data tree root is not present, possibly removed by previous modification");
+
+        final InMemoryDataTreeSnapshot tempTree = new InMemoryDataTreeSnapshot(snapshot.getSchemaContext(),
+            tempRoot.get(), strategyTree);
+        return tempTree.newModification();
+    }
+
+    Version getVersion() {
+        return version;
+    }
+
+    boolean isSealed() {
+        // a quick check, synchronizes *only* on the sealed field
+        return (int) SEALED.getAcquire(this) != 0;
+    }
+
+    private static void applyChildren(final DataTreeModificationCursor cursor, final ModifiedNode node) {
+        final Collection<ModifiedNode> children = node.getChildren();
+        if (!children.isEmpty()) {
+            cursor.enter(node.getIdentifier());
+            for (final ModifiedNode child : children) {
+                applyNode(cursor, child);
+            }
+            cursor.exit();
+        }
+    }
+
+    private static void applyNode(final DataTreeModificationCursor cursor, final ModifiedNode node) {
+        switch (node.getOperation()) {
+            case NONE:
+                break;
+            case DELETE:
+                cursor.delete(node.getIdentifier());
+                break;
+            case MERGE:
+                cursor.merge(node.getIdentifier(), node.getWrittenValue());
+                applyChildren(cursor, node);
+                break;
+            case TOUCH:
+                // TODO: we could improve efficiency of cursor use if we could understand
+                //       nested TOUCH operations. One way of achieving that would be a proxy
+                //       cursor, which would keep track of consecutive enter and exit calls
+                //       and coalesce them.
+                applyChildren(cursor, node);
+                break;
+            case WRITE:
+                cursor.write(node.getIdentifier(), node.getWrittenValue());
+                applyChildren(cursor, node);
+                break;
+            default:
+                throw new IllegalArgumentException("Unhandled node operation " + node.getOperation());
+        }
+    }
+
+    @Override
+    public void applyToCursor(final DataTreeModificationCursor cursor) {
+        for (final ModifiedNode child : rootNode.getChildren()) {
+            applyNode(cursor, child);
+        }
+    }
+
+    static void checkIdentifierReferencesData(final PathArgument arg, final NormalizedNode<?, ?> data) {
+        checkArgument(arg.equals(data.getIdentifier()),
+            "Instance identifier references %s but data identifier is %s", arg, data.getIdentifier());
+    }
+
+    private void checkIdentifierReferencesData(final YangInstanceIdentifier path,
+            final NormalizedNode<?, ?> data) {
+        final PathArgument arg;
+
+        if (!path.isEmpty()) {
+            arg = path.getLastPathArgument();
+            checkArgument(arg != null, "Instance identifier %s has invalid null path argument", path);
+        } else {
+            arg = rootNode.getIdentifier();
+        }
+
+        checkIdentifierReferencesData(arg, data);
+    }
+
+    @Override
+    public Optional<DataTreeModificationCursor> openCursor(final YangInstanceIdentifier path) {
+        final OperationWithModification op = resolveModificationFor(path);
+        return Optional.of(openCursor(new InMemoryDataTreeModificationCursor(this, path, op)));
+    }
+
+    @Override
+    public void ready() {
+        // We want a full CAS with setVolatile() memory semantics, as we want to force happen-before
+        // for everything, including whatever user code works.
+        final boolean wasRunning = SEALED.compareAndSet(this, 0, 1);
+        checkState(wasRunning, "Attempted to seal an already-sealed Data Tree.");
+
+        AbstractReadyIterator current = AbstractReadyIterator.create(rootNode, getStrategy());
+        do {
+            current = current.process(version);
+        } while (current != null);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModificationCursor.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModificationCursor.java
new file mode 100644 (file)
index 0000000..ff92617
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModificationCursor;
+
+final class InMemoryDataTreeModificationCursor extends AbstractCursor<InMemoryDataTreeModification>
+        implements DataTreeModificationCursor {
+    private final Deque<OperationWithModification> stack = new ArrayDeque<>();
+
+    InMemoryDataTreeModificationCursor(final InMemoryDataTreeModification parent, final YangInstanceIdentifier rootPath,
+            final OperationWithModification rootOp) {
+        super(parent, rootPath);
+        stack.push(rootOp);
+    }
+
+    private OperationWithModification resolveChildModification(final PathArgument child) {
+        getParent().upgradeIfPossible();
+
+        final OperationWithModification op = stack.peek();
+        final Optional<ModificationApplyOperation> potential = op.getApplyOperation().getChild(child);
+        if (potential.isPresent()) {
+            final ModificationApplyOperation operation = potential.get();
+            final ModifiedNode modification = op.getModification().modifyChild(child, operation,
+                getParent().getVersion());
+
+            return OperationWithModification.from(operation, modification);
+        }
+
+        // Node not found, construct its path
+        final Collection<PathArgument> path = new ArrayList<>();
+        path.addAll(getRootPath().getPathArguments());
+
+        final Iterator<OperationWithModification> it = stack.descendingIterator();
+        // Skip the first entry, as it's already accounted for in rootPath
+        it.next();
+
+        while (it.hasNext()) {
+            path.add(it.next().getModification().getIdentifier());
+        }
+
+        throw new SchemaValidationFailedException(String.format("Child %s is not present in schema tree.", path));
+    }
+
+    @Override
+    public void enter(final PathArgument child) {
+        stack.push(resolveChildModification(child));
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    public void enter(final Iterable<PathArgument> path) {
+        int depth = 0;
+        for (PathArgument child : path) {
+            try {
+                stack.push(resolveChildModification(child));
+            } catch (Exception e) {
+                // Undo what we have done
+                for (int i = 0; i < depth; ++i) {
+                    stack.pop();
+                }
+                throw new IllegalArgumentException(e);
+            }
+            depth++;
+        }
+    }
+
+    @Override
+    public void exit(final int depth) {
+        checkArgument(depth >= 0);
+        checkState(depth < stack.size());
+
+        for (int i = 0; i < depth; i++) {
+            stack.pop();
+        }
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> readNode(final PathArgument child) {
+        return stack.peek().read(child, getParent().getVersion());
+    }
+
+    @Override
+    public void delete(final PathArgument child) {
+        ensureNotClosed();
+        resolveChildModification(child).delete();
+    }
+
+    @Override
+    public void merge(final PathArgument child, final NormalizedNode<?, ?> data) {
+        ensureNotClosed();
+        InMemoryDataTreeModification.checkIdentifierReferencesData(child, data);
+        resolveChildModification(child).merge(data, getParent().getVersion());
+    }
+
+    @Override
+    public void write(final PathArgument child, final NormalizedNode<?, ?> data) {
+        ensureNotClosed();
+        InMemoryDataTreeModification.checkIdentifierReferencesData(child, data);
+        resolveChildModification(child).write(data);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeSnapshot.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeSnapshot.java
new file mode 100644 (file)
index 0000000..eef7964
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.CursorAwareDataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshotCursor;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+final class InMemoryDataTreeSnapshot extends AbstractCursorAware implements CursorAwareDataTreeSnapshot,
+        SchemaContextProvider {
+    private final @NonNull RootApplyStrategy applyOper;
+    private final @NonNull SchemaContext schemaContext;
+    private final @NonNull TreeNode rootNode;
+
+    InMemoryDataTreeSnapshot(final SchemaContext schemaContext, final TreeNode rootNode,
+            final RootApplyStrategy applyOper) {
+        this.schemaContext = requireNonNull(schemaContext);
+        this.rootNode = requireNonNull(rootNode);
+        this.applyOper = requireNonNull(applyOper);
+    }
+
+    TreeNode getRootNode() {
+        return rootNode;
+    }
+
+    @Override
+    public SchemaContext getSchemaContext() {
+        return schemaContext;
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> readNode(final YangInstanceIdentifier path) {
+        return NormalizedNodes.findNode(rootNode.getData(), path);
+    }
+
+    @Override
+    public InMemoryDataTreeModification newModification() {
+        return new InMemoryDataTreeModification(this, applyOper);
+    }
+
+    @Override
+    public Optional<DataTreeSnapshotCursor> openCursor(final YangInstanceIdentifier path) {
+        return NormalizedNodes.findNode(rootNode.getData(), path).map(root -> {
+            checkArgument(root instanceof NormalizedNodeContainer, "Child %s is not a container", path);
+            return openCursor(new InMemoryDataTreeSnapshotCursor(this, path, (NormalizedNodeContainer<?, ?, ?>)root));
+        });
+    }
+
+    @Override
+    public String toString() {
+        return rootNode.getSubtreeVersion().toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeSnapshotCursor.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeSnapshotCursor.java
new file mode 100644 (file)
index 0000000..0aae3f7
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+
+final class InMemoryDataTreeSnapshotCursor extends AbstractCursor<InMemoryDataTreeSnapshot> {
+    private final Deque<NormalizedNodeContainer<?, ?, ?>> stack = new ArrayDeque<>();
+
+    InMemoryDataTreeSnapshotCursor(final InMemoryDataTreeSnapshot parent, final YangInstanceIdentifier rootPath,
+            final NormalizedNodeContainer<?, ?, ?> normalizedNode) {
+        super(parent, rootPath);
+        stack.push(normalizedNode);
+    }
+
+    @Override
+    public void enter(final PathArgument child) {
+        final Optional<NormalizedNode<?, ?>> maybeChildNode = NormalizedNodes.getDirectChild(stack.peek(), child);
+        checkArgument(maybeChildNode.isPresent(), "Child %s not found", child);
+
+        final NormalizedNode<?, ?> childNode = maybeChildNode.get();
+        checkArgument(childNode instanceof NormalizedNodeContainer, "Child %s is not a container", child);
+        stack.push((NormalizedNodeContainer<?, ?, ?>) childNode);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    public void enter(final Iterable<PathArgument> path) {
+        final Optional<NormalizedNode<?, ?>> maybeChildNode = NormalizedNodes.findNode(stack.peek(), path);
+        checkArgument(maybeChildNode.isPresent(), "Child %s not found", path);
+
+        final NormalizedNode<?, ?> childNode = maybeChildNode.get();
+        checkArgument(childNode instanceof NormalizedNodeContainer, "Child %s is not a container", path);
+
+        int depth = 0;
+        for (PathArgument arg : path) {
+            try {
+                enter(arg);
+            } catch (Exception e) {
+                for (int i = 0; i < depth; ++i) {
+                    stack.pop();
+                }
+                throw new IllegalArgumentException(e);
+            }
+
+            ++depth;
+        }
+    }
+
+    @Override
+    public void exit(final int depth) {
+        checkArgument(depth >= 0);
+        checkState(depth < stack.size());
+
+        for (int i = 0; i < depth; ++i) {
+            stack.pop();
+        }
+    }
+
+    @Override
+    public Optional<NormalizedNode<?, ?>> readNode(final PathArgument child) {
+        return NormalizedNodes.findNode(stack.peek(), child);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/LatestOperationHolder.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/LatestOperationHolder.java
new file mode 100644 (file)
index 0000000..d7c9738
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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.tree;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Holder and factory for upgradable root modifications.
+ *
+ * <p>
+ * This class is factory for upgradable root modifications and provides an access to set latest backing implementation.
+ */
+final class LatestOperationHolder {
+
+    private ModificationApplyOperation current;
+
+    /**
+     * Return latest backing implementation.
+     *
+     * @return latest backing implementation
+     */
+    ModificationApplyOperation getCurrent() {
+        return current;
+    }
+
+    /**
+     * Sets latest backing implementation of associated {@link RootApplyStrategy}.
+     *
+     * <p>
+     * Note: This does not result in upgrading implementation of already existing
+     * {@link RootApplyStrategy}. Users, who obtained instances using {@link #newSnapshot()}, deriving
+     * {@link RootApplyStrategy} from this modification must explicitly invoke
+     * {@link RootApplyStrategy#upgradeIfPossible()} on their instance to be updated to latest backing
+     * implementation.
+     *
+     * @param newApplyOper New backing implementation
+     */
+    void setCurrent(final ModificationApplyOperation newApplyOper) {
+        current = requireNonNull(newApplyOper);
+    }
+
+    /**
+     * Creates new upgradable {@link RootApplyStrategy} associated with holder.
+     *
+     * @return New upgradable {@link RootApplyStrategy} with {@link #getCurrent()} used
+     *         as the backing implementation.
+     */
+    RootApplyStrategy newSnapshot() {
+        return new UpgradableRootApplyStrategy(this, current);
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/LeafSetModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/LeafSetModificationStrategy.java
new file mode 100644 (file)
index 0000000..0ff785a
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.AbstractNodeContainerModificationStrategy.Invisible;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+final class LeafSetModificationStrategy extends Invisible<LeafListSchemaNode> {
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static final NormalizedNodeContainerSupport<NodeIdentifier, OrderedLeafSetNode<?>> ORDERED_SUPPORT =
+            new NormalizedNodeContainerSupport(OrderedLeafSetNode.class, ChildTrackingPolicy.ORDERED,
+                foo -> ImmutableOrderedLeafSetNodeBuilder.create((OrderedLeafSetNode<?>)foo),
+                ImmutableOrderedLeafSetNodeBuilder::create);
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static final NormalizedNodeContainerSupport<NodeIdentifier, LeafSetNode<?>> UNORDERED_SUPPORT =
+            new NormalizedNodeContainerSupport(LeafSetNode.class,
+                foo -> ImmutableLeafSetNodeBuilder.create((LeafSetNode<?>)foo),
+                ImmutableLeafSetNodeBuilder::create);
+
+    LeafSetModificationStrategy(final LeafListSchemaNode schema, final DataTreeConfiguration treeConfig) {
+        super(schema.isUserOrdered() ? ORDERED_SUPPORT : UNORDERED_SUPPORT, treeConfig,
+                new ValueNodeModificationStrategy<>(LeafSetEntryNode.class, schema));
+    }
+
+    @Override
+    public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+        return identifier instanceof NodeWithValue ? entryStrategy() : Optional.empty();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ListEntryModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ListEntryModificationStrategy.java
new file mode 100644 (file)
index 0000000..be7c380
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+class ListEntryModificationStrategy extends DataNodeContainerModificationStrategy<ListSchemaNode> {
+    private static final class EnforcingMandatory extends ListEntryModificationStrategy {
+        private final MandatoryLeafEnforcer enforcer;
+
+        EnforcingMandatory(final ListSchemaNode schemaNode, final DataTreeConfiguration treeConfig,
+                final MandatoryLeafEnforcer enforcer) {
+            super(schemaNode, treeConfig);
+            this.enforcer = requireNonNull(enforcer);
+        }
+
+        @Override
+        void mandatoryVerifyValueChildren(final NormalizedNode<?, ?> writtenValue) {
+            enforcer.enforceOnData(writtenValue);
+        }
+
+        @Override
+        protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta,
+                final Version version) {
+            final TreeNode ret = super.applyMerge(modification, currentMeta, version);
+            enforcer.enforceOnTreeNode(ret);
+            return ret;
+        }
+
+        @Override
+        protected TreeNode applyWrite(final ModifiedNode modification, final NormalizedNode<?, ?> newValue,
+                final Optional<? extends TreeNode> currentMeta, final Version version) {
+            final TreeNode ret = super.applyWrite(modification, newValue, currentMeta, version);
+            enforcer.enforceOnTreeNode(ret);
+            return ret;
+        }
+
+        @Override
+        protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta,
+                final Version version) {
+            final TreeNode ret = super.applyTouch(modification, currentMeta, version);
+            enforcer.enforceOnTreeNode(ret);
+            return ret;
+        }
+    }
+
+    private static final NormalizedNodeContainerSupport<NodeIdentifierWithPredicates, MapEntryNode> SUPPORT =
+            new NormalizedNodeContainerSupport<>(MapEntryNode.class, ImmutableMapEntryNodeBuilder::create,
+                    ImmutableMapEntryNodeBuilder::create);
+
+    ListEntryModificationStrategy(final ListSchemaNode schema, final DataTreeConfiguration treeConfig) {
+        super(SUPPORT, schema, treeConfig);
+    }
+
+    static @NonNull ListEntryModificationStrategy of(final ListSchemaNode schema,
+            final DataTreeConfiguration treeConfig) {
+        final Optional<MandatoryLeafEnforcer> enforcer = MandatoryLeafEnforcer.forContainer(schema, treeConfig);
+        return enforcer.isPresent() ? new EnforcingMandatory(schema, treeConfig, enforcer.get())
+                : new ListEntryModificationStrategy(schema, treeConfig);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/LogicalOperation.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/LogicalOperation.java
new file mode 100644 (file)
index 0000000..bdaa954
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.tree;
+
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+
+/**
+ * Enumeration of all possible node modification states. These are used in
+ * data tree modification context to quickly assess what sort of modification
+ * the node is undergoing. This is a superset of {@link ModificationType}:
+ * where this type represents a logical operation, {@link ModificationType}
+ * represents a physical operation.
+ */
+enum LogicalOperation {
+    /**
+     * Node is currently unmodified.
+     */
+    NONE,
+
+    /**
+     * A child node, either direct or indirect, has been modified. This means
+     * that the data representation of this node has potentially changed.
+     */
+    TOUCH,
+
+    /**
+     * This node has been placed into the tree, potentially completely replacing
+     * pre-existing contents.
+     */
+    WRITE,
+
+    /**
+     * This node has been deleted along with any of its child nodes.
+     */
+    DELETE,
+
+    /**
+     * Node has been written into the tree, but instead of replacing pre-existing
+     * contents, it has been merged. This means that any incoming nodes which
+     * were present in the tree have been replaced, but their child nodes have
+     * been retained.
+     */
+    MERGE,
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MandatoryLeafEnforcer.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MandatoryLeafEnforcer.java
new file mode 100644 (file)
index 0000000..90ae47c
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import java.util.Optional;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+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.ElementCountConstraintAware;
+import org.opendaylight.yangtools.yang.model.api.MandatoryAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// TODO: would making this Serializable be useful (for Functions and similar?)
+final class MandatoryLeafEnforcer implements Immutable {
+    private static final Logger LOG = LoggerFactory.getLogger(MandatoryLeafEnforcer.class);
+
+    private final ImmutableList<YangInstanceIdentifier> mandatoryNodes;
+
+    private MandatoryLeafEnforcer(final ImmutableList<YangInstanceIdentifier> mandatoryNodes) {
+        this.mandatoryNodes = requireNonNull(mandatoryNodes);
+    }
+
+
+    static Optional<MandatoryLeafEnforcer> forContainer(final DataNodeContainer schema,
+            final DataTreeConfiguration treeConfig) {
+        if (!treeConfig.isMandatoryNodesValidationEnabled()) {
+            return Optional.empty();
+        }
+
+        final Builder<YangInstanceIdentifier> builder = ImmutableList.builder();
+        findMandatoryNodes(builder, YangInstanceIdentifier.empty(), schema, treeConfig.getTreeType());
+        final ImmutableList<YangInstanceIdentifier> mandatoryNodes = builder.build();
+        return mandatoryNodes.isEmpty() ? Optional.empty() : Optional.of(new MandatoryLeafEnforcer(mandatoryNodes));
+    }
+
+    void enforceOnData(final NormalizedNode<?, ?> data) {
+        for (final YangInstanceIdentifier id : mandatoryNodes) {
+            checkArgument(NormalizedNodes.findNode(data, id).isPresent(),
+                "Node %s is missing mandatory descendant %s", data.getIdentifier(), id);
+        }
+    }
+
+    void enforceOnTreeNode(final TreeNode tree) {
+        enforceOnData(tree.getData());
+    }
+
+    private static void findMandatoryNodes(final Builder<YangInstanceIdentifier> builder,
+            final YangInstanceIdentifier id, final DataNodeContainer schema, final TreeType type) {
+        for (final DataSchemaNode child : schema.getChildNodes()) {
+            if (SchemaAwareApplyOperation.belongsToTree(type, child)) {
+                if (child instanceof ContainerSchemaNode) {
+                    final ContainerSchemaNode container = (ContainerSchemaNode) child;
+                    if (!container.isPresenceContainer()) {
+                        findMandatoryNodes(builder, id.node(NodeIdentifier.create(child.getQName())), container, type);
+                    }
+                } else {
+                    boolean needEnforce = child instanceof MandatoryAware && ((MandatoryAware) child).isMandatory();
+                    if (!needEnforce && child instanceof ElementCountConstraintAware) {
+                        needEnforce = ((ElementCountConstraintAware) child)
+                                .getElementCountConstraint().map(constraint -> {
+                                    final Integer min = constraint.getMinElements();
+                                    return min != null && min > 0;
+                                }).orElse(Boolean.FALSE).booleanValue();
+                    }
+                    if (needEnforce) {
+                        final YangInstanceIdentifier childId = id.node(NodeIdentifier.create(child.getQName()));
+                        LOG.debug("Adding mandatory child {}", childId);
+                        builder.add(childId.toOptimized());
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MapModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MapModificationStrategy.java
new file mode 100644 (file)
index 0000000..a956d65
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o. 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.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.AbstractNodeContainerModificationStrategy.Invisible;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+final class MapModificationStrategy extends Invisible<ListSchemaNode> {
+    private static final NormalizedNodeContainerSupport<NodeIdentifier, OrderedMapNode> ORDERED_SUPPORT =
+            new NormalizedNodeContainerSupport<>(OrderedMapNode.class, ChildTrackingPolicy.ORDERED,
+                    ImmutableOrderedMapNodeBuilder::create, ImmutableOrderedMapNodeBuilder::create);
+    private static final NormalizedNodeContainerSupport<NodeIdentifier, MapNode> UNORDERED_SUPPORT =
+            new NormalizedNodeContainerSupport<>(MapNode.class, ImmutableMapNodeBuilder::create,
+                    ImmutableMapNodeBuilder::create);
+
+    private final @NonNull MapNode emptyNode;
+
+    private MapModificationStrategy(final NormalizedNodeContainerSupport<?, ?> support, final ListSchemaNode schema,
+        final DataTreeConfiguration treeConfig, final MapNode emptyNode) {
+        super(support, treeConfig, ListEntryModificationStrategy.of(schema, treeConfig));
+        this.emptyNode = requireNonNull(emptyNode);
+    }
+
+    static MapModificationStrategy of(final ListSchemaNode schema, final DataTreeConfiguration treeConfig) {
+        final NormalizedNodeContainerSupport<?, ?> support;
+        final MapNode emptyNode;
+        if (schema.isUserOrdered()) {
+            support = ORDERED_SUPPORT;
+            emptyNode = ImmutableNodes.orderedMapNode(schema.getQName());
+        } else {
+            support = UNORDERED_SUPPORT;
+            emptyNode = ImmutableNodes.mapNode(schema.getQName());
+        }
+        return new MapModificationStrategy(support, schema, treeConfig, emptyNode);
+    }
+
+    @Override
+    public Optional<ModificationApplyOperation> getChild(final YangInstanceIdentifier.PathArgument identifier) {
+        return identifier instanceof NodeIdentifierWithPredicates ? entryStrategy() : Optional.empty();
+    }
+
+    @Override
+    Optional<? extends TreeNode> apply(final ModifiedNode modification, final Optional<? extends TreeNode> storeMeta,
+            final Version version) {
+        return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, storeMeta,
+            version);
+    }
+
+    @Override
+    TreeNode defaultTreeNode() {
+        return defaultTreeNode(emptyNode);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MinMaxElementsValidation.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MinMaxElementsValidation.java
new file mode 100644 (file)
index 0000000..18e43d1
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.RequiredElementCountException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraintAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class MinMaxElementsValidation<T extends DataSchemaNode & ElementCountConstraintAware>
+        extends ModificationApplyOperation {
+    private static final Logger LOG = LoggerFactory.getLogger(MinMaxElementsValidation.class);
+
+    private final SchemaAwareApplyOperation<T> delegate;
+    private final int minElements;
+    private final int maxElements;
+
+    private MinMaxElementsValidation(final SchemaAwareApplyOperation<T> delegate, final Integer minElements,
+            final Integer maxElements) {
+        this.delegate = requireNonNull(delegate);
+        this.minElements = minElements != null ? minElements : 0;
+        this.maxElements = maxElements != null ? maxElements : Integer.MAX_VALUE;
+    }
+
+    static <T extends DataSchemaNode & ElementCountConstraintAware> ModificationApplyOperation from(
+            final SchemaAwareApplyOperation<T> delegate) {
+        final Optional<ElementCountConstraint> optConstraint = delegate.getSchema().getElementCountConstraint();
+        if (!optConstraint.isPresent()) {
+            return delegate;
+        }
+
+        final ElementCountConstraint constraint = optConstraint.get();
+        return new MinMaxElementsValidation<>(delegate, constraint.getMinElements(), constraint.getMaxElements());
+    }
+
+    @Override
+    Optional<? extends TreeNode> apply(final ModifiedNode modification, final Optional<? extends TreeNode> storeMeta,
+            final Version version) {
+        Optional<? extends TreeNode> ret = modification.getValidatedNode(this, storeMeta);
+        if (ret == null) {
+            // Deal with the result moving on us
+            ret = delegate.apply(modification, storeMeta, version);
+            if (ret.isPresent()) {
+                checkChildren(ret.get().getData());
+            }
+        }
+
+        return ret;
+    }
+
+    @Override
+    void checkApplicable(final ModificationPath path, final NodeModification modification,
+            final Optional<? extends TreeNode> current, final Version version) throws DataValidationFailedException {
+        delegate.checkApplicable(path, modification, current, version);
+
+        if (!(modification instanceof ModifiedNode)) {
+            LOG.debug("Could not validate {}, does not implement expected class {}", modification, ModifiedNode.class);
+            return;
+        }
+        final ModifiedNode modified = (ModifiedNode) modification;
+
+        // We need to actually perform the operation to deal with merge in a sane manner. We know the modification
+        // is immutable, so the result of validation will probably not change. Note we should not be checking number
+        final Optional<? extends TreeNode> maybeApplied = delegate.apply(modified, current, version);
+        if (maybeApplied.isPresent()) {
+            // We only enforce min/max on present data and rely on MandatoryLeafEnforcer to take care of the empty case
+            validateMinMaxElements(path, maybeApplied.get().getData());
+        }
+
+        // Everything passed. We now have a snapshot of the result node, it would be too bad if we just threw it out.
+        // We know what the result of an apply operation is going to be *if* the following are kept unchanged:
+        // - the 'current' node
+        // - the schemacontext (therefore, the fact this object is associated with the modification)
+        //
+        // So let's stash the result. We will pick it up during apply operation.
+        modified.setValidatedNode(this, current, maybeApplied);
+    }
+
+    @Override
+    void fullVerifyStructure(final NormalizedNode<?, ?> modification) {
+        delegate.fullVerifyStructure(modification);
+        checkChildren(modification);
+    }
+
+    @Override
+    public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+        return delegate.getChild(child);
+    }
+
+    @Override
+    ChildTrackingPolicy getChildPolicy() {
+        return delegate.getChildPolicy();
+    }
+
+    @Override
+    void mergeIntoModifiedNode(final ModifiedNode node, final NormalizedNode<?, ?> value, final Version version) {
+        delegate.mergeIntoModifiedNode(node, value, version);
+    }
+
+    @Override
+    void quickVerifyStructure(final NormalizedNode<?, ?> modification) {
+        delegate.quickVerifyStructure(modification);
+    }
+
+    @Override
+    void recursivelyVerifyStructure(final NormalizedNode<?, ?> value) {
+        delegate.recursivelyVerifyStructure(value);
+    }
+
+    private void validateMinMaxElements(final ModificationPath path, final NormalizedNode<?, ?> value)
+            throws DataValidationFailedException {
+        final PathArgument id = value.getIdentifier();
+        final int children = numOfChildrenFromValue(value);
+        if (minElements > children) {
+            throw new RequiredElementCountException(path.toInstanceIdentifier(), minElements, maxElements, children,
+                "%s does not have enough elements (%s), needs at least %s", id, children, minElements);
+        }
+        if (maxElements < children) {
+            throw new RequiredElementCountException(path.toInstanceIdentifier(), minElements, maxElements, children,
+                "%s has too many elements (%s), can have at most %s", id, children, maxElements);
+        }
+    }
+
+    private void checkChildren(final NormalizedNode<?, ?> value) {
+        final PathArgument id = value.getIdentifier();
+        final int children = numOfChildrenFromValue(value);
+        checkArgument(minElements <= children, "Node %s does not have enough elements (%s), needs at least %s", id,
+                children, minElements);
+        checkArgument(maxElements >= children, "Node %s has too many elements (%s), can have at most %s", id, children,
+                maxElements);
+    }
+
+    private static int numOfChildrenFromValue(final NormalizedNode<?, ?> value) {
+        if (value instanceof NormalizedNodeContainer) {
+            return ((NormalizedNodeContainer<?, ?, ?>) value).getValue().size();
+        } else if (value instanceof UnkeyedListNode) {
+            return ((UnkeyedListNode) value).getSize();
+        }
+
+        throw new IllegalArgumentException(String.format(
+                "Unexpected type '%s', expected types are NormalizedNodeContainer and UnkeyedListNode",
+                value.getClass()));
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationApplyOperation.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationApplyOperation.java
new file mode 100644 (file)
index 0000000..60b83b1
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+
+/**
+ * Operation responsible for applying {@link ModifiedNode} on tree.
+ *
+ * <p>
+ * Operation is composite - operation on top level node consists of
+ * suboperations on child nodes. This allows to walk operation hierarchy and
+ * invoke suboperations independently.
+ *
+ * <p>
+ * <b>Implementation notes</b>
+ * <ul>
+ * <li>
+ * Implementations MUST expose all nested suboperations which operates on child
+ * nodes expose via {@link #getChild(PathArgument)} method.
+ * <li>Same suboperations SHOULD be used when invoked via
+ * {@link #apply(ModifiedNode, Optional, Version)} if applicable.
+ * </ul>
+ *
+ * <p>
+ * Hierarchical composite operation which is responsible for applying
+ * modification on particular subtree and creating updated subtree
+ */
+abstract class ModificationApplyOperation implements StoreTreeNode<ModificationApplyOperation> {
+    /**
+     * Implementation of this operation must be stateless and must not change state of this object.
+     *
+     * @param modification
+     *            NodeModification to be applied
+     * @param storeMeta
+     *            Store Metadata Node on which NodeModification should be
+     *            applied
+     * @param version New subtree version of parent node
+     * @return new {@link TreeNode} if operation resulted in updating
+     *         node, {@link Optional#absent()} if {@link ModifiedNode}
+     *         resulted in deletion of this node.
+     * @throws IllegalArgumentException
+     *             If it is not possible to apply Operation on provided Metadata
+     *             node
+     */
+    abstract Optional<? extends TreeNode> apply(ModifiedNode modification, Optional<? extends TreeNode> storeMeta,
+            Version version);
+
+    /**
+     * Checks if provided node modification could be applied to current metadata node.
+     *
+     * @param path Path to modification
+     * @param modification Modification
+     * @param current Metadata Node to which modification should be applied
+     * @param version Metadata version
+     * @throws DataValidationFailedException if the modification is not applicable
+     */
+    abstract void checkApplicable(ModificationPath path, NodeModification modification,
+            Optional<? extends TreeNode> current, Version version) throws DataValidationFailedException;
+
+    /**
+     * Performs a quick structural verification of NodeModification, such as written values / types uses right
+     * structural elements.
+     *
+     * @param modification data to be verified.
+     * @throws IllegalArgumentException If provided NodeModification does not adhere to the
+     *         structure.
+     */
+    abstract void quickVerifyStructure(NormalizedNode<?, ?> modification);
+
+    /**
+     * Performs a full structural verification of NodeModification, such as written values / types uses right
+     * structural elements. Unlike {@link #quickVerifyStructure(NormalizedNode)} this includes recursively checking
+     * children, too.
+     *
+     * @param modification data to be verified.
+     * @throws IllegalArgumentException If provided NodeModification does not adhere to the
+     *         structure.
+     */
+    abstract void fullVerifyStructure(NormalizedNode<?, ?> modification);
+
+    /**
+     * Return the tracking policy for this node's children.
+     *
+     * @return Tracking policy, may not be null.
+     */
+    abstract ChildTrackingPolicy getChildPolicy();
+
+    /**
+     * Stage a merge operation into a {@link ModifiedNode} such that it will be processed correctly by
+     * {@link #apply(ModifiedNode, Optional, Version)}. This method is the context which is introducing this operation,
+     * and so any overheads are charged to whoever is in control of the access pattern.
+     *
+     * @param modification Original modification node
+     * @param value Value which should be merge into the modification
+     * @param version Data version as carried in the containing {@link InMemoryDataTreeModification}
+     */
+    abstract void mergeIntoModifiedNode(ModifiedNode modification, NormalizedNode<?, ?> value, Version version);
+
+    /**
+     * Returns a suboperation for specified tree node.
+     *
+     * @return Reference to suboperation for specified tree node, {@link Optional#empty()}
+     *         if suboperation is not supported for specified tree node.
+     */
+    @Override
+    public abstract Optional<ModificationApplyOperation> getChild(PathArgument child);
+
+    abstract void recursivelyVerifyStructure(NormalizedNode<?, ?> value);
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationPath.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationPath.java
new file mode 100644 (file)
index 0000000..586e0ce
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.tree;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import java.util.Arrays;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+@NonNullByDefault
+final class ModificationPath implements Mutable {
+    private static final int DEFAULT_ALLOC_SIZE = 8;
+    private static final int ALLOC_SIZE = Integer.getInteger(
+        "org.opendaylight.yangtools.yang.data.impl.schema.tree.ModificationPath.ALLOC_SIZE", DEFAULT_ALLOC_SIZE);
+
+    private final YangInstanceIdentifier root;
+
+    private PathArgument[] entries = new PathArgument[ALLOC_SIZE];
+    private int used;
+
+    ModificationPath(final YangInstanceIdentifier root) {
+        this.root = requireNonNull(root);
+    }
+
+    void push(final PathArgument arg) {
+        if (entries.length == used) {
+            final int grow = used <= 32 ? used : used / 2;
+            entries = Arrays.copyOf(entries, used + grow);
+        }
+        entries[used++] = requireNonNull(arg);
+    }
+
+    void pop() {
+        checkState(used > 0, "No elements left");
+        used--;
+    }
+
+    YangInstanceIdentifier toInstanceIdentifier() {
+        return YangInstanceIdentifier.builder(root).append(Arrays.asList(entries).subList(0, used)).build();
+    }
+
+    @Override
+    public String toString() {
+        return toInstanceIdentifier().toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModifiedNode.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModifiedNode.java
new file mode 100644 (file)
index 0000000..9c4f29a
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * 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.tree;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Predicate;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNodeFactory;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+
+/**
+ * Node Modification Node and Tree.
+ *
+ * <p>
+ * Tree which structurally resembles data tree and captures client modifications to the data store tree. This tree is
+ * lazily created and populated via {@link #modifyChild(PathArgument, ModificationApplyOperation, Version)} and
+ * {@link TreeNode} which represents original state as tracked by {@link #getOriginal()}.
+ *
+ * <p>
+ * The contract is that the state information exposed here preserves the temporal ordering of whatever modifications
+ * were executed. A child's effects pertain to data node as modified by its ancestors. This means that in order to
+ * reconstruct the effective data node presentation, it is sufficient to perform a depth-first pre-order traversal of
+ * the tree.
+ */
+final class ModifiedNode extends NodeModification implements StoreTreeNode<ModifiedNode> {
+    static final Predicate<ModifiedNode> IS_TERMINAL_PREDICATE = input -> {
+        requireNonNull(input);
+        switch (input.getOperation()) {
+            case DELETE:
+            case MERGE:
+            case WRITE:
+                return true;
+            case TOUCH:
+            case NONE:
+                return false;
+            default:
+                throw new IllegalArgumentException("Unhandled modification type " + input.getOperation());
+        }
+    };
+
+    private final Map<PathArgument, ModifiedNode> children;
+    private final Optional<? extends TreeNode> original;
+    private final PathArgument identifier;
+    private LogicalOperation operation = LogicalOperation.NONE;
+    private Optional<TreeNode> snapshotCache;
+    private NormalizedNode<?, ?> value;
+    private ModificationType modType;
+
+    // Alternative history introduced in WRITE nodes. Instantiated when we touch any child underneath such a node.
+    private TreeNode writtenOriginal;
+
+    // Internal cache for TreeNodes created as part of validation
+    private ModificationApplyOperation validatedOp;
+    private Optional<? extends TreeNode> validatedCurrent;
+    private Optional<? extends TreeNode> validatedNode;
+
+    private ModifiedNode(final PathArgument identifier, final Optional<? extends TreeNode> original,
+            final ChildTrackingPolicy childPolicy) {
+        this.identifier = identifier;
+        this.original = original;
+        this.children = childPolicy.createMap();
+    }
+
+    @Override
+    public PathArgument getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    LogicalOperation getOperation() {
+        return operation;
+    }
+
+    @Override
+    Optional<? extends TreeNode> getOriginal() {
+        return original;
+    }
+
+    /**
+     * Return the value which was written to this node. The returned object is only valid for
+     * {@link LogicalOperation#MERGE} and {@link LogicalOperation#WRITE}.
+     * operations. It should only be consulted when this modification is going to end up being
+     * {@link ModificationType#WRITE}.
+     *
+     * @return Currently-written value
+     */
+    @NonNull NormalizedNode<?, ?> getWrittenValue() {
+        return verifyNotNull(value);
+    }
+
+    /**
+     * Returns child modification if child was modified.
+     *
+     * @return Child modification if direct child or it's subtree was modified.
+     */
+    @Override
+    public Optional<ModifiedNode> getChild(final PathArgument child) {
+        return Optional.ofNullable(children.get(child));
+    }
+
+    private Optional<? extends TreeNode> metadataFromSnapshot(final @NonNull PathArgument child) {
+        return original.isPresent() ? original.get().getChild(child) : Optional.empty();
+    }
+
+    private Optional<? extends TreeNode> metadataFromData(final @NonNull PathArgument child, final Version modVersion) {
+        if (writtenOriginal == null) {
+            // Lazy instantiation, as we do not want do this for all writes. We are using the modification's version
+            // here, as that version is what the SchemaAwareApplyOperation will see when dealing with the resulting
+            // modifications.
+            writtenOriginal = TreeNodeFactory.createTreeNode(value, modVersion);
+        }
+
+        return writtenOriginal.getChild(child);
+    }
+
+    /**
+     * Determine the base tree node we are going to apply the operation to. This is not entirely trivial because
+     * both DELETE and WRITE operations unconditionally detach their descendants from the original snapshot, so we need
+     * to take the current node's operation into account.
+     *
+     * @param child Child we are looking to modify
+     * @param modVersion Version allocated by the calling {@link InMemoryDataTreeModification}
+     * @return Before-image tree node as observed by that child.
+     */
+    private Optional<? extends TreeNode> findOriginalMetadata(final @NonNull PathArgument child,
+            final Version modVersion) {
+        switch (operation) {
+            case DELETE:
+                // DELETE implies non-presence
+                return Optional.empty();
+            case NONE:
+            case TOUCH:
+            case MERGE:
+                return metadataFromSnapshot(child);
+            case WRITE:
+                // WRITE implies presence based on written data
+                return metadataFromData(child, modVersion);
+            default:
+                throw new IllegalStateException("Unhandled node operation " + operation);
+        }
+    }
+
+    /**
+     * Returns child modification if child was modified, creates {@link ModifiedNode}
+     * for child otherwise. If this node's {@link ModificationType} is {@link ModificationType#UNMODIFIED}
+     * changes modification type to {@link ModificationType#SUBTREE_MODIFIED}.
+     *
+     * @param child child identifier, may not be null
+     * @param childOper Child operation
+     * @param modVersion Version allocated by the calling {@link InMemoryDataTreeModification}
+     * @return {@link ModifiedNode} for specified child, with {@link #getOriginal()}
+     *         containing child metadata if child was present in original data.
+     */
+    ModifiedNode modifyChild(final @NonNull PathArgument child, final @NonNull ModificationApplyOperation childOper,
+            final @NonNull Version modVersion) {
+        clearSnapshot();
+        if (operation == LogicalOperation.NONE) {
+            updateOperationType(LogicalOperation.TOUCH);
+        }
+        final ModifiedNode potential = children.get(child);
+        if (potential != null) {
+            return potential;
+        }
+
+        final Optional<? extends TreeNode> currentMetadata = findOriginalMetadata(child, modVersion);
+        final ModifiedNode newlyCreated = new ModifiedNode(child, currentMetadata, childOper.getChildPolicy());
+        if (operation == LogicalOperation.MERGE && value != null) {
+            /*
+             * We are attempting to modify a previously-unmodified part of a MERGE node. If the
+             * value contains this component, we need to materialize it as a MERGE modification.
+             */
+            @SuppressWarnings({ "rawtypes", "unchecked" })
+            final Optional<NormalizedNode<?, ?>> childData = ((NormalizedNodeContainer)value).getChild(child);
+            if (childData.isPresent()) {
+                childOper.mergeIntoModifiedNode(newlyCreated, childData.get(), modVersion);
+            }
+        }
+
+        children.put(child, newlyCreated);
+        return newlyCreated;
+    }
+
+    /**
+     * Returns all recorded direct child modifications.
+     *
+     * @return all recorded direct child modifications
+     */
+    @Override
+    Collection<ModifiedNode> getChildren() {
+        return children.values();
+    }
+
+    /**
+     * Records a delete for associated node.
+     */
+    void delete() {
+        final LogicalOperation newType;
+
+        switch (operation) {
+            case DELETE:
+            case NONE:
+                // We need to record this delete.
+                newType = LogicalOperation.DELETE;
+                break;
+            case MERGE:
+                // In case of merge - delete needs to be recored and must not to be changed into NONE, because lazy
+                // expansion of parent MERGE node would reintroduce it again.
+                newType = LogicalOperation.DELETE;
+                break;
+            case TOUCH:
+            case WRITE:
+                /*
+                 * We are canceling a previous modification. This is a bit tricky, as the original write may have just
+                 * introduced the data, or it may have modified it.
+                 *
+                 * As documented in BUG-2470, a delete of data introduced in this transaction needs to be turned into
+                 * a no-op.
+                 */
+                newType = original.isPresent() ? LogicalOperation.DELETE : LogicalOperation.NONE;
+                break;
+            default:
+                throw new IllegalStateException("Unhandled deletion of node with " + operation);
+        }
+
+        clearSnapshot();
+        children.clear();
+        this.value = null;
+        updateOperationType(newType);
+    }
+
+    /**
+     * Records a write for associated node.
+     *
+     * @param newValue new value
+     */
+    void write(final NormalizedNode<?, ?> newValue) {
+        updateValue(LogicalOperation.WRITE, newValue);
+        children.clear();
+    }
+
+    /**
+     * Seal the modification node and prune any children which has not been modified.
+     *
+     * @param schema associated apply operation
+     * @param version target version
+     */
+    void seal(final ModificationApplyOperation schema, final Version version) {
+        clearSnapshot();
+        writtenOriginal = null;
+
+        switch (operation) {
+            case TOUCH:
+                // A TOUCH node without any children is a no-op
+                if (children.isEmpty()) {
+                    updateOperationType(LogicalOperation.NONE);
+                }
+                break;
+            case WRITE:
+                // A WRITE can collapse all of its children
+                if (!children.isEmpty()) {
+                    value = schema.apply(this, getOriginal(), version).map(TreeNode::getData).orElse(null);
+                    children.clear();
+                }
+
+                if (value == null) {
+                    // The write has ended up being empty, such as a write of an empty list.
+                    updateOperationType(LogicalOperation.DELETE);
+                } else {
+                    schema.fullVerifyStructure(value);
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void clearSnapshot() {
+        snapshotCache = null;
+    }
+
+    Optional<TreeNode> getSnapshot() {
+        return snapshotCache;
+    }
+
+    Optional<TreeNode> setSnapshot(final Optional<TreeNode> snapshot) {
+        snapshotCache = requireNonNull(snapshot);
+        return snapshot;
+    }
+
+    void updateOperationType(final LogicalOperation type) {
+        operation = type;
+        modType = null;
+
+        // Make sure we do not reuse previously-instantiated data-derived metadata
+        writtenOriginal = null;
+        clearSnapshot();
+    }
+
+    @Override
+    public String toString() {
+        final ToStringHelper helper = MoreObjects.toStringHelper(this).omitNullValues()
+                .add("identifier", identifier).add("operation", operation).add("modificationType", modType);
+        if (!children.isEmpty()) {
+            helper.add("childModification", children);
+        }
+        return helper.toString();
+    }
+
+    void resolveModificationType(final @NonNull ModificationType type) {
+        modType = type;
+    }
+
+    /**
+     * Update this node's value and operation type without disturbing any of its child modifications.
+     *
+     * @param type New operation type
+     * @param newValue New node value
+     */
+    void updateValue(final LogicalOperation type, final NormalizedNode<?, ?> newValue) {
+        this.value = requireNonNull(newValue);
+        updateOperationType(type);
+    }
+
+    /**
+     * Return the physical modification done to data. May return null if the
+     * operation has not been applied to the underlying tree. This is different
+     * from the logical operation in that it can actually be a no-op if the
+     * operation has no side-effects (like an empty merge on a container).
+     *
+     * @return Modification type.
+     */
+    ModificationType getModificationType() {
+        return modType;
+    }
+
+    public static ModifiedNode createUnmodified(final TreeNode metadataTree, final ChildTrackingPolicy childPolicy) {
+        return new ModifiedNode(metadataTree.getIdentifier(), Optional.of(metadataTree), childPolicy);
+    }
+
+    void setValidatedNode(final ModificationApplyOperation op, final Optional<? extends TreeNode> current,
+            final Optional<? extends TreeNode> node) {
+        this.validatedOp = requireNonNull(op);
+        this.validatedCurrent = requireNonNull(current);
+        this.validatedNode = requireNonNull(node);
+    }
+
+    Optional<? extends TreeNode> getValidatedNode(final ModificationApplyOperation op,
+            final Optional<? extends TreeNode> current) {
+        return op.equals(validatedOp) && current.equals(validatedCurrent) ? validatedNode : null;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NodeModification.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NodeModification.java
new file mode 100644 (file)
index 0000000..b652aff
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.tree;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+
+/**
+ * Internal interface representing a modification action of a particular node.
+ * It is used by the validation code to allow for a read-only view of the
+ * modification tree as we should never modify that during validation.
+ */
+abstract class NodeModification implements Identifiable<PathArgument> {
+    /**
+     * Get the type of modification.
+     *
+     * @return Operation type.
+     */
+    abstract LogicalOperation getOperation();
+
+    /**
+     * Get the original tree node to which the modification is to be applied.
+     *
+     * @return The original node, or {@link Optional#absent()} if the node is
+     *         a new node.
+     */
+    abstract Optional<? extends TreeNode> getOriginal();
+
+    /**
+     * Get a read-only view of children nodes.
+     *
+     * @return Collection of all children nodes.
+     */
+    abstract Collection<? extends NodeModification> getChildren();
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NoopDataTreeCandidate.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NoopDataTreeCandidate.java
new file mode 100644 (file)
index 0000000..f445721
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+
+/**
+ * Internal utility class for an empty candidate. We instantiate this class
+ * for empty modifications, saving memory and processing speed. Instances
+ * of this class are explicitly recognized and processing of them is skipped.
+ */
+final class NoopDataTreeCandidate extends AbstractDataTreeCandidate {
+    private static final DataTreeCandidateNode ROOT = new DataTreeCandidateNode() {
+        @Override
+        public ModificationType getModificationType() {
+            return ModificationType.UNMODIFIED;
+        }
+
+        @Override
+        public Collection<DataTreeCandidateNode> getChildNodes() {
+            return ImmutableList.of();
+        }
+
+        @Override
+        public PathArgument getIdentifier() {
+            throw new IllegalStateException("Attempted to read identifier of the no-operation change");
+        }
+
+        @Override
+        public Optional<NormalizedNode<?, ?>> getDataAfter() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<NormalizedNode<?, ?>> getDataBefore() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<DataTreeCandidateNode> getModifiedChild(final PathArgument identifier) {
+            return Optional.empty();
+        }
+    };
+
+    private final @NonNull TreeNode afterRoot;
+
+    protected NoopDataTreeCandidate(final YangInstanceIdentifier rootPath, final ModifiedNode modificationRoot,
+            final TreeNode afterRoot) {
+        super(rootPath);
+        checkArgument(modificationRoot.getOperation() == LogicalOperation.NONE);
+        this.afterRoot = requireNonNull(afterRoot);
+    }
+
+    @Override
+    public DataTreeCandidateNode getRootNode() {
+        return ROOT;
+    }
+
+    @Override
+    protected TreeNode getTipRoot() {
+        return afterRoot;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NormalizedNodeContainerSupport.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NormalizedNodeContainerSupport.java
new file mode 100644 (file)
index 0000000..e1e1b88
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o. 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+
+final class NormalizedNodeContainerSupport<K extends PathArgument, T extends NormalizedNode<K, ?>> {
+    final Function<T, NormalizedNodeContainerBuilder<K, ?, ?, T>> copyBuilder;
+    final Supplier<NormalizedNodeContainerBuilder<K, ?, ?, T>> emptyBuilder;
+    final ChildTrackingPolicy childPolicy;
+    final Class<T> requiredClass;
+
+    NormalizedNodeContainerSupport(final Class<T> requiredClass, final ChildTrackingPolicy childPolicy,
+            final Function<T, NormalizedNodeContainerBuilder<K, ?, ?, T>> copyBuilder,
+            final Supplier<NormalizedNodeContainerBuilder<K, ?, ?, T>> emptyBuilder) {
+        this.requiredClass = requireNonNull(requiredClass);
+        this.childPolicy = requireNonNull(childPolicy);
+        this.copyBuilder = requireNonNull(copyBuilder);
+        this.emptyBuilder = requireNonNull(emptyBuilder);
+    }
+
+    NormalizedNodeContainerSupport(final Class<T> requiredClass,
+            final Function<T, NormalizedNodeContainerBuilder<K, ?, ?, T>> copyBuilder,
+            final Supplier<NormalizedNodeContainerBuilder<K, ?, ?, T>> emptyBuilder) {
+        this(requiredClass, ChildTrackingPolicy.UNORDERED, copyBuilder, emptyBuilder);
+    }
+
+    NormalizedNodeContainerBuilder<?, ?, ?, T> createBuilder(final NormalizedNode<?, ?> original) {
+        return copyBuilder.apply(cast(original));
+    }
+
+    NormalizedNode<?, ?> createEmptyValue(final NormalizedNode<?, ?> original) {
+        return emptyBuilder.get().withNodeIdentifier(cast(original).getIdentifier()).build();
+    }
+
+    private T cast(final NormalizedNode<?, ?> original) {
+        checkArgument(requiredClass.isInstance(original), "Require %s, got %s", requiredClass, original);
+        return requiredClass.cast(original);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("requiredClass", requiredClass).toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NotUpgradableRootApplyStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NotUpgradableRootApplyStrategy.java
new file mode 100644 (file)
index 0000000..e405013
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+final class NotUpgradableRootApplyStrategy extends RootApplyStrategy {
+    private final ModificationApplyOperation delegate;
+
+    NotUpgradableRootApplyStrategy(final ModificationApplyOperation delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    protected ModificationApplyOperation delegate() {
+        return delegate;
+    }
+
+    @Override
+    void upgradeIfPossible() {
+        // Intentional noop
+    }
+
+    @Override
+    RootApplyStrategy snapshot() {
+        return this;
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/OperationWithModification.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/OperationWithModification.java
new file mode 100644 (file)
index 0000000..363f249
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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.tree;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+
+final class OperationWithModification {
+    private final ModificationApplyOperation applyOperation;
+    private final ModifiedNode modification;
+
+    private OperationWithModification(final ModificationApplyOperation op, final ModifiedNode mod) {
+        this.applyOperation = requireNonNull(op);
+        this.modification = requireNonNull(mod);
+    }
+
+    void write(final NormalizedNode<?, ?> value) {
+        modification.write(value);
+        /**
+         * Fast validation of structure, full validation on written data will be run during seal.
+         */
+        applyOperation.quickVerifyStructure(value);
+    }
+
+    void merge(final NormalizedNode<?, ?> data, final Version version) {
+        /*
+         * A merge operation will end up overwriting parts of the tree, retaining others. We want to
+         * make sure we do not validate the complete resulting structure, but rather just what was
+         * written. In order to do that, we first pretend the data was written, run verification and
+         * then perform the merge -- with the explicit assumption that adding the newly-validated
+         * data with the previously-validated data will not result in invalid data.
+         *
+         * We perform only quick validation here, full validation will be applied as-needed during
+         * preparation, as the merge is reconciled with current state.
+         */
+        applyOperation.quickVerifyStructure(data);
+        applyOperation.mergeIntoModifiedNode(modification, data, version);
+    }
+
+    void delete() {
+        modification.delete();
+    }
+
+    /**
+     * Read a particular child. If the child has been modified and does not have a stable
+     * view, one will we instantiated with specified version.
+     */
+    Optional<NormalizedNode<?, ?>> read(final PathArgument child, final Version version) {
+        final Optional<ModifiedNode> maybeChild = modification.getChild(child);
+        if (maybeChild.isPresent()) {
+            final ModifiedNode childNode = maybeChild.get();
+
+            Optional<? extends TreeNode> snapshot = childNode.getSnapshot();
+            if (snapshot == null) {
+                // Snapshot is not present, force instantiation
+                snapshot = applyOperation.getChild(child).get().apply(childNode, childNode.getOriginal(), version);
+            }
+
+            return snapshot.map(TreeNode::getData);
+        }
+
+        Optional<? extends TreeNode> snapshot = modification.getSnapshot();
+        if (snapshot == null) {
+            snapshot = apply(modification.getOriginal(), version);
+        }
+
+        if (snapshot.isPresent()) {
+            return snapshot.get().getChild(child).map(TreeNode::getData);
+        }
+
+        return Optional.empty();
+    }
+
+    public ModifiedNode getModification() {
+        return modification;
+    }
+
+    public ModificationApplyOperation getApplyOperation() {
+        return applyOperation;
+    }
+
+    public Optional<? extends TreeNode> apply(final Optional<? extends TreeNode> data, final Version version) {
+        return applyOperation.apply(modification, data, version);
+    }
+
+    public static OperationWithModification from(final ModificationApplyOperation operation,
+            final ModifiedNode modification) {
+        return new OperationWithModification(operation, modification);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/RootApplyStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/RootApplyStrategy.java
new file mode 100644 (file)
index 0000000..3ea9826
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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.tree;
+
+import com.google.common.collect.ForwardingObject;
+import java.util.Objects;
+
+/**
+ * Represents a {@link ModificationApplyOperation} which is rooted at conceptual
+ * top of data tree.
+ *
+ * <p>
+ * This implementation differs from other implementations in this package that
+ * is not immutable, but may be upgraded to newer state if available by
+ * explicitly invoking {@link #upgradeIfPossible()} and also serves as factory
+ * for deriving snapshot {@link RootApplyStrategy} which will not
+ * be affected by upgrade of original one.
+ *
+ * <p>
+ * There are two variations of this {@link ModificationApplyOperation}:
+ * <ul>
+ * <li>
+ * <b>Upgradable</b> - operation may be upgraded to different backing
+ * implementation by invoking {@link #upgradeIfPossible()}.</li>
+ * <li><b>Not Upgradable</b> - operation is immutable, invocation of
+ * {@link #upgradeIfPossible()} is no-op and method {@link #snapshot()} returns
+ * pointer on same object.
+ * </ul>
+ * <h3>Upgradable Root Modification Operation</h3>
+ * Upgradable Root Modification Operation may be created using:
+ * <ul>
+ * <li> {@link #from(ModificationApplyOperation)} with other upgradable root
+ * modification as an argument
+ * <li>using factory {@link LatestOperationHolder} which instantiates Upgradable
+ * Root Modification Operations and provides an option to set latest
+ * implementation.
+ * </ul>
+ *
+ * <p>
+ * Upgradable root operation is never upgraded to latest operation
+ * automatically, but client code must explicitly invoke
+ * {@link #upgradeIfPossible()} to get latest implementation.
+ *
+ * <p>
+ * Note: This is helpful for implementing
+ * {@link org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification}
+ * which may be derived from
+ * {@link org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree} before
+ * update of schema and user actually writes data after schema update. During
+ * update user did not invoked any operation.
+ */
+abstract class RootApplyStrategy extends ForwardingObject {
+
+    static RootApplyStrategy from(final ModificationApplyOperation resolver) {
+        return new NotUpgradableRootApplyStrategy(resolver);
+    }
+
+    @Override
+    protected abstract ModificationApplyOperation delegate();
+
+    @Override
+    public final boolean equals(final Object obj) {
+        return Objects.equals(delegate(), obj);
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hashCode(delegate());
+    }
+
+    @Override
+    public final String toString() {
+        return Objects.toString(delegate());
+    }
+
+    /**
+     * Creates a snapshot from this modification, which may have separate
+     * upgrade lifecycle and is not affected by upgrades.
+     *
+     * <p>
+     * Newly created snapshot uses backing implementation of this modification.
+     *
+     * @return Derived {@link RootApplyStrategy} with separate
+     *         upgrade lifecycle.
+     */
+    abstract RootApplyStrategy snapshot();
+
+    /**
+     * Upgrades backing implementation to latest available, if possible.
+     *
+     * <p>
+     * Latest implementation of {@link RootApplyStrategy} is
+     * managed by {@link LatestOperationHolder} which was used to construct this
+     * operation and latest operation is updated by
+     * {@link LatestOperationHolder#setCurrent(ModificationApplyOperation)}.
+     */
+    abstract void upgradeIfPossible();
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaAwareApplyOperation.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaAwareApplyOperation.java
new file mode 100644 (file)
index 0000000..56fea60
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.DocumentedNode.WithStatus;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class SchemaAwareApplyOperation<T extends WithStatus> extends ModificationApplyOperation {
+    private static final Logger LOG = LoggerFactory.getLogger(SchemaAwareApplyOperation.class);
+
+    public static ModificationApplyOperation from(final DataSchemaNode schemaNode,
+            final DataTreeConfiguration treeConfig) {
+        if (treeConfig.getTreeType() == TreeType.CONFIGURATION) {
+            checkArgument(schemaNode.isConfiguration(), "Supplied %s does not belongs to configuration tree.",
+                schemaNode.getPath());
+        }
+        if (schemaNode instanceof ContainerSchemaNode) {
+            return ContainerModificationStrategy.of((ContainerSchemaNode) schemaNode, treeConfig);
+        } else if (schemaNode instanceof ListSchemaNode) {
+            return fromListSchemaNode((ListSchemaNode) schemaNode, treeConfig);
+        } else if (schemaNode instanceof ChoiceSchemaNode) {
+            return new ChoiceModificationStrategy((ChoiceSchemaNode) schemaNode, treeConfig);
+        } else if (schemaNode instanceof LeafListSchemaNode) {
+            return MinMaxElementsValidation.from(new LeafSetModificationStrategy((LeafListSchemaNode) schemaNode,
+                treeConfig));
+        } else if (schemaNode instanceof LeafSchemaNode) {
+            return new ValueNodeModificationStrategy<>(LeafNode.class, (LeafSchemaNode) schemaNode);
+        }
+        throw new IllegalArgumentException("Not supported schema node type for " + schemaNode.getClass());
+    }
+
+    public static AugmentationModificationStrategy from(final DataNodeContainer resolvedTree,
+            final AugmentationTarget augSchemas, final AugmentationIdentifier identifier,
+            final DataTreeConfiguration treeConfig) {
+        for (final AugmentationSchemaNode potential : augSchemas.getAvailableAugmentations()) {
+            for (final DataSchemaNode child : potential.getChildNodes()) {
+                if (identifier.getPossibleChildNames().contains(child.getQName())) {
+                    return new AugmentationModificationStrategy(potential, resolvedTree, treeConfig);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    static void checkConflicting(final ModificationPath path, final boolean condition, final String message)
+            throws ConflictingModificationAppliedException {
+        if (!condition) {
+            throw new ConflictingModificationAppliedException(path.toInstanceIdentifier(), message);
+        }
+    }
+
+    private static ModificationApplyOperation fromListSchemaNode(final ListSchemaNode schemaNode,
+            final DataTreeConfiguration treeConfig) {
+        final List<QName> keyDefinition = schemaNode.getKeyDefinition();
+        final SchemaAwareApplyOperation<ListSchemaNode> op;
+        if (keyDefinition == null || keyDefinition.isEmpty()) {
+            op = new UnkeyedListModificationStrategy(schemaNode, treeConfig);
+        } else {
+            op = MapModificationStrategy.of(schemaNode, treeConfig);
+        }
+
+        return MinMaxElementsValidation.from(op);
+    }
+
+    protected static void checkNotConflicting(final ModificationPath path, final TreeNode original,
+            final TreeNode current) throws ConflictingModificationAppliedException {
+        checkConflicting(path, original.getVersion().equals(current.getVersion()),
+                "Node was replaced by other transaction.");
+        checkConflicting(path, original.getSubtreeVersion().equals(current.getSubtreeVersion()),
+                "Node children was modified by other transaction");
+    }
+
+    protected final ModificationApplyOperation resolveChildOperation(final PathArgument child) {
+        final Optional<ModificationApplyOperation> potential = getChild(child);
+        checkArgument(potential.isPresent(), "Operation for child %s is not defined.", child);
+        return potential.get();
+    }
+
+    @Override
+    final void checkApplicable(final ModificationPath path, final NodeModification modification,
+            final Optional<? extends TreeNode> current, final Version version) throws DataValidationFailedException {
+        switch (modification.getOperation()) {
+            case DELETE:
+                checkDeleteApplicable(modification, current);
+                break;
+            case TOUCH:
+                checkTouchApplicable(path, modification, current, version);
+                break;
+            case WRITE:
+                checkWriteApplicable(path, modification, current, version);
+                break;
+            case MERGE:
+                checkMergeApplicable(path, modification, current, version);
+                break;
+            case NONE:
+                break;
+            default:
+                throw new UnsupportedOperationException(
+                    "Suplied modification type " + modification.getOperation() + " is not supported.");
+        }
+    }
+
+    @Override
+    final void quickVerifyStructure(final NormalizedNode<?, ?> writtenValue) {
+        verifyValue(writtenValue);
+    }
+
+    @Override
+    final void fullVerifyStructure(final NormalizedNode<?, ?> writtenValue) {
+        verifyValue(writtenValue);
+        verifyValueChildren(writtenValue);
+    }
+
+    /**
+     * Verify the a written value, without performing deeper tree validation.
+     *
+     * @param writtenValue Written value
+     */
+    abstract void verifyValue(NormalizedNode<?, ?> writtenValue);
+
+    /**
+     * Verify the children implied by a written value after the value itself has been verified by
+     * {@link #verifyValue(NormalizedNode)}. Default implementation does nothing.
+     *
+     * @param writtenValue Written value
+     */
+    void verifyValueChildren(final NormalizedNode<?, ?> writtenValue) {
+        // Defaults to no-op
+    }
+
+    protected void checkMergeApplicable(final ModificationPath path, final NodeModification modification,
+            final Optional<? extends TreeNode> current, final Version version) throws DataValidationFailedException {
+        final Optional<? extends TreeNode> original = modification.getOriginal();
+        if (original.isPresent() && current.isPresent()) {
+            /*
+             * We need to do conflict detection only and only if the value of leaf changed
+             * before two transactions. If value of leaf is unchanged between two transactions
+             * it should not cause transaction to fail, since result of this merge
+             * leads to same data.
+             */
+            final TreeNode orig = original.get();
+            final TreeNode cur = current.get();
+            if (!orig.getData().equals(cur.getData())) {
+                checkNotConflicting(path, orig, cur);
+            }
+        }
+    }
+
+    /**
+     * Checks if write operation can be applied to current TreeNode.
+     * The operation checks if original tree node to which the modification is going to be applied exists and if
+     * current node in TreeNode structure exists.
+     *
+     * @param path Path from current node in TreeNode
+     * @param modification modification to apply
+     * @param current current node in TreeNode for modification to apply
+     * @throws DataValidationFailedException when a data dependency conflict is detected
+     */
+    private static void checkWriteApplicable(final ModificationPath path, final NodeModification modification,
+            final Optional<? extends TreeNode> current, final Version version) throws DataValidationFailedException {
+        final Optional<? extends TreeNode> original = modification.getOriginal();
+        if (original.isPresent() && current.isPresent()) {
+            checkNotConflicting(path, original.get(), current.get());
+        } else {
+            checkConflicting(path, !original.isPresent(), "Node was deleted by other transaction.");
+            checkConflicting(path, !current.isPresent(), "Node was created by other transaction.");
+        }
+    }
+
+    private static void checkDeleteApplicable(final NodeModification modification,
+            final Optional<? extends TreeNode> current) {
+        // Delete is always applicable, we do not expose it to subclasses
+        if (!current.isPresent()) {
+            LOG.trace("Delete operation turned to no-op on missing node {}", modification);
+        }
+    }
+
+    @Override
+    Optional<? extends TreeNode> apply(final ModifiedNode modification, final Optional<? extends TreeNode> currentMeta,
+            final Version version) {
+        switch (modification.getOperation()) {
+            case DELETE:
+                // Deletion of a non-existing node is a no-op, report it as such
+                modification.resolveModificationType(currentMeta.isPresent() ? ModificationType.DELETE
+                        : ModificationType.UNMODIFIED);
+                return modification.setSnapshot(Optional.empty());
+            case TOUCH:
+                checkArgument(currentMeta.isPresent(), "Metadata not available for modification %s", modification);
+                return modification.setSnapshot(Optional.of(applyTouch(modification, currentMeta.get(),
+                    version)));
+            case MERGE:
+                final TreeNode result;
+
+                if (!currentMeta.isPresent()) {
+                    // This is a slight optimization: a merge on a non-existing node equals to a write. Written data
+                    // structure is usually verified when the transaction is sealed. To preserve correctness, we have
+                    // to run that validation here.
+                    modification.resolveModificationType(ModificationType.WRITE);
+                    result = applyWrite(modification, modification.getWrittenValue(), currentMeta, version);
+                    fullVerifyStructure(result.getData());
+                } else {
+                    result = applyMerge(modification, currentMeta.get(), version);
+                }
+
+                return modification.setSnapshot(Optional.of(result));
+            case WRITE:
+                modification.resolveModificationType(ModificationType.WRITE);
+                return modification.setSnapshot(Optional.of(applyWrite(modification,
+                    verifyNotNull(modification.getWrittenValue()), currentMeta, version)));
+            case NONE:
+                modification.resolveModificationType(ModificationType.UNMODIFIED);
+                return currentMeta;
+            default:
+                throw new IllegalArgumentException("Provided modification type is not supported.");
+        }
+    }
+
+    /**
+     * Apply a merge operation. Since the result of merge differs based on the data type
+     * being modified, implementations of this method are responsible for calling
+     * {@link ModifiedNode#resolveModificationType(ModificationType)} as appropriate.
+     *
+     * @param modification Modified node
+     * @param currentMeta Store Metadata Node on which NodeModification should be applied
+     * @param version New subtree version of parent node
+     * @return A sealed TreeNode representing applied operation.
+     */
+    protected abstract TreeNode applyMerge(ModifiedNode modification, TreeNode currentMeta, Version version);
+
+    protected abstract TreeNode applyWrite(ModifiedNode modification, NormalizedNode<?, ?> newValue,
+            Optional<? extends TreeNode> currentMeta, Version version);
+
+    /**
+     * Apply a nested operation. Since there may not actually be a nested operation
+     * to be applied, implementations of this method are responsible for calling
+     * {@link ModifiedNode#resolveModificationType(ModificationType)} as appropriate.
+     *
+     * @param modification Modified node
+     * @param currentMeta Store Metadata Node on which NodeModification should be applied
+     * @param version New subtree version of parent node
+     * @return A sealed TreeNode representing applied operation.
+     */
+    protected abstract TreeNode applyTouch(ModifiedNode modification, TreeNode currentMeta, Version version);
+
+    /**
+     * Checks is supplied {@link NodeModification} is applicable for Subtree Modification.
+     *
+     * @param path Path to current node
+     * @param modification Node modification which should be applied.
+     * @param current Current state of data tree
+     * @throws ConflictingModificationAppliedException If subtree was changed in conflicting way
+     * @throws org.opendaylight.yangtools.yang.data.api.schema.tree.IncorrectDataStructureException If subtree
+     *         modification is not applicable (e.g. leaf node).
+     */
+    protected abstract void checkTouchApplicable(ModificationPath path, NodeModification modification,
+            Optional<? extends TreeNode> current, Version version) throws DataValidationFailedException;
+
+    /**
+     * Return the {@link WithStatus}-subclass schema associated with this operation.
+     * @return A model node
+     */
+    abstract @NonNull T getSchema();
+
+    /**
+     * Checks if supplied schema node belong to specified Data Tree type. All nodes belong to the operational tree,
+     * nodes in configuration tree are marked as such.
+     *
+     * @param treeType Tree Type
+     * @param node Schema node
+     * @return {@code true} if the node matches the tree type, {@code false} otherwise.
+     */
+    static final boolean belongsToTree(final TreeType treeType, final DataSchemaNode node) {
+        return treeType == TreeType.OPERATIONAL || node.isConfiguration();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaValidationFailedException.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaValidationFailedException.java
new file mode 100644 (file)
index 0000000..a1804d0
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+/**
+ * SchemaValidationFailedException is thrown when an attempt is made to modify the data tree and the modification
+ * does not match the schema context.
+ */
+public class SchemaValidationFailedException extends IllegalArgumentException {
+
+    private static final long serialVersionUID = 1L;
+
+    public SchemaValidationFailedException(final String message) {
+        super(message);
+    }
+
+    public SchemaValidationFailedException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/StructuralContainerModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/StructuralContainerModificationStrategy.java
new file mode 100644 (file)
index 0000000..ef7dd25
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+
+/**
+ * Structural containers are special in that they appear when implied by child nodes and disappear whenever they are
+ * empty. We could implement this as a subclass of {@link SchemaAwareApplyOperation}, but the automatic semantic
+ * is quite different from all the other strategies. We create a {@link ContainerModificationStrategy} to tap into that
+ * logic, but wrap it so we only call out into it. We do not use {@link PresenceContainerModificationStrategy} because
+ * it enforces presence of mandatory leaves, which is not something we want here, as structural containers are not
+ * root anchors for that validation.
+ */
+final class StructuralContainerModificationStrategy extends ContainerModificationStrategy {
+    private final ContainerNode emptyNode;
+
+    StructuralContainerModificationStrategy(final ContainerSchemaNode schema, final DataTreeConfiguration treeConfig) {
+        super(schema, treeConfig);
+        this.emptyNode = ImmutableNodes.containerNode(schema.getQName());
+    }
+
+    @Override
+    Optional<? extends TreeNode> apply(final ModifiedNode modification, final Optional<? extends TreeNode> storeMeta,
+            final Version version) {
+        return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, storeMeta,
+            version);
+    }
+
+    @Override
+    TreeNode defaultTreeNode() {
+        return defaultTreeNode(emptyNode);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/UnkeyedListModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/UnkeyedListModificationStrategy.java
new file mode 100644 (file)
index 0000000..9e40b18
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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.tree;
+
+import java.util.Optional;
+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.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.IncorrectDataStructureException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.MutableTreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNodeFactory;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+final class UnkeyedListModificationStrategy extends SchemaAwareApplyOperation<ListSchemaNode> {
+    private static final NormalizedNodeContainerSupport<NodeIdentifier, UnkeyedListEntryNode> ITEM_SUPPORT =
+            new NormalizedNodeContainerSupport<>(UnkeyedListEntryNode.class,
+                    ImmutableUnkeyedListEntryNodeBuilder::create, ImmutableUnkeyedListEntryNodeBuilder::create);
+
+    private final DataNodeContainerModificationStrategy<ListSchemaNode> entryStrategy;
+    private final UnkeyedListNode emptyNode;
+
+    UnkeyedListModificationStrategy(final ListSchemaNode schema, final DataTreeConfiguration treeConfig) {
+        entryStrategy = new DataNodeContainerModificationStrategy<>(ITEM_SUPPORT, schema, treeConfig);
+        emptyNode = ImmutableNodes.listNode(schema.getQName());
+    }
+
+    @Override
+    ListSchemaNode getSchema() {
+        return entryStrategy.getSchema();
+    }
+
+    @Override
+    Optional<? extends TreeNode> apply(final ModifiedNode modification, final Optional<? extends TreeNode> storeMeta,
+            final Version version) {
+        return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, storeMeta,
+            version);
+    }
+
+    @Override
+    protected ChildTrackingPolicy getChildPolicy() {
+        return ChildTrackingPolicy.ORDERED;
+    }
+
+    @Override
+    protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+        throw new IllegalStateException(String.format("Merge of modification %s on unkeyed list should never be called",
+            modification));
+    }
+
+    @Override
+    protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+        throw new UnsupportedOperationException("UnkeyedList does not support subtree change.");
+    }
+
+    @Override
+    protected TreeNode applyWrite(final ModifiedNode modification, final NormalizedNode<?, ?> newValue,
+            final Optional<? extends TreeNode> currentMeta, final Version version) {
+        final TreeNode newValueMeta = TreeNodeFactory.createTreeNode(newValue, version);
+        if (modification.getChildren().isEmpty()) {
+            return newValueMeta;
+        }
+
+        /*
+         * This is where things get interesting. The user has performed a write and
+         * then she applied some more modifications to it. So we need to make sense
+         * of that an apply the operations on top of the written value. We could have
+         * done it during the write, but this operation is potentially expensive, so
+         * we have left it out of the fast path.
+         *
+         * As it turns out, once we materialize the written data, we can share the
+         * code path with the subtree change. So let's create an unsealed TreeNode
+         * and run the common parts on it -- which end with the node being sealed.
+         */
+        final MutableTreeNode mutable = newValueMeta.mutable();
+        mutable.setSubtreeVersion(version);
+
+        @SuppressWarnings("rawtypes")
+        final NormalizedNodeContainerBuilder dataBuilder = ImmutableUnkeyedListEntryNodeBuilder
+            .create((UnkeyedListEntryNode) newValue);
+
+        return mutateChildren(mutable, dataBuilder, version, modification.getChildren());
+    }
+
+    /**
+     * Applies write/remove diff operation for each modification child in modification subtree.
+     * Operation also sets the Data tree references for each Tree Node (Index Node) in meta (MutableTreeNode) structure.
+     *
+     * @param meta MutableTreeNode (IndexTreeNode)
+     * @param data DataBuilder
+     * @param nodeVersion Version of TreeNode
+     * @param modifications modification operations to apply
+     * @return Sealed immutable copy of TreeNode structure with all Data Node references set.
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private TreeNode mutateChildren(final MutableTreeNode meta, final NormalizedNodeContainerBuilder data,
+            final Version nodeVersion, final Iterable<ModifiedNode> modifications) {
+
+        for (final ModifiedNode mod : modifications) {
+            final PathArgument id = mod.getIdentifier();
+            final Optional<? extends TreeNode> cm = meta.getChild(id);
+
+            final Optional<? extends TreeNode> result = resolveChildOperation(id).apply(mod, cm, nodeVersion);
+            if (result.isPresent()) {
+                final TreeNode tn = result.get();
+                meta.addChild(tn);
+                data.addChild(tn.getData());
+            } else {
+                meta.removeChild(id);
+                data.removeChild(id);
+            }
+        }
+
+        meta.setData(data.build());
+        return meta.seal();
+    }
+
+    @Override
+    public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+        return child instanceof NodeIdentifier ? Optional.of(entryStrategy) : Optional.empty();
+    }
+
+    @Override
+    void verifyValue(final NormalizedNode<?, ?> value) {
+        // NOOP
+    }
+
+    @Override
+    void recursivelyVerifyStructure(final NormalizedNode<?, ?> value) {
+        // NOOP
+    }
+
+    @Override
+    protected void checkTouchApplicable(final ModificationPath path, final NodeModification modification,
+            final Optional<? extends TreeNode> current, final Version version) throws IncorrectDataStructureException {
+        throw new IncorrectDataStructureException(path.toInstanceIdentifier(), "Subtree modification is not allowed.");
+    }
+
+    @Override
+    void mergeIntoModifiedNode(final ModifiedNode node, final NormalizedNode<?, ?> value, final Version version) {
+        // Unkeyed lists are always replaced
+        node.write(value);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/UpgradableRootApplyStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/UpgradableRootApplyStrategy.java
new file mode 100644 (file)
index 0000000..6ebef35
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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.tree;
+
+/**
+ * Implementation of Upgradable {@link RootApplyStrategy}
+ *
+ * <p>
+ * This implementation is associated with {@link LatestOperationHolder}
+ * which holds latest available implementation, which may be used for
+ * upgrade.
+ *
+ * <p>
+ * Upgrading {@link LatestOperationHolder} will not affect any instance,
+ * unless client invoked {@link #upgradeIfPossible()} which will result in
+ * changing delegate to the latest one.
+ */
+final class UpgradableRootApplyStrategy extends RootApplyStrategy {
+    private final LatestOperationHolder holder;
+    private ModificationApplyOperation delegate;
+
+    UpgradableRootApplyStrategy(final LatestOperationHolder holder,
+        final ModificationApplyOperation delegate) {
+        this.holder = holder;
+        this.delegate = delegate;
+    }
+
+    @Override
+    void upgradeIfPossible() {
+        ModificationApplyOperation holderCurrent = holder.getCurrent();
+        if (holderCurrent != null && holderCurrent != delegate) {
+            // FIXME: Allow update only if there is addition of models, not
+            // removals.
+            delegate = holderCurrent;
+        }
+    }
+
+    @Override
+    protected ModificationApplyOperation delegate() {
+        return delegate;
+    }
+
+    @Override
+    RootApplyStrategy snapshot() {
+        return new UpgradableRootApplyStrategy(holder, delegate);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ValueNodeModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ValueNodeModificationStrategy.java
new file mode 100644 (file)
index 0000000..a204d6e
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ValueNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.IncorrectDataStructureException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNodeFactory;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+final class ValueNodeModificationStrategy<T extends DataSchemaNode> extends SchemaAwareApplyOperation<T> {
+    private final Class<? extends ValueNode> nodeClass;
+    private final @NonNull T schema;
+
+    ValueNodeModificationStrategy(final Class<? extends ValueNode> nodeClass, final T schema) {
+        this.nodeClass = requireNonNull(nodeClass);
+        this.schema = requireNonNull(schema);
+    }
+
+    @Override
+    T getSchema() {
+        return schema;
+    }
+
+    @Override
+    public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+        throw new UnsupportedOperationException("Node " + schema.getPath()
+                + " is leaf type node. Child nodes not allowed");
+    }
+
+    @Override
+    protected ChildTrackingPolicy getChildPolicy() {
+        return ChildTrackingPolicy.NONE;
+    }
+
+    @Override
+    protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta,
+            final Version version) {
+        throw new UnsupportedOperationException("Node " + schema.getPath()
+                + " is leaf type node. Subtree change is not allowed.");
+    }
+
+    @Override
+    protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta,
+            final Version version) {
+        // Just overwrite whatever was there, but be sure to run validation
+        final NormalizedNode<?, ?> newValue = modification.getWrittenValue();
+        verifyWrittenValue(newValue);
+        modification.resolveModificationType(ModificationType.WRITE);
+        return applyWrite(modification, newValue, null, version);
+    }
+
+    @Override
+    protected TreeNode applyWrite(final ModifiedNode modification, final NormalizedNode<?, ?> newValue,
+            final Optional<? extends TreeNode> currentMeta, final Version version) {
+        return TreeNodeFactory.createTreeNode(newValue, version);
+    }
+
+    @Override
+    protected void checkTouchApplicable(final ModificationPath path, final NodeModification modification,
+            final Optional<? extends TreeNode> current, final Version version) throws IncorrectDataStructureException {
+        throw new IncorrectDataStructureException(path.toInstanceIdentifier(), "Subtree modification is not allowed.");
+    }
+
+    @Override
+    void mergeIntoModifiedNode(final ModifiedNode node, final NormalizedNode<?, ?> value, final Version version) {
+        switch (node.getOperation()) {
+            // Delete performs a data dependency check on existence of the node. Performing a merge
+            // on DELETE means we
+            // are really performing a write.
+            case DELETE:
+            case WRITE:
+                node.write(value);
+                break;
+            default:
+                node.updateValue(LogicalOperation.MERGE, value);
+        }
+    }
+
+    @Override
+    void verifyValue(final NormalizedNode<?, ?> writtenValue) {
+        verifyWrittenValue(writtenValue);
+    }
+
+    @Override
+    void recursivelyVerifyStructure(final NormalizedNode<?, ?> value) {
+        verifyWrittenValue(value);
+    }
+
+    private void verifyWrittenValue(final NormalizedNode<?, ?> value) {
+        checkArgument(nodeClass.isInstance(value), "Expected an instance of %s, have %s", nodeClass, value);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/IgnoreWhiteCharsDiffListener.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/IgnoreWhiteCharsDiffListener.java
new file mode 100644 (file)
index 0000000..3dfe836
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import org.custommonkey.xmlunit.Difference;
+import org.custommonkey.xmlunit.DifferenceConstants;
+import org.custommonkey.xmlunit.DifferenceListener;
+
+/**
+ * Implementatin of {@link DifferenceListener} ignoring white characters around text elements.
+ *
+ * @author mirehak
+ */
+public class IgnoreWhiteCharsDiffListener implements DifferenceListener {
+
+    @Override
+    public void skippedComparison(org.w3c.dom.Node control,
+            org.w3c.dom.Node test) {
+        // do nothing
+    }
+
+    @Override
+    public int differenceFound(Difference diff) {
+
+        if (diff.getId() == DifferenceConstants.TEXT_VALUE.getId()) {
+            String control = diff.getControlNodeDetail().getValue();
+            if (control != null) {
+                control = control.trim();
+                if (diff.getTestNodeDetail().getValue() != null
+                    && control.equals(diff.getTestNodeDetail().getValue().trim())) {
+                    return
+                        DifferenceListener.RETURN_IGNORE_DIFFERENCE_NODES_SIMILAR;
+                }
+            }
+        }
+        return RETURN_ACCEPT_DIFFERENCE;
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/MemoryConsumption.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/MemoryConsumption.java
new file mode 100644 (file)
index 0000000..7d416ce
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+/**
+ * Provides memory consumption and elapsed time between 2 points.
+ *
+ * @author mirehak
+ */
+public class MemoryConsumption {
+    private long memBegin;
+    private long tsBegin;
+
+    /**
+     * Record memory and timestamp.
+     */
+    public void startObserving() {
+        Runtime runtime = Runtime.getRuntime();
+        // Run the garbage collector
+        runtime.gc();
+        memBegin = getActualMemoryConsumption();
+        tsBegin = System.currentTimeMillis();
+    }
+
+    /**
+     * Return memory usage and elapsed time message.
+     *
+     * @return memory usage and time elapsed message
+     */
+    public String finishObserving() {
+        long memEnd = getActualMemoryConsumption();
+        long tsEnd = System.currentTimeMillis();
+        return String.format("Used memory: %10d B; Elapsed time: %5d ms", memEnd - memBegin, tsEnd - tsBegin);
+    }
+
+    /**
+     * Return used memory.
+     *
+     * @return actual memory usage
+     */
+    public static long getActualMemoryConsumption() {
+        Runtime runtime = Runtime.getRuntime();
+        // Calculate the used memory
+        long memory = runtime.totalMemory() - runtime.freeMemory();
+        return memory;
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/BinaryCodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/BinaryCodecStringTest.java
new file mode 100644 (file)
index 0000000..b1d3e96
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.getCodec;
+
+import java.util.Base64;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.codec.BinaryCodec;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for BinaryCodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class BinaryCodecStringTest {
+    private static final byte[] FOUR_ELEMENTS = { 1, 2, 3, 4 };
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        BinaryCodec<String> codec = getCodec(BaseTypes.binaryType(), BinaryCodec.class);
+        assertEquals(Base64.getEncoder().encodeToString(FOUR_ELEMENTS), codec.serialize(FOUR_ELEMENTS));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDererialize() {
+        BinaryCodec<String> codec = getCodec(BaseTypes.binaryType(), BinaryCodec.class);
+        assertArrayEquals(FOUR_ELEMENTS, codec.deserialize(Base64.getEncoder().encodeToString(FOUR_ELEMENTS)));
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/BitsCodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/BitsCodecStringTest.java
new file mode 100644 (file)
index 0000000..6aa528d
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.codec.BitsCodec;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.model.util.type.BitsTypeBuilder;
+
+/**
+ * Unit tests for BitsCodecString.
+ *
+ * @author Thomas Pantelis
+ *
+ */
+public class BitsCodecStringTest {
+    private  static BitsTypeDefinition toBitsTypeDefinition(final String... bits) {
+        final BitsTypeBuilder b = BaseTypes.bitsTypeBuilder(mock(SchemaPath.class));
+
+        long pos = 0;
+        for (String bit : bits) {
+            BitsTypeDefinition.Bit mockBit = mock(BitsTypeDefinition.Bit.class);
+            when(mockBit.getName()).thenReturn(bit);
+            when(mockBit.getPosition()).thenReturn(pos);
+            b.addBit(mockBit);
+            ++pos;
+        }
+
+        return b.build();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        BitsCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(toBitsTypeDefinition("foo"),
+                BitsCodec.class);
+
+        String serialized = codec.serialize(ImmutableSet.of("foo", "bar"));
+        assertNotNull(serialized);
+        assertTrue(serialized.contains("foo"));
+        assertTrue(serialized.contains("bar"));
+
+        assertEquals("", codec.serialize(ImmutableSet.of()));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        BitsCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(
+            toBitsTypeDefinition("bit1", "bit2"), BitsCodec.class);
+
+        assertEquals("deserialize", ImmutableSet.of("bit1", "bit2"), codec.deserialize("  bit1 bit2     "));
+        assertEquals("deserialize", Collections.emptySet(), codec.deserialize(""));
+
+        TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx(codec, "bit1 bit3");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/BooleanCodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/BooleanCodecStringTest.java
new file mode 100644 (file)
index 0000000..8231157
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.codec.BooleanCodec;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for BooleanCodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class BooleanCodecStringTest {
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        BooleanCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.booleanType(),
+            BooleanCodec.class);
+
+        assertEquals("serialize", "true", codec.serialize(Boolean.TRUE));
+        assertEquals("serialize", "false", codec.serialize(Boolean.FALSE));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        BooleanCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.booleanType(),
+            BooleanCodec.class);
+
+        assertEquals("deserialize", Boolean.TRUE, codec.deserialize("true"));
+        assertEquals("deserialize", Boolean.TRUE, codec.deserialize("TRUE"));
+        assertEquals("deserialize", Boolean.FALSE, codec.deserialize("FALSE"));
+        assertEquals("deserialize", Boolean.FALSE, codec.deserialize("false"));
+        TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx(codec, "foo");
+        TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx(codec, "");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/DecimalCodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/DecimalCodecStringTest.java
new file mode 100644 (file)
index 0000000..aef4719
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import java.math.BigDecimal;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.codec.DecimalCodec;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for DecimalCodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class DecimalCodecStringTest {
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        DecimalCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(getType(), DecimalCodec.class);
+        assertEquals("serialize", "123.456", codec.serialize(new BigDecimal("123.456")));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        DecimalCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(getType(), DecimalCodec.class);
+
+        assertEquals("deserialize", new BigDecimal("123.456"), codec.deserialize("123.456"));
+
+        TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx(codec, "12o.3");
+        TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx(codec, "");
+    }
+
+    private static DecimalTypeDefinition getType() {
+        return BaseTypes.decimalTypeBuilder(mock(SchemaPath.class)).setFractionDigits(2).build();
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/EmptyCodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/EmptyCodecStringTest.java
new file mode 100644 (file)
index 0000000..c2f7788
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.data.api.codec.EmptyCodec;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for EmptyCodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class EmptyCodecStringTest {
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        EmptyCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.emptyType(), EmptyCodec.class);
+
+        assertEquals("serialize", "", codec.serialize(Empty.getInstance()));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        EmptyCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.emptyType(), EmptyCodec.class);
+
+        assertEquals("deserialize", Empty.getInstance(), codec.deserialize(""));
+
+        TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx(codec, "foo");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/EnumCodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/EnumCodecStringTest.java
new file mode 100644 (file)
index 0000000..a69c006
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.getCodec;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.toEnumTypeDefinition;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.codec.EnumCodec;
+
+/**
+ * Unit tests for EnumCodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class EnumCodecStringTest {
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        EnumCodec<String> codec = getCodec(toEnumTypeDefinition("enum1", "enum2"), EnumCodec.class);
+        assertEquals("serialize", "enum1", codec.serialize("enum1"));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        EnumCodec<String> codec = getCodec(toEnumTypeDefinition("enum1", "enum2"), EnumCodec.class);
+
+        assertEquals("deserialize", "enum1", codec.deserialize("enum1"));
+        assertEquals("deserialize", "enum2", codec.deserialize("enum2"));
+
+        deserializeWithExpectedIllegalArgEx(codec, "enum3");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Int16CodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Int16CodecStringTest.java
new file mode 100644 (file)
index 0000000..71fa748
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.getCodec;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.codec.Int16Codec;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for Int8CodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class Int16CodecStringTest {
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        Int16Codec<String> codec = getCodec(BaseTypes.int16Type(), Int16Codec.class);
+        assertEquals("serialize", "10", codec.serialize(Short.valueOf((short) 10)));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        final String hexa = "+0X45c";
+        final String negHexa = "-0X45c";
+        final String octal = "02134";
+        final String negOctal = "-02134";
+        final String integer = "+1116";
+        final String negInteger = "-1116";
+
+        Int16Codec<String> codec = getCodec(BaseTypes.int16Type(), Int16Codec.class);
+
+        assertEquals("deserialize", codec.deserialize(hexa), Short.valueOf("+045c", 16));
+        assertEquals("deserialize", codec.deserialize(negHexa), Short.valueOf("-045c", 16));
+        assertEquals("deserialize", codec.deserialize(octal), Short.valueOf(octal, 8));
+        assertEquals("deserialize", codec.deserialize(negOctal), Short.valueOf(negOctal, 8));
+        assertEquals("deserialize", codec.deserialize(integer), Short.valueOf(integer, 10));
+        assertEquals("deserialize", codec.deserialize(negInteger), Short.valueOf(negInteger, 10));
+
+        deserializeWithExpectedIllegalArgEx(codec, "1o");
+        deserializeWithExpectedIllegalArgEx(codec, "");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Int32CodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Int32CodecStringTest.java
new file mode 100644 (file)
index 0000000..3792e48
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.codec.Int32Codec;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for Int32CodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class Int32CodecStringTest {
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        Int32Codec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.int32Type(), Int32Codec.class);
+        assertEquals("serialize", "10", codec.serialize(Integer.valueOf(10)));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        final String hexa = "0x45FFFCDE";
+        final String negHexa = "-0x45FFFCDE";
+        final String octal = "010577776336";
+        final String negOctal = "-010577776336";
+        final String integer = "1174404318";
+        final String negInteger = "-1174404318";
+
+        Int32Codec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.int32Type(), Int32Codec.class);
+
+        assertEquals("deserialize", codec.deserialize(hexa), Integer.valueOf("+045FFFCDE", 16));
+        assertEquals("deserialize", codec.deserialize(negHexa), Integer.valueOf("-045FFFCDE", 16));
+        assertEquals("deserialize", codec.deserialize(octal), Integer.valueOf(octal, 8));
+        assertEquals("deserialize", codec.deserialize(negOctal), Integer.valueOf(negOctal, 8));
+        assertEquals("deserialize", codec.deserialize(integer), Integer.valueOf(integer, 10));
+        assertEquals("deserialize", codec.deserialize(negInteger), Integer.valueOf(negInteger, 10));
+
+        TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx(codec, "1o");
+        TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx(codec, "");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Int64CodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Int64CodecStringTest.java
new file mode 100644 (file)
index 0000000..1fb0746
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.getCodec;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.codec.Int64Codec;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for Int64CodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class Int64CodecStringTest {
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        Int64Codec<String> codec = getCodec(BaseTypes.int64Type(), Int64Codec.class);
+        assertEquals("serialize", "12345", codec.serialize(Long.valueOf(12345)));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        final String hexa = "0X75EDC78edCBA";
+        final String negHexa = "-0X75EDC78edCBA";
+        final String octal = "+03536670743556272";
+        final String negOctal = "-03536670743556272";
+        final String integer = "+129664115727546";
+        final String negInteger = "-129664115727546";
+
+        Int64Codec<String> codec = getCodec(BaseTypes.int64Type(), Int64Codec.class);
+
+        assertEquals("deserialize", codec.deserialize(hexa), Long.valueOf("075EDC78edCBA", 16));
+        assertEquals("deserialize", codec.deserialize(negHexa), Long.valueOf("-075EDC78edCBA", 16));
+        assertEquals("deserialize", codec.deserialize(octal), Long.valueOf(octal, 8));
+        assertEquals("deserialize", codec.deserialize(negOctal), Long.valueOf(negOctal, 8));
+        assertEquals("deserialize", codec.deserialize(integer), Long.valueOf(integer, 10));
+        assertEquals("deserialize", codec.deserialize(negInteger), Long.valueOf(negInteger, 10));
+
+        deserializeWithExpectedIllegalArgEx(codec, "1234o");
+        deserializeWithExpectedIllegalArgEx(codec, "");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Int8CodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Int8CodecStringTest.java
new file mode 100644 (file)
index 0000000..300a2e0
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.codec.Int8Codec;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for Int8CodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class Int8CodecStringTest {
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        Int8Codec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.int8Type(), Int8Codec.class);
+
+        assertEquals("serialize", "10", codec.serialize(Byte.valueOf((byte) 10)));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        final String hexa = "0x40";
+        final String negHexa = "-0x40";
+        final String octal = "+0100";
+        final String negOctal = "-0100";
+        final String integer = "64";
+        final String negInteger = "-64";
+
+        Int8Codec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.int8Type(), Int8Codec.class);
+
+        assertEquals("deserialize", codec.deserialize(hexa), Byte.valueOf("040", 16));
+        assertEquals("deserialize", codec.deserialize(negHexa), Byte.valueOf("-040", 16));
+        assertEquals("deserialize", codec.deserialize(octal), Byte.valueOf(octal, 8));
+        assertEquals("deserialize", codec.deserialize(negOctal), Byte.valueOf(negOctal, 8));
+        assertEquals("deserialize", codec.deserialize(integer), Byte.valueOf(integer, 10));
+        assertEquals("deserialize", codec.deserialize(negInteger), Byte.valueOf(negInteger, 10));
+
+        TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx(codec, "1o");
+        TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx(codec, "");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/StringCodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/StringCodecStringTest.java
new file mode 100644 (file)
index 0000000..115080b
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.codec.StringCodec;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for StringCodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class StringCodecStringTest {
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        StringCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.stringType(),
+            StringCodec.class);
+
+        assertEquals("serialize", "foo", codec.serialize("foo"));
+        assertEquals("serialize", "", codec.serialize(""));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        StringCodec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.stringType(),
+            StringCodec.class);
+
+        assertEquals("deserialize", "bar", codec.deserialize("bar"));
+        assertEquals("deserialize", "", codec.deserialize(""));
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/StringPatternCheckingCodecTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/StringPatternCheckingCodecTest.java
new file mode 100644 (file)
index 0000000..5922e6b
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.getCodec;
+
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.codec.StringCodec;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StringPatternCheckingCodecTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StringPatternCheckingCodecTest.class);
+
+    @Test
+    public void testStringPatternCheckingCodec() {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResource(
+            "/string-pattern-checking-codec-test.yang");
+        assertNotNull(schemaContext);
+
+        final QNameModule testModuleQName = QNameModule.create(URI.create("string-pattern-checking-codec-test"));
+
+        final Module testModule = schemaContext.findModules("string-pattern-checking-codec-test").iterator().next();
+        final ContainerSchemaNode testContainer = (ContainerSchemaNode) testModule.findDataChildByName(
+                QName.create(testModuleQName, "test-container")).get();
+
+        final LeafSchemaNode testLeaf = (LeafSchemaNode) testContainer.findDataChildByName(
+                QName.create(testModuleQName, "string-leaf-with-valid-pattern")).get();
+
+        final StringCodec<String> codec = getCodec(testLeaf.getType(), StringCodec.class);
+        assertNotNull(codec);
+        assertEquals("ABCD", codec.serialize("ABCD"));
+        assertEquals("ABCD", codec.deserialize("ABCD"));
+
+        try {
+            codec.deserialize("abcd");
+            fail("Exception should have been thrown.");
+        } catch (final IllegalArgumentException ex) {
+            LOG.debug("IllegalArgumentException was thrown as expected", ex);
+            assertEquals("Value 'abcd' does not match regular expression '[A-Z]+'", ex.getMessage());
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/TypeDefinitionAwareCodecTestHelper.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/TypeDefinitionAwareCodecTestHelper.java
new file mode 100644 (file)
index 0000000..c729829
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.model.util.type.EnumerationTypeBuilder;
+
+public final class TypeDefinitionAwareCodecTestHelper {
+    private TypeDefinitionAwareCodecTestHelper() {
+        throw new UnsupportedOperationException();
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T getCodec(final TypeDefinition<?> def, final Class<T> clazz) {
+        Object codec = TypeDefinitionAwareCodec.fromType(def);
+        assertNotNull(codec);
+        assertTrue(clazz.isAssignableFrom(codec.getClass()));
+        return (T)codec;
+    }
+
+    public static void deserializeWithExpectedIllegalArgEx(final Codec<String, ?, IllegalArgumentException> codec,
+            final @NonNull String param) {
+        try {
+            codec.deserialize(param);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // Expected ...
+        }
+    }
+
+    public static EnumTypeDefinition toEnumTypeDefinition(final String... enums) {
+        final EnumerationTypeBuilder b = BaseTypes.enumerationTypeBuilder(mock(SchemaPath.class));
+        int val = 0;
+        for (String en : enums) {
+            EnumTypeDefinition.EnumPair mockEnum = mock(EnumTypeDefinition.EnumPair.class);
+            when(mockEnum.getName()).thenReturn(en);
+            when(mockEnum.getValue()).thenReturn(val);
+            b.addEnum(mockEnum);
+            val++;
+        }
+
+        return b.build();
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Uint16CodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Uint16CodecStringTest.java
new file mode 100644 (file)
index 0000000..796793f
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.getCodec;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.data.api.codec.Uint16Codec;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for Uint16CodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class Uint16CodecStringTest {
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        Uint16Codec<String> codec = getCodec(BaseTypes.uint16Type(), Uint16Codec.class);
+        assertEquals("10", codec.serialize(Uint16.valueOf(10)));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        final String hexa = "0X45c";
+        final String octal = "02134";
+        final String integer = "1116";
+
+        Uint16Codec<String> codec = getCodec(BaseTypes.uint16Type(), Uint16Codec.class);
+
+        assertEquals(Uint16.valueOf("045c", 16), codec.deserialize(hexa));
+        assertEquals(Uint16.valueOf(octal, 8), codec.deserialize(octal));
+        assertEquals(Uint16.valueOf(integer, 10), codec.deserialize(integer));
+
+        deserializeWithExpectedIllegalArgEx(codec, "1o");
+        deserializeWithExpectedIllegalArgEx(codec, "");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Uint32CodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Uint32CodecStringTest.java
new file mode 100644 (file)
index 0000000..2f11e0c
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.getCodec;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.data.api.codec.Uint32Codec;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for Uint32CodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class Uint32CodecStringTest {
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        Uint32Codec<String> codec = getCodec(BaseTypes.uint32Type(), Uint32Codec.class);
+        assertEquals("10", codec.serialize(Uint32.valueOf(10)));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        final String hexa = "0x45FFFCDE";
+        final String octal = "010577776336";
+        final String integer = "1174404318";
+
+        Uint32Codec<String> codec = getCodec(BaseTypes.uint32Type(), Uint32Codec.class);
+        assertEquals(Uint32.valueOf("45FFFCDE", 16), codec.deserialize(hexa));
+        assertEquals(Uint32.valueOf(octal, 8), codec.deserialize(octal));
+        assertEquals(Uint32.valueOf(integer, 10), codec.deserialize(integer));
+
+        deserializeWithExpectedIllegalArgEx(codec, "1o");
+        deserializeWithExpectedIllegalArgEx(codec, "");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Uint64CodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Uint64CodecStringTest.java
new file mode 100644 (file)
index 0000000..1ce2a38
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.data.api.codec.Uint64Codec;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for Uint64CodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class Uint64CodecStringTest {
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        Uint64Codec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.uint64Type(),
+            Uint64Codec.class);
+        assertEquals("123456789", codec.serialize(Uint64.valueOf(123456789)));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        final String hexa = "0X75EDC78edCBA";
+        final String octal = "03536670743556272";
+        final String integer = "129664115727546";
+
+        Uint64Codec<String> codec = TypeDefinitionAwareCodecTestHelper.getCodec(BaseTypes.uint64Type(),
+            Uint64Codec.class);
+
+        assertEquals(Uint64.valueOf("75EDC78edCBA", 16), codec.deserialize(hexa));
+        assertEquals(Uint64.valueOf(octal, 8), codec.deserialize(octal));
+        assertEquals(Uint64.valueOf(integer, 10), codec.deserialize(integer));
+
+        TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx(codec, "12345o");
+        TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx(codec, "");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Uint8CodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/Uint8CodecStringTest.java
new file mode 100644 (file)
index 0000000..5b75df9
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.getCodec;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.data.api.codec.Uint8Codec;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Unit tests for Uint8CodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class Uint8CodecStringTest {
+    @SuppressWarnings({ "unchecked" })
+    @Test
+    public void testSerialize() {
+        Uint8Codec<String> codec = getCodec(BaseTypes.uint8Type(), Uint8Codec.class);
+        assertEquals("10", codec.serialize(Uint8.valueOf((short) 10)));
+    }
+
+    @SuppressWarnings({ "unchecked" })
+    @Test
+    public void testDererialize() {
+        final String hexa = "0x40";
+        final String octal = "0100";
+        final String integer = "64";
+
+        Uint8Codec<String> codec = getCodec(BaseTypes.uint8Type(), Uint8Codec.class);
+
+        assertEquals(Uint8.valueOf("040", 16), codec.deserialize(hexa));
+        assertEquals(Uint8.valueOf(octal, 8), codec.deserialize(octal));
+        assertEquals(Uint8.valueOf(integer, 10), codec.deserialize(integer));
+        assertEquals(Uint8.valueOf("0", 10), codec.deserialize("0"));
+
+        deserializeWithExpectedIllegalArgEx(codec, "1o");
+        deserializeWithExpectedIllegalArgEx(codec, "");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/UnionCodecStringTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codecs/UnionCodecStringTest.java
new file mode 100644 (file)
index 0000000..3f188c5
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 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.codecs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.deserializeWithExpectedIllegalArgEx;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.getCodec;
+import static org.opendaylight.yangtools.yang.data.impl.codecs.TypeDefinitionAwareCodecTestHelper.toEnumTypeDefinition;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.data.api.codec.UnionCodec;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.model.util.type.UnionTypeBuilder;
+
+/**
+ * Unit tests forUnionCodecString.
+ *
+ * @author Thomas Pantelis
+ */
+public class UnionCodecStringTest {
+    private static UnionTypeDefinition toUnionTypeDefinition(final TypeDefinition<?>... types) {
+        final UnionTypeBuilder builder = BaseTypes.unionTypeBuilder(mock(SchemaPath.class));
+
+        for (TypeDefinition<?> t : types) {
+            builder.addType(t);
+        }
+
+        return builder.build();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSerialize() {
+        UnionCodec<String> codec = getCodec(toUnionTypeDefinition(toEnumTypeDefinition("enum1", "enum2"),
+                toUnionTypeDefinition(BaseTypes.int32Type(),BaseTypes.int64Type()), BaseTypes.emptyType()),
+                UnionCodec.class);
+
+        assertEquals("serialize", "enum1", codec.serialize("enum1"));
+        assertEquals("serialize", "123", codec.serialize("123"));
+        assertEquals("serialize", "123", codec.serialize(123));
+        assertEquals("serialize", "", codec.serialize(""));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testDeserialize() {
+        UnionCodec<String> codec = getCodec(toUnionTypeDefinition(toEnumTypeDefinition("enum1", "enum2"),
+                toUnionTypeDefinition(BaseTypes.int32Type(),BaseTypes.int64Type()), BaseTypes.emptyType()),
+                UnionCodec.class);
+
+        assertEquals("deserialize", "enum1", codec.deserialize("enum1"));
+        assertEquals("deserialize", 123, codec.deserialize("123"));
+        assertEquals("deserialize", -123, codec.deserialize("-123"));
+        assertEquals("deserialize", 41234567890L, codec.deserialize("41234567890"));
+        assertEquals("deserialize", Empty.getInstance(), codec.deserialize(""));
+
+        deserializeWithExpectedIllegalArgEx(codec, "enum3");
+        deserializeWithExpectedIllegalArgEx(codec, "123o");
+        deserializeWithExpectedIllegalArgEx(codec, "true");
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/Bug7844Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/Bug7844Test.java
new file mode 100644 (file)
index 0000000..e4cb067
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2017 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.leafref;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableList;
+import java.net.URI;
+import java.util.Map;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug7844Test {
+    private static final String FOO_NS = "foo";
+    private static final String BAR_NS = "bar";
+    private static final String BAZ_NS = "baz";
+
+    @Test
+    public void test() {
+        final SchemaContext context = YangParserTestUtils.parseYangResourceDirectory("/bug7844");
+        assertNotNull(context);
+
+        final LeafRefContext leafRefContext = LeafRefContext.create(context);
+        assertNotNull(leafRefContext);
+
+        final Map<QName, LeafRefContext> referencingChilds = leafRefContext.getReferencingChilds();
+        assertEquals(7, referencingChilds.size());
+
+        final QNameModule bazQNameModule = QNameModule.create(URI.create(BAZ_NS));
+        final LeafRefPath expectedPathToBazTarget = LeafRefPath.create(true,
+                new QNameWithPredicateImpl(bazQNameModule, "root", ImmutableList.of()),
+                new QNameWithPredicateImpl(bazQNameModule, "target", ImmutableList.of()));
+        final LeafRefContext myLeafCtx = referencingChilds.get(foo("my-leaf"));
+        assertLeafRef(myLeafCtx, expectedPathToBazTarget);
+        assertLeafRef(referencingChilds.get(foo("my-leaf-2")), expectedPathToBazTarget);
+        assertLeafRef(referencingChilds.get(foo("bar-base-leafref")), expectedPathToBazTarget);
+        assertLeafRef(referencingChilds.get(foo("bar-base-leafref-2")), expectedPathToBazTarget);
+        assertLeafRef(referencingChilds.get(bar("my-leafref-in-bar")), expectedPathToBazTarget);
+        assertLeafRef(referencingChilds.get(bar("my-leafref-in-bar-2")), expectedPathToBazTarget);
+
+        final QNameModule barQNameModule = QNameModule.create(URI.create(BAR_NS));
+        final LeafRefPath expectedPathToBarTarget = LeafRefPath.create(true,
+                new QNameWithPredicateImpl(barQNameModule, "bar-target", ImmutableList.of()));
+        assertLeafRef(referencingChilds.get(foo("direct-leafref")), expectedPathToBarTarget);
+
+        final Map<QName, LeafRefContext> referencedByChilds = leafRefContext.getReferencedByChilds();
+        assertEquals(2, referencedByChilds.size());
+
+        final LeafRefContext rootCtx = referencedByChilds.get(baz("root"));
+        assertEquals(1, rootCtx.getReferencedByChilds().size());
+        assertTrue(rootCtx.getReferencingChilds().isEmpty());
+        assertFalse(rootCtx.isReferencing());
+        assertFalse(rootCtx.isReferenced());
+
+        final LeafRefContext targetCtx = rootCtx.getReferencedChildByName(baz("target"));
+        assertTrue(targetCtx.getReferencedByChilds().isEmpty());
+        assertTrue(targetCtx.getReferencingChilds().isEmpty());
+        assertTrue(targetCtx.isReferenced());
+        assertFalse(targetCtx.isReferencing());
+
+        final Map<QName, LeafRefContext> allReferencedByLeafRefCtxs = targetCtx.getAllReferencedByLeafRefCtxs();
+        assertEquals(6, allReferencedByLeafRefCtxs.size());
+        assertTrue(myLeafCtx == targetCtx.getReferencedByLeafRefCtxByName(foo("my-leaf")));
+    }
+
+    private static void assertLeafRef(final LeafRefContext leafRefToTest, final LeafRefPath expectedLeafRefPath) {
+        assertNotNull(leafRefToTest);
+        assertNotNull(expectedLeafRefPath);
+        assertTrue(leafRefToTest.getReferencedByChilds().isEmpty());
+        assertTrue(leafRefToTest.getReferencingChilds().isEmpty());
+        assertFalse(leafRefToTest.isReferenced());
+        assertTrue(leafRefToTest.isReferencing());
+        assertEquals(expectedLeafRefPath, leafRefToTest.getAbsoluteLeafRefTargetPath());
+    }
+
+    private static QName foo(final String localName) {
+        return QName.create(FOO_NS, localName);
+    }
+
+    private static QName bar(final String localName) {
+        return QName.create(BAR_NS, localName);
+    }
+
+    private static QName baz(final String localName) {
+        return QName.create(BAZ_NS, localName);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/Bug8713Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/Bug8713Test.java
new file mode 100644 (file)
index 0000000..ca7681a
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 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.leafref.context;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefValidation;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug8713Test {
+    private static final String FOO_NS = "foo";
+    private static final String BAR_NS = "bar";
+    private static final String REV = "2017-09-06";
+
+    @Test
+    public void dataTreeCanditateValidationTest() throws Exception {
+        final SchemaContext context = YangParserTestUtils.parseYangResourceDirectory("/bug8713/");
+        final LeafRefContext rootLeafRefContext = LeafRefContext.create(context);
+        final DataTree inMemoryDataTree = new InMemoryDataTreeFactory().create(
+            DataTreeConfiguration.DEFAULT_OPERATIONAL, context);
+
+        final ContainerNode root = createRootContainer();
+        final YangInstanceIdentifier rootPath = YangInstanceIdentifier.of(foo("root"));
+        final DataTreeModification writeModification = inMemoryDataTree.takeSnapshot().newModification();
+        writeModification.write(rootPath, root);
+        writeModification.ready();
+
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree.prepare(writeModification);
+
+        LeafRefValidation.validate(writeContributorsCandidate, rootLeafRefContext);
+        inMemoryDataTree.commit(writeContributorsCandidate);
+    }
+
+    private static ContainerNode createRootContainer() {
+        return Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(foo("root")))
+                .withChild(ImmutableNodes.leafNode(bar("target"), "target value"))
+                .withChild(ImmutableNodes.leafNode(bar("ref"), "target value")).build();
+    }
+
+    private static QName foo(final String localName) {
+        return QName.create(FOO_NS, REV, localName);
+    }
+
+    private static QName bar(final String localName) {
+        return QName.create(BAR_NS, REV, localName);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/DataTreeCandidateValidatorTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/DataTreeCandidateValidatorTest.java
new file mode 100644 (file)
index 0000000..b5327e1
--- /dev/null
@@ -0,0 +1,792 @@
+/*
+ * Copyright (c) 2016 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.leafref.context;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import org.apache.log4j.BasicConfigurator;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+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.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefDataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefValidation;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+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.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DataTreeCandidateValidatorTest {
+
+    private static SchemaContext context;
+    private static Module valModule;
+    private static QNameModule valModuleQname;
+    private static LeafRefContext rootLeafRefContext;
+    public static DataTree inMemoryDataTree;
+
+    private static QName odl;
+    private static QName project;
+    private static QName name;
+    private static QName desc;
+    private static QName lead;
+    private static QName owner;
+    private static QName odlContributor;
+    private static QName contributor;
+    private static QName odlProjectName;
+    private static QName odlProjectDesc;
+    private static QName login;
+    private static QName contributorName;
+    private static QName l1;
+    private static QName l2;
+    private static QName con1;
+    private static QName ch1;
+    private static QName ch2;
+    private static QName leafrefInChoice;
+    private static QName listInChoice;
+
+    private static QName leafrefInChoiceToChoice;
+    private static QName con3;
+    private static QName list3InChoice;
+    private static QName l3;
+    private static QName choiceInCon3;
+
+    private static QName listInChoiceKey;
+    private static QName k;
+
+    private static QName leafrefLeafList;
+
+    private static final Logger LOG = LoggerFactory.getLogger(DataTreeCandidateValidatorTest.class);
+    private static final String NEW_LINE = System.getProperty("line.separator");
+
+    static {
+        BasicConfigurator.configure();
+    }
+
+    @BeforeClass
+    public static void init() throws DataValidationFailedException {
+        context = YangParserTestUtils.parseYangResourceDirectory("/leafref-validation");
+
+        final Set<Module> modules = context.getModules();
+        for (final Module module : modules) {
+            if (module.getName().equals("leafref-validation")) {
+                valModule = module;
+            }
+        }
+
+        valModuleQname = valModule.getQNameModule();
+        rootLeafRefContext = LeafRefContext.create(context);
+
+        odl = QName.create(valModuleQname, "odl-project");
+        project = QName.create(valModuleQname, "project");
+        name = QName.create(valModuleQname, "name");
+        desc = QName.create(valModuleQname, "desc");
+        lead = QName.create(valModuleQname, "project-lead");
+        owner = QName.create(valModuleQname, "project-owner");
+
+        odlContributor = QName.create(valModuleQname, "odl-contributor");
+        contributor = QName.create(valModuleQname, "contributor");
+        odlProjectName = QName.create(valModuleQname, "odl-project-name");
+        login = QName.create(valModuleQname, "login");
+        contributorName = QName.create(valModuleQname, "contributor-name");
+
+        con1 = QName.create(valModuleQname, "con1");
+        l1 = QName.create(valModuleQname, "l1");
+        l2 = QName.create(valModuleQname, "l2");
+        odlProjectDesc = QName.create(valModuleQname, "odl-project-desc");
+
+        ch1 = QName.create(valModuleQname, "ch1");
+        ch2 = QName.create(valModuleQname, "ch2");
+        leafrefInChoice = QName.create(valModuleQname, "leafref-in-choice");
+        listInChoice = QName.create(valModuleQname, "list-in-choice");
+
+        leafrefInChoiceToChoice = QName.create(valModuleQname,
+                "leafref-in-choice-to-choice");
+        con3 = QName.create(valModuleQname, "con3");
+        list3InChoice = QName.create(valModuleQname, "list3-in-choice");
+        l3 = QName.create(valModuleQname, "l3");
+        choiceInCon3 = QName.create(valModuleQname, "choice-in-con3");
+
+        listInChoiceKey = QName.create(valModuleQname, "list-in-choice-key");
+        k = QName.create(valModuleQname, "k");
+
+        leafrefLeafList = QName.create(valModuleQname, "leafref-leaf-list");
+
+        inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL, context);
+
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree
+                .takeSnapshot().newModification();
+
+        final ContainerSchemaNode odlProjContSchemaNode = (ContainerSchemaNode) valModule.findDataChildByName(odl)
+                .get();
+
+        final ContainerNode odlProjectContainer = createOdlContainer(odlProjContSchemaNode);
+
+        final YangInstanceIdentifier path = YangInstanceIdentifier.of(odl);
+        initialDataTreeModification.write(path, odlProjectContainer);
+        initialDataTreeModification.ready();
+
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree
+                .prepare(initialDataTreeModification);
+        inMemoryDataTree.commit(writeContributorsCandidate);
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        inMemoryDataTree = null;
+        rootLeafRefContext = null;
+        valModule = null;
+        context = null;
+    }
+
+    @Test
+    public void dataTreeCanditateValidationTest() throws DataValidationFailedException {
+        write();
+
+        write2();
+
+        delete();
+
+        writeContributors();
+
+        writeMapEntry();
+
+        writeIntoMapEntry();
+    }
+
+    private static void writeContributors() throws DataValidationFailedException {
+
+        final ContainerSchemaNode contributorContSchemaNode = (ContainerSchemaNode) valModule
+                .findDataChildByName(odlContributor).get();
+
+        final ContainerNode contributorContainer = createBasicContributorContainer(contributorContSchemaNode);
+
+        final YangInstanceIdentifier contributorPath = YangInstanceIdentifier
+                .of(odlContributor);
+        final DataTreeModification writeModification = inMemoryDataTree
+                .takeSnapshot().newModification();
+        writeModification.write(contributorPath, contributorContainer);
+        writeModification.ready();
+
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree
+                .prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before write of contributors: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidation.validate(writeContributorsCandidate,
+                    rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:{}{}", NEW_LINE, e.getMessage());
+            assertEquals(3, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        inMemoryDataTree.commit(writeContributorsCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After write of contributors: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        assertTrue(exception);
+
+    }
+
+    private static void writeIntoMapEntry() throws DataValidationFailedException {
+
+        final Map<QName, Object> keys = new HashMap<>();
+        keys.put(name, "New Project");
+        final NodeIdentifierWithPredicates mapEntryPath = NodeIdentifierWithPredicates.of(project, keys);
+
+        final YangInstanceIdentifier leaderPath = YangInstanceIdentifier
+                .of(odl).node(project).node(mapEntryPath).node(lead);
+
+        final LeafNode<String> leader = ImmutableNodes.leafNode(lead,
+                "Updated leader");
+
+        final DataTreeModification writeModification = inMemoryDataTree
+                .takeSnapshot().newModification();
+        writeModification.write(leaderPath, leader);
+        writeModification.ready();
+
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree
+                .prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before write into map entry (update of leader name): ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidation.validate(writeContributorsCandidate,
+                    rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:{}{}", NEW_LINE, e.getMessage());
+            assertEquals(1, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        inMemoryDataTree.commit(writeContributorsCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After write into map entry (update of leader name): ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        assertTrue(exception);
+
+    }
+
+    private static void writeMapEntry() throws DataValidationFailedException {
+
+        final Map<QName, Object> keys = new HashMap<>();
+        keys.put(name, "New Project");
+        final NodeIdentifierWithPredicates mapEntryPath = NodeIdentifierWithPredicates.of(project, keys);
+
+        final YangInstanceIdentifier newOdlProjectMapEntryPath = YangInstanceIdentifier
+                .of(odl).node(project).node(mapEntryPath);
+
+        final ContainerSchemaNode odlProjContSchemaNode = (ContainerSchemaNode) valModule.findDataChildByName(odl)
+                .get();
+        final ListSchemaNode projListSchemaNode = (ListSchemaNode) odlProjContSchemaNode.findDataChildByName(project)
+                .get();
+        final MapEntryNode newProjectMapEntry = createProjectListEntry(
+                "New Project", "New Project description ...",
+                "Leader of New Project", "Owner of New Project",
+                projListSchemaNode);
+
+        final DataTreeModification writeModification = inMemoryDataTree
+                .takeSnapshot().newModification();
+        writeModification.write(newOdlProjectMapEntryPath, newProjectMapEntry);
+        writeModification.ready();
+
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree
+                .prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before map entry write: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidation.validate(writeContributorsCandidate,
+                    rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:{}{}", NEW_LINE, e.getMessage());
+            assertEquals(2, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        inMemoryDataTree.commit(writeContributorsCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After map entry write: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        assertTrue(exception);
+
+    }
+
+    private static void write() throws DataValidationFailedException {
+
+        final ContainerSchemaNode contributorContSchemaNode = (ContainerSchemaNode) valModule
+                .findDataChildByName(odlContributor).get();
+
+        final ContainerNode contributorContainer = createContributorContainer(contributorContSchemaNode);
+
+        final YangInstanceIdentifier contributorPath = YangInstanceIdentifier
+                .of(odlContributor);
+        final DataTreeModification writeModification = inMemoryDataTree
+                .takeSnapshot().newModification();
+        writeModification.write(contributorPath, contributorContainer);
+
+        writeModification.write(YangInstanceIdentifier.of(l1),
+                ImmutableNodes.leafNode(l1, "Leafref l1 under the root"));
+        writeModification
+                .write(YangInstanceIdentifier.of(l2), ImmutableNodes.leafNode(
+                        l2, "Leafref target l2 under the root"));
+
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree
+                .prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before write: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidation.validate(writeContributorsCandidate,
+                    rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:{}{}", NEW_LINE, e.getMessage());
+            assertEquals(12, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        inMemoryDataTree.commit(writeContributorsCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After write: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        assertTrue(exception);
+    }
+
+    private static void write2() throws DataValidationFailedException {
+
+        final ContainerSchemaNode odlCon = (ContainerSchemaNode) valModule.findDataChildByName(odl).get();
+        final ContainerSchemaNode con1Con = (ContainerSchemaNode) odlCon.findDataChildByName(con1).get();
+        final LeafNode<String> l1Leaf = ImmutableNodes.leafNode(l1, "l1 value");
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder = Builders
+                .containerBuilder(con1Con);
+        containerBuilder.addChild(l1Leaf);
+        final ContainerNode con1Node = containerBuilder.build();
+
+        final YangInstanceIdentifier con1Path = YangInstanceIdentifier.of(odl)
+                .node(con1);
+        final DataTreeModification writeModification = inMemoryDataTree
+                .takeSnapshot().newModification();
+        writeModification.write(con1Path, con1Node);
+
+        final ChoiceNode choiceNode = createChoiceNode();
+        final YangInstanceIdentifier choicePath = YangInstanceIdentifier
+                .of(odl).node(ch1);
+        writeModification.write(choicePath, choiceNode);
+
+        final ContainerNode con3Node = createCon3Node();
+        final YangInstanceIdentifier con3Path = YangInstanceIdentifier.of(odl)
+                .node(con3);
+        writeModification.write(con3Path, con3Node);
+
+        final LeafSetNode<?> leafListNode = createLeafRefLeafListNode();
+        final YangInstanceIdentifier leafListPath = YangInstanceIdentifier.of(
+                odl).node(leafrefLeafList);
+        writeModification.write(leafListPath, leafListNode);
+        writeModification.ready();
+
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree
+                .prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before write2: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidation.validate(writeContributorsCandidate,
+                    rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:{}{}", NEW_LINE, e.getMessage());
+            assertEquals(6, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        assertTrue(exception);
+
+        inMemoryDataTree.commit(writeContributorsCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After write2: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+    }
+
+    private static LeafSetNode<?> createLeafRefLeafListNode() {
+
+        final ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafSetBuilder = Builders
+                .leafSetBuilder();
+        leafSetBuilder.withNodeIdentifier(new NodeIdentifier(leafrefLeafList));
+
+        leafSetBuilder.addChild(createLeafSetEntry(leafrefLeafList, "k1"));
+        leafSetBuilder.addChild(createLeafSetEntry(leafrefLeafList, "k2"));
+        leafSetBuilder.addChild(createLeafSetEntry(leafrefLeafList, "k3"));
+
+        return leafSetBuilder.build();
+    }
+
+    private static ContainerNode createCon3Node() {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> mapBuilder = Builders
+                .mapBuilder();
+        mapBuilder.withNodeIdentifier(new NodeIdentifier(list3InChoice));
+
+        mapBuilder.addChild(createList3Entry("k1", "val1", "valA", "valX"));
+        mapBuilder.addChild(createList3Entry("k2", "val2", "valB", "valY"));
+
+        final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> choiceBuilder = Builders
+                .choiceBuilder();
+        choiceBuilder.withNodeIdentifier(new NodeIdentifier(choiceInCon3));
+
+        choiceBuilder.addChild(mapBuilder.build());
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder = Builders
+                .containerBuilder();
+        containerBuilder.withNodeIdentifier(new NodeIdentifier(con3));
+
+        containerBuilder.addChild(choiceBuilder.build());
+
+        return containerBuilder.build();
+    }
+
+    private static MapEntryNode createList3Entry(final String keyVal,
+            final String l3Val1, final String l3Val2, final String l3Val3) {
+        final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = Builders
+                .mapEntryBuilder();
+        mapEntryBuilder.withNodeIdentifier(NodeIdentifierWithPredicates.of(list3InChoice, k, keyVal));
+
+        final ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafSetBuilder = Builders
+                .leafSetBuilder();
+        leafSetBuilder.withNodeIdentifier(new NodeIdentifier(l3));
+
+        leafSetBuilder.addChild(createLeafSetEntry(l3, l3Val1));
+        leafSetBuilder.addChild(createLeafSetEntry(l3, l3Val2));
+        leafSetBuilder.addChild(createLeafSetEntry(l3, l3Val3));
+
+        mapEntryBuilder.addChild(ImmutableNodes.leafNode(k, keyVal));
+        mapEntryBuilder.addChild(leafSetBuilder.build());
+
+        return mapEntryBuilder.build();
+    }
+
+    private static LeafSetEntryNode<Object> createLeafSetEntry(
+            final QName qname, final String val) {
+        final NormalizedNodeBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> leafSetEntryBuilder = Builders
+                .leafSetEntryBuilder();
+        leafSetEntryBuilder.withNodeIdentifier(new NodeWithValue<>(qname, val));
+        leafSetEntryBuilder.withValue(val);
+        return leafSetEntryBuilder.build();
+    }
+
+    private static ChoiceNode createChoiceNode() {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> listInChoiceBuilder = Builders
+                .mapBuilder();
+        listInChoiceBuilder
+                .withNodeIdentifier(new NodeIdentifier(listInChoice));
+
+        listInChoiceBuilder.addChild(createListInChoiceEntry("key1",
+                "leafref-in-choice value", "val1"));
+        listInChoiceBuilder.addChild(createListInChoiceEntry("key2",
+                "l1 value", "val2"));
+        listInChoiceBuilder.addChild(createListInChoiceEntry("key3",
+                "l1 value", "val3"));
+
+        final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> choice2Builder = Builders
+                .choiceBuilder();
+        choice2Builder.withNodeIdentifier(new NodeIdentifier(ch2));
+
+        choice2Builder.addChild(listInChoiceBuilder.build());
+
+        final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> choiceBuilder = Builders
+                .choiceBuilder();
+        choiceBuilder.withNodeIdentifier(new NodeIdentifier(ch1));
+        choiceBuilder.addChild(choice2Builder.build());
+
+        return choiceBuilder.build();
+    }
+
+    private static MapEntryNode createListInChoiceEntry(final String keyVal,
+            final String leafrefInChoiceVal,
+            final String leafrefInChoiceToChoiceVal) {
+
+        final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = Builders
+                .mapEntryBuilder();
+
+        mapEntryBuilder.withNodeIdentifier(NodeIdentifierWithPredicates.of(
+                listInChoice, listInChoiceKey, keyVal));
+
+        mapEntryBuilder.addChild(ImmutableNodes.leafNode(listInChoiceKey,
+                keyVal));
+        mapEntryBuilder.addChild(ImmutableNodes.leafNode(leafrefInChoice,
+                leafrefInChoiceVal));
+        mapEntryBuilder.addChild(ImmutableNodes.leafNode(
+                leafrefInChoiceToChoice, leafrefInChoiceToChoiceVal));
+
+        return mapEntryBuilder.build();
+    }
+
+    private static void delete() throws DataValidationFailedException {
+
+        final YangInstanceIdentifier contributorPath = YangInstanceIdentifier
+                .of(odlContributor);
+        final DataTreeModification delete = inMemoryDataTree.takeSnapshot()
+                .newModification();
+        delete.delete(contributorPath);
+        delete.ready();
+
+        final DataTreeCandidate deleteContributorsCanditate = inMemoryDataTree.prepare(delete);
+
+        LOG.debug("*************************");
+        LOG.debug("Before delete: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidation.validate(deleteContributorsCanditate,
+                    rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:{}{}", NEW_LINE, e.getMessage());
+            assertEquals(6, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        assertTrue(exception);
+
+        inMemoryDataTree.commit(deleteContributorsCanditate);
+
+        LOG.debug("*************************");
+        LOG.debug("After delete: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+    }
+
+    private static ContainerNode createContributorContainer(
+            final ContainerSchemaNode contributorContSchemaNode) {
+
+        final ListSchemaNode contributorListSchemaNode = (ListSchemaNode) contributorContSchemaNode
+                .findDataChildByName(contributor).get();
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> contributorContainerBldr = Builders
+                .containerBuilder(contributorContSchemaNode);
+
+        final MapNode contributorMap = createContributorList(contributorListSchemaNode);
+        contributorContainerBldr.addChild(contributorMap);
+
+        final ContainerNode contributorContainer = contributorContainerBldr
+                .build();
+
+        return contributorContainer;
+
+    }
+
+    private static MapNode createContributorList(
+            final ListSchemaNode contributorListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> contributorMapBldr = Builders
+                .mapBuilder(contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry1 = createContributorListEntry(
+                "Leader of Yangtools", "Yangtools Leader name", "Yangtools",
+                "Yangtools description ...", contributorListSchemaNode);
+        final MapEntryNode contributorMapEntry2 = createContributorListEntry(
+                "Leader of MD-SAL", "MD-SAL Leader name", "MD-SAL",
+                "MD-SAL description ...", contributorListSchemaNode);
+        final MapEntryNode contributorMapEntry3 = createContributorListEntry(
+                "Leader of Controller", "Controller Leader name", "Controller",
+                "Controller description ...", contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry4 = createContributorListEntry(
+                "jdoe", "John Doe", "MD-SAL", "Yangtools description ...",
+                contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry5 = createContributorListEntry(
+                "foo", "foo name", "Controller", "MD-SAL description ...",
+                contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry6 = createContributorListEntry(
+                "bar", "bar name", "Yangtools", "Controller description ...",
+                contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry7 = createContributorListEntry(
+                "baz", "baz name", "Unknown Project",
+                "Unknown Project description ...", contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry8 = createContributorListEntry(
+                "pk", "pk name", "Unknown Project 2",
+                "Controller description ...", contributorListSchemaNode);
+
+        contributorMapBldr.addChild(contributorMapEntry1);
+        contributorMapBldr.addChild(contributorMapEntry2);
+        contributorMapBldr.addChild(contributorMapEntry3);
+        contributorMapBldr.addChild(contributorMapEntry4);
+        contributorMapBldr.addChild(contributorMapEntry5);
+        contributorMapBldr.addChild(contributorMapEntry6);
+        contributorMapBldr.addChild(contributorMapEntry7);
+        contributorMapBldr.addChild(contributorMapEntry8);
+
+        final MapNode contributorMap = contributorMapBldr.build();
+
+        return contributorMap;
+
+    }
+
+    private static MapEntryNode createContributorListEntry(
+            final String loginVal, final String contributorNameVal,
+            final String odlProjectNameVal, final String odlProjectDescVal,
+            final ListSchemaNode contributorListSchemaNode) {
+
+        final LeafNode<String> loginLeaf = ImmutableNodes.leafNode(login, loginVal);
+        final LeafNode<String> contributorNameLeaf = ImmutableNodes.leafNode(contributorName, contributorNameVal);
+        final LeafNode<String> odlProjectNameLeafRef = ImmutableNodes.leafNode(odlProjectName, odlProjectNameVal);
+        final LeafNode<String> odlProjectDescLeafRef = ImmutableNodes.leafNode(odlProjectDesc, odlProjectDescVal);
+
+        return Builders.mapEntryBuilder(contributorListSchemaNode)
+                .addChild(loginLeaf)
+                .addChild(contributorNameLeaf)
+                .addChild(odlProjectNameLeafRef)
+                .addChild(odlProjectDescLeafRef)
+                .build();
+    }
+
+    private static ContainerNode createOdlContainer(
+            final ContainerSchemaNode container) {
+
+        final ListSchemaNode projListSchemaNode = (ListSchemaNode) container.findDataChildByName(project).get();
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> odlProjectContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode projectMap = createProjectList(projListSchemaNode);
+        odlProjectContainerBldr.addChild(projectMap);
+
+        final ContainerNode odlProjectContainer = odlProjectContainerBldr
+                .build();
+
+        return odlProjectContainer;
+    }
+
+    private static MapNode createProjectList(
+            final ListSchemaNode projListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> projectMapBldr = Builders
+                .mapBuilder(projListSchemaNode);
+
+        final MapEntryNode projMapEntry1 = createProjectListEntry("Yangtools",
+                "Yangtools description ...", "Leader of Yangtools",
+                "Owner of Yangtools", projListSchemaNode);
+        final MapEntryNode projMapEntry2 = createProjectListEntry("MD-SAL",
+                "MD-SAL description ...", "Leader of MD-SAL",
+                "Owner of MD-SAL", projListSchemaNode);
+        final MapEntryNode projMapEntry3 = createProjectListEntry("Controller",
+                "Controller description ...", "Leader of Controller",
+                "Owner of Controller", projListSchemaNode);
+
+        projectMapBldr.addChild(projMapEntry1);
+        projectMapBldr.addChild(projMapEntry2);
+        projectMapBldr.addChild(projMapEntry3);
+
+        final MapNode projectMap = projectMapBldr.build();
+
+        return projectMap;
+    }
+
+    private static MapEntryNode createProjectListEntry(final String nameVal,
+            final String descVal, final String leadVal, final String ownerVal,
+            final ListSchemaNode projListSchemaNode) {
+
+        final LeafNode<String> nameLeaf = ImmutableNodes
+                .leafNode(name, nameVal);
+        final LeafNode<String> descLeaf = ImmutableNodes
+                .leafNode(desc, descVal);
+        final LeafNode<String> leadLeafRef = ImmutableNodes.leafNode(lead,
+                leadVal);
+        final LeafNode<String> ownerLeafRef = ImmutableNodes.leafNode(owner,
+                ownerVal);
+
+        final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> projMapEntryBldr = Builders
+                .mapEntryBuilder(projListSchemaNode);
+
+        projMapEntryBldr.addChild(nameLeaf);
+        projMapEntryBldr.addChild(descLeaf);
+        projMapEntryBldr.addChild(leadLeafRef);
+        projMapEntryBldr.addChild(ownerLeafRef);
+        final MapEntryNode projMapEntry = projMapEntryBldr.build();
+
+        return projMapEntry;
+    }
+
+    private static ContainerNode createBasicContributorContainer(
+            final ContainerSchemaNode contributorContSchemaNode) {
+
+        final ListSchemaNode contributorListSchemaNode = (ListSchemaNode) contributorContSchemaNode
+                .findDataChildByName(contributor).get();
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> contributorContainerBldr = Builders
+                .containerBuilder(contributorContSchemaNode);
+
+        final MapNode contributorMap = createBasicContributorList(contributorListSchemaNode);
+        contributorContainerBldr.addChild(contributorMap);
+
+        final ContainerNode contributorContainer = contributorContainerBldr
+                .build();
+
+        return contributorContainer;
+
+    }
+
+    private static MapNode createBasicContributorList(
+            final ListSchemaNode contributorListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> contributorMapBldr = Builders
+                .mapBuilder(contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry1 = createContributorListEntry(
+                "Leader of Yangtools", "Yangtools Leader name", "Yangtools",
+                "Yangtools description ...", contributorListSchemaNode);
+        final MapEntryNode contributorMapEntry2 = createContributorListEntry(
+                "Leader of MD-SAL", "MD-SAL Leader name", "MD-SAL",
+                "MD-SAL description ...", contributorListSchemaNode);
+        final MapEntryNode contributorMapEntry3 = createContributorListEntry(
+                "Leader of Controller", "Controller Leader name", "Controller",
+                "Controller description ...", contributorListSchemaNode);
+
+        contributorMapBldr.addChild(contributorMapEntry1);
+        contributorMapBldr.addChild(contributorMapEntry2);
+        contributorMapBldr.addChild(contributorMapEntry3);
+
+        final MapNode contributorMap = contributorMapBldr.build();
+
+        return contributorMap;
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/DataTreeCandidateValidatorTest2.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/DataTreeCandidateValidatorTest2.java
new file mode 100644 (file)
index 0000000..7ea56da
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2016 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.leafref.context;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Set;
+import org.apache.log4j.BasicConfigurator;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefDataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefValidation;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+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.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DataTreeCandidateValidatorTest2 {
+
+    private static SchemaContext context;
+    private static Module mainModule;
+    private static QNameModule rootModuleQname;
+    private static LeafRefContext rootLeafRefContext;
+    public static DataTree inMemoryDataTree;
+
+    private static QName chips;
+    private static QName chip;
+    private static QName devType;
+    private static QName chipDesc;
+
+    private static QName devices;
+    private static QName device;
+    private static QName typeChoice;
+    private static QName typeText;
+    private static QName devDesc;
+    private static QName sn;
+    private static QName defaultIp;
+
+    private static QName deviceTypeStr;
+    private static QName deviceType;
+    private static QName type;
+    private static QName desc;
+
+    private static final Logger LOG = LoggerFactory.getLogger(DataTreeCandidateValidatorTest2.class);
+    private static final String NEW_LINE = System.getProperty("line.separator");
+
+    static {
+        BasicConfigurator.configure();
+    }
+
+    @BeforeClass
+    public static void init() throws DataValidationFailedException {
+        context = YangParserTestUtils.parseYangResourceDirectory("/leafref-validation");
+
+        final Set<Module> modules = context.getModules();
+        for (final Module module : modules) {
+            if (module.getName().equals("leafref-validation2")) {
+                mainModule = module;
+            }
+        }
+
+        rootModuleQname = mainModule.getQNameModule();
+        rootLeafRefContext = LeafRefContext.create(context);
+
+        chips = QName.create(rootModuleQname, "chips");
+        chip = QName.create(rootModuleQname, "chip");
+        devType = QName.create(rootModuleQname, "dev_type");
+        chipDesc = QName.create(rootModuleQname, "chip_desc");
+
+        devices = QName.create(rootModuleQname, "devices");
+        device = QName.create(rootModuleQname, "device");
+        typeText = QName.create(rootModuleQname, "type_text");
+        devDesc = QName.create(rootModuleQname, "dev_desc");
+        sn = QName.create(rootModuleQname, "sn");
+        defaultIp = QName.create(rootModuleQname, "default_ip");
+
+        deviceTypeStr = QName.create(rootModuleQname, "device_types");
+        deviceType = QName.create(rootModuleQname, "device_type");
+        type = QName.create(rootModuleQname, "type");
+        desc = QName.create(rootModuleQname, "desc");
+
+        inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL, context);
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        final ContainerSchemaNode chipsListContSchemaNode = (ContainerSchemaNode) mainModule.findDataChildByName(chips)
+                .get();
+        final ContainerNode chipsContainer = createChipsContainer(chipsListContSchemaNode);
+        final YangInstanceIdentifier path1 = YangInstanceIdentifier.of(chips);
+        initialDataTreeModification.write(path1, chipsContainer);
+
+        final ContainerSchemaNode devTypesListContSchemaNode = (ContainerSchemaNode) mainModule
+                .findDataChildByName(deviceTypeStr).get();
+        final ContainerNode deviceTypesContainer = createDevTypeStrContainer(devTypesListContSchemaNode);
+        final YangInstanceIdentifier path2 = YangInstanceIdentifier.of(deviceTypeStr);
+        initialDataTreeModification.write(path2, deviceTypesContainer);
+
+        initialDataTreeModification.ready();
+        final DataTreeCandidate writeChipsCandidate = inMemoryDataTree.prepare(initialDataTreeModification);
+
+        inMemoryDataTree.commit(writeChipsCandidate);
+        LOG.debug("{}", inMemoryDataTree);
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        inMemoryDataTree = null;
+        rootLeafRefContext = null;
+        mainModule = null;
+        context = null;
+    }
+
+    @Test
+    public void dataTreeCanditateValidationTest2() throws DataValidationFailedException {
+        writeDevices();
+    }
+
+    private static void writeDevices() throws DataValidationFailedException {
+
+        final ContainerSchemaNode devicesContSchemaNode = (ContainerSchemaNode) mainModule.findDataChildByName(devices)
+                .get();
+
+        final ContainerNode devicesContainer = createDevicesContainer(devicesContSchemaNode);
+
+        final YangInstanceIdentifier devicesPath = YangInstanceIdentifier.of(devices);
+        final DataTreeModification writeModification = inMemoryDataTree.takeSnapshot().newModification();
+        writeModification.write(devicesPath, devicesContainer);
+
+        writeModification.ready();
+        final DataTreeCandidate writeDevicesCandidate = inMemoryDataTree.prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before writeDevices: ");
+        LOG.debug("*************************");
+        LOG.debug("{}", inMemoryDataTree);
+
+        boolean exception = false;
+        try {
+            LeafRefValidation.validate(writeDevicesCandidate, rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:{}{}", NEW_LINE, e.getMessage());
+
+            assertEquals(4, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        assertTrue(exception);
+
+        inMemoryDataTree.commit(writeDevicesCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After write: ");
+        LOG.debug("*************************");
+        LOG.debug("{}", inMemoryDataTree);
+    }
+
+    private static ContainerNode createDevTypeStrContainer(final ContainerSchemaNode container) {
+
+        final ListSchemaNode devTypeListSchemaNode = (ListSchemaNode) container.findDataChildByName(deviceType).get();
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> devTypeContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode devTypeMap = createDevTypeList(devTypeListSchemaNode);
+        devTypeContainerBldr.addChild(devTypeMap);
+
+        return devTypeContainerBldr.build();
+    }
+
+    private static MapNode createDevTypeList(final ListSchemaNode devTypeListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> devTypeMapBldr = Builders.mapBuilder(devTypeListSchemaNode);
+
+        devTypeMapBldr.addChild(createDevTypeListEntry("dev_type_1", "typedesc1", devTypeListSchemaNode));
+        devTypeMapBldr.addChild(createDevTypeListEntry("dev_type_2", "typedesc2", devTypeListSchemaNode));
+        devTypeMapBldr.addChild(createDevTypeListEntry("dev_type_3", "typedesc3", devTypeListSchemaNode));
+
+        return devTypeMapBldr.build();
+    }
+
+    private static MapEntryNode createDevTypeListEntry(final String typeVal, final String descVal,
+            final ListSchemaNode devTypeListSchemaNode) {
+
+        final LeafNode<String> typeLeaf = ImmutableNodes.leafNode(type, typeVal);
+        final LeafNode<String> descLeaf = ImmutableNodes.leafNode(desc, descVal);
+
+        final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> devTypeMapEntryBldr = Builders
+                .mapEntryBuilder(devTypeListSchemaNode);
+
+        devTypeMapEntryBldr.addChild(typeLeaf);
+        devTypeMapEntryBldr.addChild(descLeaf);
+
+        return devTypeMapEntryBldr.build();
+    }
+
+    private static ContainerNode createChipsContainer(final ContainerSchemaNode container) {
+
+        final ListSchemaNode chipsListSchemaNode = (ListSchemaNode) container.findDataChildByName(chip).get();
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> chipsContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode chipsMap = createChipsList(chipsListSchemaNode);
+        chipsContainerBldr.addChild(chipsMap);
+
+        return chipsContainerBldr.build();
+    }
+
+    private static MapNode createChipsList(final ListSchemaNode chipsListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> chipsMapBldr = Builders.mapBuilder(chipsListSchemaNode);
+
+        chipsMapBldr.addChild(createChipsListEntry("dev_type_1", "desc1", chipsListSchemaNode));
+        chipsMapBldr.addChild(createChipsListEntry("dev_type_2", "desc2", chipsListSchemaNode));
+
+        return chipsMapBldr.build();
+    }
+
+    private static MapEntryNode createChipsListEntry(final String devTypeVal, final String chipDescVal,
+            final ListSchemaNode chipsListSchemaNode) {
+
+        final LeafNode<String> devTypeLeaf = ImmutableNodes.leafNode(devType, devTypeVal);
+        final LeafNode<String> chipDescLeaf = ImmutableNodes.leafNode(chipDesc, chipDescVal);
+
+        final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> chipsMapEntryBldr = Builders
+                .mapEntryBuilder(chipsListSchemaNode);
+
+        chipsMapEntryBldr.addChild(devTypeLeaf);
+        chipsMapEntryBldr.addChild(chipDescLeaf);
+
+        return chipsMapEntryBldr.build();
+    }
+
+    private static ContainerNode createDevicesContainer(final ContainerSchemaNode container) {
+
+        final ListSchemaNode devicesListSchemaNode = (ListSchemaNode) container.findDataChildByName(device).get();
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> devicesContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode devicesMap = createDeviceList(devicesListSchemaNode);
+        devicesContainerBldr.addChild(devicesMap);
+
+        return devicesContainerBldr.build();
+    }
+
+    private static MapNode createDeviceList(final ListSchemaNode deviceListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> devicesMapBldr = Builders.mapBuilder(deviceListSchemaNode);
+
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type_1", "typedesc1", 123456, "192.168.0.1",
+                deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type_2", "typedesc2", 123457, "192.168.0.1",
+                deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type_2", "typedesc3", 123457, "192.168.0.1",
+                deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type_1", "typedesc2", 123458, "192.168.0.1",
+                deviceListSchemaNode));
+        devicesMapBldr
+                .addChild(createDeviceListEntry("unknown", "unknown", 123457, "192.168.0.1", deviceListSchemaNode));
+
+        return devicesMapBldr.build();
+    }
+
+    private static MapEntryNode createDeviceListEntry(final String typeTextVal, final String descVal, final int snVal,
+            final String defaultIpVal, final ListSchemaNode devicesListSchemaNode) {
+
+        final LeafNode<String> typeTextLeaf = ImmutableNodes.leafNode(typeText, typeTextVal);
+        final LeafNode<String> descLeaf = ImmutableNodes.leafNode(devDesc, descVal);
+        final LeafNode<Integer> snValLeaf = ImmutableNodes.leafNode(sn, snVal);
+        final LeafNode<String> defaultIpLeaf = ImmutableNodes.leafNode(defaultIp, defaultIpVal);
+
+        final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> devicesMapEntryBldr = Builders
+                .mapEntryBuilder(devicesListSchemaNode);
+
+        devicesMapEntryBldr.addChild(typeTextLeaf);
+        devicesMapEntryBldr.addChild(descLeaf);
+        devicesMapEntryBldr.addChild(snValLeaf);
+        devicesMapEntryBldr.addChild(defaultIpLeaf);
+
+        return devicesMapEntryBldr.build();
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/DataTreeCandidateValidatorTest3.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/DataTreeCandidateValidatorTest3.java
new file mode 100644 (file)
index 0000000..0aa847c
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2016 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.leafref.context;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Set;
+import org.apache.log4j.BasicConfigurator;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefDataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefValidation;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+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.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DataTreeCandidateValidatorTest3 {
+
+    private static SchemaContext context;
+    private static Module mainModule;
+    private static QNameModule rootModuleQname;
+    private static LeafRefContext rootLeafRefContext;
+    public static DataTree inMemoryDataTree;
+
+    private static QName chips;
+    private static QName chip;
+    private static QName devType;
+    private static QName chipDesc;
+
+    private static QName devices;
+    private static QName device;
+    private static QName typeText1;
+    private static QName typeText2;
+    private static QName typeText3;
+    private static QName devDesc;
+    private static QName sn;
+    private static QName defaultIp;
+
+    private static QName deviceTypeStr;
+    private static QName deviceType;
+    private static QName type1;
+    private static QName type2;
+    private static QName type3;
+    private static QName desc;
+
+    private static final Logger LOG = LoggerFactory.getLogger(DataTreeCandidateValidatorTest3.class);
+    private static final String NEW_LINE = System.getProperty("line.separator");
+
+    static {
+        BasicConfigurator.configure();
+    }
+
+    @BeforeClass
+    public static void init() throws DataValidationFailedException {
+        context = YangParserTestUtils.parseYangResourceDirectory("/leafref-validation");
+
+        final Set<Module> modules = context.getModules();
+        for (final Module module : modules) {
+            if (module.getName().equals("leafref-validation3")) {
+                mainModule = module;
+            }
+        }
+
+        rootModuleQname = mainModule.getQNameModule();
+        rootLeafRefContext = LeafRefContext.create(context);
+
+
+        chips = QName.create(rootModuleQname, "chips");
+        chip = QName.create(rootModuleQname, "chip");
+        devType = QName.create(rootModuleQname, "dev_type");
+        chipDesc = QName.create(rootModuleQname, "chip_desc");
+
+        devices = QName.create(rootModuleQname, "devices");
+        device = QName.create(rootModuleQname, "device");
+        typeText1 = QName.create(rootModuleQname, "type_text1");
+        typeText2 = QName.create(rootModuleQname, "type_text2");
+        typeText3 = QName.create(rootModuleQname, "type_text3");
+        devDesc = QName.create(rootModuleQname, "dev_desc");
+        sn = QName.create(rootModuleQname, "sn");
+        defaultIp = QName.create(rootModuleQname, "default_ip");
+
+        deviceTypeStr = QName.create(rootModuleQname, "device_types");
+        deviceType = QName.create(rootModuleQname, "device_type");
+        type1 = QName.create(rootModuleQname, "type1");
+        type2 = QName.create(rootModuleQname, "type2");
+        type3 = QName.create(rootModuleQname, "type3");
+        desc = QName.create(rootModuleQname, "desc");
+
+        inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL, context);
+
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+
+        final ContainerSchemaNode chipsListContSchemaNode = (ContainerSchemaNode) mainModule.findDataChildByName(chips)
+                .get();
+        final ContainerNode chipsContainer = createChipsContainer(chipsListContSchemaNode);
+        final YangInstanceIdentifier path1 = YangInstanceIdentifier.of(chips);
+        initialDataTreeModification.write(path1, chipsContainer);
+
+        final ContainerSchemaNode devTypesListContSchemaNode = (ContainerSchemaNode) mainModule
+                .findDataChildByName(deviceTypeStr).get();
+        final ContainerNode deviceTypesContainer = createDevTypeStrContainer(devTypesListContSchemaNode);
+        final YangInstanceIdentifier path2 = YangInstanceIdentifier.of(deviceTypeStr);
+        initialDataTreeModification.write(path2, deviceTypesContainer);
+
+        initialDataTreeModification.ready();
+        final DataTreeCandidate writeChipsCandidate = inMemoryDataTree.prepare(initialDataTreeModification);
+
+        inMemoryDataTree.commit(writeChipsCandidate);
+
+        LOG.debug("{}", inMemoryDataTree);
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        inMemoryDataTree = null;
+        rootLeafRefContext = null;
+        mainModule = null;
+        context = null;
+    }
+
+    @Test
+    public void dataTreeCanditateValidationTest2() throws DataValidationFailedException {
+        writeDevices();
+        mergeDevices();
+    }
+
+    private static void writeDevices() throws DataValidationFailedException {
+        final ContainerSchemaNode devicesContSchemaNode = (ContainerSchemaNode) mainModule.findDataChildByName(devices)
+                .get();
+        final ContainerNode devicesContainer = createDevicesContainer(devicesContSchemaNode);
+        final YangInstanceIdentifier devicesPath = YangInstanceIdentifier.of(devices);
+        final DataTreeModification writeModification = inMemoryDataTree.takeSnapshot().newModification();
+        writeModification.write(devicesPath, devicesContainer);
+
+        writeModification.ready();
+        final DataTreeCandidate writeDevicesCandidate = inMemoryDataTree.prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before writeDevices: ");
+        LOG.debug("*************************");
+        LOG.debug("{}", inMemoryDataTree);
+
+        boolean exception = false;
+        try {
+            LeafRefValidation.validate(writeDevicesCandidate, rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:{}{}", NEW_LINE, e.getMessage());
+            assertEquals(6, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        assertTrue(exception);
+
+        inMemoryDataTree.commit(writeDevicesCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After writeDevices: ");
+        LOG.debug("*************************");
+        LOG.debug("{}", inMemoryDataTree);
+    }
+
+    private static void mergeDevices() throws DataValidationFailedException {
+
+        final ContainerSchemaNode devicesContSchemaNode = (ContainerSchemaNode) mainModule.findDataChildByName(devices)
+                .get();
+
+        final ContainerNode devicesContainer = createDevices2Container(devicesContSchemaNode);
+
+        final YangInstanceIdentifier devicesPath = YangInstanceIdentifier.of(devices);
+        final DataTreeModification mergeModification = inMemoryDataTree.takeSnapshot().newModification();
+        mergeModification.write(devicesPath, devicesContainer);
+        mergeModification.merge(devicesPath, devicesContainer);
+
+        mergeModification.ready();
+        final DataTreeCandidate mergeDevicesCandidate = inMemoryDataTree.prepare(mergeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before mergeDevices: ");
+        LOG.debug("*************************");
+        LOG.debug("{}", inMemoryDataTree);
+
+        boolean exception = false;
+        try {
+            LeafRefValidation.validate(mergeDevicesCandidate, rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:{}{}", NEW_LINE, e.getMessage());
+            // :TODO verify errors count gz
+            assertEquals(6, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        inMemoryDataTree.commit(mergeDevicesCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After mergeDevices: ");
+        LOG.debug("*************************");
+        LOG.debug("{}", inMemoryDataTree);
+
+        assertTrue(exception);
+    }
+
+    private static ContainerNode createDevTypeStrContainer(final ContainerSchemaNode container) {
+
+        final ListSchemaNode devTypeListSchemaNode = (ListSchemaNode) container.findDataChildByName(deviceType).get();
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> devTypeContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode devTypeMap = createDevTypeList(devTypeListSchemaNode);
+        devTypeContainerBldr.addChild(devTypeMap);
+
+        return devTypeContainerBldr.build();
+    }
+
+    private static MapNode createDevTypeList(final ListSchemaNode devTypeListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> devTypeMapBldr = Builders.mapBuilder(devTypeListSchemaNode);
+
+        devTypeMapBldr.addChild(createDevTypeListEntry("dev_type1_1", "dev_type2_1", "dev_type3_1", "typedesc1",
+                devTypeListSchemaNode));
+        devTypeMapBldr.addChild(createDevTypeListEntry("dev_type1_2", "dev_type2_2", "dev_type3_2", "typedesc2",
+                devTypeListSchemaNode));
+        devTypeMapBldr.addChild(createDevTypeListEntry("dev_type1_3", "dev_type2_3", "dev_type3_3", "typedesc3",
+                devTypeListSchemaNode));
+
+        return devTypeMapBldr.build();
+    }
+
+    private static MapEntryNode createDevTypeListEntry(final String type1Val, final String type2Val,
+            final String type3Val, final String descVal, final ListSchemaNode devTypeListSchemaNode) {
+
+        final LeafNode<String> type1Leaf = ImmutableNodes.leafNode(type1, type1Val);
+        final LeafNode<String> type2Leaf = ImmutableNodes.leafNode(type2, type2Val);
+        final LeafNode<String> type3Leaf = ImmutableNodes.leafNode(type3, type3Val);
+        final LeafNode<String> descLeaf = ImmutableNodes.leafNode(desc, descVal);
+
+        final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> devTypeMapEntryBldr = Builders
+                .mapEntryBuilder(devTypeListSchemaNode);
+
+        devTypeMapEntryBldr.addChild(type1Leaf);
+        devTypeMapEntryBldr.addChild(type2Leaf);
+        devTypeMapEntryBldr.addChild(type3Leaf);
+        devTypeMapEntryBldr.addChild(descLeaf);
+
+        return devTypeMapEntryBldr.build();
+    }
+
+    private static ContainerNode createChipsContainer(final ContainerSchemaNode container) {
+
+        final ListSchemaNode chipsListSchemaNode = (ListSchemaNode) container.findDataChildByName(chip).get();
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> chipsContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode chipsMap = createChipsList(chipsListSchemaNode);
+        chipsContainerBldr.addChild(chipsMap);
+
+        return chipsContainerBldr.build();
+    }
+
+    private static MapNode createChipsList(final ListSchemaNode chipsListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> chipsMapBldr = Builders.mapBuilder(chipsListSchemaNode);
+
+        chipsMapBldr.addChild(createChipsListEntry("dev_type_1", "desc1", chipsListSchemaNode));
+        chipsMapBldr.addChild(createChipsListEntry("dev_type_2", "desc2", chipsListSchemaNode));
+
+        return chipsMapBldr.build();
+    }
+
+    private static MapEntryNode createChipsListEntry(final String devTypeVal, final String chipDescVal,
+            final ListSchemaNode chipsListSchemaNode) {
+
+        final LeafNode<String> devTypeLeaf = ImmutableNodes.leafNode(devType, devTypeVal);
+        final LeafNode<String> chipDescLeaf = ImmutableNodes.leafNode(chipDesc, chipDescVal);
+
+        final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> chipsMapEntryBldr = Builders
+                .mapEntryBuilder(chipsListSchemaNode);
+
+        chipsMapEntryBldr.addChild(devTypeLeaf);
+        chipsMapEntryBldr.addChild(chipDescLeaf);
+
+        return chipsMapEntryBldr.build();
+    }
+
+    private static ContainerNode createDevicesContainer(final ContainerSchemaNode container) {
+
+        final ListSchemaNode devicesListSchemaNode = (ListSchemaNode) container.findDataChildByName(device).get();
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> devicesContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode devicesMap = createDeviceList(devicesListSchemaNode);
+        devicesContainerBldr.addChild(devicesMap);
+
+        return devicesContainerBldr.build();
+    }
+
+    private static MapNode createDeviceList(final ListSchemaNode deviceListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> devicesMapBldr = Builders.mapBuilder(deviceListSchemaNode);
+
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type1_1", "dev_type2_1", "dev_type3_1", "typedesc1", 123456,
+                "192.168.0.1", deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type1_2", "dev_type2_2", "dev_type3_2", "typedesc1", 123457,
+                "192.168.0.1", deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type1_1", "dev_type2_2", "dev_type3_3", "typedesc2", 123458,
+                "192.168.0.1", deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("unk11", "unk22", "unk33", "unk_desc2", 123457, "192.168.0.1",
+                deviceListSchemaNode));
+
+        return devicesMapBldr.build();
+    }
+
+    private static ContainerNode createDevices2Container(final ContainerSchemaNode container) {
+
+        final ListSchemaNode devicesListSchemaNode = (ListSchemaNode) container.findDataChildByName(device).get();
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> devicesContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode devicesMap = createDevice2List(devicesListSchemaNode);
+        devicesContainerBldr.addChild(devicesMap);
+
+        return devicesContainerBldr.build();
+    }
+
+    private static MapNode createDevice2List(final ListSchemaNode deviceListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> devicesMapBldr = Builders.mapBuilder(deviceListSchemaNode);
+
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type1_3", "dev_type2_3", "dev_type3_3", "typedesc3", 123459,
+                "192.168.0.1", deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type1_3", "dev_type2_3", "dev_type3_3", "typedesc2", 123460,
+                "192.168.0.1", deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type1_3", "dev_type2_2", "dev_type3_1", "typedesc1", 123461,
+                "192.168.0.1", deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("unk1", "unk2", "unk3", "unk_desc", 123462, "192.168.0.1",
+                deviceListSchemaNode));
+
+        return devicesMapBldr.build();
+    }
+
+    private static MapEntryNode createDeviceListEntry(final String type1TextVal, final String type2TextVal,
+            final String type3TextVal, final String descVal, final int snVal, final String defaultIpVal,
+            final ListSchemaNode devicesListSchemaNode) {
+
+        final LeafNode<String> typeText1Leaf = ImmutableNodes.leafNode(typeText1, type1TextVal);
+        final LeafNode<String> typeText2Leaf = ImmutableNodes.leafNode(typeText2, type2TextVal);
+        final LeafNode<String> typeText3Leaf = ImmutableNodes.leafNode(typeText3, type3TextVal);
+        final LeafNode<String> descLeaf = ImmutableNodes.leafNode(devDesc, descVal);
+        final LeafNode<Integer> snValLeaf = ImmutableNodes.leafNode(sn, snVal);
+        final LeafNode<String> defaultIpLeaf = ImmutableNodes.leafNode(defaultIp, defaultIpVal);
+
+        final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> devicesMapEntryBldr = Builders
+                .mapEntryBuilder(devicesListSchemaNode);
+
+        devicesMapEntryBldr.addChild(typeText1Leaf);
+        devicesMapEntryBldr.addChild(typeText2Leaf);
+        devicesMapEntryBldr.addChild(typeText3Leaf);
+        devicesMapEntryBldr.addChild(descLeaf);
+        devicesMapEntryBldr.addChild(snValLeaf);
+        devicesMapEntryBldr.addChild(defaultIpLeaf);
+
+        return devicesMapEntryBldr.build();
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/LeafRefContextTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/LeafRefContextTest.java
new file mode 100644 (file)
index 0000000..7722201
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016 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.leafref.context;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.Set;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContextUtils;
+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.test.util.YangParserTestUtils;
+
+public class LeafRefContextTest {
+    private static SchemaContext context;
+    private static Module rootMod;
+    private static QNameModule root;
+    private static LeafRefContext rootLeafRefContext;
+
+    @BeforeClass
+    public static void init() {
+        context = YangParserTestUtils.parseYangResourceDirectory("/leafref-context-test/correct-modules");
+
+        final Set<Module> modules = context.getModules();
+        for (final Module module : modules) {
+            if (module.getName().equals("leafref-test2")) {
+                rootMod = module;
+            }
+        }
+
+        root = rootMod.getQNameModule();
+        rootLeafRefContext = LeafRefContext.create(context);
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        context = null;
+        root = null;
+        rootMod = null;
+        rootLeafRefContext = null;
+    }
+
+    @Test
+    public void test() {
+
+        final QName q1 = QName.create(root, "ref1");
+        final QName q2 = QName.create(root, "leaf1");
+        final QName q3 = QName.create(root, "cont1");
+        final QName q4 = QName.create(root, "cont2");
+        final QName q5 = QName.create(root, "list1");
+        final QName q6 = QName.create(root, "name");
+
+        final DataSchemaNode leafRefNode = rootMod.findDataChildByName(q1).get();
+        final DataSchemaNode targetNode = rootMod.findDataChildByName(q2).get();
+        final DataSchemaNode cont1Node = rootMod.findDataChildByName(q3).get();
+        final DataSchemaNode cont2Node = rootMod.findDataChildByName(q4).get();
+        final DataSchemaNode name1Node = rootMod.findDataChildByName(q3, q5, q6).get();
+
+        assertTrue(LeafRefContextUtils.isLeafRef(leafRefNode, rootLeafRefContext));
+        assertFalse(LeafRefContextUtils.isLeafRef(targetNode, rootLeafRefContext));
+
+        assertTrue(LeafRefContextUtils.hasLeafRefChild(cont1Node, rootLeafRefContext));
+        assertFalse(LeafRefContextUtils.hasLeafRefChild(leafRefNode, rootLeafRefContext));
+
+        assertTrue(LeafRefContextUtils.isReferencedByLeafRef(targetNode, rootLeafRefContext));
+        assertFalse(LeafRefContextUtils.isReferencedByLeafRef(leafRefNode, rootLeafRefContext));
+
+        assertTrue(LeafRefContextUtils.hasChildReferencedByLeafRef(cont2Node, rootLeafRefContext));
+        assertFalse(LeafRefContextUtils.hasChildReferencedByLeafRef(leafRefNode, rootLeafRefContext));
+
+        Map<QName, LeafRefContext> leafRefs = LeafRefContextUtils.getAllLeafRefsReferencingThisNode(name1Node,
+                rootLeafRefContext);
+        assertEquals(4, leafRefs.size());
+        leafRefs = LeafRefContextUtils.getAllLeafRefsReferencingThisNode(leafRefNode, rootLeafRefContext);
+        assertTrue(leafRefs.isEmpty());
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/LeafRefContextTreeBuilderTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/LeafRefContextTreeBuilderTest.java
new file mode 100644 (file)
index 0000000..1ca9962
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2016 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.leafref.context;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Set;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContextUtils;
+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.test.util.YangParserTestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LeafRefContextTreeBuilderTest {
+    private static final Logger LOG = LoggerFactory.getLogger(LeafRefContextTreeBuilderTest.class);
+
+    private static SchemaContext context;
+    private static Module tstMod;
+    private static QNameModule tst;
+    private static LeafRefContext rootLeafRefContext;
+
+    @BeforeClass
+    public static void init() {
+        context = YangParserTestUtils.parseYangResourceDirectory("/leafref-context-test/correct-modules");
+
+        final Set<Module> modules = context.getModules();
+        for (final Module module : modules) {
+            if (module.getName().equals("leafref-test")) {
+                tstMod = module;
+            }
+        }
+
+        tst = tstMod.getQNameModule();
+
+        rootLeafRefContext = LeafRefContext.create(context);
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        context = null;
+        tst = null;
+        tstMod = null;
+        rootLeafRefContext = null;
+    }
+
+    @Test
+    public void buildLeafRefContextTreeTest1() {
+
+        final QName q1 = QName.create(tst, "odl-project");
+        final QName q2 = QName.create(tst, "project");
+        final QName q3 = QName.create(tst, "project-lead");
+
+        final LeafRefContext leafRefCtx = rootLeafRefContext.getReferencingChildByName(q1)
+                .getReferencingChildByName(q2).getReferencingChildByName(q3);
+
+        assertTrue(leafRefCtx.isReferencing());
+        assertNotNull(leafRefCtx.getLeafRefTargetPath());
+        assertFalse(leafRefCtx.getLeafRefTargetPath().isAbsolute());
+        assertNotNull(leafRefCtx.getAbsoluteLeafRefTargetPath());
+        assertTrue(leafRefCtx.getAbsoluteLeafRefTargetPath().isAbsolute());
+
+        LOG.debug("******* Test 1 ************");
+        LOG.debug("Original definition string: {}", leafRefCtx.getLeafRefTargetPathString());
+        LOG.debug("Parsed leafref path: {}", leafRefCtx.getLeafRefTargetPath());
+        LOG.debug("Absolute leafref path: {}", leafRefCtx.getAbsoluteLeafRefTargetPath());
+    }
+
+    @Test
+    public void buildLeafRefContextTreeTest2() {
+
+        final QName q1 = QName.create(tst, "odl-project");
+        final QName q2 = QName.create(tst, "project");
+        final QName q4 = QName.create(tst, "project-lead2");
+
+        final LeafRefContext leafRefCtx2 = rootLeafRefContext.getReferencingChildByName(q1)
+                .getReferencingChildByName(q2).getReferencingChildByName(q4);
+
+        assertTrue(leafRefCtx2.isReferencing());
+        assertNotNull(leafRefCtx2.getLeafRefTargetPath());
+        assertTrue(leafRefCtx2.getLeafRefTargetPath().isAbsolute());
+        assertNotNull(leafRefCtx2.getAbsoluteLeafRefTargetPath());
+        assertTrue(leafRefCtx2.getAbsoluteLeafRefTargetPath().isAbsolute());
+
+        LOG.debug("******* Test 2 ************");
+        LOG.debug("Original definition string2: {}", leafRefCtx2.getLeafRefTargetPathString());
+        LOG.debug("Parsed leafref path2: {}", leafRefCtx2.getLeafRefTargetPath());
+        LOG.debug("Absolute leafref path2: {}", leafRefCtx2.getAbsoluteLeafRefTargetPath());
+    }
+
+    @Test
+    public void buildLeafRefContextTreeXPathTest() {
+        final QName q1 = QName.create(tst, "odl-project");
+        final QName q2 = QName.create(tst, "project");
+        final QName q5 = QName.create(tst, "ch1");
+        final QName q6 = QName.create(tst, "c1");
+        final QName q7 = QName.create(tst, "ch2");
+        final QName q8 = QName.create(tst, "l1");
+        final LeafRefContext leafRefCtx3 = rootLeafRefContext.getReferencingChildByName(q1)
+                .getReferencingChildByName(q2).getReferencingChildByName(q5).getReferencingChildByName(q6)
+                .getReferencingChildByName(q7).getReferencingChildByName(q6).getReferencingChildByName(q8);
+
+        assertTrue(leafRefCtx3.isReferencing());
+        assertNotNull(leafRefCtx3.getLeafRefTargetPath());
+        assertFalse(leafRefCtx3.getLeafRefTargetPath().isAbsolute());
+        assertNotNull(leafRefCtx3.getAbsoluteLeafRefTargetPath());
+        assertTrue(leafRefCtx3.getAbsoluteLeafRefTargetPath().isAbsolute());
+
+        LOG.debug("******* Test 3 ************");
+        LOG.debug("Original definition string2: {}", leafRefCtx3.getLeafRefTargetPathString());
+        LOG.debug("Parsed leafref path2: {}", leafRefCtx3.getLeafRefTargetPath());
+        LOG.debug("Absolute leafref path2: {}", leafRefCtx3.getAbsoluteLeafRefTargetPath());
+    }
+
+    @Test
+    public void buildLeafRefContextTreeTest4() {
+        final QName q9 = QName.create(tst, "odl-project");
+        final QName q10 = QName.create(tst, "project");
+        final QName q11 = QName.create(tst, "name");
+
+        final LeafRefContext leafRefCtx4 = rootLeafRefContext.getReferencedChildByName(q9)
+                .getReferencedChildByName(q10).getReferencedChildByName(q11);
+
+        assertNotNull(leafRefCtx4);
+        assertTrue(leafRefCtx4.isReferenced());
+        assertEquals(6, leafRefCtx4.getAllReferencedByLeafRefCtxs().size());
+
+    }
+
+    @Test
+    public void leafRefContextUtilsTest() {
+        final QName q1 = QName.create(tst, "odl-contributor");
+        final QName q2 = QName.create(tst, "contributor");
+        final QName q3 = QName.create(tst, "odl-project-name");
+
+        final LeafRefContext odlContrProjNameCtx = rootLeafRefContext.getReferencingChildByName(q1)
+                .getReferencingChildByName(q2).getReferencingChildByName(q3);
+
+        final DataSchemaNode odlContrProjNameNode = tstMod.findDataChildByName(q1, q2, q3).get();
+
+        final LeafRefContext foundOdlContrProjNameCtx = LeafRefContextUtils.getLeafRefReferencingContext(
+                odlContrProjNameNode, rootLeafRefContext);
+
+        assertNotNull(foundOdlContrProjNameCtx);
+        assertTrue(foundOdlContrProjNameCtx.isReferencing());
+        assertNotNull(foundOdlContrProjNameCtx.getLeafRefTargetPath());
+        assertEquals(odlContrProjNameCtx, foundOdlContrProjNameCtx);
+    }
+
+    @Test
+    public void leafRefContextUtilsTest2() {
+        final QName q1 = QName.create(tst, "odl-project");
+        final QName q2 = QName.create(tst, "project");
+        final QName q3 = QName.create(tst, "name");
+
+        final LeafRefContext leafRefCtx = rootLeafRefContext.getReferencedChildByName(q1).getReferencedChildByName(q2)
+                .getReferencedChildByName(q3);
+
+        final DataSchemaNode odlProjNameNode = tstMod.findDataChildByName(q1, q2, q3).get();
+
+        LeafRefContext foundOdlProjNameCtx = LeafRefContextUtils.getLeafRefReferencingContext(odlProjNameNode,
+                rootLeafRefContext);
+
+        assertNull(foundOdlProjNameCtx);
+
+        foundOdlProjNameCtx = LeafRefContextUtils.getLeafRefReferencedByContext(odlProjNameNode, rootLeafRefContext);
+
+        assertNotNull(foundOdlProjNameCtx);
+        assertTrue(foundOdlProjNameCtx.isReferenced());
+        assertFalse(foundOdlProjNameCtx.getAllReferencedByLeafRefCtxs().isEmpty());
+        assertEquals(6, foundOdlProjNameCtx.getAllReferencedByLeafRefCtxs().size());
+        assertEquals(leafRefCtx, foundOdlProjNameCtx);
+    }
+
+    @Test
+    public void leafRefContextUtilsTest3() {
+        final QName q16 = QName.create(tst, "con1");
+        final DataSchemaNode con1 = tstMod.findDataChildByName(q16).get();
+        final List<LeafRefContext> allLeafRefChilds = LeafRefContextUtils.findAllLeafRefChilds(con1,
+            rootLeafRefContext);
+
+        assertNotNull(allLeafRefChilds);
+        assertFalse(allLeafRefChilds.isEmpty());
+        assertEquals(4, allLeafRefChilds.size());
+
+        final QName q17 = QName.create(tst, "odl-contributor");
+        final DataSchemaNode odlContributorNode = tstMod.findDataChildByName(q17).get();
+        List<LeafRefContext> allChildsReferencedByLeafRef = LeafRefContextUtils.findAllChildsReferencedByLeafRef(
+                odlContributorNode, rootLeafRefContext);
+
+        assertNotNull(allChildsReferencedByLeafRef);
+        assertFalse(allChildsReferencedByLeafRef.isEmpty());
+        assertEquals(1, allChildsReferencedByLeafRef.size());
+
+        allChildsReferencedByLeafRef = LeafRefContextUtils.findAllChildsReferencedByLeafRef(con1, rootLeafRefContext);
+
+        assertNotNull(allChildsReferencedByLeafRef);
+        assertTrue(allChildsReferencedByLeafRef.isEmpty());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @Ignore
+    public void incorrectLeafRefPathTest() {
+        LeafRefContext.create(
+                YangParserTestUtils.parseYangResourceDirectory("/leafref-context-test/incorrect-modules"));
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/BuilderTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/BuilderTest.java
new file mode 100644 (file)
index 0000000..e236845
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2016 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+import org.opendaylight.yangtools.util.UnmodifiableCollection;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+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.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedLeafSetNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableYangModeledAnyXmlNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class BuilderTest {
+    private static final QName ROOT_CONTAINER = QName.create("test.namespace.builder.test", "2016-01-01",
+        "root-container");
+    private static final QName LIST_MAIN = QName.create(ROOT_CONTAINER, "list-ordered-by-user-with-key");
+    private static final QName LEAF_LIST_MAIN = QName.create(ROOT_CONTAINER, "leaf-list-ordered-by-user");
+    private static final QName LIST_MAIN_CHILD_QNAME_1 = QName.create(ROOT_CONTAINER, "leaf-a");
+    private static final NodeIdentifier NODE_IDENTIFIER_LIST = NodeIdentifier.create(LIST_MAIN);
+    private static final NodeIdentifier NODE_IDENTIFIER_LEAF_LIST = NodeIdentifier.create(LEAF_LIST_MAIN);
+    private static final NodeIdentifier NODE_IDENTIFIER_LEAF = NodeIdentifier.create(LIST_MAIN_CHILD_QNAME_1);
+    private static final MapEntryNode LIST_MAIN_CHILD_1 = ImmutableNodes.mapEntry(LIST_MAIN, LIST_MAIN_CHILD_QNAME_1,
+            1);
+    private static final MapEntryNode LIST_MAIN_CHILD_2 = ImmutableNodes.mapEntry(LIST_MAIN, LIST_MAIN_CHILD_QNAME_1,
+            2);
+    private static final MapEntryNode LIST_MAIN_CHILD_3 = ImmutableNodes.mapEntry(LIST_MAIN, LIST_MAIN_CHILD_QNAME_1,
+            3);
+    private static final int SIZE = 3;
+    private static final NodeWithValue<String> BAR_PATH = new NodeWithValue<>(LEAF_LIST_MAIN, "bar");
+    private static final LeafSetEntryNode<String> LEAF_SET_ENTRY_NODE =
+            ImmutableLeafSetEntryNodeBuilder.<String>create()
+            .withNodeIdentifier(BAR_PATH)
+            .withValue("bar")
+            .build();
+    private ListSchemaNode list;
+    private LeafListSchemaNode leafList;
+
+    @Before
+    public void setup() throws URISyntaxException {
+        final File leafRefTestYang = new File(getClass().getResource("/builder-test/immutable-ordered-map-node.yang")
+                .toURI());
+        final SchemaContext schema = YangParserTestUtils.parseYangFiles(leafRefTestYang);
+        final Module module = schema.getModules().iterator().next();
+        final DataSchemaNode root = module.findDataChildByName(ROOT_CONTAINER).get();
+        list = (ListSchemaNode)((ContainerSchemaNode) root).findDataChildByName(LIST_MAIN).get();
+        leafList = (LeafListSchemaNode)((ContainerSchemaNode) root).findDataChildByName(LEAF_LIST_MAIN).get();
+    }
+
+    @Test
+    public void immutableOrderedMapBuilderTest() {
+        final LinkedList<MapEntryNode> mapEntryNodeColl = new LinkedList<>();
+        mapEntryNodeColl.add(LIST_MAIN_CHILD_3);
+        final Map<QName, Object> keys = new HashMap<>();
+        keys.put(LIST_MAIN_CHILD_QNAME_1, 1);
+        final NodeIdentifierWithPredicates mapEntryPath = NodeIdentifierWithPredicates.of(LIST_MAIN, keys);
+        final OrderedMapNode orderedMapNodeCreateNull = ImmutableOrderedMapNodeBuilder.create()
+                .withNodeIdentifier(NODE_IDENTIFIER_LIST)
+                .withChild(LIST_MAIN_CHILD_1)
+                .addChild(LIST_MAIN_CHILD_2)
+                .withValue(mapEntryNodeColl)
+                .build();
+        final OrderedMapNode orderedMapNodeCreateSize = ImmutableOrderedMapNodeBuilder.create(SIZE)
+                .withNodeIdentifier(NODE_IDENTIFIER_LIST)
+                .build();
+        final OrderedMapNode orderedMapNodeCreateNode = ImmutableOrderedMapNodeBuilder.create(orderedMapNodeCreateNull)
+                .removeChild(mapEntryPath)
+                .build();
+        final OrderedMapNode orderedMapNodeSchemaAware = ImmutableOrderedMapNodeSchemaAwareBuilder.create(list)
+                .withChild(LIST_MAIN_CHILD_1)
+                .build();
+        final OrderedMapNode orderedMapNodeSchemaAwareMapNodeConst = ImmutableOrderedMapNodeSchemaAwareBuilder.create(
+                list, getImmutableOrderedMapNode())
+                .build();
+
+        assertNotNull(Builders.orderedMapBuilder(list));
+        assertEquals(SIZE, orderedMapNodeCreateNull.getSize());
+        assertEquals(orderedMapNodeCreateNode.getSize(), orderedMapNodeCreateNull.getSize() - 1);
+        assertEquals(NODE_IDENTIFIER_LIST, orderedMapNodeCreateSize.getIdentifier());
+        assertEquals(LIST_MAIN_CHILD_1, orderedMapNodeCreateNull.getChild(0));
+        assertEquals(SIZE, orderedMapNodeCreateNull.getValue().size());
+        assertEquals(orderedMapNodeSchemaAware.getChild(0), orderedMapNodeSchemaAwareMapNodeConst.getChild(0));
+    }
+
+    @Test
+    public void immutableOrderedLeafSetNodeBuilderTest() {
+        final NormalizedNode<?, ?> orderedLeafSet = ImmutableOrderedLeafSetNodeBuilder.<String>create()
+                .withNodeIdentifier(NODE_IDENTIFIER_LEAF_LIST)
+                .withChild(LEAF_SET_ENTRY_NODE)
+                .withChildValue("baz")
+                .removeChild(BAR_PATH)
+                .build();
+        final LinkedList<LeafSetNode<?>> mapEntryNodeColl = new LinkedList<>();
+        mapEntryNodeColl.add((LeafSetNode<?>)orderedLeafSet);
+        final UnmodifiableCollection<?> leafSetCollection = (UnmodifiableCollection<?>)orderedLeafSet.getValue();
+        final NormalizedNode<?, ?> orderedMapNodeSchemaAware = ImmutableOrderedLeafSetNodeSchemaAwareBuilder.create(
+            leafList).withChildValue("baz").build();
+        final UnmodifiableCollection<?> SchemaAwareleafSetCollection =
+                (UnmodifiableCollection<?>)orderedMapNodeSchemaAware.getValue();
+        final NormalizedNode<?, ?> orderedLeafSetShemaAware = ImmutableOrderedLeafSetNodeSchemaAwareBuilder.create(
+            leafList,(LeafSetNode<?>)orderedLeafSet).build();
+
+        assertNotNull(Builders.orderedLeafSetBuilder(leafList));
+        assertNotNull(Builders.anyXmlBuilder());
+        assertNotNull(orderedLeafSetShemaAware);
+        assertEquals(1, ((OrderedLeafSetNode<?>)orderedLeafSet).getSize());
+        assertEquals("baz", ((OrderedLeafSetNode<?>)orderedLeafSet).getChild(0).getValue());
+        assertNotNull(((OrderedLeafSetNode<?>)orderedLeafSet).getChild(BAR_PATH));
+        assertEquals(1, leafSetCollection.size());
+        assertEquals(1, SchemaAwareleafSetCollection.size());
+    }
+
+    @Test
+    public void immutableMapNodeBuilderTest() {
+        final LinkedList<MapEntryNode> mapEntryNodeColl = new LinkedList<>();
+        mapEntryNodeColl.add(LIST_MAIN_CHILD_3);
+        final CollectionNodeBuilder<MapEntryNode, MapNode> collectionNodeBuilder = ImmutableMapNodeBuilder.create(1);
+        assertNotNull(collectionNodeBuilder);
+        collectionNodeBuilder.withNodeIdentifier(NODE_IDENTIFIER_LEAF_LIST);
+        collectionNodeBuilder.withValue(mapEntryNodeColl);
+        final MapNode mapNode = collectionNodeBuilder.build();
+        final MapNode mapNodeSchemaAware = ImmutableMapNodeSchemaAwareBuilder.create(list, getImmutableMapNode())
+                .build();
+        assertNotNull(mapNodeSchemaAware);
+        assertNotNull(Builders.mapBuilder(mapNode));
+    }
+
+    @Test
+    public void immutableUnkeyedListEntryNodeBuilderTest() {
+        final UnkeyedListEntryNode unkeyedListEntryNode = ImmutableUnkeyedListEntryNodeBuilder.create()
+                .withNodeIdentifier(NODE_IDENTIFIER_LIST)
+                .build();
+        final UnkeyedListEntryNode unkeyedListEntryNodeSize = ImmutableUnkeyedListEntryNodeBuilder.create(1)
+                .withNodeIdentifier(NODE_IDENTIFIER_LIST)
+                .build();
+        final UnkeyedListEntryNode unkeyedListEntryNodeNode = ImmutableUnkeyedListEntryNodeBuilder
+                .create(unkeyedListEntryNode).build();
+        assertEquals(unkeyedListEntryNode.getNodeType().getLocalName(), unkeyedListEntryNodeSize.getNodeType()
+                .getLocalName());
+        assertEquals(unkeyedListEntryNodeSize.getNodeType().getLocalName(), unkeyedListEntryNodeNode.getNodeType()
+                .getLocalName());
+    }
+
+    @Test
+    public void immutableUnkeyedListNodeBuilderTest() {
+        final UnkeyedListEntryNode unkeyedListEntryNode = ImmutableUnkeyedListEntryNodeBuilder.create()
+                .withNodeIdentifier(NODE_IDENTIFIER_LEAF)
+                .build();
+        final ImmutableUnkeyedListNodeBuilder immutableUnkeyedListNodeBuilder = (ImmutableUnkeyedListNodeBuilder)
+                ImmutableUnkeyedListNodeBuilder.create();
+        final UnkeyedListNode unkeyedListNode = immutableUnkeyedListNodeBuilder
+                .withNodeIdentifier(NODE_IDENTIFIER_LEAF_LIST)
+                .addChild(unkeyedListEntryNode)
+                .build();
+        final UnkeyedListNode unkeyedListNodeSize = ImmutableUnkeyedListNodeBuilder.create(1)
+                .withNodeIdentifier(NODE_IDENTIFIER_LEAF_LIST)
+                .build();
+        final UnkeyedListNode unkeyedListNodeCreated = ImmutableUnkeyedListNodeBuilder.create(unkeyedListNode)
+                .build();
+        try {
+            unkeyedListNodeSize.getChild(1);
+        } catch (IndexOutOfBoundsException e) {
+            // Ignored on purpose
+        }
+
+        assertNotNull(unkeyedListNodeSize.getValue());
+        assertEquals(unkeyedListEntryNode, unkeyedListNodeCreated.getChild(0));
+        assertEquals(unkeyedListNode.getNodeType().getLocalName(), unkeyedListNodeSize.getNodeType()
+                .getLocalName());
+        assertNotNull(unkeyedListNodeCreated);
+    }
+
+    @Test
+    public void immutableChoiceNodeBuilderTest() {
+        final ChoiceNode choiceNode = ImmutableChoiceNodeBuilder.create(1).withNodeIdentifier(NODE_IDENTIFIER_LIST)
+                .build();
+        final ChoiceNode choiceNodeCreated = ImmutableChoiceNodeBuilder.create(choiceNode).build();
+        assertEquals(choiceNodeCreated.getIdentifier(), choiceNode.getIdentifier());
+    }
+
+
+    @Test(expected = NullPointerException.class)
+    public void immutableAugmentationNodeBuilderExceptionTest() {
+        ImmutableAugmentationNodeBuilder.create(1).build();
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void immutableContainerNodeBuilderExceptionTest() {
+        final ContainerNode immutableContainerNode = ImmutableContainerNodeBuilder.create(1)
+                .withNodeIdentifier(NODE_IDENTIFIER_LIST)
+                .build();
+        assertNotNull(immutableContainerNode);
+        final ContainerSchemaNode containerSchemaNode = mock(ContainerSchemaNode.class);
+        ImmutableContainerNodeSchemaAwareBuilder.create(containerSchemaNode, immutableContainerNode)
+                .withNodeIdentifier(NODE_IDENTIFIER_LIST)
+                .build();
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void immutableLeafSetNodeBuilderExceptionTest() {
+        final LeafSetNode<?> leafSetNode = ImmutableLeafSetNodeBuilder.create(1)
+                .withNodeIdentifier(NODE_IDENTIFIER_LEAF_LIST).build();
+        assertNotNull(leafSetNode);
+        ImmutableLeafSetNodeSchemaAwareBuilder.create(mock(LeafListSchemaNode.class), leafSetNode).build();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void immutableLeafSetEntryNodeSchemaAwareBuilderExceptionTest() {
+        final LeafListSchemaNode leafListSchemaNode = mock(LeafListSchemaNode.class);
+        ImmutableLeafSetEntryNodeSchemaAwareBuilder.create(leafListSchemaNode).withNodeIdentifier(BAR_PATH).build();
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void immutableMapEntryNodeBuilderExceptionTest() {
+        ImmutableMapEntryNodeBuilder.create(1).build();
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void immutableYangModeledAnyXmlNodeBuilderExceptionTest() {
+        ImmutableYangModeledAnyXmlNodeBuilder.create(mock(YangModeledAnyxmlSchemaNode.class), 1);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void immutableUnkeyedListNodeBuilderExceptionTest() {
+        ImmutableUnkeyedListNodeBuilder.create().withNodeIdentifier(NODE_IDENTIFIER_LEAF)
+                .removeChild(NODE_IDENTIFIER_LIST).build();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void immutableOrderedMapNotSchemaAwareExceptionTest1() {
+        ImmutableOrderedMapNodeBuilder.create(getImmutableMapNode()).build();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void immutableMapNodeSchemaAwareExceptionTest() {
+        ImmutableMapNodeSchemaAwareBuilder.create(list, getImmutableMapNode()).withNodeIdentifier(NODE_IDENTIFIER_LIST)
+        .build();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void immutableOrderedMapSchemaAwareExceptionTest1() {
+        ImmutableOrderedMapNodeSchemaAwareBuilder.create(list).withNodeIdentifier(NODE_IDENTIFIER_LIST).build();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void immutableOrderedMapSchemaAwareExceptionTest2() {
+        ImmutableOrderedMapNodeSchemaAwareBuilder.create(list, getImmutableMapNode()).build();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void immutableOrderedLeafSetNodeExceptionTest1() {
+        ImmutableOrderedLeafSetNodeBuilder.create(getImmutableLeafSetNode()).build();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void immutableOrderedLeafSetNodeSchemaAwareExceptionTest1() {
+        ImmutableOrderedLeafSetNodeSchemaAwareBuilder.create(leafList).withNodeIdentifier(NODE_IDENTIFIER_LEAF_LIST)
+        .build();
+    }
+
+    private static LeafSetNode<?> getImmutableLeafSetNode() {
+        final ListNodeBuilder<String, LeafSetEntryNode<String>> leafSetBuilder = Builders.leafSetBuilder();
+        leafSetBuilder.withNodeIdentifier(NODE_IDENTIFIER_LEAF_LIST);
+        leafSetBuilder.addChild(LEAF_SET_ENTRY_NODE);
+        return leafSetBuilder.build();
+    }
+
+    private static MapNode getImmutableMapNode() {
+        return ImmutableMapNodeBuilder.create().withNodeIdentifier(NODE_IDENTIFIER_LIST).withChild(LIST_MAIN_CHILD_1)
+                .build();
+    }
+
+    private static MapNode getImmutableOrderedMapNode() {
+        return ImmutableOrderedMapNodeBuilder.create().withNodeIdentifier(NODE_IDENTIFIER_LIST)
+                .withChild(LIST_MAIN_CHILD_1).build();
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriterTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriterTest.java
new file mode 100644 (file)
index 0000000..2596012
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2016 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.xml.transform.dom.DOMSource;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+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.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+
+public class ImmutableNormalizedNodeStreamWriterTest {
+
+    private QNameModule bazModule;
+
+    private QName outerContainer;
+
+    private QName myContainer1;
+    private QName myKeyedList;
+    private QName myKeyLeaf;
+    private QName myLeafInList1;
+    private QName myLeafInList2;
+    private QName myOrderedList;
+    private QName myKeyLeafInOrderedList;
+    private QName myLeafInOrderedList1;
+    private QName myLeafInOrderedList2;
+    private QName myUnkeyedList;
+    private QName myLeafInUnkeyedList;
+    private QName myLeaf1;
+    private QName myLeafList;
+    private QName myOrderedLeafList;
+
+    private QName myContainer2;
+    private QName innerContainer;
+    private QName myLeaf2;
+    private QName myLeaf3;
+    private QName myChoice;
+    private QName myLeafInCase2;
+    private QName myAnyxml;
+
+    private QName myContainer3;
+    private QName myDoublyKeyedList;
+    private QName myFirstKeyLeaf;
+    private QName mySecondKeyLeaf;
+    private QName myLeafInList3;
+
+    private DOMSource anyxmlDomSource;
+
+    @Before
+    public void setup() throws URISyntaxException, ParseException {
+        bazModule = QNameModule.create(new URI("baz-namespace"), Revision.of("1970-01-01"));
+
+        outerContainer = QName.create(bazModule, "outer-container");
+
+        myContainer1 = QName.create(bazModule, "my-container-1");
+        myKeyedList = QName.create(bazModule, "my-keyed-list");
+        myKeyLeaf = QName.create(bazModule, "my-key-leaf");
+        myLeafInList1 = QName.create(bazModule, "my-leaf-in-list-1");
+        myLeafInList2 = QName.create(bazModule, "my-leaf-in-list-2");
+        myOrderedList = QName.create(bazModule, "my-ordered-list");
+        myKeyLeafInOrderedList = QName.create(bazModule, "my-key-leaf-in-ordered-list");
+        myLeafInOrderedList1 = QName.create(bazModule, "my-leaf-in-ordered-list-1");
+        myLeafInOrderedList2 = QName.create(bazModule, "my-leaf-in-ordered-list-2");
+        myUnkeyedList = QName.create(bazModule, "my-unkeyed-list");
+        myLeafInUnkeyedList = QName.create(bazModule, "my-leaf-in-unkeyed-list");
+        myLeaf1 = QName.create(bazModule, "my-leaf-1");
+        myLeafList = QName.create(bazModule, "my-leaf-list");
+        myOrderedLeafList = QName.create(bazModule, "my-ordered-leaf-list");
+
+        myContainer2 = QName.create(bazModule, "my-container-2");
+        innerContainer = QName.create(bazModule, "inner-container");
+        myLeaf2 = QName.create(bazModule, "my-leaf-2");
+        myLeaf3 = QName.create(bazModule, "my-leaf-3");
+        myChoice = QName.create(bazModule, "my-choice");
+        myLeafInCase2 = QName.create(bazModule, "my-leaf-in-case-2");
+        myAnyxml = QName.create(bazModule, "my-anyxml");
+
+        myContainer3 = QName.create(bazModule, "my-container-3");
+        myDoublyKeyedList = QName.create(bazModule, "my-doubly-keyed-list");
+        myFirstKeyLeaf = QName.create(bazModule, "my-first-key-leaf");
+        mySecondKeyLeaf = QName.create(bazModule, "my-second-key-leaf");
+        myLeafInList3 = QName.create(bazModule, "my-leaf-in-list-3");
+
+        anyxmlDomSource = new DOMSource();
+    }
+
+    @Test
+    public void testImmutableNormalizedNodeStreamWriter() throws IOException {
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter immutableNormalizedNodeStreamWriter = ImmutableNormalizedNodeStreamWriter
+                .from(result);
+        final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter
+                .forStreamWriter(immutableNormalizedNodeStreamWriter);
+        normalizedNodeWriter.write(buildOuterContainerNode());
+
+        final NormalizedNode<?, ?> output = result.getResult();
+        assertNotNull(output);
+
+        final NormalizedNode<?, ?> expectedNormalizedNode = buildOuterContainerNode();
+        assertNotNull(expectedNormalizedNode);
+
+        assertEquals(expectedNormalizedNode, output);
+    }
+
+    private NormalizedNode<?, ?> buildOuterContainerNode() {
+        // my-container-1
+        MapNode myKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myKeyedList))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myKeyedList, myKeyLeaf, "listkeyvalue1"))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList1))
+                                .withValue("listleafvalue1").build())
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList2))
+                                .withValue("listleafvalue2").build()).build())
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myKeyedList, myKeyLeaf, "listkeyvalue2"))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList1))
+                                .withValue("listleafvalue12").build())
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList2))
+                                .withValue("listleafvalue22").build()).build()).build();
+
+        OrderedMapNode myOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+            new NodeIdentifier(myOrderedList))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myOrderedList, myKeyLeafInOrderedList, "olistkeyvalue1"))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInOrderedList1))
+                                .withValue("olistleafvalue1").build())
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInOrderedList2))
+                                .withValue("olistleafvalue2").build()).build())
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myOrderedList, myKeyLeafInOrderedList, "olistkeyvalue2"))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInOrderedList1))
+                                .withValue("olistleafvalue12").build())
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInOrderedList2))
+                                .withValue("olistleafvalue22").build()).build()).build();
+
+        final UnkeyedListEntryNode unkeyedListEntry = Builders.unkeyedListEntryBuilder()
+                .withNodeIdentifier(new NodeIdentifier(myLeafInUnkeyedList))
+                .withChild(ImmutableNodes.leafNode(myLeafInUnkeyedList, "foo")).build();
+
+        final List<UnkeyedListEntryNode> unkeyedListEntries = new ArrayList<>();
+        unkeyedListEntries.add(unkeyedListEntry);
+
+        UnkeyedListNode myUnkeyedListNode = Builders.unkeyedListBuilder().withNodeIdentifier(
+                new NodeIdentifier(myUnkeyedList))
+                .withValue(unkeyedListEntries).build();
+
+        LeafNode<?> myLeaf1Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf1))
+                .withValue("value1").build();
+
+        LeafSetNode<?> myLeafListNode = Builders.leafSetBuilder().withNodeIdentifier(new NodeIdentifier(myLeafList))
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(myLeafList, "lflvalue1")).withValue("lflvalue1").build())
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(myLeafList, "lflvalue2")).withValue("lflvalue2").build()).build();
+
+        LeafSetNode<?> myOrderedLeafListNode = Builders.orderedLeafSetBuilder().withNodeIdentifier(
+                new NodeIdentifier(myOrderedLeafList))
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(myOrderedLeafList, "olflvalue1")).withValue("olflvalue1").build())
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(myOrderedLeafList, "olflvalue2")).withValue("olflvalue2").build()).build();
+
+        ContainerNode myContainer1Node = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer1))
+                .withChild(myKeyedListNode)
+                .withChild(myOrderedListNode)
+                .withChild(myUnkeyedListNode)
+                .withChild(myLeaf1Node)
+                .withChild(myLeafListNode)
+                .withChild(myOrderedLeafListNode)
+                .build();
+
+        // my-container-2
+        ContainerNode innerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(innerContainer))
+                .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf2))
+                        .withValue("value2").build()).build();
+
+        LeafNode<?> myLeaf3Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf3))
+                .withValue("value3").build();
+
+        ChoiceNode myChoiceNode = Builders.choiceBuilder().withNodeIdentifier(new NodeIdentifier(myChoice))
+                .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInCase2))
+                        .withValue("case2value").build()).build();
+
+        DOMSourceAnyxmlNode myAnyxmlNode = Builders.anyXmlBuilder().withNodeIdentifier(new NodeIdentifier(myAnyxml))
+                .withValue(anyxmlDomSource).build();
+
+        ContainerNode myContainer2Node = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer2))
+                .withChild(innerContainerNode)
+                .withChild(myLeaf3Node)
+                .withChild(myChoiceNode)
+                .withChild(myAnyxmlNode).build();
+
+        // my-container-3
+        Map<QName, Object> keys = new HashMap<>();
+        keys.put(myFirstKeyLeaf, "listkeyvalue1");
+        keys.put(mySecondKeyLeaf, "listkeyvalue2");
+
+        MapNode myDoublyKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myDoublyKeyedList))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(myDoublyKeyedList, keys))
+                        .withChild(Builders.leafBuilder().withNodeIdentifier(
+                                new NodeIdentifier(myLeafInList3)).withValue("listleafvalue1").build()).build())
+                .build();
+
+        AugmentationNode myDoublyKeyedListAugNode = Builders.augmentationBuilder().withNodeIdentifier(
+                new AugmentationIdentifier(ImmutableSet.of(myDoublyKeyedList)))
+                .withChild(myDoublyKeyedListNode).build();
+
+        ContainerNode myContainer3Node = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(myContainer3))
+                .withChild(myDoublyKeyedListAugNode).build();
+
+        AugmentationNode myContainer3AugNode = Builders.augmentationBuilder().withNodeIdentifier(
+                new AugmentationIdentifier(ImmutableSet.of(myContainer3)))
+                .withChild(myContainer3Node).build();
+
+        ContainerNode outerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(outerContainer))
+                .withChild(myContainer1Node)
+                .withChild(myContainer2Node)
+                .withChild(myContainer3AugNode).build();
+
+        return outerContainerNode;
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToNodesTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToNodesTest.java
new file mode 100644 (file)
index 0000000..788b2be
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2016 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 static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableSet;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class InstanceIdToNodesTest {
+
+    private static final String NS = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
+    private static final String REVISION = "2014-03-13";
+    private static final QName ID = QName.create(NS, REVISION, "id");
+    private static SchemaContext ctx;
+
+    private final NodeIdentifier rootContainer = new NodeIdentifier(QName.create(NS, REVISION, "test"));
+    private final NodeIdentifier outerContainer = new NodeIdentifier(QName.create(NS, REVISION, "outer-container"));
+    private final NodeIdentifier augmentedLeaf = new NodeIdentifier(QName.create(NS, REVISION, "augmented-leaf"));
+    private final AugmentationIdentifier augmentation = new AugmentationIdentifier(
+            ImmutableSet.of(augmentedLeaf.getNodeType()));
+
+    private final NodeIdentifier outerList = new NodeIdentifier(QName.create(NS, REVISION, "outer-list"));
+    private final NodeIdentifierWithPredicates outerListWithKey = NodeIdentifierWithPredicates.of(
+            QName.create(NS, REVISION, "outer-list"), ID, 1);
+    private final NodeIdentifier choice = new NodeIdentifier(QName.create(NS, REVISION, "outer-choice"));
+    private final NodeIdentifier leafFromCase = new NodeIdentifier(QName.create(NS, REVISION, "one"));
+
+    private final NodeIdentifier leafList = new NodeIdentifier(QName.create(NS, REVISION, "ordered-leaf-list"));
+    private final NodeWithValue<?> leafListWithValue = new NodeWithValue<>(leafList.getNodeType(), "abcd");
+
+    @BeforeClass
+    public static void setUp() {
+        ctx = YangParserTestUtils.parseYangResources(InstanceIdToNodesTest.class, "/filter-test.yang");
+    }
+
+    @AfterClass
+    public static void teardown() {
+        ctx = null;
+    }
+
+    @Test
+    public void testInAugment() {
+        final LeafNode<?> leaf = Builders.leafBuilder().withNodeIdentifier(augmentedLeaf).withValue("").build();
+        final ContainerNode expectedFilter = Builders
+                .containerBuilder()
+                .withNodeIdentifier(rootContainer)
+                .withChild(
+                        Builders.containerBuilder()
+                                .withNodeIdentifier(outerContainer)
+                                .withChild(
+                                        Builders.augmentationBuilder()
+                                                .withNodeIdentifier(augmentation)
+                                                .withChild(
+                                                        leaf).build()).build()).build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer, outerContainer, augmentation, augmentedLeaf), leaf);
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testInAugmentLeafOverride() {
+        final LeafNode<Object> lastLeaf = Builders.leafBuilder().withNodeIdentifier(augmentedLeaf)
+                .withValue("randomValue").build();
+
+        final ContainerNode expectedFilter = Builders
+                .containerBuilder()
+                .withNodeIdentifier(rootContainer)
+                .withChild(
+                        Builders.containerBuilder()
+                                .withNodeIdentifier(outerContainer)
+                                .withChild(
+                                        Builders.augmentationBuilder().withNodeIdentifier(augmentation)
+                                                .withChild(lastLeaf).build()).build()).build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer, outerContainer, augmentation, augmentedLeaf), lastLeaf);
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testListChoice() {
+        final LeafNode<?> leaf = Builders.leafBuilder().withNodeIdentifier(leafFromCase).withValue("").build();
+        final ContainerNode expectedFilter = Builders
+                .containerBuilder()
+                .withNodeIdentifier(rootContainer)
+                .withChild(
+                        Builders.mapBuilder()
+                                .withNodeIdentifier(outerList)
+                                .withChild(
+                                        Builders.mapEntryBuilder()
+                                                .withNodeIdentifier(outerListWithKey)
+                                                .withChild(
+                                                        Builders.leafBuilder()
+                                                                .withNodeIdentifier(
+                                                                        new NodeIdentifier(ID))
+                                                                .withValue(1).build())
+                                                .withChild(
+                                                        Builders.choiceBuilder()
+                                                                .withNodeIdentifier(choice)
+                                                                .withChild(leaf)
+                                                                .build())
+                                                .build())
+                                .build())
+                .build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey, choice, leafFromCase), leaf);
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testTopContainerLastChildOverride() {
+        final ContainerNode expectedStructure = Builders
+                .containerBuilder()
+                .withNodeIdentifier(rootContainer)
+                .withChild(
+                        Builders.mapBuilder()
+                                .withNodeIdentifier(outerList)
+                                .withChild(
+                                        Builders.mapEntryBuilder()
+                                                .withNodeIdentifier(outerListWithKey)
+                                                .withChild(
+                                                        Builders.leafBuilder()
+                                                                .withNodeIdentifier(
+                                                                        new NodeIdentifier(ID))
+                                                                .withValue(1).build())
+                                                .withChild(
+                                                        Builders.choiceBuilder()
+                                                                .withNodeIdentifier(choice)
+                                                                .withChild(
+                                                                        Builders.leafBuilder()
+                                                                                .withNodeIdentifier(leafFromCase)
+                                                                                .withValue("")
+                                                                                .build()).build()).build()).build())
+                .build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer), expectedStructure);
+        assertEquals(expectedStructure, filter);
+    }
+
+    @Test
+    public void testListLastChildOverride() {
+        final MapEntryNode outerListEntry = Builders
+                .mapEntryBuilder()
+                .withNodeIdentifier(outerListWithKey)
+                .withChild(
+                        Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(ID))
+                                .withValue(1).build()).build();
+        final MapNode lastChild = Builders.mapBuilder().withNodeIdentifier(this.outerList).withChild(outerListEntry)
+                .build();
+        final ContainerNode expectedStructure = Builders.containerBuilder().withNodeIdentifier(rootContainer)
+                .withChild(lastChild).build();
+
+        NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey), outerListEntry);
+        assertEquals(expectedStructure, filter);
+        filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey));
+        assertEquals(expectedStructure, filter);
+    }
+
+    @Test
+    public void testLeafList() {
+        final ContainerNode expectedFilter = Builders
+                .containerBuilder()
+                .withNodeIdentifier(rootContainer)
+                .withChild(
+                        Builders.orderedLeafSetBuilder()
+                                .withNodeIdentifier(leafList)
+                                .withChild(
+                                        Builders.leafSetEntryBuilder().withNodeIdentifier(leafListWithValue)
+                                                .withValue(leafListWithValue.getValue()).build()).build()).build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer, leafList, leafListWithValue));
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testEmptyInstanceIdentifier() {
+        assertEquals(ImmutableNodes.containerNode(SchemaContext.NAME),
+            ImmutableNodes.fromInstanceId(ctx, YangInstanceIdentifier.empty()));
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedDataBuilderTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedDataBuilderTest.java
new file mode 100644 (file)
index 0000000..7e9a77a
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2016 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 static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+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.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class NormalizedDataBuilderTest {
+
+    private ContainerSchemaNode containerNode;
+    private SchemaContext schema;
+
+    @Before
+    public void setUp() throws URISyntaxException {
+        schema = YangParserTestUtils.parseYangFiles(new File(getClass().getResource("test.yang").toURI()));
+        containerNode = (ContainerSchemaNode) getSchemaNode(schema, "test", "container");
+    }
+
+    @Test
+    public void testSchemaUnaware() {
+        // Container
+        DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder = Builders
+                .containerBuilder().withNodeIdentifier(getNodeIdentifier("container"));
+
+        // leaf
+        LeafNode<String> leafChild = Builders.<String>leafBuilder().withNodeIdentifier(getNodeIdentifier("leaf"))
+                .withValue("String").build();
+        builder.withChild(leafChild);
+
+        // leafList
+        LeafSetNode<Integer> leafList = Builders.<Integer>leafSetBuilder()
+                .withNodeIdentifier(getNodeIdentifier("leaf"))
+                .withChildValue(1)
+                .withChild(
+                        Builders.<Integer>leafSetEntryBuilder()
+                                .withNodeIdentifier(getNodeWithValueIdentifier("leaf", 3)).withValue(3).build())
+                .build();
+        builder.withChild(leafList);
+
+        // list
+        MapEntryNode listChild1 = Builders
+                .mapEntryBuilder()
+                .withChild(
+                        Builders.<Integer>leafBuilder().withNodeIdentifier(getNodeIdentifier("uint32InList"))
+                                .withValue(1).build())
+                .withChild(Builders.containerBuilder().withNodeIdentifier(getNodeIdentifier("containerInList")).build())
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(
+                                getNodeIdentifier("list").getNodeType(), Collections.singletonMap(
+                                getNodeIdentifier("uint32InList").getNodeType(), 1))).build();
+
+        MapNode list = Builders.mapBuilder().withChild(listChild1).withNodeIdentifier(getNodeIdentifier("list"))
+                .build();
+        builder.withChild(list);
+
+        AugmentationNode augmentation = Builders
+                .augmentationBuilder()
+                .withNodeIdentifier(
+                        new AugmentationIdentifier(ImmutableSet.of(getQName("augmentUint32"))))
+                .withChild(
+                        Builders.<Integer>leafBuilder().withNodeIdentifier(getNodeIdentifier("augmentUint32"))
+                                .withValue(11).build()).build();
+
+        builder.withChild(augmentation);
+
+        // This works without schema (adding child from augment as a direct
+        // child)
+        builder.withChild(Builders.<Integer>leafBuilder().withNodeIdentifier(getNodeIdentifier("augmentUint32"))
+                .withValue(11).build());
+    }
+
+    @Test
+    public void testSchemaAware() {
+        DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder = Builders.containerBuilder(containerNode);
+
+        LeafSchemaNode schemaNode = (LeafSchemaNode) getSchemaNode(schema, "test", "uint32");
+        LeafNode<String> leafChild = Builders.<String>leafBuilder(schemaNode).withValue("String").build();
+        builder.withChild(leafChild);
+
+        LeafListSchemaNode leafListSchemaNode = (LeafListSchemaNode) getSchemaNode(schema, "test", "leafList");
+        LeafSetNode<Integer> leafList = Builders.<Integer>leafSetBuilder(leafListSchemaNode).withChildValue(1)
+                .withChild(Builders.<Integer>leafSetEntryBuilder(leafListSchemaNode).withValue(3).build()).build();
+        builder.withChild(leafList);
+
+        ListSchemaNode listSchema = (ListSchemaNode) getSchemaNode(schema, "test", "list");
+        LeafSchemaNode uint32InListSchemaNode = (LeafSchemaNode) getSchemaNode(schema, "test", "uint32InList");
+        ContainerSchemaNode containerInListSchemaNode = (ContainerSchemaNode) getSchemaNode(schema, "test",
+                "containerInList");
+
+        MapEntryNode listChild1 = Builders.mapEntryBuilder(listSchema)
+                .withChild(Builders.<Integer>leafBuilder(uint32InListSchemaNode).withValue(1).build())
+                .withChild(Builders.containerBuilder(containerInListSchemaNode).build()).build();
+
+        MapNode list = ImmutableMapNodeSchemaAwareBuilder.create(listSchema).withChild(listChild1).build();
+        builder.withChild(list);
+
+        LeafSchemaNode augmentUint32SchemaNode = (LeafSchemaNode) getSchemaNode(schema, "test", "augmentUint32");
+        AugmentationSchemaNode augmentationSchema = getAugmentationSchemaForChild(containerNode,
+                augmentUint32SchemaNode.getQName());
+
+        AugmentationNode augmentation = Builders.augmentationBuilder(augmentationSchema)
+                .withChild(Builders.<Integer>leafBuilder(augmentUint32SchemaNode).withValue(11).build()).build();
+
+        builder.withChild(augmentation);
+
+        // This should fail with schema, since the leaf comes from augmentation
+        // builder.withChild(ImmutableLeafNodeSchemaAwareBuilder.<Integer>get(augmentUint32SchemaNode).withValue(11)
+        // .build());
+
+        LeafSchemaNode augumentString1SchemaNode = (LeafSchemaNode) getSchemaNode(schema, "test", "augmentString1");
+        LeafSchemaNode augumentString2SchemaNode = (LeafSchemaNode) getSchemaNode(schema, "test", "augmentString2");
+
+        ChoiceSchemaNode choice1SchemaNode = (ChoiceSchemaNode) getSchemaNode(schema, "test", "choice");
+        ChoiceNode choice = ImmutableChoiceNodeSchemaAwareBuilder.create(choice1SchemaNode)
+                .withChild(Builders.<String>leafBuilder(augumentString1SchemaNode).withValue("case1").build())
+                // This should fail, since child node belongs to different case
+                // .withChild(Builders.<String>leafBuilder(augumentString2SchemaNode).withValue("case2")
+                // .build())
+                .build();
+
+        builder.withChild(choice);
+
+        // This should fail, child from case
+        // builder.withChild(Builders.<String>leafBuilder(augumentString1SchemaNode).withValue("case1")
+        // .build());
+    }
+
+    private static AugmentationSchemaNode getAugmentationSchemaForChild(final ContainerSchemaNode containerNode,
+            final QName qname) {
+        for (AugmentationSchemaNode augmentationSchema : containerNode.getAvailableAugmentations()) {
+            if (augmentationSchema.findDataChildByName(qname).isPresent()) {
+                return augmentationSchema;
+            }
+        }
+        throw new IllegalStateException("Unable to find child augmentation in " + containerNode);
+    }
+
+    private static <T> NodeWithValue<T> getNodeWithValueIdentifier(final String localName, final T value) {
+        return new NodeWithValue<>(getQName(localName), value);
+    }
+
+    private static QName getQName(final String localName) {
+        return QName.create(URI.create("namespace"), localName);
+    }
+
+    private static NodeIdentifier getNodeIdentifier(final String localName) {
+        return new NodeIdentifier(getQName(localName));
+    }
+
+    public static DataSchemaNode getSchemaNode(final SchemaContext context, final String moduleName,
+            final String childNodeName) {
+        for (Module module : context.getModules()) {
+            if (module.getName().equals(moduleName)) {
+                DataSchemaNode found = findChildNode(module, childNodeName);
+                checkState(found != null, "Unable to find %s", childNodeName);
+                return found;
+            }
+        }
+        throw new IllegalStateException("Unable to find child node " + childNodeName);
+    }
+
+    private static DataSchemaNode findChildNode(final DataNodeContainer container, final String name) {
+        for (DataSchemaNode dataSchemaNode : container.getChildNodes()) {
+            if (dataSchemaNode.getQName().getLocalName().equals(name)) {
+                return dataSchemaNode;
+            }
+            if (dataSchemaNode instanceof DataNodeContainer) {
+                DataSchemaNode retVal = findChildNode((DataNodeContainer) dataSchemaNode, name);
+                if (retVal != null) {
+                    return retVal;
+                }
+            } else if (dataSchemaNode instanceof ChoiceSchemaNode) {
+                for (CaseSchemaNode caseNode : ((ChoiceSchemaNode) dataSchemaNode).getCases().values()) {
+                    DataSchemaNode retVal = findChildNode(caseNode, name);
+                    if (retVal != null) {
+                        return retVal;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeUtilsTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/NormalizedNodeUtilsTest.java
new file mode 100644 (file)
index 0000000..6ac64e1
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2016 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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+
+/*
+ * Schema structure of document is:
+ *
+ * container root { 
+ *      list list-a {
+ *              key leaf-a;
+ *              leaf leaf-a;
+ *              choice choice-a {
+ *                      case one {
+ *                              leaf one;
+ *                      }
+ *                      case two-three {
+ *                              leaf two;
+ *                              leaf three;
+ *                      }
+ *              }
+ *              list list-b {
+ *                      key leaf-b;
+ *                      leaf leaf-b;
+ *              }
+ *      }
+ * }
+ */
+public class NormalizedNodeUtilsTest {
+
+    private static final QName ROOT_QNAME = QName.create("urn:opendaylight:controller:sal:dom:store:test", "2014-03-13",
+            "root");
+    private static final QName LIST_A_QNAME = QName.create(ROOT_QNAME, "list-a");
+    private static final QName LIST_B_QNAME = QName.create(ROOT_QNAME, "list-b");
+    private static final QName LEAF_A_QNAME = QName.create(ROOT_QNAME, "leaf-a");
+    private static final QName LEAF_B_QNAME = QName.create(ROOT_QNAME, "leaf-b");
+    private static final String FOO = "foo";
+    private static final String BAR = "bar";
+    private static final String ONE = "one";
+    private static final String TWO = "two";
+
+    private static final YangInstanceIdentifier LIST_A_FOO_PATH = YangInstanceIdentifier.builder()
+                .node(LIST_A_QNAME)
+                .nodeWithKey(LIST_A_QNAME, LEAF_A_QNAME, FOO)
+                .build();
+    private static final YangInstanceIdentifier LIST_B_TWO_PATH = YangInstanceIdentifier.builder()
+                .node(LIST_A_QNAME)
+                .nodeWithKey(LIST_A_QNAME, LEAF_A_QNAME, BAR)
+                .node(LIST_B_QNAME)
+                .nodeWithKey(LIST_B_QNAME,LEAF_B_QNAME,TWO)
+                .build();
+
+    /**
+     * Returns a test document.
+     *
+     * <pre>
+     * root
+     *     list-a
+     *          leaf-a "foo"
+     *     list-a
+     *          leaf-a "bar"
+     *          list-b
+     *                  leaf-b "one"
+     *          list-b
+     *                  leaf-b "two"
+     *
+     * </pre>
+     *
+     * @return A test document
+     */
+    private static NormalizedNode<?, ?> createDocumentOne() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new NodeIdentifier(ROOT_QNAME))
+                .withChild(
+                        mapNodeBuilder(LIST_A_QNAME)
+                                .withChild(mapEntry(LIST_A_QNAME, LEAF_A_QNAME, FOO))
+                                .withChild(
+                                        mapEntryBuilder(LIST_A_QNAME, LEAF_A_QNAME, BAR).withChild(
+                                                mapNodeBuilder(LIST_B_QNAME)
+                                                        .withChild(mapEntry(LIST_B_QNAME, LEAF_B_QNAME, ONE))
+                                                        .withChild(mapEntry(LIST_B_QNAME, LEAF_B_QNAME, TWO)).build())
+                                                .build()).build()).build();
+
+    }
+
+    @Test
+    public void findNodeTest() {
+        NormalizedNode<?, ?> tree = createDocumentOne();
+        assertNotNull(tree);
+
+        Optional<NormalizedNode<?, ?>> listFooResult = NormalizedNodes.findNode(tree, LIST_A_FOO_PATH);
+        assertTrue(listFooResult.isPresent());
+
+        Optional<NormalizedNode<?, ?>> listTwoResult = NormalizedNodes.findNode(tree, LIST_B_TWO_PATH);
+        assertTrue(listTwoResult.isPresent());
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaUtilsTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaUtilsTest.java
new file mode 100644 (file)
index 0000000..b04b831
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+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.SchemaPath;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class SchemaUtilsTest {
+    private static final String NS = "my-namespace";
+
+    @Test
+    public void test() {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResource("/schema-utils-test/foo.yang");
+        assertTrue(SchemaUtils.findDataParentSchemaOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name"), qN("my-name-a"))) instanceof ContainerSchemaNode);
+        assertTrue(SchemaUtils.findDataParentSchemaOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-2"), qN("my-name-b"))) instanceof NotificationDefinition);
+        assertTrue(SchemaUtils.findDataParentSchemaOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-2"), qN("my-name-2-b"))) instanceof ActionDefinition);
+    }
+
+    @Test
+    public void testNameConflicts() {
+        final SchemaContext schemaContext = YangParserTestUtils
+                .parseYangResource("/schema-utils-test/name-conflicts.yang");
+        // test my-name conflicts
+        assertEquals(8, SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name"), qN("my-name-nested"), qN("my-name-nested2"))).size());
+
+        // test target container
+        final Collection<SchemaNode> target = SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-2"), qN("my-name-nested"), qN("target")));
+        assertEquals(1, target.size());
+        assertTrue(target.iterator().next() instanceof ContainerSchemaNode);
+
+        // test l schema nodes (i.e. container and two leafs)
+        Collection<SchemaNode> schema = SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-3"), qN("input"), qN("con-3"), qN("l")));
+        assertEquals(1, schema.size());
+        assertTrue(schema.iterator().next() instanceof ContainerSchemaNode);
+
+        schema = SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-3"), qN("input"), qN("con-1"), qN("l")));
+        assertEquals(1, schema.size());
+        assertTrue(schema.iterator().next() instanceof LeafSchemaNode);
+
+        schema = SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-3"), qN("input"), qN("con-2"), qN("l")));
+        assertTrue(schema.isEmpty());
+
+        schema = SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-3"), qN("output"), qN("con-2"), qN("l")));
+        assertEquals(1, schema.size());
+        assertTrue(schema.iterator().next() instanceof LeafSchemaNode);
+    }
+
+    private static QName qN(final String localName) {
+        return QName.create(NS, localName);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueAttrNodeTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueAttrNodeTest.java
new file mode 100644 (file)
index 0000000..effb1a5
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * 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.nodes;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+public class AbstractImmutableNormalizedValueAttrNodeTest {
+
+    private static final QName ROOT_QNAME = QName.create("urn:test", "2014-03-13", "root");
+    private static final QName LEAF_QNAME = QName.create(ROOT_QNAME, "my-leaf");
+    private static final QName SAME_LEAF_QNAME = QName.create(ROOT_QNAME, "my-leaf");
+    private static final QName OTHER_LEAF_QNAME = QName.create(ROOT_QNAME, "my-other-leaf");
+
+    @Test
+    // This test is based on using different references; we're testing equals()
+    @SuppressWarnings({"RedundantStringConstructorCall", "EqualsWithItself"})
+    public void equalsByteTest() {
+        byte[] value = "test".getBytes();
+        byte[] equalValue = "test".getBytes();
+
+        LeafNode<byte[]> leafNode = ImmutableNodes.leafNode(LEAF_QNAME, value);
+        LeafNode<byte[]> equalLeafNode = ImmutableNodes.leafNode(SAME_LEAF_QNAME, equalValue);
+
+        assertTrue(leafNode.equals(leafNode));
+        assertTrue(leafNode.equals(equalLeafNode));
+        assertTrue(equalLeafNode.equals(leafNode));
+
+        Byte[] value2 = new Byte[] { new Byte("1"), new Byte("2") };
+        Byte[] equalValue2 = new Byte[] { new Byte("1"), new Byte("2") };
+
+        LeafNode<Byte[]> leafNode2 = ImmutableNodes.leafNode(LEAF_QNAME, value2);
+        LeafNode<Byte[]> equalLeafNode2 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, equalValue2);
+
+        assertTrue(leafNode2.equals(leafNode2));
+        assertTrue(leafNode2.equals(equalLeafNode2));
+        assertTrue(equalLeafNode2.equals(leafNode2));
+
+        byte[][] value3 = new byte[][] { "test".getBytes(), "test2".getBytes() };
+        byte[][] equalValue3 = new byte[][] { "test".getBytes(), "test2".getBytes() };
+
+        LeafNode<byte[][]> leafNode3 = ImmutableNodes.leafNode(LEAF_QNAME,
+                value3);
+        LeafNode<byte[][]> equalLeafNode3 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, equalValue3);
+
+        assertTrue(leafNode3.equals(leafNode3));
+        assertTrue(leafNode3.equals(equalLeafNode3));
+        assertTrue(equalLeafNode3.equals(leafNode3));
+
+        Byte[][] value4 = new Byte[][] {
+            new Byte[] { new Byte("1"), new Byte("2") },
+            new Byte[] { new Byte("3"), new Byte("4") },
+        };
+        Byte[][] equalValue4 = new Byte[][] {
+            new Byte[] { new Byte("1"), new Byte("2") },
+            new Byte[] { new Byte("3"), new Byte("4") },
+        };
+
+        LeafNode<Byte[][]> leafNode4 = ImmutableNodes.leafNode(LEAF_QNAME,value4);
+        LeafNode<Byte[][]> equalLeafNode4 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, equalValue4);
+
+        assertTrue(leafNode4.equals(leafNode4));
+        assertTrue(leafNode4.equals(equalLeafNode4));
+        assertTrue(equalLeafNode4.equals(leafNode4));
+
+        Byte value6 = new Byte("1");
+        Byte equalValue6 = new Byte("1");
+
+        LeafNode<Byte> leafNode6 = ImmutableNodes.leafNode(LEAF_QNAME, value6);
+        LeafNode<Byte> equalLeafNode6 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue6);
+
+        assertTrue(leafNode6.equals(leafNode6));
+        assertTrue(leafNode6.equals(equalLeafNode6));
+        assertTrue(equalLeafNode6.equals(leafNode6));
+
+        String value5 = "test";
+        String equalValue5 = new String("test");
+
+        LeafNode<String> leafNode5 = ImmutableNodes.leafNode(LEAF_QNAME, value5);
+        LeafNode<String> equalLeafNode5 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, equalValue5);
+
+        assertTrue(leafNode5.equals(leafNode5));
+        assertTrue(leafNode5.equals(equalLeafNode5));
+        assertTrue(equalLeafNode5.equals(leafNode5));
+    }
+
+    @Test
+    // We're testing equals()
+    @SuppressWarnings({"ObjectEqualsNull", "EqualsBetweenInconvertibleTypes"})
+    public void notEqualByteTest() {
+
+        byte[] value = "test".getBytes();
+        byte[] equalValue = "test".getBytes();
+
+        LeafNode<byte[]> leafNode = ImmutableNodes.leafNode(LEAF_QNAME, value);
+        LeafNode<byte[]> otherLeafNode = ImmutableNodes.leafNode(OTHER_LEAF_QNAME, equalValue);
+
+        assertFalse(leafNode.equals(null));
+        assertFalse(leafNode.equals(new Object()));
+        assertFalse(leafNode.equals(otherLeafNode));
+        assertFalse(otherLeafNode.equals(leafNode));
+
+        byte[] value1 = "test".getBytes();
+        byte[] otherValue1 = "test1".getBytes();
+
+        LeafNode<byte[]> leafNode1 = ImmutableNodes.leafNode(LEAF_QNAME, value1);
+        LeafNode<byte[]> otherLeafNode1 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, otherValue1);
+
+        assertFalse(leafNode1.equals(otherLeafNode1));
+        assertFalse(otherLeafNode1.equals(leafNode1));
+
+        Byte[] value2 = new Byte[] { new Byte("1"), new Byte("1") };
+        Byte[] otherValue2 = new Byte[] { new Byte("1"), new Byte("2") };
+
+        LeafNode<Byte[]> leafNode2 = ImmutableNodes.leafNode(LEAF_QNAME, value2);
+        LeafNode<Byte[]> otherLeafNode2 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, otherValue2);
+
+        assertFalse(leafNode2.equals(otherLeafNode2));
+        assertFalse(otherLeafNode2.equals(leafNode2));
+
+        byte[][] value3 = new byte[][] { "test".getBytes(), "test2".getBytes() };
+        byte[][] otherValue3 = new byte[][] { "test".getBytes(), "test3".getBytes() };
+
+        LeafNode<byte[][]> leafNode3 = ImmutableNodes.leafNode(LEAF_QNAME, value3);
+        LeafNode<byte[][]> otherLeafNode3 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, otherValue3);
+
+        assertFalse(leafNode3.equals(otherLeafNode3));
+        assertFalse(otherLeafNode3.equals(leafNode3));
+
+        Byte[][] value4 = new Byte[][] {
+            new Byte[] { new Byte("1"), new Byte("2") },
+            new Byte[] { new Byte("3"), new Byte("4") },
+        };
+        Byte[][] otherValue4 = new Byte[][] {
+            new Byte[] { new Byte("1"), new Byte("2") },
+            new Byte[] { new Byte("3"), new Byte("5") },
+        };
+
+        LeafNode<Byte[][]> leafNode4 = ImmutableNodes.leafNode(LEAF_QNAME, value4);
+        LeafNode<Byte[][]> otherLeafNode4 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, otherValue4);
+
+        assertFalse(leafNode4.equals(otherLeafNode4));
+        assertFalse(otherLeafNode4.equals(leafNode4));
+
+        Byte value6 = new Byte("1");
+        Byte otherValue6 = new Byte("2");
+
+        LeafNode<Byte> leafNode6 = ImmutableNodes.leafNode(LEAF_QNAME, value6);
+        LeafNode<Byte> otherLeafNode6 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, otherValue6);
+
+        assertFalse(leafNode6.equals(otherLeafNode6));
+        assertFalse(otherLeafNode6.equals(leafNode6));
+
+        String value5 = "test";
+        String otherValue5 = "test2";
+
+        LeafNode<String> leafNode5 = ImmutableNodes.leafNode(LEAF_QNAME, value5);
+        LeafNode<String> otherLeafNode5 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, otherValue5);
+
+        assertFalse(leafNode5.equals(otherLeafNode5));
+        assertFalse(otherLeafNode5.equals(leafNode5));
+        assertFalse(leafNode5.equals(leafNode));
+        assertFalse(leafNode5.equals(leafNode1));
+        assertFalse(leafNode5.equals(leafNode2));
+        assertFalse(leafNode5.equals(leafNode3));
+        assertFalse(leafNode5.equals(leafNode4));
+        assertFalse(leafNode5.equals(leafNode6));
+        assertFalse(leafNode.equals(leafNode5));
+        assertFalse(leafNode1.equals(leafNode5));
+        assertFalse(leafNode2.equals(leafNode5));
+        assertFalse(leafNode3.equals(leafNode5));
+        assertFalse(leafNode4.equals(leafNode5));
+        assertFalse(leafNode6.equals(leafNode5));
+
+        byte[] byteValue = new byte[] { 1, 1 };
+
+        LeafNode<byte[]> byteLeafNode = ImmutableNodes.leafNode(SAME_LEAF_QNAME, byteValue);
+        assertFalse(byteLeafNode.equals(leafNode2));
+        assertFalse(leafNode2.equals(byteLeafNode));
+    }
+
+    @Test
+    // We're testing equals()
+    @SuppressWarnings({"EqualsWithItself", "EqualsBetweenInconvertibleTypes"})
+    public void equalsOtherTypesTest() {
+
+        char[] valueChar = "test".toCharArray();
+        char[] equalValueChar = "test".toCharArray();
+
+        LeafNode<char[]> leafNodeChar = ImmutableNodes.leafNode(LEAF_QNAME, valueChar);
+        LeafNode<char[]> equalLeafNodeChar = ImmutableNodes.leafNode(SAME_LEAF_QNAME, equalValueChar);
+
+        assertTrue(leafNodeChar.equals(leafNodeChar));
+        assertTrue(leafNodeChar.equals(equalLeafNodeChar));
+        assertTrue(equalLeafNodeChar.equals(leafNodeChar));
+
+        boolean[] value = new boolean[] { true, false };
+        boolean[] equalValue = new boolean[] { true, false };
+
+        LeafNode<boolean[]> leafNode = ImmutableNodes.leafNode(LEAF_QNAME, value);
+        LeafNode<boolean[]> equalLeafNode = ImmutableNodes.leafNode(SAME_LEAF_QNAME, equalValue);
+
+        assertTrue(leafNode.equals(leafNode));
+        assertTrue(leafNode.equals(equalLeafNode));
+        assertTrue(equalLeafNode.equals(leafNode));
+
+        int[] value2 = new int[] { 1, 2 };
+        int[] equalValue2 = new int[] { 1, 2 };
+
+        LeafNode<int[]> leafNode2 = ImmutableNodes.leafNode(LEAF_QNAME, value2);
+        LeafNode<int[]> equalLeafNode2 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, equalValue2);
+
+        assertTrue(leafNode2.equals(leafNode2));
+        assertTrue(leafNode2.equals(equalLeafNode2));
+        assertTrue(equalLeafNode2.equals(leafNode2));
+
+        short[] value3 = new short[] { 1, 2 };
+        short[] equalValue3 = new short[] { 1, 2 };
+
+        LeafNode<short[]> leafNode3 = ImmutableNodes.leafNode(LEAF_QNAME, value3);
+        LeafNode<short[]> equalLeafNode3 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, equalValue3);
+
+        assertTrue(leafNode3.equals(leafNode3));
+        assertTrue(leafNode3.equals(equalLeafNode3));
+        assertTrue(equalLeafNode3.equals(leafNode3));
+
+        long[] value4 = new long[] { 1, 2 };
+        long[] equalValue4 = new long[] { 1, 2 };
+
+        LeafNode<long[]> leafNode4 = ImmutableNodes.leafNode(LEAF_QNAME, value4);
+        LeafNode<long[]> equalLeafNode4 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, equalValue4);
+
+        assertTrue(leafNode4.equals(leafNode4));
+        assertTrue(leafNode4.equals(equalLeafNode4));
+        assertTrue(equalLeafNode4.equals(leafNode4));
+
+        double[] value6 = new double[] { 1, 2 };
+        double[] equalValue6 = new double[] { 1, 2 };
+
+        LeafNode<double[]> leafNode6 = ImmutableNodes.leafNode(LEAF_QNAME, value6);
+        LeafNode<double[]> equalLeafNode6 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, equalValue6);
+
+        assertTrue(leafNode6.equals(leafNode6));
+        assertTrue(leafNode6.equals(equalLeafNode6));
+        assertTrue(equalLeafNode6.equals(leafNode6));
+
+        float[] value5 = new float[] { 1, 2 };
+        float[] equalValue5 = new float[] { 1, 2 };
+
+        LeafNode<float[]> leafNode5 = ImmutableNodes.leafNode(LEAF_QNAME, value5);
+        LeafNode<float[]> equalLeafNode5 = ImmutableNodes.leafNode(SAME_LEAF_QNAME, equalValue5);
+
+        assertTrue(leafNode5.equals(leafNode5));
+        assertTrue(leafNode5.equals(equalLeafNode5));
+        assertTrue(equalLeafNode5.equals(leafNode5));
+
+        assertFalse(leafNode.equals(leafNode5));
+        assertFalse(leafNode2.equals(leafNode5));
+        assertFalse(leafNode3.equals(leafNode5));
+        assertFalse(leafNode4.equals(leafNode5));
+        assertFalse(leafNodeChar.equals(leafNode5));
+        assertFalse(leafNode6.equals(leafNode5));
+
+        assertFalse(leafNode5.equals(leafNode));
+        assertFalse(leafNode5.equals(leafNode2));
+        assertFalse(leafNode5.equals(leafNode3));
+        assertFalse(leafNode5.equals(leafNode4));
+        assertFalse(leafNode5.equals(leafNodeChar));
+        assertFalse(leafNode5.equals(leafNode6));
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractTestModelTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractTestModelTest.java
new file mode 100644 (file)
index 0000000..f79c6a6
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o. 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.tree;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+
+public abstract class AbstractTestModelTest {
+    static EffectiveModelContext SCHEMA_CONTEXT;
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = TestModel.createTestContext();
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug2690Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug2690Test.java
new file mode 100644 (file)
index 0000000..d22a68f
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+public class Bug2690Test extends AbstractTestModelTest {
+    private DataTree inMemoryDataTree;
+
+    @Before
+    public void prepare() {
+        inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
+            SCHEMA_CONTEXT);
+    }
+
+    @Test
+    public void testWriteMerge1() throws DataValidationFailedException {
+        final MapEntryNode fooEntryNode = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
+        final MapEntryNode barEntryNode = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
+        final MapNode mapNode1 = ImmutableNodes.mapNodeBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
+                .withChild(fooEntryNode).build();
+        final MapNode mapNode2 = ImmutableNodes.mapNodeBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
+                .withChild(barEntryNode).build();
+
+        final ContainerNode cont1 = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(mapNode1).build();
+
+        final ContainerNode cont2 = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(mapNode2).build();
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(TestModel.TEST_PATH, cont1);
+        modificationTree.merge(TestModel.TEST_PATH, cont2);
+        commit(modificationTree);
+
+        final DataTreeSnapshot snapshotAfterTx = inMemoryDataTree.takeSnapshot();
+        final DataTreeModification modificationAfterTx = snapshotAfterTx.newModification();
+        final Optional<NormalizedNode<?, ?>> readNode = modificationAfterTx.readNode(TestModel.OUTER_LIST_PATH);
+        assertTrue(readNode.isPresent());
+        assertEquals(2, ((NormalizedNodeContainer<?,?,?>)readNode.get()).getValue().size());
+    }
+
+    @Test
+    public void testDeleteStructuralAndWriteChild() throws DataValidationFailedException {
+        final DataTreeModification modificationTree = setupTestDeleteStructuralAndWriteChild();
+        verifyTestDeleteStructuralAndWriteChild(modificationTree);
+    }
+
+    @Test
+    public void testDeleteStructuralAndWriteChildWithCommit() throws DataValidationFailedException {
+        final DataTreeModification modificationTree = setupTestDeleteStructuralAndWriteChild();
+        commit(modificationTree);
+        verifyTestDeleteStructuralAndWriteChild(inMemoryDataTree.takeSnapshot());
+    }
+
+    private DataTreeModification setupTestDeleteStructuralAndWriteChild() {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.delete(TestModel.NON_PRESENCE_PATH);
+        modificationTree.write(TestModel.NAME_PATH, ImmutableNodes.leafNode(TestModel.NAME_QNAME, "abc"));
+        return modificationTree;
+    }
+
+    private static void verifyTestDeleteStructuralAndWriteChild(final DataTreeSnapshot snapshot) {
+        final Optional<NormalizedNode<?, ?>> readNode = snapshot.readNode(TestModel.NAME_PATH);
+        assertTrue(readNode.isPresent());
+    }
+
+    private void commit(final DataTreeModification modificationTree) throws DataValidationFailedException {
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug3674Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug3674Test.java
new file mode 100644 (file)
index 0000000..cd9c35f
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+/**
+ * BUG-3674: issuing a delete on a non-existent entry must be preserved in
+ *           DataTreeModification, but should appear as UNMODIFIED in the
+ *           resulting DataTreeCandidate.
+ */
+public class Bug3674Test extends AbstractTestModelTest {
+    private DataTree tree;
+
+    @Before
+    public void setUp() throws DataValidationFailedException {
+        tree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL, SCHEMA_CONTEXT);
+
+        // Create the top-level container
+        final DataTreeModification mod = tree.takeSnapshot().newModification();
+        mod.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        mod.ready();
+        tree.commit(tree.prepare(mod));
+    }
+
+    @Test
+    public void testDeleteOfNonExistingNode() throws DataValidationFailedException {
+        final DataTreeModification mod = tree.takeSnapshot().newModification();
+        mod.delete(TestModel.OUTER_LIST_PATH);
+        mod.ready();
+
+        final DataTreeCandidate candidate = tree.prepare(mod);
+        final DataTreeCandidateNode root = candidate.getRootNode();
+        assertEquals(ModificationType.UNMODIFIED, root.getModificationType());
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug4295Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug4295Test.java
new file mode 100644 (file)
index 0000000..bb7528c
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.tree;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.net.URI;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+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.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug4295Test {
+
+    private DataTree inMemoryDataTree;
+    private QName root;
+    private QName subRoot;
+    private QName outerList;
+    private QName innerList;
+    private QName oid;
+    private QName iid;
+    private QName oleaf;
+    private QName ileaf;
+    private QNameModule foo;
+
+    @Before
+    public void init() {
+        foo = QNameModule.create(URI.create("foo"));
+        root = QName.create(foo, "root");
+        subRoot = QName.create(foo, "sub-root");
+        outerList = QName.create(foo, "outer-list");
+        innerList = QName.create(foo, "inner-list");
+        oid = QName.create(foo, "o-id");
+        iid = QName.create(foo, "i-id");
+        oleaf = QName.create(foo, "o");
+        ileaf = QName.create(foo, "i");
+        inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
+            YangParserTestUtils.parseYangResource("/bug-4295/foo.yang"));
+    }
+
+    @Test
+    public void test() throws DataValidationFailedException {
+        firstModification();
+        secondModification(1);
+        secondModification(2);
+        secondModification(3);
+    }
+
+
+    private void firstModification() throws DataValidationFailedException {
+        /*  MERGE */
+        MapNode outerListNode = ImmutableNodes.mapNodeBuilder().withNodeIdentifier(NodeIdentifier.create(outerList))
+                .withChild(createOuterListEntry("1", "o-1"))
+                .withChild(createOuterListEntry("2", "o-2"))
+                .withChild(createOuterListEntry("3", "o-3"))
+                .build();
+        ContainerNode rootContainerNode = createRootContainerBuilder()
+                .withChild(createSubRootContainerBuilder().withChild(outerListNode).build())
+                .build();
+        YangInstanceIdentifier path = YangInstanceIdentifier.of(root);
+        DataTreeModification modification = inMemoryDataTree.takeSnapshot().newModification();
+        modification.merge(path, rootContainerNode);
+
+        /*  WRITE INNER LIST WITH ENTRIES*/
+        MapNode innerListNode = createInnerListBuilder()
+            .withChild(createInnerListEntry("a", "i-a"))
+            .withChild(createInnerListEntry("b", "i-b"))
+            .build();
+        path = YangInstanceIdentifier.of(root).node(subRoot).node(outerList).node(createOuterListEntryPath("2"))
+                .node(innerList);
+        modification.write(path, innerListNode);
+
+        /*  COMMIT */
+        modification.ready();
+        inMemoryDataTree.validate(modification);
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(modification));
+    }
+
+    private void secondModification(final int testScenarioNumber) throws DataValidationFailedException {
+        /*  MERGE */
+        MapNode outerListNode = ImmutableNodes.mapNodeBuilder().withNodeIdentifier(NodeIdentifier.create(outerList))
+                .withChild(createOuterListEntry("3", "o-3"))
+                .withChild(createOuterListEntry("4", "o-4"))
+                .withChild(createOuterListEntry("5", "o-5"))
+                .build();
+
+        ContainerNode rootContainerNode = createRootContainerBuilder()
+                .withChild(createSubRootContainerBuilder().withChild(outerListNode).build())
+                .build();
+
+        YangInstanceIdentifier path = YangInstanceIdentifier.of(root);
+        DataTreeModification modification = inMemoryDataTree.takeSnapshot().newModification();
+        modification.merge(path, rootContainerNode);
+
+        if (testScenarioNumber == 1) {
+            /* WRITE EMPTY INNER LIST */
+            writeEmptyInnerList(modification, "2");
+        } else if (testScenarioNumber == 2) {
+            /* WRITE INNER LIST ENTRY */
+            MapEntryNode innerListEntryA = createInnerListEntry("a", "i-a-2");
+            path = YangInstanceIdentifier.of(root).node(subRoot).node(outerList).node(createOuterListEntryPath("2"))
+                    .node(innerList).node(createInnerListEntryPath("a"));
+            modification.write(path, innerListEntryA);
+        } else if (testScenarioNumber == 3) {
+            /* WRITE INNER LIST WITH ENTRIES */
+            MapNode innerListNode = createInnerListBuilder().withChild(createInnerListEntry("a", "i-a-3"))
+                    .withChild(createInnerListEntry("c", "i-c")).build();
+            path = YangInstanceIdentifier.of(root).node(subRoot).node(outerList).node(createOuterListEntryPath("2"))
+                    .node(innerList);
+            modification.write(path, innerListNode);
+        }
+
+        /*  COMMIT */
+        modification.ready();
+        inMemoryDataTree.validate(modification);
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(modification));
+    }
+
+    private void writeEmptyInnerList(final DataTreeModification modification, final String outerListEntryKey) {
+        YangInstanceIdentifier path = YangInstanceIdentifier.of(root).node(subRoot).node(outerList)
+                .node(createOuterListEntryPath(outerListEntryKey)).node(innerList);
+        modification.write(path, createInnerListBuilder().build());
+    }
+
+    private DataContainerNodeBuilder<NodeIdentifier, ContainerNode> createRootContainerBuilder() {
+        return ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(root));
+    }
+
+    private DataContainerNodeBuilder<NodeIdentifier, ContainerNode> createSubRootContainerBuilder() {
+        return ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(subRoot));
+    }
+
+    private CollectionNodeBuilder<MapEntryNode, MapNode> createInnerListBuilder() {
+        return ImmutableNodes.mapNodeBuilder().withNodeIdentifier(NodeIdentifier.create(innerList));
+    }
+
+    private NodeIdentifierWithPredicates createInnerListEntryPath(final String keyValue) {
+        Builder<QName, Object> builder = ImmutableMap.builder();
+        ImmutableMap<QName, Object> keys = builder.put(iid, keyValue).build();
+        return NodeIdentifierWithPredicates.of(innerList, keys);
+    }
+
+    private NodeIdentifierWithPredicates createOuterListEntryPath(final String keyValue) {
+        Builder<QName, Object> builder = ImmutableMap.builder();
+        ImmutableMap<QName, Object> keys = builder.put(oid, keyValue).build();
+        return NodeIdentifierWithPredicates.of(outerList, keys);
+    }
+
+    private MapEntryNode createOuterListEntry(final String keyValue, final String leafValue) {
+        return ImmutableNodes.mapEntryBuilder(outerList, oid, keyValue)
+                .withChild(ImmutableNodes.leafNode(oleaf, leafValue)).build();
+    }
+
+    private MapEntryNode createInnerListEntry(final String keyValue, final String leafValue) {
+        return ImmutableNodes.mapEntryBuilder(innerList, iid, keyValue)
+                .withChild(ImmutableNodes.leafNode(ileaf, leafValue)).build();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug4454Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug4454Test.java
new file mode 100644 (file)
index 0000000..4241ea6
--- /dev/null
@@ -0,0 +1,448 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.tree;
+
+import static junit.framework.TestCase.assertFalse;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.util.UnmodifiableCollection;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug4454Test {
+
+    private static final QName MASTER_CONTAINER_QNAME = QName
+            .create("urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model", "2015-02-02",
+                    "master-container");
+    private static final QName MIN_MAX_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-list");
+    private static final QName MIN_MAX_LEAF_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-leaf-list");
+    private static final QName MIN_MAX_LIST_QNAME_NO_MINMAX = QName
+            .create(MASTER_CONTAINER_QNAME, "min-max-list-no-minmax");
+    private static final QName MIN_MAX_KEY_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-key-leaf");
+    private static final QName MIN_MAX_VALUE_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-value-leaf");
+    private static final QName PRESENCE_QNAME = QName.create(MASTER_CONTAINER_QNAME, "presence");
+
+    private static final YangInstanceIdentifier MASTER_CONTAINER_PATH = YangInstanceIdentifier
+            .of(MASTER_CONTAINER_QNAME);
+    private static final YangInstanceIdentifier MIN_MAX_LIST_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH)
+            .node(MIN_MAX_LIST_QNAME).build();
+    private static final YangInstanceIdentifier PRESENCE_PATH = YangInstanceIdentifier.of(PRESENCE_QNAME);
+    private static final YangInstanceIdentifier PRESENCE_MIN_MAX_LIST_PATH = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME);
+    private static final YangInstanceIdentifier MIN_MAX_LIST_NO_MINMAX_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH)
+            .node(MIN_MAX_LIST_QNAME_NO_MINMAX).build();
+    private static final YangInstanceIdentifier MIN_MAX_LEAF_LIST_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LEAF_LIST_QNAME).build();
+
+    private static final Map<QName, Object> FOO_PREDICATES = ImmutableMap.of(MIN_MAX_KEY_LEAF_QNAME, "foo");
+    private static final Map<QName, Object> BAZ_PREDICATES = ImmutableMap.of(MIN_MAX_KEY_LEAF_QNAME, "baz");
+
+    private final MapEntryNode fooEntryNodeWithValue = ImmutableMapEntryNodeBuilder.create().withNodeIdentifier(
+        NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, FOO_PREDICATES))
+            .withChild(ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "footest")).build();
+    private final MapEntryNode bazEntryNodeWithValue = ImmutableMapEntryNodeBuilder.create().withNodeIdentifier(
+        NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, BAZ_PREDICATES))
+            .withChild(ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "baztest")).build();
+    private final MapEntryNode fooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
+            "foo");
+    private final MapEntryNode barEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
+            "bar");
+    private final MapEntryNode bazEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
+            "baz");
+    private final MapNode mapNodeBazFuzWithNodes = ImmutableNodes.mapNodeBuilder()
+            .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
+            .withChild(bazEntryNode).withChild(bazEntryNodeWithValue).withChild(fooEntryNode)
+            .build();
+    private final MapNode mapNodeFooWithNodes = ImmutableNodes.mapNodeBuilder()
+            .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
+            .withChild(fooEntryNode).withChild(fooEntryNodeWithValue).withChild(barEntryNode).withChild(bazEntryNode)
+            .build();
+    private final MapNode mapNodeBar = ImmutableNodes.mapNodeBuilder()
+            .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
+            .withChild(barEntryNode).build();
+    private final MapNode mapNodeBaz = ImmutableNodes.mapNodeBuilder()
+            .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
+            .withChild(bazEntryNode).build();
+
+    private static SchemaContext schemaContext;
+
+    private DataTree inMemoryDataTree;
+
+    @BeforeClass
+    public static void beforeClass() {
+        schemaContext = YangParserTestUtils.parseYangResource("/bug-4454-test.yang");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        schemaContext = null;
+    }
+
+    @Before
+    public void prepare() throws DataValidationFailedException {
+        inMemoryDataTree =  new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
+            schemaContext);
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+        final DataTreeModification modificationTree = initialDataTreeSnapshot.newModification();
+
+        modificationTree.write(MASTER_CONTAINER_PATH, ImmutableNodes.containerNode(MASTER_CONTAINER_QNAME));
+        modificationTree.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(modificationTree));
+    }
+
+    @Test
+    public void minMaxListDeleteWriteTest() throws DataValidationFailedException {
+        final DataTreeModification modificationTree1 = inMemoryDataTree.takeSnapshot().newModification();
+
+        Map<QName, Object> key = new HashMap<>();
+        key.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
+
+        NodeIdentifierWithPredicates mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , key);
+
+        final YangInstanceIdentifier minMaxLeafFoo = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
+                .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2).build();
+
+        key.clear();
+        key.put(MIN_MAX_KEY_LEAF_QNAME, "NON-EXISTING-LEAF");
+
+        mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, key);
+
+        final YangInstanceIdentifier minMaxLeafNel = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
+                .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2).build();
+
+        final Map<QName, Object> keyTemp = new HashMap<>();
+        keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "baz");
+
+        NodeIdentifierWithPredicates mapEntryPathTest = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , keyTemp);
+
+        final YangInstanceIdentifier pathToBaz = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
+                .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTest).node(MIN_MAX_VALUE_LEAF_QNAME).build();
+
+        keyTemp.clear();
+        keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "bar");
+
+        mapEntryPathTest = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , keyTemp);
+
+        final YangInstanceIdentifier pathToBar = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
+                .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTest).node(MIN_MAX_VALUE_LEAF_QNAME).build();
+
+        keyTemp.clear();
+        keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
+
+        final NodeIdentifierWithPredicates mapEntryPathTestKey = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
+            keyTemp);
+
+        final YangInstanceIdentifier pathToKeyFoo = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
+                .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTestKey).node(MIN_MAX_KEY_LEAF_QNAME).build();
+
+        final LeafNode<String> newNode = ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "test");
+        final LeafNode<String> newNode1 = ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "test1");
+        final LeafNode<String> newNode2 = ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "test2");
+        final LeafNode<String> newNodekey = ImmutableNodes.leafNode(MIN_MAX_KEY_LEAF_QNAME, "foo");
+
+        assertFalse(inMemoryDataTree.toString().contains("list"));
+
+        DataTreeSnapshot snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        Optional<NormalizedNode<?, ?>> minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
+        assertTrue(!minMaxListRead.isPresent());
+
+        modificationTree1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
+        modificationTree1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
+        modificationTree1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
+        modificationTree1.merge(MIN_MAX_LIST_PATH, mapNodeBar);
+        modificationTree1.merge(MIN_MAX_LIST_PATH, mapNodeBaz);
+        modificationTree1.write(pathToKeyFoo, newNodekey);
+        modificationTree1.write(pathToBaz, newNode2);
+        modificationTree1.write(pathToBaz, newNode1);
+        modificationTree1.write(pathToBaz, newNode);
+        modificationTree1.delete(minMaxLeafFoo);
+        modificationTree1.delete(minMaxLeafNel);
+
+        modificationTree1.ready();
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare);
+
+        DataTreeSnapshot test = inMemoryDataTree.takeSnapshot();
+        testLoop(test, "bar", "test");
+
+        DataTreeModification tempMod = test.newModification();
+        tempMod.write(pathToBaz, newNode2);
+        tempMod.write(pathToBaz, newNode1);
+        tempMod.merge(pathToBaz, newNode2);
+        tempMod.write(pathToBaz, newNode1);
+
+        tempMod.ready();
+        inMemoryDataTree.validate(tempMod);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(tempMod);
+        inMemoryDataTree.commit(prepare1);
+
+        DataTreeSnapshot test1 = inMemoryDataTree.takeSnapshot();
+        testLoop(test1, "bar", "test1");
+
+        DataTreeModification tempMod1 = test1.newModification();
+        tempMod1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
+
+        tempMod1.ready();
+        inMemoryDataTree.validate(tempMod1);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(tempMod1);
+        inMemoryDataTree.commit(prepare2);
+
+        DataTreeSnapshot test2 = inMemoryDataTree.takeSnapshot();
+        minMaxListRead = test2.readNode(MIN_MAX_LIST_PATH);
+        assertTrue(minMaxListRead.isPresent());
+        assertTrue(((NormalizedNodeContainer<?, ?, ?>) minMaxListRead.get()).getValue().size() == 3);
+
+        DataTreeModification tempMod2 = test2.newModification();
+        tempMod2.write(MIN_MAX_LIST_PATH, mapNodeBaz);
+        tempMod2.write(pathToBaz, newNode2);
+
+        tempMod2.ready();
+        inMemoryDataTree.validate(tempMod2);
+        final DataTreeCandidate prepare3 = inMemoryDataTree.prepare(tempMod2);
+        inMemoryDataTree.commit(prepare3);
+
+        DataTreeSnapshot test3 = inMemoryDataTree.takeSnapshot();
+        minMaxListRead = test3.readNode(MIN_MAX_LIST_PATH);
+        assertTrue(minMaxListRead.isPresent());
+        assertTrue(((NormalizedNodeContainer<?, ?, ?>) minMaxListRead.get()).getValue().size() == 1);
+        assertTrue(minMaxListRead.get().getValue().toString().contains("test2"));
+
+        DataTreeModification tempMod3 = test3.newModification();
+        tempMod3.merge(MIN_MAX_LIST_PATH, mapNodeBar);
+        tempMod3.merge(pathToBar, newNode1);
+
+        tempMod3.ready();
+        inMemoryDataTree.validate(tempMod3);
+        final DataTreeCandidate prepare4 = inMemoryDataTree.prepare(tempMod3);
+        inMemoryDataTree.commit(prepare4);
+
+        DataTreeSnapshot test4 = inMemoryDataTree.takeSnapshot();
+        testLoop(test4, "test1", "test2");
+    }
+
+    @Test
+    public void minMaxLeafListPass() throws DataValidationFailedException {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        final NodeWithValue<?> barPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "bar");
+        final NodeWithValue<?> gooPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "goo");
+
+        final LeafSetEntryNode<Object> barLeafSetEntry = ImmutableLeafSetEntryNodeBuilder.create()
+                .withNodeIdentifier(barPath)
+                .withValue("bar").build();
+        final LeafSetEntryNode<Object> gooLeafSetEntry = ImmutableLeafSetEntryNodeBuilder.create()
+                .withNodeIdentifier(gooPath)
+                .withValue("goo").build();
+
+        final LeafSetNode<Object> fooLeafSetNode = ImmutableLeafSetNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME))
+                .withChildValue("foo").build();
+
+        modificationTree.write(MIN_MAX_LEAF_LIST_PATH, fooLeafSetNode);
+        modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare1);
+
+        DataTreeSnapshot test1 = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification tempMod1 = test1.newModification();
+        tempMod1.write(MIN_MAX_LEAF_LIST_PATH.node(gooPath), gooLeafSetEntry);
+        tempMod1.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
+        tempMod1.ready();
+
+        inMemoryDataTree.validate(tempMod1);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(tempMod1);
+        inMemoryDataTree.commit(prepare2);
+
+        final DataTreeSnapshot snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        final Optional<NormalizedNode<?, ?>> masterContainer = snapshotAfterCommit.readNode(MASTER_CONTAINER_PATH);
+        assertTrue(masterContainer.isPresent());
+        final Optional<NormalizedNodeContainer<?, ?, ?>> leafList = ((NormalizedNodeContainer) masterContainer.get())
+                .getChild(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME));
+        assertTrue(leafList.isPresent());
+        assertTrue(leafList.get().getValue().size() == 3);
+    }
+
+    @Test
+    public void minMaxListDeleteTest() throws DataValidationFailedException {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+
+        NodeIdentifierWithPredicates mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
+            ImmutableMap.of(MIN_MAX_KEY_LEAF_QNAME, "foo"));
+
+        final YangInstanceIdentifier minMaxLeafFoo = MASTER_CONTAINER_PATH
+                .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
+
+        mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
+            ImmutableMap.of(MIN_MAX_KEY_LEAF_QNAME, "bar"));
+
+        final YangInstanceIdentifier minMaxLeafBar = MASTER_CONTAINER_PATH
+                .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
+
+        mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
+            ImmutableMap.of(MIN_MAX_KEY_LEAF_QNAME, "baz"));
+
+        final YangInstanceIdentifier minMaxLeafBaz = MASTER_CONTAINER_PATH
+                .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
+
+        modificationTree.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
+        modificationTree.merge(MIN_MAX_LIST_PATH, mapNodeBar);
+        modificationTree.merge(MIN_MAX_LIST_PATH, mapNodeBaz);
+        modificationTree.delete(minMaxLeafFoo);
+        modificationTree.delete(minMaxLeafBar);
+        modificationTree.delete(minMaxLeafBaz);
+
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+
+        // Empty list should have disappeared, along with the container, as we are not enforcing root
+        final NormalizedNode<?, ?> data = inMemoryDataTree.takeSnapshot()
+                .readNode(YangInstanceIdentifier.empty()).get();
+        assertTrue(data instanceof ContainerNode);
+        assertEquals(0, ((ContainerNode) data).getValue().size());
+    }
+
+    @Test
+    public void minMaxListDeleteExceptionTest() throws DataValidationFailedException {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        NodeIdentifierWithPredicates mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
+            ImmutableMap.of(MIN_MAX_KEY_LEAF_QNAME, "foo"));
+
+        final YangInstanceIdentifier minMaxLeafFoo = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
+
+        mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
+            ImmutableMap.of(MIN_MAX_KEY_LEAF_QNAME, "bar"));
+
+        final YangInstanceIdentifier minMaxLeafBar = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
+
+        mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
+            ImmutableMap.of(MIN_MAX_KEY_LEAF_QNAME, "baz"));
+
+        final YangInstanceIdentifier minMaxLeafBaz = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
+
+        modificationTree.write(PRESENCE_PATH, ImmutableNodes.containerNode(PRESENCE_QNAME));
+        modificationTree.write(PRESENCE_MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
+        modificationTree.merge(PRESENCE_MIN_MAX_LIST_PATH, mapNodeBar);
+        modificationTree.merge(PRESENCE_MIN_MAX_LIST_PATH, mapNodeBaz);
+        modificationTree.delete(minMaxLeafFoo);
+        modificationTree.delete(minMaxLeafBar);
+        modificationTree.delete(minMaxLeafBaz);
+
+        try {
+            // Unlike minMaxListDeleteTest(), presence container enforces the list to be present
+            modificationTree.ready();
+            fail("Should have failed with IAE");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Node (urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model?"
+                    + "revision=2015-02-02)presence is missing mandatory descendant "
+                    + "/(urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model?"
+                    + "revision=2015-02-02)min-max-list", e.getMessage());
+        }
+    }
+
+    @Test
+    public void minMaxListNoMinMaxDeleteTest() throws DataValidationFailedException {
+        final MapEntryNode fooEntryNoMinMaxNode =
+                ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME_NO_MINMAX, MIN_MAX_KEY_LEAF_QNAME, "foo");
+        final MapNode mapNode1 = ImmutableNodes.mapNodeBuilder()
+                .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME_NO_MINMAX))
+                .withChild(fooEntryNoMinMaxNode).build();
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        Map<QName, Object> key = new HashMap<>();
+        key.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
+
+        NodeIdentifierWithPredicates mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME_NO_MINMAX, key);
+
+        final YangInstanceIdentifier minMaxLeafFoo = MASTER_CONTAINER_PATH
+                .node(MIN_MAX_LIST_QNAME_NO_MINMAX).node(mapEntryPath2);
+
+        key.clear();
+        key.put(MIN_MAX_KEY_LEAF_QNAME, "non-existing-leaf");
+
+        mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME_NO_MINMAX, key);
+
+        YangInstanceIdentifier minMaxLeafNel = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
+                .node(MIN_MAX_LIST_QNAME_NO_MINMAX).node(mapEntryPath2).build();
+
+        modificationTree.write(MIN_MAX_LIST_NO_MINMAX_PATH, mapNode1);
+        modificationTree.delete(minMaxLeafFoo);
+        modificationTree.delete(minMaxLeafNel);
+
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+
+        final DataTreeSnapshot snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        final Optional<NormalizedNode<?, ?>> minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_NO_MINMAX_PATH);
+
+        // Empty list should have disappeared
+        assertFalse(minMaxListRead.isPresent());
+    }
+
+    private static void testLoop(final DataTreeSnapshot snapshot, final String first, final String second) {
+        Optional<NormalizedNode<?, ?>> minMaxListRead = snapshot.readNode(MIN_MAX_LIST_PATH);
+        assertTrue(minMaxListRead.isPresent());
+        assertTrue(((NormalizedNodeContainer<?, ?, ?>) minMaxListRead.get()).getValue().size() == 2);
+        UnmodifiableCollection<?> collectionChildren = (UnmodifiableCollection<?>) minMaxListRead.get().getValue();
+
+        for (Object collectionChild : collectionChildren) {
+            if (collectionChild.toString().contains(first)) {
+                assertTrue(collectionChild.toString().contains(first));
+            } else {
+                assertTrue(collectionChild.toString().contains(second));
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug5830Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug5830Test.java
new file mode 100644 (file)
index 0000000..ce45c4c
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2016 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.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug5830Test {
+    private static final String NS = "foo";
+    private static final String REV = "2016-05-17";
+    private static final QName TASK_CONTAINER = QName.create(NS, REV, "task-container");
+    private static final QName TASK = QName.create(NS, REV, "task");
+    private static final QName TASK_ID = QName.create(NS, REV, "task-id");
+    private static final QName TASK_DATA = QName.create(NS, REV, "task-data");
+    private static final QName OTHER_DATA = QName.create(NS, REV, "other-data");
+    private static final QName MANDATORY_DATA = QName.create(NS, REV, "mandatory-data");
+    private static final QName TASK_MANDATORY_LEAF = QName.create(NS, REV, "task-mandatory-leaf");
+    private static final QName NON_PRESENCE_CONTAINER = QName.create(NS, REV, "non-presence-container");
+    private static final QName NON_PRESENCE_CONTAINER_2 = QName.create(NS, REV, "non-presence-container-2");
+    private static final QName PRESENCE_CONTAINER_2 = QName.create(NS, REV, "presence-container-2");
+    private static final QName MANDATORY_LEAF_2 = QName.create(NS, REV, "mandatory-leaf-2");
+
+    private static DataTree initDataTree(final SchemaContext schemaContext)
+            throws DataValidationFailedException {
+        DataTree inMemoryDataTree = new InMemoryDataTreeFactory().create(
+                DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
+
+        final MapNode taskNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(TASK)).build();
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK), taskNode);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+        return inMemoryDataTree;
+    }
+
+    @Test
+    public void testMandatoryNodes() throws DataValidationFailedException {
+        testPresenceContainer();
+        testNonPresenceContainer();
+        testMultipleContainers();
+    }
+
+    private static void testPresenceContainer() throws DataValidationFailedException {
+        final SchemaContext schemaContext = TestModel.createTestContext("/bug5830/foo-presence.yang");
+        assertNotNull("Schema context must not be null.", schemaContext);
+
+        testContainerIsNotPresent(schemaContext);
+        try {
+            testContainerIsPresent(schemaContext);
+            fail("Should fail due to missing mandatory node under present presence container.");
+        } catch (IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-05-17)task-data is missing mandatory descendant /(foo?revision=2016-05-17)"
+                            + "mandatory-data", e.getMessage());
+        }
+        testMandatoryDataLeafIsPresent(schemaContext);
+    }
+
+    private static void testNonPresenceContainer() throws DataValidationFailedException {
+        final SchemaContext schemaContext = TestModel.createTestContext("/bug5830/foo-non-presence.yang");
+        assertNotNull("Schema context must not be null.", schemaContext);
+
+        try {
+            testContainerIsNotPresent(schemaContext);
+            fail("Should fail due to missing mandatory node.");
+        } catch (IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=123}] is missing mandatory "
+                            + "descendant /(foo?revision=2016-05-17)task-data/mandatory-data", e.getMessage());
+        }
+
+        try {
+            testContainerIsPresent(schemaContext);
+            fail("Should fail due to missing mandatory node.");
+        } catch (IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=123}] is missing mandatory "
+                            + "descendant /(foo?revision=2016-05-17)task-data/mandatory-data", e.getMessage());
+        }
+        testMandatoryDataLeafIsPresent(schemaContext);
+    }
+
+    private static void testMultipleContainers() throws DataValidationFailedException {
+        final SchemaContext schemaContext = TestModel.createTestContext("/bug5830/foo-multiple.yang");
+        assertNotNull("Schema context must not be null.", schemaContext);
+
+        testContainerIsNotPresent(schemaContext);
+
+        try {
+            testContainerIsPresent(schemaContext);
+            fail("Should fail due to missing mandatory node under present presence container.");
+        } catch (IllegalArgumentException e) {
+            assertTrue(e.getMessage().startsWith(
+                    "Node (foo?revision=2016-05-17)task-data is missing mandatory descendant"));
+        }
+
+        try {
+            testMandatoryDataLeafIsPresent(schemaContext);
+            fail("Should fail due to missing mandatory node under present presence container.");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Node (foo?revision=2016-05-17)task-data "
+                    + "is missing mandatory descendant /(foo?revision=2016-05-17)non-presence-container/"
+                    + "non-presence-container-2/mandatory-leaf-2", e.getMessage());
+        }
+
+        testMandatoryLeaf2IsPresent(schemaContext, false);
+
+        try {
+            testMandatoryLeaf2IsPresent(schemaContext, true);
+            fail("Should fail due to missing mandatory node under present presence container.");
+        } catch (IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-05-17)presence-container-2 is missing mandatory "
+                            + "descendant /(foo?revision=2016-05-17)mandatory-leaf-3", e.getMessage());
+        }
+    }
+
+    private static void testContainerIsNotPresent(final SchemaContext schemaContext)
+            throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(schemaContext);
+        final MapEntryNode taskEntryNode = Builders.mapEntryBuilder()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, ImmutableMap.of(TASK_ID, "123")))
+                .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
+                .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data")).build();
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(
+                YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
+                        .node(NodeIdentifierWithPredicates.of(TASK, ImmutableMap.of(TASK_ID, "123"))), taskEntryNode);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    private static void testContainerIsPresent(final SchemaContext schemaContext) throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(schemaContext);
+
+        final MapEntryNode taskEntryNode = Builders.mapEntryBuilder()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, ImmutableMap.of(TASK_ID, "123")))
+                .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
+                .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
+                .withChild(createTaskDataContainer(false)).build();
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(
+                YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
+                        .node(NodeIdentifierWithPredicates.of(TASK, ImmutableMap.of(TASK_ID, "123"))), taskEntryNode);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    private static void testMandatoryDataLeafIsPresent(final SchemaContext schemaContext)
+            throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(schemaContext);
+
+        final MapEntryNode taskEntryNode = Builders.mapEntryBuilder()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, ImmutableMap.of(TASK_ID, "123")))
+                .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
+                .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
+                .withChild(createTaskDataContainer(true)).build();
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(
+                YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
+                        .node(NodeIdentifierWithPredicates.of(TASK, ImmutableMap.of(TASK_ID, "123"))), taskEntryNode);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    private static void testMandatoryLeaf2IsPresent(final SchemaContext schemaContext,
+            final boolean withPresenceContianer) throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(schemaContext);
+
+        final MapEntryNode taskEntryNode = Builders.mapEntryBuilder()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, ImmutableMap.of(TASK_ID, "123")))
+                .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
+                .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
+                .withChild(createTaskDataMultipleContainer(withPresenceContianer)).build();
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(
+                YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
+                        .node(NodeIdentifierWithPredicates.of(TASK, ImmutableMap.of(TASK_ID, "123"))), taskEntryNode);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    private static DataContainerChild<?, ?> createTaskDataContainer(final boolean withMandatoryNode) {
+        DataContainerNodeBuilder<NodeIdentifier, ContainerNode> taskDataBuilder = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TASK_DATA))
+                .withChild(ImmutableNodes.leafNode(OTHER_DATA, "foo"));
+        if (withMandatoryNode) {
+            taskDataBuilder.withChild(ImmutableNodes.leafNode(MANDATORY_DATA, "mandatory-data-value"));
+        }
+        return taskDataBuilder.build();
+    }
+
+    private static DataContainerChild<?, ?> createTaskDataMultipleContainer(final boolean withPresenceContianer) {
+        DataContainerNodeBuilder<NodeIdentifier, ContainerNode> nonPresenceContainerBuilder = Builders
+                .containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(NON_PRESENCE_CONTAINER))
+                .withChild(
+                        Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(NON_PRESENCE_CONTAINER_2))
+                                .withChild(ImmutableNodes.leafNode(MANDATORY_LEAF_2, "mandatory leaf data 2")).build());
+
+        if (withPresenceContianer) {
+            nonPresenceContainerBuilder.withChild(Builders.containerBuilder()
+                    .withNodeIdentifier(new NodeIdentifier(PRESENCE_CONTAINER_2)).build());
+        }
+
+        DataContainerNodeBuilder<NodeIdentifier, ContainerNode> taskDataBuilder = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TASK_DATA))
+                .withChild(ImmutableNodes.leafNode(OTHER_DATA, "foo"));
+        taskDataBuilder.withChild(ImmutableNodes.leafNode(MANDATORY_DATA, "mandatory-data-value"));
+        taskDataBuilder.withChild(nonPresenceContainerBuilder.build());
+
+        return taskDataBuilder.build();
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug5968MergeTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug5968MergeTest.java
new file mode 100644 (file)
index 0000000..0c8b7d0
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) 2016 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.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+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.model.api.SchemaContext;
+
+public class Bug5968MergeTest {
+    private static final String NS = "foo";
+    private static final String REV = "2016-07-28";
+    private static final QName ROOT = QName.create(NS, REV, "root");
+    private static final QName MY_LIST = QName.create(NS, REV, "my-list");
+    private static final QName LIST_ID = QName.create(NS, REV, "list-id");
+    private static final QName MANDATORY_LEAF = QName.create(NS, REV, "mandatory-leaf");
+    private static final QName COMMON_LEAF = QName.create(NS, REV, "common-leaf");
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = TestModel.createTestContext("/bug5968/foo.yang");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    private static DataTree initDataTree(final SchemaContext schemaContext, final boolean withMapNode)
+            throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = new InMemoryDataTreeFactory().create(
+                DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> root = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(ROOT));
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT),
+                withMapNode ? root.withChild(
+                        Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_LIST)).build()).build() : root
+                        .build());
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+
+        return inMemoryDataTree;
+    }
+
+    private static DataTree emptyDataTree(final SchemaContext schemaContext)
+            throws DataValidationFailedException {
+        return new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
+    }
+
+    @Test
+    public void mergeInvalidContainerTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+
+        final MapNode myList = createMap(true);
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> root = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(ROOT)).withChild(myList);
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.merge(YangInstanceIdentifier.of(ROOT), root.build());
+
+        try {
+            modificationTree.ready();
+            inMemoryDataTree.validate(modificationTree);
+            final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+            inMemoryDataTree.commit(prepare);
+            fail("Should fail due to missing mandatory leaf.");
+        } catch (final IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-07-28)my-list[{(foo?revision=2016-07-28)list-id=1}] is missing mandatory "
+                            + "descendant /(foo?revision=2016-07-28)mandatory-leaf", e.getMessage());
+        }
+    }
+
+    @Test
+    public void mergeInvalidMapTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        mergeMap(modificationTree, true);
+
+        try {
+            modificationTree.ready();
+            inMemoryDataTree.validate(modificationTree);
+            final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+            inMemoryDataTree.commit(prepare);
+            fail("Should fail due to missing mandatory leaf.");
+        } catch (final IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-07-28)my-list[{(foo?revision=2016-07-28)list-id=1}] is missing mandatory "
+                            + "descendant /(foo?revision=2016-07-28)mandatory-leaf", e.getMessage());
+        }
+    }
+
+    @Test
+    public void mergeInvalidMapEntryTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(SCHEMA_CONTEXT, true);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        mergeMapEntry(modificationTree, "1", null, "common-value");
+
+        try {
+            modificationTree.ready();
+            inMemoryDataTree.validate(modificationTree);
+            final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+            inMemoryDataTree.commit(prepare);
+            fail("Should fail due to missing mandatory leaf.");
+        } catch (final IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-07-28)my-list[{(foo?revision=2016-07-28)list-id=1}] is missing mandatory "
+                            + "descendant /(foo?revision=2016-07-28)mandatory-leaf", e.getMessage());
+        }
+    }
+
+    private static void mergeMap(final DataTreeModification modificationTree,
+            final boolean mandatoryDataMissing) throws DataValidationFailedException {
+        final MapNode myList = createMap(mandatoryDataMissing);
+        modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), myList);
+    }
+
+    private static MapNode createMap(final boolean mandatoryDataMissing) throws DataValidationFailedException {
+        return Builders
+                .mapBuilder()
+                .withNodeIdentifier(new NodeIdentifier(MY_LIST))
+                .withChild(
+                        mandatoryDataMissing ? createMapEntry("1", "common-value") : createMapEntry("1",
+                                "mandatory-value", "common-value")).build();
+    }
+
+    private static void mergeMapEntry(final DataTreeModification modificationTree, final Object listIdValue,
+            final Object mandatoryLeafValue, final Object commonLeafValue) throws DataValidationFailedException {
+        final MapEntryNode taskEntryNode = mandatoryLeafValue == null ? createMapEntry(listIdValue, commonLeafValue)
+                : createMapEntry(listIdValue, mandatoryLeafValue, commonLeafValue);
+
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue))),
+                taskEntryNode);
+    }
+
+    private static MapEntryNode createMapEntry(final Object listIdValue, final Object mandatoryLeafValue,
+            final Object commonLeafValue) throws DataValidationFailedException {
+        return Builders.mapEntryBuilder()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue)))
+                .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue))
+                .withChild(ImmutableNodes.leafNode(MANDATORY_LEAF, mandatoryLeafValue))
+                .withChild(ImmutableNodes.leafNode(COMMON_LEAF, commonLeafValue)).build();
+    }
+
+    private static MapEntryNode createMapEntry(final Object listIdValue, final Object commonLeafValue)
+            throws DataValidationFailedException {
+        return Builders.mapEntryBuilder()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue)))
+                .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue))
+                .withChild(ImmutableNodes.leafNode(COMMON_LEAF, commonLeafValue)).build();
+    }
+
+    private static MapEntryNode createMapEntryM(final Object listIdValue, final Object mandatoryLeafValue)
+            throws DataValidationFailedException {
+        return Builders.mapEntryBuilder()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue)))
+                .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue))
+                .withChild(ImmutableNodes.leafNode(MANDATORY_LEAF, mandatoryLeafValue)).build();
+    }
+
+    @Test
+    public void mergeValidContainerTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+
+        final MapNode myList = createMap(false);
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> root = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(ROOT)).withChild(myList);
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.merge(YangInstanceIdentifier.of(ROOT), root.build());
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test
+    public void mergeValidMapTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        mergeMap(modificationTree, false);
+
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test
+    public void mergeValidMapEntryTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(SCHEMA_CONTEXT, true);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        mergeMapEntry(modificationTree, "1", "mandatory-value", "common-value");
+
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test
+    public void validMultiStepsMergeTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        modificationTree.merge(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
+        modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createEmptyMapEntryBuilder("1").build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createMapEntry("1", "mandatory-value", "common-value"));
+
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test
+    public void invalidMultiStepsMergeTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        modificationTree.merge(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
+        modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createEmptyMapEntryBuilder("1").build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createMapEntry("1", "common-value"));
+
+        try {
+            modificationTree.ready();
+            inMemoryDataTree.validate(modificationTree);
+            final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+            inMemoryDataTree.commit(prepare);
+            fail("Should fail due to missing mandatory leaf.");
+        } catch (final IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-07-28)my-list[{(foo?revision=2016-07-28)list-id=1}] is missing mandatory "
+                            + "descendant /(foo?revision=2016-07-28)mandatory-leaf", e.getMessage());
+        }
+    }
+
+    private static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> createEmptyMapEntryBuilder(
+            final Object listIdValue) throws DataValidationFailedException {
+        return Builders.mapEntryBuilder()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue)))
+                .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue));
+    }
+
+    private static CollectionNodeBuilder<MapEntryNode, MapNode> createMapBuilder() {
+        return Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_LIST));
+    }
+
+    private static DataContainerNodeBuilder<NodeIdentifier, ContainerNode> createContainerBuilder() {
+        return Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(ROOT));
+    }
+
+    @Test
+    public void validMultiStepsWriteAndMergeTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
+        modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createEmptyMapEntryBuilder("1").build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createMapEntry("1", "mandatory-value", "common-value"));
+
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test
+    public void invalidMultiStepsWriteAndMergeTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
+        modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createEmptyMapEntryBuilder("1").build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createMapEntry("1", "common-value"));
+
+        try {
+            modificationTree.ready();
+            inMemoryDataTree.validate(modificationTree);
+            final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+            inMemoryDataTree.commit(prepare);
+            fail("Should fail due to missing mandatory leaf.");
+        } catch (final IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-07-28)my-list[{(foo?revision=2016-07-28)list-id=1}] is missing mandatory "
+                            + "descendant /(foo?revision=2016-07-28)mandatory-leaf", e.getMessage());
+        }
+    }
+
+    @Test
+    public void validMapEntryMultiCommitMergeTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
+        modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createEmptyMapEntryBuilder("1").build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createMapEntryM("1", "mandatory-value"));
+
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+
+        final DataTreeModification modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree2.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createMapEntry("1", "common-value"));
+        modificationTree2.ready();
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+    }
+
+    @Test
+    public void invalidMapEntryMultiCommitMergeTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
+        modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createEmptyMapEntryBuilder("1").build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createMapEntryM("1", "mandatory-value"));
+
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+
+        final DataTreeModification modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree2.write(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createMapEntry("1", "common-value"));
+        modificationTree2.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createMapEntryM("1", "mandatory-value"));
+        modificationTree2.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "2"))),
+                createMapEntry("2", "common-value"));
+        try {
+            modificationTree2.ready();
+            inMemoryDataTree.validate(modificationTree2);
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+            fail("Should fail due to missing mandatory leaf.");
+        } catch (final IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-07-28)my-list[{(foo?revision=2016-07-28)list-id=2}] is missing mandatory "
+                            + "descendant /(foo?revision=2016-07-28)mandatory-leaf", e.getMessage());
+        }
+    }
+
+    /*
+     * This test consists of two transactions (i.e. data tree modifications) on
+     * empty data tree. The first one writes mandatory data and second one
+     * writes common data without any mandatory data.
+     */
+    @Test
+    public void validMapEntryMultiCommitMergeTest2() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        final DataTreeModification modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
+
+        modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
+        modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createEmptyMapEntryBuilder("1").build());
+        modificationTree.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createMapEntryM("1", "mandatory-value"));
+
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+
+        modificationTree2.merge(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
+                createMapEntry("1", "common-value"));
+        modificationTree2.ready();
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug5968Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug5968Test.java
new file mode 100644 (file)
index 0000000..9aa7311
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2016 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.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug5968Test {
+    private static final String NS = "foo";
+    private static final String REV = "2016-07-28";
+    private static final QName ROOT = QName.create(NS, REV, "root");
+    private static final QName MY_LIST = QName.create(NS, REV, "my-list");
+    private static final QName LIST_ID = QName.create(NS, REV, "list-id");
+    private static final QName MANDATORY_LEAF = QName.create(NS, REV, "mandatory-leaf");
+    private static final QName COMMON_LEAF = QName.create(NS, REV, "common-leaf");
+
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = TestModel.createTestContext("/bug5968/foo.yang");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    private static DataTree initDataTree(final SchemaContext schemaContext, final boolean withMapNode)
+            throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = new InMemoryDataTreeFactory().create(
+                DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
+
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> root = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(ROOT));
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(
+                YangInstanceIdentifier.of(ROOT),
+                withMapNode ? root.withChild(
+                        Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_LIST)).build()).build() : root
+                        .build());
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+
+        return inMemoryDataTree;
+    }
+
+    private static DataTree emptyDataTree(final SchemaContext schemaContext)
+            throws DataValidationFailedException {
+        return new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
+    }
+
+    @Test
+    public void writeInvalidContainerTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+
+        final MapNode myList = createMap(true);
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> root = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(ROOT)).withChild(myList);
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(YangInstanceIdentifier.of(ROOT), root.build());
+
+        try {
+            modificationTree.ready();
+            inMemoryDataTree.validate(modificationTree);
+            final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+            inMemoryDataTree.commit(prepare);
+            fail("Should fail due to missing mandatory leaf.");
+        } catch (final IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-07-28)my-list[{(foo?revision=2016-07-28)list-id=1}] is missing mandatory "
+                            + "descendant /(foo?revision=2016-07-28)mandatory-leaf", e.getMessage());
+        }
+    }
+
+    @Test
+    public void writeInvalidMapTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        writeMap(modificationTree, true);
+
+        try {
+            modificationTree.ready();
+            inMemoryDataTree.validate(modificationTree);
+            final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+            inMemoryDataTree.commit(prepare);
+            fail("Should fail due to missing mandatory leaf.");
+        } catch (final IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-07-28)my-list[{(foo?revision=2016-07-28)list-id=1}] is missing mandatory "
+                            + "descendant /(foo?revision=2016-07-28)mandatory-leaf", e.getMessage());
+        }
+    }
+
+    @Test
+    public void writeInvalidMapEntryTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(SCHEMA_CONTEXT, true);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        writeMapEntry(modificationTree, "1", null, "common-value");
+
+        try {
+            modificationTree.ready();
+            inMemoryDataTree.validate(modificationTree);
+            final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+            inMemoryDataTree.commit(prepare);
+            fail("Should fail due to missing mandatory leaf.");
+        } catch (final IllegalArgumentException e) {
+            assertEquals(
+                    "Node (foo?revision=2016-07-28)my-list[{(foo?revision=2016-07-28)list-id=1}] is missing mandatory "
+                            + "descendant /(foo?revision=2016-07-28)mandatory-leaf", e.getMessage());
+        }
+    }
+
+    private static void writeMap(final DataTreeModification modificationTree, final boolean mandatoryDataMissing) {
+        final MapNode myList = createMap(mandatoryDataMissing);
+        modificationTree.write(YangInstanceIdentifier.of(ROOT).node(MY_LIST), myList);
+    }
+
+    private static MapNode createMap(final boolean mandatoryDataMissing) {
+        return Builders
+                .mapBuilder()
+                .withNodeIdentifier(new NodeIdentifier(MY_LIST))
+                .withChild(
+                        mandatoryDataMissing ? createMapEntry("1", "common-value") : createMapEntry("1",
+                                "mandatory-value", "common-value")).build();
+    }
+
+    private static void writeMapEntry(final DataTreeModification modificationTree, final Object listIdValue,
+            final Object mandatoryLeafValue, final Object commonLeafValue) throws DataValidationFailedException {
+        final MapEntryNode taskEntryNode = mandatoryLeafValue == null ? createMapEntry(listIdValue, commonLeafValue)
+                : createMapEntry(listIdValue, mandatoryLeafValue, commonLeafValue);
+
+        modificationTree.write(
+                YangInstanceIdentifier.of(ROOT).node(MY_LIST)
+                        .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue))),
+                taskEntryNode);
+    }
+
+    private static MapEntryNode createMapEntry(final Object listIdValue, final Object mandatoryLeafValue,
+            final Object commonLeafValue) {
+        return Builders.mapEntryBuilder()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue)))
+                .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue))
+                .withChild(ImmutableNodes.leafNode(MANDATORY_LEAF, mandatoryLeafValue))
+                .withChild(ImmutableNodes.leafNode(COMMON_LEAF, commonLeafValue)).build();
+    }
+
+    private static MapEntryNode createMapEntry(final Object listIdValue, final Object commonLeafValue) {
+        return Builders.mapEntryBuilder()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue)))
+                .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue))
+                .withChild(ImmutableNodes.leafNode(COMMON_LEAF, commonLeafValue)).build();
+    }
+
+    @Test
+    public void writeValidContainerTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+
+        final MapNode myList = createMap(false);
+        final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> root = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(ROOT)).withChild(myList);
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(YangInstanceIdentifier.of(ROOT), root.build());
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test
+    public void writeValidMapTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        writeMap(modificationTree, false);
+
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test
+    public void writeValidMapEntryTest() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(SCHEMA_CONTEXT, true);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        writeMapEntry(modificationTree, "1", "mandatory-value", "common-value");
+
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug8291Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Bug8291Test.java
new file mode 100644 (file)
index 0000000..b4b31c0
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2017 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.tree;
+
+import static org.junit.Assert.assertNotNull;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug8291Test {
+    private static final String NS = "foo";
+    private static final QName ROOT = QName.create(NS, "root");
+    private static final QName OUTER_LIST = QName.create(NS, "outer-list");
+    private static final QName OUTER_LIST_ID = QName.create(NS, "id");
+    private static final QName INNER_LIST = QName.create(NS, "inner-list");
+    private SchemaContext schemaContext;
+
+    @Before
+    public void init() {
+        this.schemaContext = TestModel.createTestContext("/bug8291/foo.yang");
+        assertNotNull("Schema context must not be null.", this.schemaContext);
+    }
+
+    private static DataTree initDataTree(final SchemaContext schemaContext)
+            throws DataValidationFailedException {
+        final DataTreeConfiguration config = new DataTreeConfiguration.Builder(TreeType.CONFIGURATION).setRootPath(
+                YangInstanceIdentifier.of(ROOT).node(OUTER_LIST)).build();
+        return new InMemoryDataTreeFactory().create(config, schemaContext);
+    }
+
+    @Test
+    public void test() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(schemaContext);
+        writeOuterListMapEntry(inMemoryDataTree);
+        writeInnerList(inMemoryDataTree);
+    }
+
+    private static void writeInnerList(final DataTree inMemoryDataTree) throws DataValidationFailedException {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(
+                YangInstanceIdentifier.create(
+                        NodeIdentifierWithPredicates.of(OUTER_LIST, ImmutableMap.of(OUTER_LIST_ID, 1))).node(
+                        INNER_LIST), Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(INNER_LIST)).build());
+
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    private static void writeOuterListMapEntry(final DataTree inMemoryDataTree)
+            throws DataValidationFailedException {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        final MapEntryNode outerListMapEntry = Builders.mapEntryBuilder()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(OUTER_LIST, ImmutableMap.of(OUTER_LIST_ID, 1)))
+                .withChild(ImmutableNodes.leafNode(OUTER_LIST_ID, 1)).build();
+
+        modificationTree.write(YangInstanceIdentifier.create(NodeIdentifierWithPredicates.of(OUTER_LIST,
+                ImmutableMap.of(OUTER_LIST_ID, 1))), outerListMapEntry);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseAugmentTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseAugmentTest.java
new file mode 100644 (file)
index 0000000..82cdd36
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2017 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.tree;
+
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+
+import com.google.common.collect.ImmutableSet;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+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.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class CaseAugmentTest {
+    private static final QName CHOICE1_QNAME = QName.create(TestModel.TEST_QNAME, "choice1");
+    private static final QName C1L1_QNAME = QName.create(TestModel.TEST_QNAME, "case1-leaf1");
+    private static final QName C1L2_QNAME = QName.create(TestModel.TEST_QNAME, "case1-leaf2");
+    private static final QName C1L3_QNAME = QName.create(TestModel.TEST_QNAME, "case1-leaf3");
+    private static final QName C2L1_QNAME = QName.create(TestModel.TEST_QNAME, "case2-leaf1");
+    private static final NodeIdentifier CHOICE_ID = new NodeIdentifier(CHOICE1_QNAME);
+    private static final AugmentationIdentifier AUGMENT_ID = new AugmentationIdentifier(
+        ImmutableSet.of(C1L2_QNAME, C1L3_QNAME));
+
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = TestModel.createTestContext("/case-augment-test.yang");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    private static DataTree initDataTree() {
+        DataTree inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION,
+            SCHEMA_CONTEXT);
+        return inMemoryDataTree;
+    }
+
+    @Test
+    public void testWriteAugment() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree();
+
+        AugmentationNode augmentationNode = Builders.augmentationBuilder()
+                .withNodeIdentifier(AUGMENT_ID)
+                .withChild(leafNode(C1L2_QNAME, "leaf-value"))
+                .build();
+
+        final ContainerNode container = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder().withNodeIdentifier(CHOICE_ID)
+                                .withChild(augmentationNode)
+                                .build()).build();
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(TestModel.TEST_PATH, container);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test
+    public void testWriteCase1All() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree();
+
+        AugmentationNode augmentationNode = Builders.augmentationBuilder()
+                .withNodeIdentifier(AUGMENT_ID)
+                .withChild(leafNode(C1L2_QNAME, "leaf-value"))
+                .withChild(leafNode(C1L3_QNAME, "leaf-value"))
+                .build();
+
+        final ContainerNode container = Builders
+                .containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder().withNodeIdentifier(CHOICE_ID)
+                                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case1-leaf1"), "leaf-value"))
+                                .withChild(augmentationNode)
+                                .build()).build();
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(TestModel.TEST_PATH, container);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWriteConflict() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree();
+
+        AugmentationNode augmentationNode = Builders.augmentationBuilder()
+                .withNodeIdentifier(AUGMENT_ID)
+                .withChild(leafNode(C1L2_QNAME, "leaf-value"))
+                .build();
+
+        final ContainerNode container = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder().withNodeIdentifier(CHOICE_ID)
+                                .withChild(augmentationNode)
+                                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf1"), "leaf-value"))
+                                .build()).build();
+
+        try {
+            final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+            modificationTree.write(TestModel.TEST_PATH, container);
+            modificationTree.ready();
+
+            inMemoryDataTree.validate(modificationTree);
+            final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+            inMemoryDataTree.commit(prepare);
+        } catch (IllegalArgumentException e) {
+            assertTrue(e.getMessage().contains("implies non-presence of child"));
+            throw e;
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseExclusionTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseExclusionTest.java
new file mode 100644 (file)
index 0000000..4ac26ac
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class CaseExclusionTest {
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    private DataTree inMemoryDataTree;
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = TestModel.createTestContext("/case-exclusion-test.yang");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    @Before
+    public void before() {
+        inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION,
+            SCHEMA_CONTEXT);
+    }
+
+    @Test
+    public void testCorrectCaseWrite() throws DataValidationFailedException {
+        final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+
+        final ContainerNode container = Builders
+                .containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder().withNodeIdentifier(choice1Id)
+                                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case1-leaf1"), "leaf-value"))
+                                .build()).build();
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(TestModel.TEST_PATH, container);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCaseExclusion() throws DataValidationFailedException {
+        final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+
+        final ContainerNode container = Builders
+                .containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder()
+                                .withNodeIdentifier(choice1Id)
+                                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case1-leaf1"), "leaf-value"))
+                                .withChild(
+                                        ImmutableNodes.containerNode(QName.create(TestModel.TEST_QNAME, "case2-cont")))
+                                .build()).build();
+        try {
+            final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+            modificationTree.write(TestModel.TEST_PATH, container);
+            modificationTree.ready();
+
+            inMemoryDataTree.validate(modificationTree);
+            final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+            inMemoryDataTree.commit(prepare);
+        } catch (IllegalArgumentException e) {
+            assertTrue(e.getMessage().contains("implies non-presence of child"));
+            throw e;
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCaseExclusionOnChoiceWrite() throws DataValidationFailedException {
+        // Container write
+        final ContainerNode container = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME)).build();
+
+        final DataTreeModification modificationTree1 = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree1.write(TestModel.TEST_PATH, container);
+        modificationTree1.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        // Choice write
+        final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+        final ChoiceNode choice = Builders.choiceBuilder().withNodeIdentifier(choice1Id)
+                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case1-leaf1"), "leaf-value"))
+                .withChild(ImmutableNodes.containerNode(QName.create(TestModel.TEST_QNAME, "case2-cont"))).build();
+
+        try {
+            final DataTreeModification modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
+            modificationTree2.write(TestModel.TEST_PATH.node(choice1Id), choice);
+            modificationTree2.ready();
+
+            inMemoryDataTree.validate(modificationTree2);
+
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (IllegalArgumentException e) {
+            assertTrue(e.getMessage().contains("implies non-presence of child"));
+            throw e;
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ConcurrentTreeModificationTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ConcurrentTreeModificationTest.java
new file mode 100644 (file)
index 0000000..8643452
--- /dev/null
@@ -0,0 +1,639 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConcurrentTreeModificationTest extends AbstractTestModelTest {
+    private static final Logger LOG = LoggerFactory.getLogger(ConcurrentTreeModificationTest.class);
+
+    private static final Short ONE_ID = 1;
+    private static final Short TWO_ID = 2;
+
+    private static final YangInstanceIdentifier OUTER_LIST_1_PATH = YangInstanceIdentifier.builder(
+        TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID)
+            .build();
+
+    private static final YangInstanceIdentifier OUTER_LIST_2_PATH = YangInstanceIdentifier.builder(
+        TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID)
+            .build();
+
+    private static final MapEntryNode FOO_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID)
+            .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME).build())
+            .build();
+
+    private static final MapEntryNode BAR_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID)
+            .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME).build())
+            .build();
+
+    private DataTree inMemoryDataTree;
+
+
+    @Before
+    public void prepare() {
+        inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
+            SCHEMA_CONTEXT);
+    }
+
+    private static ContainerNode createFooTestContainerNode() {
+        return ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+                                .withChild(FOO_NODE).build()).build();
+    }
+
+    private static ContainerNode createBarTestContainerNode() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+                                .withChild(BAR_NODE).build()).build();
+    }
+
+    private static <T> T assertPresentAndType(final Optional<?> potential, final Class<T> type) {
+        assertNotNull(potential);
+        assertTrue(potential.isPresent());
+        assertTrue(type.isInstance(potential.get()));
+        return type.cast(potential.get());
+    }
+
+    @Test
+    public void writeWrite1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        modificationTree2.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        modificationTree1.ready();
+        modificationTree2.ready();
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+        } catch (final ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - was thrown as expected", ex);
+        }
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final Optional<NormalizedNode<?, ?>> testNodeAfterCommits = modificationTree1.readNode(TestModel.TEST_PATH);
+        assertPresentAndType(testNodeAfterCommits, ContainerNode.class);
+    }
+
+    @Test
+    public void writeMerge1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        modificationTree2.merge(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final Optional<NormalizedNode<?, ?>> testNodeAfterCommits = modificationTree1.readNode(TestModel.TEST_PATH);
+        assertPresentAndType(testNodeAfterCommits, ContainerNode.class);
+    }
+
+    @Test
+    public void writeWriteFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (final ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - was thrown as expected", ex);
+        }
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertFalse(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH).isPresent());
+    }
+
+    @Test
+    public void writeMergeFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void mergeWriteFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (final ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - was thrown as expected", ex);
+        }
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertFalse(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH).isPresent());
+    }
+
+    @Test
+    public void mergeMergeFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void writeWriteFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (final ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException was thrown as expected", ex);
+        }
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertFalse(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH).isPresent());
+    }
+
+    @Test
+    public void writeMergeFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void mergeWriteFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (final ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException was thrown as expected", ex);
+        }
+
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertFalse(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH).isPresent());
+    }
+
+    @Test
+    public void mergeMergeFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void deleteWriteFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.delete(TestModel.TEST_PATH);
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (final ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException was thrown as expected", ex);
+        }
+
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertFalse(snapshotAfterCommits.readNode(TestModel.TEST_PATH).isPresent());
+    }
+
+    @Test
+    public void deleteMergeFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.delete(TestModel.TEST_PATH);
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void writeWriteFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+            .build());
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.write(OUTER_LIST_1_PATH, FOO_NODE);
+        modificationTree2.write(OUTER_LIST_2_PATH, BAR_NODE);
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void writeMergeFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+            .build());
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.write(OUTER_LIST_1_PATH, FOO_NODE);
+        modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void mergeWriteFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+            .build());
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.merge(OUTER_LIST_1_PATH, FOO_NODE);
+        modificationTree2.write(OUTER_LIST_2_PATH, BAR_NODE);
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void mergeMergeFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+            .build());
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.merge(OUTER_LIST_1_PATH, FOO_NODE);
+        modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void deleteWriteFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+            .build());
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.delete(TestModel.TEST_PATH);
+        modificationTree2.write(OUTER_LIST_2_PATH, BAR_NODE);
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+            fail("Exception should have been thrown");
+        } catch (final ConflictingModificationAppliedException e) {
+            LOG.debug("Exception was thrown because path no longer exist in tree", e);
+        }
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertFalse(snapshotAfterCommits.readNode(TestModel.TEST_PATH).isPresent());
+    }
+
+    @Test
+    public void deleteMergeFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+            .build());
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = initialDataTreeSnapshot.newModification();
+        final DataTreeModification modificationTree2 = initialDataTreeSnapshot.newModification();
+
+        modificationTree1.delete(TestModel.TEST_PATH);
+        modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+            fail("Exception should have been thrown");
+        } catch (final ConflictingModificationAppliedException e) {
+            LOG.debug("Exception was thrown because path no longer exist in tree", e);
+        }
+
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertFalse(snapshotAfterCommits.readNode(TestModel.TEST_PATH).isPresent());
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ConfigStatementValidationTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ConfigStatementValidationTest.java
new file mode 100644 (file)
index 0000000..6c099df
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+
+// TODO: expand these tests to catch some more obscure cases
+public class ConfigStatementValidationTest extends AbstractTestModelTest {
+    private static final Short ONE_ID = 1;
+    private static final Short TWO_ID = 2;
+
+    private static final YangInstanceIdentifier OUTER_LIST_1_PATH = YangInstanceIdentifier
+            .builder(TestModel.OUTER_LIST_PATH).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID)
+            .build();
+
+    private static final YangInstanceIdentifier OUTER_LIST_2_PATH = YangInstanceIdentifier
+            .builder(TestModel.OUTER_LIST_PATH).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID)
+            .build();
+
+    private static final MapEntryNode INNER_FOO_ENTRY_NODE = ImmutableNodes.mapEntry(TestModel.INNER_LIST_QNAME,
+            TestModel.NAME_QNAME, "foo");
+
+    private static final MapEntryNode INNER_BAR_ENTRY_NODE = ImmutableNodes
+            .mapEntryBuilder(QName.create(TestModel.TEST_QNAME, "inner-list2"), TestModel.NAME_QNAME, "foo")
+            .withChild(ImmutableNodes.leafNode(TestModel.VALUE_QNAME, "value")).build();
+
+    private static final MapEntryNode FOO_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID)
+            .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(INNER_FOO_ENTRY_NODE)
+                    .build())
+            .build();
+
+    private static final MapEntryNode BAR_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID)
+            .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(INNER_BAR_ENTRY_NODE)
+                    .build())
+            .build();
+
+    private static ContainerNode createFooTestContainerNode() {
+        return ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(mapNodeBuilder(TestModel.OUTER_LIST_QNAME).withChild(FOO_NODE).build()).build();
+    }
+
+    private static ContainerNode createBarTestContainerNode() {
+        return ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(mapNodeBuilder(TestModel.OUTER_LIST_QNAME).withChild(BAR_NODE).build()).build();
+    }
+
+    @Test(expected = SchemaValidationFailedException.class)
+    public void testOnPathFail() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = new InMemoryDataTreeFactory().create(
+            DataTreeConfiguration.DEFAULT_CONFIGURATION, SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        final YangInstanceIdentifier ii = OUTER_LIST_1_PATH.node(
+                new YangInstanceIdentifier.NodeIdentifier(TestModel.INNER_LIST_QNAME)).node(
+                INNER_FOO_ENTRY_NODE.getIdentifier());
+        modificationTree.write(ii, INNER_FOO_ENTRY_NODE);
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test(expected = SchemaValidationFailedException.class)
+    public void testOnDataFail() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = new InMemoryDataTreeFactory().create(
+            DataTreeConfiguration.DEFAULT_CONFIGURATION, SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test(expected = SchemaValidationFailedException.class)
+    public void testOnDataLeafFail() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = new InMemoryDataTreeFactory().create(
+            DataTreeConfiguration.DEFAULT_CONFIGURATION, SCHEMA_CONTEXT);
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test(expected = SchemaValidationFailedException.class)
+    public void testOnPathCaseLeafFail() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = new InMemoryDataTreeFactory().create(
+            DataTreeConfiguration.DEFAULT_CONFIGURATION, SCHEMA_CONTEXT);
+        final YangInstanceIdentifier.NodeIdentifier choice1Id = new YangInstanceIdentifier.NodeIdentifier(QName.create(
+                TestModel.TEST_QNAME, "choice1"));
+        final YangInstanceIdentifier.NodeIdentifier case2ContId = new YangInstanceIdentifier.NodeIdentifier(
+                QName.create(TestModel.TEST_QNAME, "case2-cont"));
+        final YangInstanceIdentifier ii = TestModel.TEST_PATH.node(choice1Id).node(case2ContId);
+        final ContainerNode case2Cont = Builders.containerBuilder().withNodeIdentifier(case2ContId)
+                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf1"), "leaf-value")).build();
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(ii, case2Cont);
+        modificationTree.ready();
+    }
+
+    @Test(expected = SchemaValidationFailedException.class)
+    public void testOnDataCaseLeafFail() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = new InMemoryDataTreeFactory().create(
+            DataTreeConfiguration.DEFAULT_CONFIGURATION, SCHEMA_CONTEXT);
+        final YangInstanceIdentifier.NodeIdentifier choice1Id = new YangInstanceIdentifier.NodeIdentifier(QName.create(
+                TestModel.TEST_QNAME, "choice1"));
+        final YangInstanceIdentifier ii = TestModel.TEST_PATH.node(choice1Id);
+        final ChoiceNode choice1 = Builders.choiceBuilder().withNodeIdentifier(choice1Id)
+                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case1-leaf1"), "leaf-value")).build();
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(ii, choice1);
+
+        modificationTree.ready();
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataTreeCandidatesTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataTreeCandidatesTest.java
new file mode 100644 (file)
index 0000000..669adee
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2016 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.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Collection;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModificationCursor;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DataTreeCandidatesTest extends AbstractTestModelTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DataTreeCandidatesTest.class);
+
+    private DataTree dataTree;
+
+    @Before
+    public void setUp() throws Exception {
+        dataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL, SCHEMA_CONTEXT);
+
+        final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(ImmutableContainerNodeBuilder.create()
+                        .withNodeIdentifier(new NodeIdentifier(SchemaContext.NAME))
+                        .build())
+                .build();
+
+        final InMemoryDataTreeModification modification = (InMemoryDataTreeModification) dataTree.takeSnapshot()
+                .newModification();
+        final DataTreeModificationCursor cursor = modification.openCursor();
+        cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
+        modification.ready();
+
+        dataTree.validate(modification);
+        final DataTreeCandidate candidate = dataTree.prepare(modification);
+        dataTree.commit(candidate);
+    }
+
+    @Test
+    public void testRootedCandidate() throws DataValidationFailedException {
+        final DataTree innerDataTree = new InMemoryDataTreeFactory().create(
+            new DataTreeConfiguration.Builder(TreeType.OPERATIONAL)
+            .setMandatoryNodesValidation(true)
+            .setRootPath(TestModel.INNER_CONTAINER_PATH)
+            .setUniqueIndexes(true).build(), SCHEMA_CONTEXT);
+
+        final LeafNode<String> leaf = ImmutableLeafNodeBuilder.<String>create()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.VALUE_QNAME))
+                .withValue("testing-value")
+                .build();
+
+        final DataTreeModification modification = innerDataTree.takeSnapshot().newModification();
+        modification.write(TestModel.VALUE_PATH, leaf);
+
+        modification.ready();
+        dataTree.validate(modification);
+        final DataTreeCandidate candidate = dataTree.prepare(modification);
+        dataTree.commit(candidate);
+
+        final DataTreeModification newModification = dataTree.takeSnapshot().newModification();
+        final DataTreeCandidate newCandidate = DataTreeCandidates.newDataTreeCandidate(TestModel.INNER_CONTAINER_PATH,
+            candidate.getRootNode());
+
+        try {
+            // lets see if getting the identifier of the root node throws an exception
+            newCandidate.getRootNode().getIdentifier();
+            fail();
+        } catch (IllegalStateException e) {
+            LOG.debug("Cannot get identifier of root node candidate which is correct", e);
+        }
+
+        // lets see if we can apply this rooted candidate to a new dataTree
+        DataTreeCandidates.applyToModification(newModification,
+                newCandidate);
+
+        final LeafNode<?> readLeaf = (LeafNode<?>) newModification.readNode(TestModel.INNER_VALUE_PATH).get();
+        assertEquals(readLeaf, leaf);
+    }
+
+    @Test
+    public void testEmptyMergeOnContainer() throws DataValidationFailedException {
+        DataTreeModification modification = dataTree.takeSnapshot().newModification();
+        modification.merge(TestModel.NON_PRESENCE_PATH, ImmutableNodes.containerNode(TestModel.NON_PRESENCE_QNAME));
+        modification.ready();
+        dataTree.validate(modification);
+
+        // The entire transaction needs to fizzle to a no-op
+        DataTreeCandidate candidate = dataTree.prepare(modification);
+        DataTreeCandidateNode node = candidate.getRootNode();
+        assertEquals(ModificationType.UNMODIFIED, node.getModificationType());
+
+        // 'test'
+        assertUnmodified(1, node.getChildNodes());
+    }
+
+    @Test
+    public void testEmptyWriteOnContainer() throws DataValidationFailedException {
+        DataTreeModification modification = dataTree.takeSnapshot().newModification();
+        modification.write(TestModel.NON_PRESENCE_PATH, ImmutableNodes.containerNode(TestModel.NON_PRESENCE_QNAME));
+        modification.ready();
+        dataTree.validate(modification);
+
+        // The entire transaction needs to fizzle to a no-op
+        DataTreeCandidate candidate = dataTree.prepare(modification);
+        DataTreeCandidateNode node = candidate.getRootNode();
+        assertEquals(ModificationType.UNMODIFIED, node.getModificationType());
+
+        // 'test'
+        assertUnmodified(1, node.getChildNodes());
+    }
+
+    @Test
+    public void testEmptyMergesOnDeleted() throws DataValidationFailedException {
+        DataTreeModification modification = dataTree.takeSnapshot().newModification();
+        modification.delete(TestModel.NON_PRESENCE_PATH);
+        modification.merge(TestModel.DEEP_CHOICE_PATH, ImmutableNodes.choiceNode(TestModel.DEEP_CHOICE_QNAME));
+        modification.ready();
+        dataTree.validate(modification);
+
+        final DataTreeCandidate candidate = dataTree.prepare(modification);
+        assertEquals(YangInstanceIdentifier.empty(), candidate.getRootPath());
+        final DataTreeCandidateNode node = candidate.getRootNode();
+        assertEquals(ModificationType.UNMODIFIED, node.getModificationType());
+
+        // 'test'
+        assertUnmodified(1, node.getChildNodes());
+    }
+
+    @Test
+    public void testEmptyMergesOnExisting() throws DataValidationFailedException {
+        // Make sure 'non-presence' is present
+        DataTreeModification modification = dataTree.takeSnapshot().newModification();
+        modification.write(TestModel.NAME_PATH, ImmutableNodes.leafNode(TestModel.NAME_QNAME, "foo"));
+        modification.ready();
+        dataTree.validate(modification);
+        dataTree.commit(dataTree.prepare(modification));
+
+        // Issue an empty merge on it and a child choice
+        modification = dataTree.takeSnapshot().newModification();
+        modification.merge(TestModel.NON_PRESENCE_PATH, ImmutableNodes.containerNode(TestModel.NON_PRESENCE_QNAME));
+        modification.merge(TestModel.DEEP_CHOICE_PATH, ImmutableNodes.choiceNode(TestModel.DEEP_CHOICE_QNAME));
+        modification.ready();
+        dataTree.validate(modification);
+
+        // The entire transaction needs to fizzle to a no-op
+        final DataTreeCandidate candidate = dataTree.prepare(modification);
+        assertEquals(YangInstanceIdentifier.empty(), candidate.getRootPath());
+        final DataTreeCandidateNode node = candidate.getRootNode();
+        assertEquals(ModificationType.UNMODIFIED, node.getModificationType());
+
+        // 'non-presence' and 'test'
+        assertUnmodified(2, node.getChildNodes());
+    }
+
+    private static void assertUnmodified(final int expSize, final Collection<DataTreeCandidateNode> nodes) {
+        assertEquals(expSize, nodes.size());
+        nodes.forEach(node -> assertEquals(ModificationType.UNMODIFIED, node.getModificationType()));
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataTreeTransactionTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/DataTreeTransactionTest.java
new file mode 100644 (file)
index 0000000..b2ba0af
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+
+public class DataTreeTransactionTest extends AbstractTestModelTest {
+    private DataTree tree;
+
+    @Before
+    public void setUp() {
+        tree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL, SCHEMA_CONTEXT);
+    }
+
+    @Test
+    public void testSealedValidate() throws DataValidationFailedException {
+        final DataTreeModification mod = tree.takeSnapshot().newModification();
+        mod.ready();
+        tree.validate(mod);
+    }
+
+    @Test
+    public void testSealedPrepare() throws DataValidationFailedException {
+        final DataTreeModification mod = tree.takeSnapshot().newModification();
+        mod.ready();
+        tree.prepare(mod);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testUnsealedValidate() throws DataValidationFailedException {
+        final DataTreeModification mod = tree.takeSnapshot().newModification();
+        tree.validate(mod);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testUnsealedPrepare() throws DataValidationFailedException {
+        final DataTreeModification mod = tree.takeSnapshot().newModification();
+        tree.prepare(mod);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ErrorReportingTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ErrorReportingTest.java
new file mode 100644 (file)
index 0000000..771cdfb
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+public class ErrorReportingTest extends AbstractTestModelTest {
+
+    private DataTree tree;
+
+    @Before
+    public void setup() {
+        tree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL, SCHEMA_CONTEXT);
+    }
+
+    @Test
+    public void writeWithoutParentExisting() {
+        DataTreeModification modification = tree.takeSnapshot().newModification();
+        // We write node without creating parent
+        modification.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+            .build());
+        modification.ready();
+        try {
+            tree.validate(modification);
+            fail("ModifiedNodeDoesNotExistException should be raised");
+        } catch (ModifiedNodeDoesNotExistException e) {
+            assertEquals(TestModel.TEST_PATH, e.getPath());
+        } catch (DataValidationFailedException e) {
+            fail("ModifiedNodeDoesNotExistException expected");
+        }
+    }
+
+    @Test
+    public void parentConcurrentlyDeletedExisting() throws DataValidationFailedException {
+        DataTreeModification initial = tree.takeSnapshot().newModification();
+        // We write node without creating parent
+        initial.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initial.ready();
+        // We commit transaction
+        tree.commit(tree.prepare(initial));
+
+        final DataTreeModification writeTx = tree.takeSnapshot().newModification();
+        final DataTreeModification deleteTx = tree.takeSnapshot().newModification();
+        deleteTx.delete(TestModel.TEST_PATH);
+        deleteTx.ready();
+        // We commit delete modification
+        tree.commit(tree.prepare(deleteTx));
+
+        writeTx.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+        writeTx.ready();
+        try {
+            tree.validate(writeTx);
+            fail("ConflictingModificationAppliedException should be raised");
+        } catch (ConflictingModificationAppliedException e) {
+            assertEquals(TestModel.TEST_PATH, e.getPath());
+        } catch (DataValidationFailedException e) {
+            fail("ConflictingModificationAppliedException expected");
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ListConstraintsValidation.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ListConstraintsValidation.java
new file mode 100644 (file)
index 0000000..86b08d6
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+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.NodeWithValue;
+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.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class ListConstraintsValidation {
+    private static final QName MASTER_CONTAINER_QNAME = QName.create(
+            "urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model", "2015-02-02",
+            "master-container");
+    private static final QName MIN_MAX_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-list");
+    private static final QName MIN_MAX_KEY_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-key-leaf");
+    private static final QName UNBOUNDED_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unbounded-list");
+    private static final QName UNBOUNDED_KEY_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unbounded-key-leaf");
+    private static final QName MIN_MAX_LEAF_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-leaf-list");
+    private static final QName UNBOUNDED_LEAF_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unbounded-leaf-list");
+    private static final QName UNKEYED_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unkeyed-list");
+    private static final QName UNKEYED_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unkeyed-leaf");
+
+    private static final YangInstanceIdentifier MASTER_CONTAINER_PATH = YangInstanceIdentifier
+            .of(MASTER_CONTAINER_QNAME);
+    private static final YangInstanceIdentifier MIN_MAX_LIST_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LIST_QNAME).build();
+    private static final YangInstanceIdentifier UNBOUNDED_LIST_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH).node(UNBOUNDED_LIST_QNAME).build();
+    private static final YangInstanceIdentifier MIN_MAX_LEAF_LIST_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LEAF_LIST_QNAME).build();
+    private static final YangInstanceIdentifier UNBOUNDED_LEAF_LIST_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH).node(UNBOUNDED_LEAF_LIST_QNAME).build();
+    private static final YangInstanceIdentifier UNKEYED_LIST_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH).node(UNKEYED_LIST_QNAME).build();
+
+    private static SchemaContext schemaContext;
+
+    private DataTree inMemoryDataTree;
+
+    @BeforeClass
+    public static void beforeClass() {
+        schemaContext = YangParserTestUtils.parseYangResource("/list-constraints-validation-test-model.yang");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        schemaContext = null;
+    }
+
+    @Before
+    public void prepare() throws DataValidationFailedException {
+        inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
+            schemaContext);
+        final DataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+        final DataTreeModification modificationTree = initialDataTreeSnapshot.newModification();
+
+        modificationTree.write(MASTER_CONTAINER_PATH, ImmutableNodes.containerNode(MASTER_CONTAINER_QNAME));
+        modificationTree.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(modificationTree));
+    }
+
+    @Test
+    public void minMaxListTestPass() throws DataValidationFailedException {
+
+        final MapEntryNode fooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "foo");
+        final MapEntryNode barEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
+        final MapNode mapNode1 = ImmutableNodes.mapNodeBuilder()
+                .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
+                .withChild(fooEntryNode).build();
+        final MapNode mapNode2 = ImmutableNodes.mapNodeBuilder()
+                .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
+                .withChild(barEntryNode).build();
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(MIN_MAX_LIST_PATH, mapNode1);
+        modificationTree.merge(MIN_MAX_LIST_PATH, mapNode2);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+
+        final DataTreeSnapshot snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        final Optional<NormalizedNode<?, ?>> minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
+        assertTrue(minMaxListRead.isPresent());
+        assertTrue(((NormalizedNodeContainer<?, ?, ?>) minMaxListRead.get()).getValue().size() == 2);
+    }
+
+    @Test(expected = DataValidationFailedException.class)
+    public void minMaxListFail() throws DataValidationFailedException {
+        DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        final MapEntryNode fooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "foo");
+        final MapEntryNode barEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
+        final MapEntryNode gooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "goo");
+        final MapNode mapNode = ImmutableNodes.mapNodeBuilder()
+                .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
+                .withChild(fooEntryNode).build();
+
+        final YangInstanceIdentifier fooPath = MIN_MAX_LIST_PATH.node(fooEntryNode.getIdentifier());
+        final YangInstanceIdentifier barPath = MIN_MAX_LIST_PATH.node(barEntryNode.getIdentifier());
+        final YangInstanceIdentifier gooPath = MIN_MAX_LIST_PATH.node(gooEntryNode.getIdentifier());
+
+        modificationTree.write(MIN_MAX_LIST_PATH, mapNode);
+        modificationTree.merge(barPath, barEntryNode);
+        modificationTree.write(gooPath, gooEntryNode);
+        modificationTree.delete(gooPath);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare1);
+
+        DataTreeSnapshot snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        Optional<NormalizedNode<?, ?>> minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
+        assertTrue(minMaxListRead.isPresent());
+        assertTrue(((NormalizedNodeContainer<?, ?, ?>) minMaxListRead.get()).getValue().size() == 2);
+
+        modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(gooPath, gooEntryNode);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        prepare1 = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare1);
+
+        snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
+        assertTrue(minMaxListRead.isPresent());
+        assertTrue(((NormalizedNodeContainer<?, ?, ?>) minMaxListRead.get()).getValue().size() == 3);
+
+        modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        modificationTree.delete(gooPath);
+        modificationTree.delete(fooPath);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+    }
+
+    @Test
+    public void minMaxLeafListPass() throws DataValidationFailedException {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        final NodeWithValue<Object> barPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "bar");
+        final NodeWithValue<Object> gooPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "goo");
+
+        final LeafSetEntryNode<Object> barLeafSetEntry = ImmutableLeafSetEntryNodeBuilder.create()
+                .withNodeIdentifier(barPath)
+                .withValue("bar").build();
+        final LeafSetEntryNode<Object> gooLeafSetEntry = ImmutableLeafSetEntryNodeBuilder.create()
+                .withNodeIdentifier(gooPath)
+                .withValue("goo").build();
+
+        final LeafSetNode<Object> fooLeafSetNode = ImmutableLeafSetNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME))
+                .withChildValue("foo").build();
+
+        modificationTree.write(MIN_MAX_LEAF_LIST_PATH, fooLeafSetNode);
+        modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
+        modificationTree.merge(MIN_MAX_LEAF_LIST_PATH.node(gooPath), gooLeafSetEntry);
+        modificationTree.delete(MIN_MAX_LEAF_LIST_PATH.node(gooPath));
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare1);
+
+        final DataTreeSnapshot snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        final Optional<NormalizedNode<?, ?>> masterContainer = snapshotAfterCommit.readNode(MASTER_CONTAINER_PATH);
+        assertTrue(masterContainer.isPresent());
+        final Optional<NormalizedNodeContainer<?, ?, ?>> leafList = ((NormalizedNodeContainer) masterContainer.get())
+                .getChild(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME));
+        assertTrue(leafList.isPresent());
+        assertTrue(leafList.get().getValue().size() == 2);
+    }
+
+    @Test
+    public void minMaxLeafListFail() {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        final NodeWithValue<Object> barPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "bar");
+        final NodeWithValue<Object> gooPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "goo");
+        final NodeWithValue<Object> fuuPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "fuu");
+
+        final LeafSetEntryNode<Object> barLeafSetEntry = ImmutableLeafSetEntryNodeBuilder.create()
+                .withNodeIdentifier(barPath)
+                .withValue("bar").build();
+        final LeafSetEntryNode<Object> gooLeafSetEntry = ImmutableLeafSetEntryNodeBuilder.create()
+                .withNodeIdentifier(gooPath)
+                .withValue("goo").build();
+        final LeafSetEntryNode<Object> fuuLeafSetEntry = ImmutableLeafSetEntryNodeBuilder.create()
+                .withNodeIdentifier(fuuPath)
+                .withValue("fuu").build();
+
+        final LeafSetNode<Object> fooLeafSetNode = ImmutableLeafSetNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME))
+                .withChildValue("foo").build();
+
+        modificationTree.write(MIN_MAX_LEAF_LIST_PATH, fooLeafSetNode);
+        modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
+        modificationTree.merge(MIN_MAX_LEAF_LIST_PATH.node(gooPath), gooLeafSetEntry);
+        modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(fuuPath), fuuLeafSetEntry);
+
+        try {
+            modificationTree.ready();
+            fail("Should have failed with IAE");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Node (urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model?"
+                    + "revision=2015-02-02)min-max-leaf-list has too many elements (4), can have at most 3",
+                    e.getMessage());
+        }
+    }
+
+    @Test
+    public void unkeyedListTestPass() throws DataValidationFailedException {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        final UnkeyedListEntryNode foo = ImmutableUnkeyedListEntryNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(UNKEYED_LEAF_QNAME))
+                .withChild(ImmutableNodes.leafNode(UNKEYED_LEAF_QNAME, "foo")).build();
+        final List<UnkeyedListEntryNode> unkeyedEntries = new ArrayList<>();
+        unkeyedEntries.add(foo);
+        final UnkeyedListNode unkeyedListNode = ImmutableUnkeyedListNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(UNKEYED_LIST_QNAME))
+                .withValue(unkeyedEntries).build();
+
+        modificationTree.write(MASTER_CONTAINER_PATH, ImmutableNodes.containerNode(MASTER_CONTAINER_QNAME));
+        modificationTree.merge(UNKEYED_LIST_PATH, unkeyedListNode);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare1);
+
+        final DataTreeSnapshot snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        final Optional<NormalizedNode<?, ?>> unkeyedListRead = snapshotAfterCommit.readNode(UNKEYED_LIST_PATH);
+        assertTrue(unkeyedListRead.isPresent());
+        assertTrue(((UnkeyedListNode) unkeyedListRead.get()).getSize() == 1);
+    }
+
+    @Test
+    public void unkeyedListTestFail() {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        final UnkeyedListEntryNode foo = ImmutableUnkeyedListEntryNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(UNKEYED_LEAF_QNAME))
+                .withChild(ImmutableNodes.leafNode(UNKEYED_LEAF_QNAME, "foo")).build();
+        final UnkeyedListEntryNode bar = ImmutableUnkeyedListEntryNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(UNKEYED_LEAF_QNAME))
+                .withChild(ImmutableNodes.leafNode(UNKEYED_LEAF_QNAME, "bar")).build();
+        final List<UnkeyedListEntryNode> unkeyedEntries = new ArrayList<>();
+        unkeyedEntries.add(foo);
+        unkeyedEntries.add(bar);
+        final UnkeyedListNode unkeyedListNode = ImmutableUnkeyedListNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(UNKEYED_LIST_QNAME))
+                .withValue(unkeyedEntries).build();
+
+        modificationTree.write(UNKEYED_LIST_PATH, unkeyedListNode);
+        try {
+            modificationTree.ready();
+            fail("Should have failed with IAE");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Node (urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model?"
+                    + "revision=2015-02-02)unkeyed-list has too many elements (2), can have at most 1", e.getMessage());
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MandatoryLeafTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MandatoryLeafTest.java
new file mode 100644 (file)
index 0000000..943c1de
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class MandatoryLeafTest {
+
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = TestModel.createTestContext("/mandatory-leaf-test.yang");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    private static DataTree initDataTree(final boolean enableValidation) {
+        return new InMemoryDataTreeFactory().create(
+                new DataTreeConfiguration.Builder(TreeType.CONFIGURATION).setMandatoryNodesValidation(enableValidation)
+                        .build(), SCHEMA_CONTEXT);
+    }
+
+    @Test
+    public void testCorrectMandatoryLeafWrite() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(true);
+        final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+
+        final ContainerNode container = Builders
+                .containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder()
+                                .withNodeIdentifier(choice1Id)
+                                .withChild(
+                                        Builders.containerBuilder()
+                                                .withNodeIdentifier(
+                                                        new NodeIdentifier(QName.create(TestModel.TEST_QNAME,
+                                                                "case2-cont")))
+                                                .withChild(
+                                                        leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf1"),
+                                                                "leaf-value"))
+                                                .withChild(
+                                                        leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf2"),
+                                                                "leaf-value2")).build()).build()).build();
+
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(TestModel.TEST_PATH, container);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test
+    public void testCorrectMandatoryLeafChoiceWrite() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(true);
+        // Container write
+        final ContainerNode container = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME)).build();
+
+        final DataTreeModification modificationTree1 = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree1.write(TestModel.TEST_PATH, container);
+        modificationTree1.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        // Choice write
+        final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+        final ChoiceNode choice = Builders
+                .choiceBuilder()
+                .withNodeIdentifier(choice1Id)
+                .withChild(
+                        Builders.containerBuilder()
+                                .withNodeIdentifier(
+                                        new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "case2-cont")))
+                                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf1"), "leaf-value"))
+                                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf2"), "leaf-value2"))
+                                .build()).build();
+
+        final DataTreeModification modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree2.write(TestModel.TEST_PATH.node(choice1Id), choice);
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testMandatoryLeafViolation() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(true);
+        final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+
+        final ContainerNode container = Builders
+                .containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder()
+                                .withNodeIdentifier(choice1Id)
+                                .withChild(
+                                        Builders.containerBuilder()
+                                                .withNodeIdentifier(
+                                                        new NodeIdentifier(QName.create(TestModel.TEST_QNAME,
+                                                                "case2-cont")))
+                                                .withChild(
+                                                        leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf2"),
+                                                                "leaf-value2")).build()).build()).build();
+        try {
+            final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+            modificationTree.write(TestModel.TEST_PATH, container);
+            modificationTree.ready();
+
+            inMemoryDataTree.validate(modificationTree);
+            final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+            inMemoryDataTree.commit(prepare);
+        } catch (final IllegalArgumentException e) {
+            assertEquals("Node (urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test?"
+                    + "revision=2014-03-13)choice1 is missing mandatory descendant /(urn:opendaylight:params:xml:ns:"
+                    + "yang:controller:md:sal:dom:store:test?revision=2014-03-13)case2-cont/case2-leaf1",
+                    e.getMessage());
+            throw e;
+        }
+    }
+
+    @Test
+    public void testDisabledValidation() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(false);
+        final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+
+        final ContainerNode container = Builders
+                .containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder()
+                                .withNodeIdentifier(choice1Id)
+                                .withChild(
+                                        Builders.containerBuilder()
+                                                .withNodeIdentifier(
+                                                        new NodeIdentifier(QName.create(TestModel.TEST_QNAME,
+                                                                "case2-cont")))
+                                                .withChild(
+                                                        leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf2"),
+                                                                "leaf-value2")).build()).build()).build();
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(TestModel.TEST_PATH, container);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testMandatoryLeafViolationChoiceWrite() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(true);
+        // Container write
+        final ContainerNode container = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME)).build();
+
+        final DataTreeModification modificationTree1 = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree1.write(TestModel.TEST_PATH, container);
+        modificationTree1.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        // Choice write
+        final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+        final ChoiceNode choice = Builders
+                .choiceBuilder()
+                .withNodeIdentifier(choice1Id)
+                .withChild(
+                        Builders.containerBuilder()
+                                .withNodeIdentifier(
+                                        new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "case2-cont")))
+                                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf2"), "leaf-value2"))
+                                .build()).build();
+
+        try {
+            final DataTreeModification modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
+            modificationTree2.write(TestModel.TEST_PATH.node(choice1Id), choice);
+            modificationTree2.ready();
+            inMemoryDataTree.validate(modificationTree2);
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (final IllegalArgumentException e) {
+            assertEquals("Node (urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test?"
+                    + "revision=2014-03-13)choice1 is missing mandatory descendant /(urn:opendaylight:params:xml:ns:"
+                    + "yang:controller:md:sal:dom:store:test?revision=2014-03-13)case2-cont/case2-leaf1",
+                    e.getMessage());
+            throw e;
+        }
+    }
+
+    @Test
+    public void testDisabledValidationChoiceWrite() throws DataValidationFailedException {
+        final DataTree inMemoryDataTree = initDataTree(false);
+        // Container write
+        final ContainerNode container = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME)).build();
+
+        final DataTreeModification modificationTree1 = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree1.write(TestModel.TEST_PATH, container);
+        modificationTree1.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        // Choice write
+        final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+        final ChoiceNode choice = Builders
+                .choiceBuilder()
+                .withNodeIdentifier(choice1Id)
+                .withChild(
+                        Builders.containerBuilder()
+                                .withNodeIdentifier(
+                                        new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "case2-cont")))
+                                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf2"), "leaf-value2"))
+                                .build()).build();
+
+        final DataTreeModification modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree2.write(TestModel.TEST_PATH.node(choice1Id), choice);
+        modificationTree2.ready();
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MapEntryRootTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/MapEntryRootTest.java
new file mode 100644 (file)
index 0000000..836e4cf
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o. 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.tree;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+
+public class MapEntryRootTest extends AbstractTestModelTest {
+
+    @Test
+    public void testMapEntryRoot() {
+        final DataTreeConfiguration treeConfig = DataTreeConfiguration.builder(TreeType.OPERATIONAL).setRootPath(
+            TestModel.TEST_PATH.node(TestModel.OUTER_LIST_QNAME).node(
+                NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, (short) 12))).build();
+        final DataTree dataTree = new InMemoryDataTreeFactory().create(treeConfig, SCHEMA_CONTEXT);
+        assertTrue(dataTree instanceof InMemoryDataTree);
+
+        final InMemoryDataTree imdt = (InMemoryDataTree) dataTree;
+        final InMemoryDataTreeModification mod = imdt.takeSnapshot().newModification();
+        final ModificationApplyOperation strategy = mod.getStrategy();
+        assertThat(strategy, instanceOf(ListEntryModificationStrategy.class));
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationMetadataTreeTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationMetadataTreeTest.java
new file mode 100644 (file)
index 0000000..3021952
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNodeFactory;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+
+/*
+ * Schema structure of document is
+ *
+ * container root { 
+ *      list list-a {
+ *              key leaf-a;
+ *              leaf leaf-a;
+ *              choice choice-a {
+ *                      case one {
+ *                              leaf one;
+ *                      }
+ *                      case two-three {
+ *                              leaf two;
+ *                              leaf three;
+ *                      }
+ *              }
+ *              list list-b {
+ *                      key leaf-b;
+ *                      leaf leaf-b;
+ *              }
+ *      }
+ * }
+ */
+public class ModificationMetadataTreeTest extends AbstractTestModelTest {
+
+    private static final Short ONE_ID = 1;
+    private static final Short TWO_ID = 2;
+    private static final String TWO_ONE_NAME = "one";
+    private static final String TWO_TWO_NAME = "two";
+
+    private static final YangInstanceIdentifier OUTER_LIST_1_PATH =
+            YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID)
+            .build();
+
+    private static final YangInstanceIdentifier OUTER_LIST_2_PATH =
+            YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID)
+            .build();
+
+    private static final YangInstanceIdentifier TWO_TWO_PATH = YangInstanceIdentifier.builder(OUTER_LIST_2_PATH)
+            .node(TestModel.INNER_LIST_QNAME)
+            .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, TWO_TWO_NAME)
+            .build();
+
+    private static final YangInstanceIdentifier TWO_TWO_VALUE_PATH = YangInstanceIdentifier.builder(TWO_TWO_PATH)
+            .node(TestModel.VALUE_QNAME)
+            .build();
+
+    private static final MapEntryNode BAR_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID)
+            .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME)
+                    .withChild(mapEntry(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, TWO_ONE_NAME))
+                    .withChild(mapEntry(TestModel.INNER_LIST_QNAME,TestModel.NAME_QNAME, TWO_TWO_NAME))
+                    .build())
+                    .build();
+
+    private RootApplyStrategy rootOper;
+
+    @Before
+    public void prepare() {
+        rootOper = RootApplyStrategy.from(SchemaAwareApplyOperation.from(SCHEMA_CONTEXT,
+            DataTreeConfiguration.DEFAULT_OPERATIONAL));
+    }
+
+    /**
+     * Returns a test document.
+     * <pre>
+     * test
+     *     outer-list
+     *          id 1
+     *     outer-list
+     *          id 2
+     *          inner-list
+     *                  name "one"
+     *          inner-list
+     *                  name "two"
+     *
+     * </pre>
+     *
+     * @return a test document
+     */
+    public NormalizedNode<?, ?> createDocumentOne() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new NodeIdentifier(SCHEMA_CONTEXT.getQName()))
+                .withChild(createTestContainer()).build();
+
+    }
+
+    private static ContainerNode createTestContainer() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+                        .withChild(mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID))
+                        .withChild(BAR_NODE).build()).build();
+    }
+
+    @Test
+    public void basicReadWrites() {
+        final DataTreeModification modificationTree = new InMemoryDataTreeModification(
+            new InMemoryDataTreeSnapshot(SCHEMA_CONTEXT,
+                TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper), rootOper);
+        final Optional<NormalizedNode<?, ?>> originalBarNode = modificationTree.readNode(OUTER_LIST_2_PATH);
+        assertTrue(originalBarNode.isPresent());
+        assertSame(BAR_NODE, originalBarNode.get());
+
+        // writes node to /outer-list/1/inner_list/two/value
+        modificationTree.write(TWO_TWO_VALUE_PATH, ImmutableNodes.leafNode(TestModel.VALUE_QNAME, "test"));
+
+        // reads node to /outer-list/1/inner_list/two/value
+        // and checks if node is already present
+        final Optional<NormalizedNode<?, ?>> barTwoCModified = modificationTree.readNode(TWO_TWO_VALUE_PATH);
+        assertTrue(barTwoCModified.isPresent());
+        assertEquals(ImmutableNodes.leafNode(TestModel.VALUE_QNAME, "test"), barTwoCModified.get());
+
+        // delete node to /outer-list/1/inner_list/two/value
+        modificationTree.delete(TWO_TWO_VALUE_PATH);
+        final Optional<NormalizedNode<?, ?>> barTwoCAfterDelete = modificationTree.readNode(TWO_TWO_VALUE_PATH);
+        assertFalse(barTwoCAfterDelete.isPresent());
+    }
+
+
+    public DataTreeModification createEmptyModificationTree() {
+        /**
+         * Creates empty Snapshot with associated schema context.
+         */
+        final DataTree t = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
+            SCHEMA_CONTEXT);
+
+        /**
+         *
+         * Creates Mutable Data Tree based on provided snapshot and schema
+         * context.
+         *
+         */
+        return t.takeSnapshot().newModification();
+    }
+
+    @Test
+    public void createFromEmptyState() {
+
+        final DataTreeModification modificationTree = createEmptyModificationTree();
+        // Writes empty container node to /test
+        modificationTree.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        // Writes empty list node to /test/outer-list
+        modificationTree.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+            .build());
+
+        // Reads list node from /test/outer-list.
+        final Optional<NormalizedNode<?, ?>> potentialOuterList = modificationTree.readNode(TestModel.OUTER_LIST_PATH);
+        assertFalse(potentialOuterList.isPresent());
+
+        // Reads container node from /test and verifies that it contains test node.
+        final Optional<NormalizedNode<?, ?>> potentialTest = modificationTree.readNode(TestModel.TEST_PATH);
+        assertPresentAndType(potentialTest, ContainerNode.class);
+    }
+
+    @Test
+    public void writeSubtreeReadChildren() {
+        final DataTreeModification modificationTree = createEmptyModificationTree();
+        modificationTree.write(TestModel.TEST_PATH, createTestContainer());
+        final Optional<NormalizedNode<?, ?>> potential = modificationTree.readNode(TWO_TWO_PATH);
+        assertPresentAndType(potential, MapEntryNode.class);
+    }
+
+    @Test
+    public void writeSubtreeDeleteChildren() {
+        final DataTreeModification modificationTree = createEmptyModificationTree();
+        modificationTree.write(TestModel.TEST_PATH, createTestContainer());
+
+        // We verify data are present
+        final Optional<NormalizedNode<?, ?>> potentialBeforeDelete = modificationTree.readNode(TWO_TWO_PATH);
+        assertPresentAndType(potentialBeforeDelete, MapEntryNode.class);
+
+        modificationTree.delete(TWO_TWO_PATH);
+        final Optional<NormalizedNode<?, ?>> potentialAfterDelete = modificationTree.readNode(TWO_TWO_PATH);
+        assertFalse(potentialAfterDelete.isPresent());
+
+    }
+
+    private static <T> T assertPresentAndType(final Optional<?> potential, final Class<T> type) {
+        assertNotNull(potential);
+        assertTrue(potential.isPresent());
+        assertTrue(type.isInstance(potential.get()));
+        return type.cast(potential.get());
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/OrderedListTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/OrderedListTest.java
new file mode 100644 (file)
index 0000000..048f826
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2016 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.tree;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableMap;
+import java.net.URI;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OrderedListTest {
+    private static final Logger LOG = LoggerFactory.getLogger(OrderedListTest.class);
+
+    private DataTree inMemoryDataTree;
+
+    private QNameModule testModule;
+    private QName parentContainer;
+    private QName childContainer;
+    private QName parentOrderedList;
+    private QName childOrderedList;
+    private QName parentKeyLeaf;
+    private QName parentOrdinaryLeaf;
+    private QName childKeyLeaf;
+    private QName childOrdinaryLeaf;
+
+    @Before
+    public void setup() {
+        testModule = QNameModule.create(URI.create("ordered-list-modification-test"));
+        parentContainer = QName.create(testModule, "parent-container");
+        childContainer = QName.create(testModule, "child-container");
+        parentOrderedList = QName.create(testModule, "parent-ordered-list");
+        childOrderedList = QName.create(testModule, "child-ordered-list");
+        parentKeyLeaf = QName.create(testModule, "parent-key-leaf");
+        childKeyLeaf = QName.create(testModule, "child-key-leaf");
+        parentOrdinaryLeaf = QName.create(testModule, "parent-ordinary-leaf");
+        childOrdinaryLeaf = QName.create(testModule, "child-ordinary-leaf");
+        inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
+            YangParserTestUtils.parseYangResource("/ordered-list-modification-test.yang"));
+    }
+
+    @Test
+    public void testsequentialModifications() throws DataValidationFailedException {
+        modification1();
+        modification2();
+        delete1();
+        delete2();
+        modification3();
+        modification4();
+    }
+
+    public void modification1() throws DataValidationFailedException {
+        OrderedMapNode parentOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+                new NodeIdentifier(parentOrderedList))
+                .withChild(createParentOrderedListEntry("pkval1", "plfval1"))
+                .withChild(createParentOrderedListEntry("pkval2", "plfval2"))
+                .withChild(createParentOrderedListEntry("pkval3", "plfval3")).build();
+
+        ContainerNode parentContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(parentContainer)).withChild(Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(childContainer)).withChild(parentOrderedListNode).build())
+                .build();
+
+        YangInstanceIdentifier path1 = YangInstanceIdentifier.of(parentContainer);
+
+        DataTreeModification treeModification = inMemoryDataTree.takeSnapshot().newModification();
+        treeModification.write(path1, parentContainerNode);
+
+        OrderedMapNode childOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+                new NodeIdentifier(childOrderedList))
+                .withChild(createChildOrderedListEntry("chkval1", "chlfval1"))
+                .withChild(createChildOrderedListEntry("chkval2", "chlfval2")).build();
+
+        YangInstanceIdentifier path2 = YangInstanceIdentifier.of(parentContainer).node(childContainer)
+                .node(parentOrderedList).node(createParentOrderedListEntryPath("pkval2")).node(childOrderedList);
+
+        treeModification.write(path2, childOrderedListNode);
+        treeModification.ready();
+        inMemoryDataTree.validate(treeModification);
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification));
+
+        DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path1);
+        assertTrue(readNode.isPresent());
+
+        readNode = snapshotAfterCommits.readNode(path2);
+        assertTrue(readNode.isPresent());
+    }
+
+    public void modification2() throws DataValidationFailedException {
+        OrderedMapNode parentOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+                new NodeIdentifier(parentOrderedList))
+                .withChild(createParentOrderedListEntry("pkval3", "plfval3updated"))
+                .withChild(createParentOrderedListEntry("pkval4", "plfval4"))
+                .withChild(createParentOrderedListEntry("pkval5", "plfval5")).build();
+
+        ContainerNode parentContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(parentContainer)).withChild(Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(childContainer)).withChild(parentOrderedListNode).build())
+                .build();
+
+        DataTreeModification treeModification = inMemoryDataTree.takeSnapshot().newModification();
+
+        YangInstanceIdentifier path1 = YangInstanceIdentifier.of(parentContainer);
+        treeModification.merge(path1, parentContainerNode);
+
+        OrderedMapNode childOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+                new NodeIdentifier(childOrderedList))
+                .withChild(createChildOrderedListEntry("chkval1", "chlfval1updated"))
+                .withChild(createChildOrderedListEntry("chkval2", "chlfval2updated"))
+                .withChild(createChildOrderedListEntry("chkval3", "chlfval3")).build();
+
+        YangInstanceIdentifier path2 = YangInstanceIdentifier.of(parentContainer).node(childContainer)
+                .node(parentOrderedList).node(createParentOrderedListEntryPath("pkval2")).node(childOrderedList);
+        treeModification.merge(path2, childOrderedListNode);
+
+        treeModification.ready();
+        inMemoryDataTree.validate(treeModification);
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification));
+
+        DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path1);
+        assertTrue(readNode.isPresent());
+
+        readNode = snapshotAfterCommits.readNode(path2);
+        assertTrue(readNode.isPresent());
+    }
+
+    public void modification3() throws DataValidationFailedException {
+        OrderedMapNode parentOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+                new NodeIdentifier(parentOrderedList))
+                .withChild(createParentOrderedListEntry("pkval1", "plfval1")).build();
+
+        ContainerNode parentContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(parentContainer)).withChild(Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(childContainer)).withChild(parentOrderedListNode).build())
+                .build();
+
+        YangInstanceIdentifier path1 = YangInstanceIdentifier.of(parentContainer);
+
+        DataTreeModification treeModification = inMemoryDataTree.takeSnapshot().newModification();
+        treeModification.write(path1, parentContainerNode);
+
+        OrderedMapNode childOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+                new NodeIdentifier(childOrderedList))
+                .withChild(createChildOrderedListEntry("chkval1", "chlfval1new")).build();
+
+        YangInstanceIdentifier path2 = YangInstanceIdentifier.of(parentContainer).node(childContainer)
+                .node(parentOrderedList)
+                .node(createParentOrderedListEntryPath("pkval4")).node(childOrderedList);
+
+        treeModification.merge(path2, childOrderedListNode);
+
+        try {
+            treeModification.ready();
+            fail("Exception should have been thrown.");
+            inMemoryDataTree.validate(treeModification);
+            inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification));
+        } catch (final IllegalArgumentException ex) {
+            LOG.debug("IllegalArgumentException was thrown as expected", ex);
+            assertTrue(ex.getMessage().contains("Metadata not available for modification ModifiedNode"));
+        }
+
+        DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path1);
+        assertTrue(readNode.isPresent());
+
+        readNode = snapshotAfterCommits.readNode(path2);
+        assertFalse(readNode.isPresent());
+    }
+
+    public void modification4() throws DataValidationFailedException {
+        DataTreeModification treeModification1 = inMemoryDataTree.takeSnapshot().newModification();
+        DataTreeModification treeModification2 = inMemoryDataTree.takeSnapshot().newModification();
+
+        OrderedMapNode parentOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+            new NodeIdentifier(parentOrderedList)).withChild(createParentOrderedListEntry("pkval1", "plfval1"))
+                .build();
+
+        OrderedMapNode parentOrderedListNode2 = Builders.orderedMapBuilder().withNodeIdentifier(
+            new NodeIdentifier(parentOrderedList)).withChild(createParentOrderedListEntry("pkval2", "plfval2"))
+                .build();
+
+        ContainerNode parentContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(parentContainer)).withChild(Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(childContainer)).withChild(parentOrderedListNode).build())
+                .build();
+
+        ContainerNode parentContainerNode2 = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(parentContainer)).withChild(Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(childContainer)).withChild(parentOrderedListNode2).build())
+                .build();
+
+        YangInstanceIdentifier path = YangInstanceIdentifier.of(parentContainer);
+
+        treeModification1.write(path, parentContainerNode);
+        treeModification2.write(path, parentContainerNode2);
+        treeModification1.ready();
+        treeModification2.ready();
+
+        inMemoryDataTree.validate(treeModification1);
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification1));
+
+        try {
+            inMemoryDataTree.validate(treeModification2);
+            fail("Exception should have been thrown.");
+            inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification2));
+        } catch (ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException was thrown as expected", ex);
+            assertTrue(ex.getMessage().contains("Node was replaced by other transaction"));
+        }
+
+        DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path);
+        assertTrue(readNode.isPresent());
+    }
+
+    public void delete1() throws DataValidationFailedException {
+        YangInstanceIdentifier path = YangInstanceIdentifier.of(parentContainer).node(childContainer)
+                .node(parentOrderedList).node(createParentOrderedListEntryPath("pkval2")).node(childOrderedList)
+                .node(createChildOrderedListEntryPath("chkval1"));
+
+        DataTreeModification treeModification = inMemoryDataTree.takeSnapshot().newModification();
+        treeModification.delete(path);
+        treeModification.ready();
+        inMemoryDataTree.validate(treeModification);
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification));
+
+        DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path);
+        assertFalse(readNode.isPresent());
+    }
+
+    public void delete2() throws DataValidationFailedException {
+        YangInstanceIdentifier path = YangInstanceIdentifier.of(parentContainer).node(childContainer)
+                .node(parentOrderedList).node(createParentOrderedListEntryPath("pkval2"));
+
+        DataTreeModification treeModification = inMemoryDataTree.takeSnapshot().newModification();
+        treeModification.delete(path);
+        treeModification.ready();
+        inMemoryDataTree.validate(treeModification);
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification));
+
+        DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path);
+        assertFalse(readNode.isPresent());
+    }
+
+    private MapEntryNode createParentOrderedListEntry(final String keyValue, final String leafValue) {
+        return Builders.mapEntryBuilder().withNodeIdentifier(NodeIdentifierWithPredicates.of(parentOrderedList,
+                parentKeyLeaf, keyValue))
+                .withChild(Builders.leafBuilder().withNodeIdentifier(NodeIdentifier.create(parentOrdinaryLeaf))
+                    .withValue(leafValue).build()).build();
+    }
+
+    private MapEntryNode createChildOrderedListEntry(final String keyValue, final String leafValue) {
+        return Builders.mapEntryBuilder().withNodeIdentifier(NodeIdentifierWithPredicates.of(childOrderedList,
+                childKeyLeaf, keyValue))
+                .withChild(Builders.leafBuilder().withNodeIdentifier(NodeIdentifier.create(childOrdinaryLeaf))
+                    .withValue(leafValue).build()).build();
+    }
+
+    private NodeIdentifierWithPredicates createParentOrderedListEntryPath(final String keyValue) {
+        ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+        ImmutableMap<QName, Object> keys = builder.put(parentKeyLeaf, keyValue).build();
+        return NodeIdentifierWithPredicates.of(parentOrderedList, keys);
+    }
+
+    private NodeIdentifierWithPredicates createChildOrderedListEntryPath(final String keyValue) {
+        ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+        ImmutableMap<QName, Object> keys = builder.put(childKeyLeaf, keyValue).build();
+        return NodeIdentifierWithPredicates.of(childOrderedList, keys);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/StoreTreeNodesTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/StoreTreeNodesTest.java
new file mode 100644 (file)
index 0000000..2ee3a29
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNodeFactory;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StoreTreeNodesTest extends AbstractTestModelTest {
+    private static final Logger LOG = LoggerFactory.getLogger(StoreTreeNodesTest.class);
+
+    private static final Short ONE_ID = 1;
+    private static final Short TWO_ID = 2;
+    private static final String TWO_ONE_NAME = "one";
+    private static final String TWO_TWO_NAME = "two";
+
+    private static final YangInstanceIdentifier OUTER_LIST_1_PATH = YangInstanceIdentifier.builder(
+        TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID)
+            .build();
+
+    private static final YangInstanceIdentifier OUTER_LIST_2_PATH = YangInstanceIdentifier.builder(
+        TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID)
+            .build();
+
+    private static final YangInstanceIdentifier TWO_TWO_PATH = YangInstanceIdentifier.builder(OUTER_LIST_2_PATH)
+            .node(TestModel.INNER_LIST_QNAME)
+            .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, TWO_TWO_NAME)
+            .build();
+
+    private static final MapEntryNode BAR_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID)
+            .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME)
+                    .withChild(mapEntry(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, TWO_ONE_NAME))
+                    .withChild(mapEntry(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, TWO_TWO_NAME))
+                    .build())
+                    .build();
+
+    private RootApplyStrategy rootOper;
+
+    @Before
+    public void prepare() {
+        rootOper = RootApplyStrategy.from(SchemaAwareApplyOperation.from(SCHEMA_CONTEXT,
+            DataTreeConfiguration.DEFAULT_OPERATIONAL));
+    }
+
+    public NormalizedNode<?, ?> createDocumentOne() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(SCHEMA_CONTEXT.getQName()))
+                .withChild(createTestContainer()).build();
+
+    }
+
+    @Test
+    public void findNodeTestNodeFound() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(SCHEMA_CONTEXT,
+                TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final Optional<? extends TreeNode> node = StoreTreeNodes.findNode(rootNode, OUTER_LIST_1_PATH);
+        assertPresentAndType(node, TreeNode.class);
+    }
+
+    @Test
+    public void findNodeTestNodeNotFound() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(SCHEMA_CONTEXT,
+                TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final YangInstanceIdentifier outerList1InvalidPath = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+                .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3) //
+                .build();
+        final Optional<? extends TreeNode> node = StoreTreeNodes.findNode(rootNode, outerList1InvalidPath);
+        assertFalse(node.isPresent());
+    }
+
+    @Test
+    public void findNodeCheckedTestNodeFound() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(SCHEMA_CONTEXT,
+                TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        TreeNode foundNode = null;
+        try {
+            foundNode = StoreTreeNodes.findNodeChecked(rootNode, OUTER_LIST_1_PATH);
+        } catch (final IllegalArgumentException e) {
+            fail("Illegal argument exception was thrown and should not have been" + e.getMessage());
+        }
+        assertNotNull(foundNode);
+    }
+
+    @Test
+    public void findNodeCheckedTestNodeNotFound() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(SCHEMA_CONTEXT,
+                TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final YangInstanceIdentifier outerList1InvalidPath = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+                .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3) //
+                .build();
+        try {
+            StoreTreeNodes.findNodeChecked(rootNode, outerList1InvalidPath);
+            fail("Illegal argument exception should have been thrown");
+        } catch (final IllegalArgumentException e) {
+            LOG.debug("Illegal argument exception was thrown as expected: '{}' - '{}'", e.getClass(), e.getMessage());
+        }
+    }
+
+    @Test
+    public void findClosestOrFirstMatchTestNodeExists() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(SCHEMA_CONTEXT,
+                TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final Optional<? extends TreeNode> expectedNode = StoreTreeNodes.findNode(rootNode, TWO_TWO_PATH);
+        assertPresentAndType(expectedNode, TreeNode.class);
+
+        final Entry<YangInstanceIdentifier, TreeNode> actualNode = StoreTreeNodes.findClosest(rootNode, TWO_TWO_PATH);
+        assertTreeNodeEquals(expectedNode.get(), actualNode.getValue());
+    }
+
+    @Test
+    public void findClosestOrFirstMatchTestNodeDoesNotExist() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(SCHEMA_CONTEXT,
+                TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final YangInstanceIdentifier outerListInnerListPath = YangInstanceIdentifier.builder(OUTER_LIST_2_PATH)
+                .node(TestModel.INNER_LIST_QNAME)
+                .build();
+        final YangInstanceIdentifier twoTwoInvalidPath = YangInstanceIdentifier.builder(OUTER_LIST_2_PATH)
+                .node(TestModel.INNER_LIST_QNAME)
+                .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "three")
+                .build();
+        final Optional<? extends TreeNode> expectedNode = StoreTreeNodes.findNode(rootNode, outerListInnerListPath);
+        assertPresentAndType(expectedNode, TreeNode.class);
+
+        final Entry<YangInstanceIdentifier, TreeNode> actualNode = StoreTreeNodes.findClosest(rootNode,
+            twoTwoInvalidPath);
+        assertTreeNodeEquals(expectedNode.get(), actualNode.getValue());
+    }
+
+    @Test
+    public void getChildTestChildFound() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(SCHEMA_CONTEXT,
+                TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final Optional<? extends TreeNode> node = StoreTreeNodes.getChild(Optional.ofNullable(rootNode),
+                TestModel.TEST_PATH.getLastPathArgument());
+        assertPresentAndType(node, TreeNode.class);
+    }
+
+    @Test
+    public void getChildTestChildNotFound() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(SCHEMA_CONTEXT,
+                TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final Optional<? extends TreeNode> node = StoreTreeNodes.getChild(Optional.ofNullable(rootNode),
+                TestModel.OUTER_LIST_PATH.getLastPathArgument());
+        assertFalse(node.isPresent());
+    }
+
+    private static ContainerNode createTestContainer() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+                        .withChild(mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID))
+                        .withChild(BAR_NODE).build()).build();
+    }
+
+    private static <T extends TreeNode> T assertPresentAndType(final Optional<? extends TreeNode> potential,
+            final Class<T> type) {
+        assertNotNull(potential);
+        assertTrue(potential.isPresent());
+        assertTrue(type.isInstance(potential.get()));
+        return type.cast(potential.get());
+    }
+
+    private static void assertTreeNodeEquals(final TreeNode expected, final TreeNode actual) {
+        assertEquals(expected.getIdentifier(), actual.getIdentifier());
+        assertEquals(expected.getVersion(), actual.getVersion());
+        assertEquals(expected.getSubtreeVersion(), actual.getSubtreeVersion());
+        assertEquals(expected.getData(), actual.getData());
+        assertEquals(expected.toString(), actual.toString());
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/StructuralApplyModificationTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/StructuralApplyModificationTest.java
new file mode 100644 (file)
index 0000000..e08ff0c
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2016 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.tree;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+public final class StructuralApplyModificationTest extends AbstractTestModelTest {
+    private DataTree inMemoryDataTree;
+
+    @Before
+    public void setUp() {
+        inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION);
+        inMemoryDataTree.setEffectiveModelContext(SCHEMA_CONTEXT);
+    }
+
+    @Test
+    public void testMapNodeParentAutoCreateDelete() throws DataValidationFailedException {
+        final DataTreeModification addListEntryModification = inMemoryDataTree.takeSnapshot().newModification();
+
+        // Prepare root
+        final YangInstanceIdentifier.NodeIdentifier rootContainerId = getNId(TestModel.TEST_QNAME);
+        addListEntryModification.write(YangInstanceIdentifier.create(rootContainerId),
+            Builders.containerBuilder().withNodeIdentifier(rootContainerId).build());
+
+        final NodeIdentifierWithPredicates outerListEntryId = NodeIdentifierWithPredicates.of(
+            TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
+
+        // Write list entry (MapEntryNode) without creating list parent (MapNode)
+        final MapEntryNode outerListEntry = Builders.mapEntryBuilder().withNodeIdentifier(outerListEntryId).build();
+        final YangInstanceIdentifier outerListParentPath = YangInstanceIdentifier.create(getNId(TestModel.TEST_QNAME),
+            getNId(TestModel.OUTER_LIST_QNAME));
+        final YangInstanceIdentifier outerListEntryPath = outerListParentPath.node(outerListEntryId);
+        addListEntryModification.write(outerListEntryPath, outerListEntry);
+
+        addListEntryModification.ready();
+        inMemoryDataTree.validate(addListEntryModification);
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(addListEntryModification));
+
+        // Check list parent auto created
+        assertNodeExistence(outerListParentPath, true);
+
+        // Now delete
+        final DataTreeModification deleteListEntryModification = inMemoryDataTree.takeSnapshot().newModification();
+        deleteListEntryModification.delete(outerListEntryPath);
+        deleteListEntryModification.ready();
+        inMemoryDataTree.validate(deleteListEntryModification);
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(deleteListEntryModification));
+
+        // Check list parent auto deleted
+        assertNodeExistence(outerListParentPath, false);
+    }
+
+    @Test
+    public void testMapNodeDirectEmptyWrite() {
+        final DataTreeModification addListEntryModification = inMemoryDataTree.takeSnapshot().newModification();
+
+        // Prepare root container
+        final YangInstanceIdentifier.NodeIdentifier rootContainerId = getNId(TestModel.TEST_QNAME);
+        addListEntryModification.write(YangInstanceIdentifier.create(rootContainerId),
+            Builders.containerBuilder().withNodeIdentifier(rootContainerId).build());
+
+        final YangInstanceIdentifier outerListParentPath = YangInstanceIdentifier.create(getNId(TestModel.TEST_QNAME),
+            getNId(TestModel.OUTER_LIST_QNAME));
+        addListEntryModification.merge(outerListParentPath, ImmutableNodes.mapNode(TestModel.OUTER_LIST_QNAME));
+
+        // Check empty map node auto deleted
+        assertNodeExistence(outerListParentPath, false);
+    }
+
+    @Test
+    public void testNonPresenceContainerDirectEmptyWrite() throws DataValidationFailedException {
+        final DataTreeModification addListEntryModification = inMemoryDataTree.takeSnapshot().newModification();
+
+        final YangInstanceIdentifier.NodeIdentifier rootContainerId = getNId(TestModel.NON_PRESENCE_QNAME);
+        final YangInstanceIdentifier path = YangInstanceIdentifier.create(rootContainerId);
+        addListEntryModification.write(path, Builders.containerBuilder().withNodeIdentifier(rootContainerId).build());
+
+        addListEntryModification.ready();
+        inMemoryDataTree.validate(addListEntryModification);
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(addListEntryModification));
+
+        // Check empty container auto deleted
+        assertNodeExistence(path, false);
+    }
+
+    @Test
+    public void testNestedStrucutralNodes() throws DataValidationFailedException {
+        final DataTreeModification addListEntryModification = inMemoryDataTree.takeSnapshot().newModification();
+
+        final YangInstanceIdentifier path = TestModel.DEEP_CHOICE_PATH.node(TestModel.A_LIST_QNAME)
+            .node(getNId(TestModel.A_LIST_QNAME, TestModel.A_NAME_QNAME, "1"));
+
+        addListEntryModification.write(path,
+            Builders.mapEntryBuilder()
+                .withNodeIdentifier(getNId(TestModel.A_LIST_QNAME, TestModel.A_NAME_QNAME, "1"))
+                .build());
+
+        addListEntryModification.ready();
+        inMemoryDataTree.validate(addListEntryModification);
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(addListEntryModification));
+
+        // Check parent structure auto created
+        assertNodeExistence(path, true);
+        assertNodeExistence(TestModel.NON_PRESENCE_PATH, true);
+        assertNodeExistence(TestModel.DEEP_CHOICE_PATH, true);
+    }
+
+    private void assertNodeExistence(final YangInstanceIdentifier outerListParentPath, final boolean shouldBePresent) {
+        final DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        final Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(outerListParentPath);
+        assertEquals(readNode.isPresent(), shouldBePresent);
+    }
+
+    private static NodeIdentifier getNId(final QName qname) {
+        return YangInstanceIdentifier.NodeIdentifier.create(qname);
+    }
+
+    private static NodeIdentifierWithPredicates getNId(final QName qname, final QName key, final String val) {
+        return NodeIdentifierWithPredicates.of(qname, key, val);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/TestModel.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/TestModel.java
new file mode 100644 (file)
index 0000000..a1ff511
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015 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.tree;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public final class TestModel {
+
+    public static final QName TEST_QNAME = QName.create(
+            "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13", "test");
+    public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list");
+    public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list");
+    public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice");
+    public static final QName INNER_CONTAINER_QNAME = QName.create(TEST_QNAME, "inner-container");
+    public static final QName ID_QNAME = QName.create(TEST_QNAME, "id");
+    public static final QName NAME_QNAME = QName.create(TEST_QNAME, "name");
+    public static final QName VALUE_QNAME = QName.create(TEST_QNAME, "value");
+    private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
+
+    public static final QName NON_PRESENCE_QNAME = QName.create(TEST_QNAME, "non-presence");
+    public static final QName DEEP_CHOICE_QNAME = QName.create(TEST_QNAME, "deep-choice");
+    public static final QName A_LIST_QNAME = QName.create(TEST_QNAME, "a-list");
+    public static final QName A_NAME_QNAME = QName.create(TEST_QNAME, "a-name");
+
+    public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME);
+    public static final YangInstanceIdentifier OUTER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH)
+            .node(OUTER_LIST_QNAME).build();
+    public static final YangInstanceIdentifier INNER_CONTAINER_PATH = TEST_PATH.node(INNER_CONTAINER_QNAME);
+    public static final YangInstanceIdentifier VALUE_PATH = YangInstanceIdentifier.of(VALUE_QNAME);
+    public static final YangInstanceIdentifier INNER_VALUE_PATH = INNER_CONTAINER_PATH.node(VALUE_QNAME);
+    public static final YangInstanceIdentifier NON_PRESENCE_PATH = YangInstanceIdentifier.of(NON_PRESENCE_QNAME);
+    public static final YangInstanceIdentifier DEEP_CHOICE_PATH = NON_PRESENCE_PATH.node(DEEP_CHOICE_QNAME);
+    public static final YangInstanceIdentifier NAME_PATH = NON_PRESENCE_PATH.node(NAME_QNAME);
+
+    public static final QName TWO_QNAME = QName.create(TEST_QNAME, "two");
+    public static final QName THREE_QNAME = QName.create(TEST_QNAME, "three");
+
+    private TestModel() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static EffectiveModelContext createTestContext() {
+        return createTestContext(DATASTORE_TEST_YANG);
+    }
+
+    public static EffectiveModelContext createTestContext(final String resourcePath) {
+        return YangParserTestUtils.parseYangResources(TestModel.class, resourcePath);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/YT776Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/YT776Test.java
new file mode 100644 (file)
index 0000000..2186464
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.choiceBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.containerBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.leafBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.leafSetBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.mapBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.mapEntryBuilder;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class YT776Test {
+    private static final QName MODULE = QName.create("yt776", "yt776");
+    private static final NodeIdentifier BOX = new NodeIdentifier(QName.create(MODULE, "box"));
+    private static final QName OBJECT = QName.create(MODULE, "object");
+    private static final QName OBJECT_ID = QName.create(MODULE, "object-id");
+    private static final NodeIdentifier OBJECT_LIST = new NodeIdentifier(OBJECT);
+    private static final NodeIdentifierWithPredicates OBJECT_ITEM = NodeIdentifierWithPredicates.of(OBJECT,
+        ImmutableMap.of(OBJECT_ID, "1"));
+    private static final LeafNode<?> OBJECT_ID_LEAF = leafBuilder().withNodeIdentifier(new NodeIdentifier(OBJECT_ID))
+            .withValue("1").build();
+    private static final NodeIdentifier ATTRIBUTES = new NodeIdentifier(QName.create(MODULE, "attributes"));
+
+    private static final QName NESTED = QName.create(MODULE, "nested");
+    private static final QName NESTED_ATTRIBUTE = QName.create(MODULE, "nested-attribute");
+    private static final NodeIdentifier NESTED_LIST = new NodeIdentifier(NESTED);
+    private static final NodeIdentifierWithPredicates NESTED_ITEM = NodeIdentifierWithPredicates.of(NESTED,
+        ImmutableMap.of(NESTED_ATTRIBUTE, "foo"));
+
+    private static final NodeIdentifier ANY_OF = new NodeIdentifier(QName.create(MODULE, "any-of"));
+    private static final QName SOME_LEAF = QName.create(MODULE, "some-leaf");
+    private static final NodeIdentifier SOME_LEAF_ID = new NodeIdentifier(SOME_LEAF);
+    private static final QName SOME_LIST = QName.create(MODULE, "some-list");
+    private static final NodeIdentifier SOME_LIST_ID = new NodeIdentifier(SOME_LIST);
+    private static final NodeIdentifierWithPredicates SOME_LIST_ITEM = NodeIdentifierWithPredicates.of(SOME_LIST,
+                ImmutableMap.of(SOME_LEAF, "foo"));
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    private DataTree dataTree;
+
+    @BeforeClass
+    public static void beforeClass() {
+        SCHEMA_CONTEXT = TestModel.createTestContext("/yt776/yt776.yang");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    @Before
+    public void init() {
+        dataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION, SCHEMA_CONTEXT);
+    }
+
+    @Test
+    public void testNoAttributes() {
+        final DataTreeModification mod = dataTree.takeSnapshot().newModification();
+        mod.write(YangInstanceIdentifier.create(BOX), containerBuilder().withNodeIdentifier(BOX)
+            .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
+                .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
+                    .withChild(OBJECT_ID_LEAF)
+                    .build())
+                .build())
+            .build());
+
+        try {
+            mod.ready();
+            fail("Should fail with IAE");
+        } catch (IllegalArgumentException e) {
+            // FIXME: This is actually mandatory leaf enforcer kicking in: attributes have to be present. This is
+            //        most probably not what we want.
+            assertEquals("Node (yt776)object[{(yt776)object-id=1}] is missing mandatory descendant /(yt776)attributes",
+                e.getMessage());
+        }
+    }
+
+    @Test
+    public void testEmptyAttributes() throws DataValidationFailedException {
+        final DataTreeModification mod = write(containerBuilder().withNodeIdentifier(BOX)
+            .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
+                .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
+                    .withChild(OBJECT_ID_LEAF)
+                    .withChild(leafSetBuilder().withNodeIdentifier(ATTRIBUTES).build())
+                    .build())
+                .build())
+            .build());
+
+        try {
+            mod.ready();
+            fail("Should fail with IAE");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Node (yt776)attributes does not have enough elements (0), needs at least 1", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testOneAttribute() throws DataValidationFailedException {
+        writeAndCommit(containerBuilder().withNodeIdentifier(BOX)
+            .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
+                .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
+                    .withChild(OBJECT_ID_LEAF)
+                    .withChild(leafSetBuilder().withNodeIdentifier(ATTRIBUTES)
+                        .withChildValue("object1")
+                        .build())
+                    .build())
+                .build())
+            .build());
+    }
+
+    @Test
+    public void testTwoAttributes() throws DataValidationFailedException {
+        writeAndCommit(containerBuilder().withNodeIdentifier(BOX)
+            .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
+                .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
+                    .withChild(OBJECT_ID_LEAF)
+                    .withChild(leafSetBuilder().withNodeIdentifier(ATTRIBUTES)
+                        .withChildValue("object1")
+                        .withChildValue("object2")
+                        .build())
+                    .build())
+                .build())
+            .build());
+    }
+
+    @Test
+    public void testThreeAttributes() throws DataValidationFailedException {
+        final DataTreeModification mod = write(containerBuilder().withNodeIdentifier(BOX)
+            .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
+                .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
+                    .withChild(OBJECT_ID_LEAF)
+                    .withChild(leafSetBuilder().withNodeIdentifier(ATTRIBUTES)
+                        .withChildValue("object1")
+                        .withChildValue("object2")
+                        .withChildValue("object3")
+                        .build())
+                    .build())
+                .build())
+            .build());
+
+        try {
+            mod.ready();
+            fail("Should fail with IAE");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Node (yt776)attributes has too many elements (3), can have at most 2", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testEmptyAndMergeOne() throws DataValidationFailedException {
+        final DataTreeModification mod = dataTree.takeSnapshot().newModification();
+        mod.write(YangInstanceIdentifier.create(BOX), containerBuilder().withNodeIdentifier(BOX)
+            .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
+                .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
+                    .withChild(OBJECT_ID_LEAF)
+                    .build())
+                .build())
+            .build());
+        mod.merge(YangInstanceIdentifier.create(BOX), containerBuilder().withNodeIdentifier(BOX)
+            .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
+                .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
+                    .withChild(OBJECT_ID_LEAF)
+                    .withChild(leafSetBuilder().withNodeIdentifier(ATTRIBUTES)
+                        .withChildValue("object1")
+                        .build())
+                    .build())
+                .build())
+            .build());
+
+        commit(mod);
+    }
+
+    @Test
+    public void testEmptyAndMergeOneWithListTouched() throws DataValidationFailedException {
+        final DataTreeModification mod = dataTree.takeSnapshot().newModification();
+        mod.write(YangInstanceIdentifier.create(BOX), containerBuilder().withNodeIdentifier(BOX)
+            .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
+                .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
+                    .withChild(OBJECT_ID_LEAF)
+                    .build())
+                .build())
+            .build());
+        mod.merge(YangInstanceIdentifier.create(BOX), containerBuilder().withNodeIdentifier(BOX)
+            .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
+                .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
+                    .withChild(OBJECT_ID_LEAF)
+                    .withChild(leafSetBuilder().withNodeIdentifier(ATTRIBUTES)
+                        .withChildValue("object1")
+                        .build())
+                    .build())
+                .build())
+            .build());
+
+        mod.delete(YangInstanceIdentifier.create(BOX, OBJECT_LIST, OBJECT_ITEM, NESTED_LIST, NESTED_ITEM));
+
+        commit(mod);
+    }
+
+    @Test
+    public void testDisappearInChoice() throws DataValidationFailedException {
+        DataTreeModification mod = dataTree.takeSnapshot().newModification();
+        // Initialize choice with list
+        mod.write(YangInstanceIdentifier.create(BOX), containerBuilder().withNodeIdentifier(BOX)
+            .withChild(choiceBuilder().withNodeIdentifier(ANY_OF)
+                .withChild(mapBuilder().withNodeIdentifier(SOME_LIST_ID)
+                    .withChild(mapEntryBuilder()
+                        .withNodeIdentifier(SOME_LIST_ITEM)
+                        .withChild(leafBuilder().withNodeIdentifier(SOME_LEAF_ID).withValue("foo").build())
+                        .build())
+                    .build())
+                .build())
+            .build());
+        commit(mod);
+
+        // Now delete the single item, causing the list to fizzle, while creating the alterinative case
+        mod = dataTree.takeSnapshot().newModification();
+        mod.delete(YangInstanceIdentifier.create(BOX, ANY_OF, SOME_LIST_ID, SOME_LIST_ITEM));
+        mod.write(YangInstanceIdentifier.create(BOX, ANY_OF, SOME_LEAF_ID),
+            leafBuilder().withNodeIdentifier(SOME_LEAF_ID).withValue("foo").build());
+
+        commit(mod);
+    }
+
+    private DataTreeModification write(final ContainerNode data) throws DataValidationFailedException {
+        final DataTreeModification mod = dataTree.takeSnapshot().newModification();
+        mod.write(YangInstanceIdentifier.create(BOX), data);
+        return mod;
+    }
+
+    private void writeAndCommit(final ContainerNode data) throws DataValidationFailedException {
+        final DataTreeModification mod = dataTree.takeSnapshot().newModification();
+        mod.write(YangInstanceIdentifier.create(BOX), data);
+        commit(mod);
+    }
+
+    private void commit(final DataTreeModification mod) throws DataValidationFailedException {
+        mod.ready();
+        dataTree.validate(mod);
+        dataTree.commit(dataTree.prepare(mod));
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/MyXmlGenerator.groovy b/yang/yang-data-impl/src/test/resources/MyXmlGenerator.groovy
new file mode 100644 (file)
index 0000000..59cc9e1
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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
+ */
+import groovy.xml.MarkupBuilder
+import org.opendaylight.yangtools.yang.data.impl.MyNodeBuilder
+
+/**
+ * wrapper class - applies hardcoded builder on given data closure
+ */
+class MyXmlGenerator {
+
+    def myBuilder
+    
+    MyXmlGenerator() {
+        myBuilder = MyNodeBuilder.newInstance();
+    }
+
+    MyNodeBuilder getBuilder() { 
+      return myBuilder;
+    }
+        
+    void buildTree(data) {
+        data.setDelegate(myBuilder)
+        data()
+    }
+    
+    /**
+     * tests builder execution
+     */
+    static void main(args) {
+        println 'hello'
+        def data = {
+          network(xmlns: 'urn:opendaylight:controller:network') {
+            topologies {
+              topology {
+                'topology-id'('topId_01')
+                
+                nodes {
+                  node {
+                    'node-id'('nodeId_02')
+                    'supporting-ne'('networkId_03')
+                    'termination-points' {
+                      'termination-point' {
+                        'tp-id'('tpId_04')
+                      }
+                    }
+                  }
+                  node {
+                    'node-id'('nodeId_05')
+                    'supporting-ne'('networkId_06')
+                    'termination-points' {
+                      'termination-point' {
+                        'tp-id'('tpId_07')
+                      }
+                    }
+                  }
+                  node {
+                    'node-id'('nodeId_08')
+                    'supporting-ne'('networkId_09')
+                    'termination-points' {
+                      'termination-point' {
+                        'tp-id'('tpId_10')
+                      }
+                      'termination-point' {
+                        'tp-id'('tpId_11')
+                      }
+                    }
+                  }
+                }
+                links {
+                  link {
+                    'link-id'('linkId_12')
+                    source {
+                      'source-node'('nodeId_13')
+                      'source-tp'('tpId_13')
+                    }
+                    destination {
+                      'dest-node'('nodeId_14')
+                      'dest-tp'('tpId_14')
+                    }
+                  }
+                  link {
+                    'link-id'('linkId_15')
+                    source {
+                      'source-node'('nodeId_16')
+                      'source-tp'('tpId_16')
+                    }
+                    destination {
+                      'dest-node'('nodeId_17')
+                      'dest-tp'('tpId_17')
+                    }
+                  }
+                }
+              }
+            }
+            'network-elements' {
+              'network-element' {
+                'element-id'('ntElementId_18')
+              }
+              'network-element' {
+                'element-id'('ntElementId_19')
+              }
+            }
+          }
+
+        }
+
+        def xmlGen = new MyXmlGenerator()
+        xmlGen.buildTree(data)
+        println xmlGen.getBuilder().getRootNode()
+    }
+
+}
+
+
+
diff --git a/yang/yang-data-impl/src/test/resources/anyxml-support/serialization/baz.xml b/yang/yang-data-impl/src/test/resources/anyxml-support/serialization/baz.xml
new file mode 100644 (file)
index 0000000..16029d1
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns="baz">
+    <baz>
+        <my-container-1>
+            <my-leaf-1>value1</my-leaf-1>
+        </my-container-1>
+        <my-container-2>
+            <inner-container>
+                <my-leaf-2>value2</my-leaf-2>
+            </inner-container>
+            <my-leaf-3>value3</my-leaf-3>
+        </my-container-2>
+
+        <my-anyxml-data>
+            <inner-container>
+                <my-leaf-2>any-xml-leaf-2-value</my-leaf-2>
+            </inner-container>
+            <my-leaf-3>any-xml-leaf-3-value</my-leaf-3>
+        </my-anyxml-data>
+    </baz>
+</root>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/anyxml-support/serialization/baz.yang b/yang/yang-data-impl/src/test/resources/anyxml-support/serialization/baz.yang
new file mode 100644 (file)
index 0000000..9cd2233
--- /dev/null
@@ -0,0 +1,29 @@
+module baz {
+    namespace "baz";
+    prefix baz;
+
+    import yang-ext { prefix ext; revision-date 2013-07-09; }
+
+    container baz {
+        container my-container-1 {
+            leaf my-leaf-1 {
+                type string;
+            }
+        }
+
+        container my-container-2 {
+            container inner-container {
+                leaf my-leaf-2 {
+                    type string;
+                }
+            }
+            leaf my-leaf-3 {
+                type string;
+            }
+        }
+
+        anyxml my-anyxml-data {
+            ext:anyxml-schema-location "/baz/my-container-2";
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/anyxml-support/serialization/yang-ext.yang b/yang/yang-data-impl/src/test/resources/anyxml-support/serialization/yang-ext.yang
new file mode 100644 (file)
index 0000000..2cdc211
--- /dev/null
@@ -0,0 +1,75 @@
+module yang-ext {
+    yang-version 1;
+    namespace "urn:opendaylight:yang:extension:yang-ext";
+    prefix "ext";
+
+    contact "Anton Tkacik <ttkacik@cisco.com>";
+
+    description
+        "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";
+
+    revision "2013-07-09" {
+        description "";
+    }
+
+    // Augmentation name
+
+    extension "augment-identifier" {
+        description
+                "YANG language extension which assigns an identifier to augmentation.
+                Augment identifier is used to identify specific augment statement
+                by name. The identifier syntax is defined formally defined by
+                the rule 'identifier' in Section 12 of RFC 6020. All augment identifiers
+                defined in a namespace MUST be unique. The namespace of augment
+                identifiers is shared by module and its submodules.";
+
+        /*
+                Discussion:
+                This extension allows for ease of development / debug
+                of YANG modules and it is suitable for code generation,
+                where each augment statement is nicely identified by
+                unique name instead of combination of augment target
+                and when condition. 
+            */
+        argument "identifier";
+    }
+
+    // Context-aware RPCs
+
+    grouping rpc-context-ref {
+        description
+                "A reference to RPC context.";
+        leaf context-instance {
+            type instance-identifier;
+            description "Pointer to the context. ";
+        }
+    }
+
+    extension "rpc-context-instance" {
+        description
+                "YANG language extension which defines enclosing (parent) schema
+                node as referencable context for RPCs. The argument is identity
+                which is used to identify RPC context type.";
+
+        argument "context-type";
+    }
+
+    extension "context-reference" {
+        argument "context-type";
+    }
+
+    extension "context-instance" {
+        argument "context-type";
+    }
+
+    extension "instance-target" {
+        argument "path";
+    }
+
+    extension "anyxml-schema-location" {
+        argument "target-node";
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/anyxml-support/xml/bar.xml b/yang/yang-data-impl/src/test/resources/anyxml-support/xml/bar.xml
new file mode 100644 (file)
index 0000000..2bf7dbe
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<my-anyxml-data xmlns="bar">
+    <my-container-1>
+        <my-leaf-1>value1</my-leaf-1>
+    </my-container-1>
+    <my-container-2>
+        <my-leaf-2>value2</my-leaf-2>
+    </my-container-2>
+</my-anyxml-data>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/anyxml-support/xml/foo.xml b/yang/yang-data-impl/src/test/resources/anyxml-support/xml/foo.xml
new file mode 100644 (file)
index 0000000..2492e95
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+    <my-container-1>
+        <my-leaf-1>value1</my-leaf-1>
+    </my-container-1>
+    <my-container-2>
+        <inner-container>
+            <my-leaf-2>value2</my-leaf-2>
+        </inner-container>
+        <my-leaf-3>value3</my-leaf-3>
+    </my-container-2>
+
+    <my-anyxml-data>
+        <inner-container>
+            <my-leaf-2>any-xml-leaf-2-value</my-leaf-2>
+        </inner-container>
+        <my-leaf-3>any-xml-leaf-3-value</my-leaf-3>
+    </my-anyxml-data>
+</foo>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/anyxml-support/yang/bar.yang b/yang/yang-data-impl/src/test/resources/anyxml-support/yang/bar.yang
new file mode 100644 (file)
index 0000000..b8c57a8
--- /dev/null
@@ -0,0 +1,18 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    container bar {
+        container my-container-1 {
+            leaf my-leaf-1 {
+                type string;
+            }
+        }
+
+        container my-container-2 {
+            leaf my-leaf-2 {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/anyxml-support/yang/foo.yang b/yang/yang-data-impl/src/test/resources/anyxml-support/yang/foo.yang
new file mode 100644 (file)
index 0000000..8d9920a
--- /dev/null
@@ -0,0 +1,27 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    
+    import yang-ext { prefix ext; revision-date 2013-07-09; }
+    
+    container my-container-1 {
+        leaf my-leaf-1 {
+            type string;
+        }
+    }
+
+    container my-container-2 {
+        container inner-container {
+            leaf my-leaf-2 {
+                type string;
+            }
+        }
+        leaf my-leaf-3 {
+            type string;
+        }
+    }
+
+    anyxml my-anyxml-data {
+        ext:anyxml-schema-location "/my-container-2";
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/anyxml-support/yang/yang-ext.yang b/yang/yang-data-impl/src/test/resources/anyxml-support/yang/yang-ext.yang
new file mode 100644 (file)
index 0000000..2cdc211
--- /dev/null
@@ -0,0 +1,75 @@
+module yang-ext {
+    yang-version 1;
+    namespace "urn:opendaylight:yang:extension:yang-ext";
+    prefix "ext";
+
+    contact "Anton Tkacik <ttkacik@cisco.com>";
+
+    description
+        "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";
+
+    revision "2013-07-09" {
+        description "";
+    }
+
+    // Augmentation name
+
+    extension "augment-identifier" {
+        description
+                "YANG language extension which assigns an identifier to augmentation.
+                Augment identifier is used to identify specific augment statement
+                by name. The identifier syntax is defined formally defined by
+                the rule 'identifier' in Section 12 of RFC 6020. All augment identifiers
+                defined in a namespace MUST be unique. The namespace of augment
+                identifiers is shared by module and its submodules.";
+
+        /*
+                Discussion:
+                This extension allows for ease of development / debug
+                of YANG modules and it is suitable for code generation,
+                where each augment statement is nicely identified by
+                unique name instead of combination of augment target
+                and when condition. 
+            */
+        argument "identifier";
+    }
+
+    // Context-aware RPCs
+
+    grouping rpc-context-ref {
+        description
+                "A reference to RPC context.";
+        leaf context-instance {
+            type instance-identifier;
+            description "Pointer to the context. ";
+        }
+    }
+
+    extension "rpc-context-instance" {
+        description
+                "YANG language extension which defines enclosing (parent) schema
+                node as referencable context for RPCs. The argument is identity
+                which is used to identify RPC context type.";
+
+        argument "context-type";
+    }
+
+    extension "context-reference" {
+        argument "context-type";
+    }
+
+    extension "context-instance" {
+        argument "context-type";
+    }
+
+    extension "instance-target" {
+        argument "path";
+    }
+
+    extension "anyxml-schema-location" {
+        argument "target-node";
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug-4295/foo.yang b/yang/yang-data-impl/src/test/resources/bug-4295/foo.yang
new file mode 100644 (file)
index 0000000..be6abf3
--- /dev/null
@@ -0,0 +1,27 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    container root {
+        container sub-root {
+            list outer-list {
+                key "o-id";
+                leaf o-id {
+                    type string;
+                }
+                list inner-list {
+                    key "i-id";
+                    leaf i-id {
+                        type string;
+                    }
+                    leaf i {
+                        type string;
+                    }
+                }
+                leaf o {
+                    type string;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug-4454-test.yang b/yang/yang-data-impl/src/test/resources/bug-4454-test.yang
new file mode 100644 (file)
index 0000000..8db5e73
--- /dev/null
@@ -0,0 +1,54 @@
+module Bug4454Test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model";
+    prefix "list-constraints-validation";
+
+    revision "2015-02-02" {
+        description "Initial revision.";
+    }
+
+    container master-container {
+        list min-max-list {
+            min-elements 1;
+            max-elements 3;
+
+            key "min-max-key-leaf";
+
+            leaf min-max-key-leaf {
+                type string;
+            }
+            leaf min-max-value-leaf {
+                type string;
+            }
+        }
+
+        list min-max-list-no-minmax {
+            key "min-max-key-leaf";
+
+            leaf min-max-key-leaf {
+                type string;
+            }
+        }
+
+        leaf-list min-max-leaf-list {
+            min-elements 0;
+            max-elements 10;
+            type string;
+        }
+    }
+
+    container presence {
+        presence "anchor point";
+
+        list min-max-list {
+            min-elements 2;
+            max-elements 3;
+
+            key "min-max-key-leaf";
+
+            leaf min-max-key-leaf {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug5396/xml/foo.xml b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo.xml
new file mode 100644 (file)
index 0000000..26fe222
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+    <root>
+        <my-leaf>dp1o34</my-leaf>
+    </root>
+</foo>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/bug5396/xml/foo2.xml b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo2.xml
new file mode 100644 (file)
index 0000000..72b9185
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+    <root>
+        <my-leaf>dp0s3f9</my-leaf>
+    </root>
+</foo>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/bug5396/xml/foo3.xml b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo3.xml
new file mode 100644 (file)
index 0000000..c18e3cc
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+    <root>
+        <my-leaf>dp09P1p2s3</my-leaf>
+    </root>
+</foo>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/bug5396/xml/foo4.xml b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo4.xml
new file mode 100644 (file)
index 0000000..154a114
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+    <root>
+        <my-leaf>dp0p3p1</my-leaf>
+    </root>
+</foo>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/bug5396/xml/foo5.xml b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo5.xml
new file mode 100644 (file)
index 0000000..7a5ce58
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+    <root>
+        <my-leaf>dp0s3</my-leaf>
+    </root>
+</foo>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/bug5396/xml/invalid-foo.xml b/yang/yang-data-impl/src/test/resources/bug5396/xml/invalid-foo.xml
new file mode 100644 (file)
index 0000000..cf86806
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+    <root>
+        <my-leaf>dp09P1p2s1234</my-leaf>
+    </root>
+</foo>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/bug5396/yang/foo.yang b/yang/yang-data-impl/src/test/resources/bug5396/yang/foo.yang
new file mode 100644 (file)
index 0000000..bad4e39
--- /dev/null
@@ -0,0 +1,32 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix "foo";
+
+    revision "2016-03-22" {
+        description "test";
+    }
+
+    container root {
+        leaf my-leaf {
+            type my-type;
+        }
+    }
+
+    typedef my-type {
+        type union {
+            type string {
+                pattern "dp[0-9]+o[0-9]+";
+            }
+            type string {
+                pattern "dp[0-9]+s[0-9]+(f[0-9]+)?(d[0-9]+)?";
+            }
+            type string {
+                pattern "dp[0-9]+(P[0-9]+)?p[0-9]{1,3}s[0-9]{1,3}(f[0-9]+)?(d[0-9]+)?";
+            }
+            type string {
+                pattern "dp[0-9]+p[0-9]+p[0-9]+";
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug5446/xml/foo.xml b/yang/yang-data-impl/src/test/resources/bug5446/xml/foo.xml
new file mode 100644 (file)
index 0000000..b9b8abf
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<foo xmlns="foo">
+    <root>
+        <ip-address>fwAAAQ==</ip-address>
+    </root>
+</foo>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/bug5446/yang/foo.yang b/yang/yang-data-impl/src/test/resources/bug5446/yang/foo.yang
new file mode 100644 (file)
index 0000000..b5f78ca
--- /dev/null
@@ -0,0 +1,33 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix "foo";
+
+    revision "2015-11-05" {
+    }
+
+    typedef ipv4-address-binary {
+        type binary {
+            length "4";
+        }
+    }
+
+    typedef ipv6-address-binary {
+        type binary {
+            length "16";
+        }
+    }
+
+    typedef ip-address-binary {
+        type union {
+            type ipv4-address-binary;
+            type ipv6-address-binary;
+        }
+    }
+    
+    container root {
+        leaf ip-address {
+            type ip-address-binary;
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug5830/foo-multiple.yang b/yang/yang-data-impl/src/test/resources/bug5830/foo-multiple.yang
new file mode 100644 (file)
index 0000000..7947fae
--- /dev/null
@@ -0,0 +1,56 @@
+module foo-multiple {
+    yang-version 1;
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-05-17 {
+        description "test";
+    }
+
+    container task-container {
+        list task {
+            key "task-id";
+
+            leaf task-id {
+                type string;
+            }
+            leaf task-mandatory-leaf {
+                type string;
+                mandatory true;
+            }
+
+            container task-data {
+                presence "Task data";
+                leaf mandatory-data {
+                    type string;
+                    mandatory true;
+                }
+                leaf other-data {
+                    type string;
+                }
+                container non-presence-container {
+                    container presence-container {
+                        presence "presence container";
+                        leaf mandatory-leaf {
+                            mandatory true;
+                            type string;
+                        }
+                    }
+                    container non-presence-container-2 {
+                        leaf mandatory-leaf-2 {
+                            mandatory true;
+                            type string;
+                        }
+                    }
+                    container presence-container-2 {
+                        presence "presence container";
+                        leaf mandatory-leaf-3 {
+                            mandatory true;
+                            type string;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug5830/foo-non-presence.yang b/yang/yang-data-impl/src/test/resources/bug5830/foo-non-presence.yang
new file mode 100644 (file)
index 0000000..3fc1a72
--- /dev/null
@@ -0,0 +1,33 @@
+module foo-non-presence {
+    yang-version 1;
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-05-17 {
+        description "test";
+    }
+
+    container task-container {
+        list task {
+            key "task-id";
+
+            leaf task-id {
+                type string;
+            }
+            leaf task-mandatory-leaf {
+                type string;
+                mandatory true;
+            }
+
+            container task-data {
+                leaf mandatory-data {
+                    type string;
+                    mandatory true;
+                }
+                leaf other-data {
+                    type string;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug5830/foo-presence.yang b/yang/yang-data-impl/src/test/resources/bug5830/foo-presence.yang
new file mode 100644 (file)
index 0000000..af03827
--- /dev/null
@@ -0,0 +1,34 @@
+module foo-presence {
+    yang-version 1;
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-05-17 {
+        description "test";
+    }
+
+    container task-container {
+        list task {
+            key "task-id";
+
+            leaf task-id {
+                type string;
+            }
+            leaf task-mandatory-leaf {
+                type string;
+                mandatory true;
+            }
+
+            container task-data {
+                presence "Task data";
+                leaf mandatory-data {
+                    type string;
+                    mandatory true;
+                }
+                leaf other-data {
+                    type string;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug5968/foo.yang b/yang/yang-data-impl/src/test/resources/bug5968/foo.yang
new file mode 100644 (file)
index 0000000..a3bdec4
--- /dev/null
@@ -0,0 +1,25 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-07-28 {
+        description "test";
+    }
+
+    container root {
+        list my-list {
+            key "list-id";
+            leaf list-id {
+                type string;
+            }
+            leaf mandatory-leaf {
+                type string;
+                mandatory true;
+            }
+            leaf common-leaf {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug6392/foo.xml b/yang/yang-data-impl/src/test/resources/bug6392/foo.xml
new file mode 100644 (file)
index 0000000..0c6e12e
--- /dev/null
@@ -0,0 +1,13 @@
+<root-cont xmlns="foo">
+    <my-outer-unkeyed-list>
+        <ip>192.168.0.1</ip>
+        <leaf-a>something</leaf-a>
+    </my-outer-unkeyed-list>
+    <my-outer-unkeyed-list>
+        <ip>192.168.0.2</ip>
+        <my-inner-unkeyed-list>
+            <leaf-b>something else</leaf-b>
+            <ip>192.168.0.3</ip>
+        </my-inner-unkeyed-list>
+    </my-outer-unkeyed-list>
+</root-cont>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/bug6392/foo.yang b/yang/yang-data-impl/src/test/resources/bug6392/foo.yang
new file mode 100644 (file)
index 0000000..2c53ee3
--- /dev/null
@@ -0,0 +1,20 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    revision 2017-03-10;
+
+    container root-cont {
+        list my-outer-unkeyed-list {
+            leaf leaf-a {
+                type string;
+            }
+
+            list my-inner-unkeyed-list {
+                leaf leaf-b {
+                    type string;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/bug7844/bar.yang b/yang/yang-data-impl/src/test/resources/bug7844/bar.yang
new file mode 100644 (file)
index 0000000..42dab93
--- /dev/null
@@ -0,0 +1,30 @@
+module bar {
+    namespace bar;
+    prefix bar-mod;
+
+    import baz { prefix baz-imp; }
+
+    typedef bar-leafref {
+        type baz-imp:my-leafref;
+        description "bar-leafref";
+    }
+
+    typedef bar-base-leafref {
+        type leafref {
+            path "/baz-imp:root/baz-imp:target";
+        }
+    }
+
+    leaf my-leafref-in-bar {
+        type bar-base-leafref;
+    }
+
+    leaf my-leafref-in-bar-2 {
+        type bar-base-leafref;
+        description "bar-base-leafref-2";
+    }
+
+    leaf bar-target {
+        type string;
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug7844/baz.yang b/yang/yang-data-impl/src/test/resources/bug7844/baz.yang
new file mode 100644 (file)
index 0000000..7b746b6
--- /dev/null
@@ -0,0 +1,17 @@
+module baz {
+    namespace baz;
+    prefix baz-mod;
+
+    typedef my-leafref {
+        type leafref {
+            path "/baz-mod:root/baz-mod:target";
+        }
+        description "baz-leafref";
+    }
+
+    container root {
+        leaf target {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug7844/foo.yang b/yang/yang-data-impl/src/test/resources/bug7844/foo.yang
new file mode 100644 (file)
index 0000000..6a50b73
--- /dev/null
@@ -0,0 +1,39 @@
+module foo {
+    namespace foo;
+    prefix foo-mod;
+
+    import bar { prefix bar-imp; }
+
+    leaf my-leaf {
+        type foo-leafref;
+    }
+
+    typedef foo-leafref {
+        type bar-imp:bar-leafref;
+        description "foo-leafref";
+    }
+
+    leaf my-leaf-2 {
+        type foo-leafref-2;
+    }
+
+    typedef foo-leafref-2 {
+        type bar-imp:bar-base-leafref;
+        description "foo-leaf-ref-2";
+    }
+
+    leaf bar-base-leafref {
+        type bar-imp:bar-base-leafref;
+    }
+
+    leaf bar-base-leafref-2 {
+        type bar-imp:bar-base-leafref;
+        description "bar-base-leafref-2";
+    }
+
+    leaf direct-leafref {
+        type leafref {
+            path "/bar-imp:bar-target";
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug8291/foo.yang b/yang/yang-data-impl/src/test/resources/bug8291/foo.yang
new file mode 100644 (file)
index 0000000..3c7fc93
--- /dev/null
@@ -0,0 +1,23 @@
+module odl-datastore-test {
+    yang-version 1;
+    namespace "foo";
+    prefix foo;
+
+    container root {
+        list outer-list {
+            key "id";
+            leaf id {
+                type uint16;
+            }
+           list inner-list {
+                key name;
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/bug8713/bar.yang b/yang/yang-data-impl/src/test/resources/bug8713/bar.yang
new file mode 100644 (file)
index 0000000..883ad63
--- /dev/null
@@ -0,0 +1,19 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    import foo { prefix foo; revision-date 2017-09-06; }
+
+    revision 2017-09-06;
+
+    augment "/foo:root" {
+        leaf ref {
+            type leafref {
+                path "../target" ;
+            }
+        }
+        leaf target {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/bug8713/foo.yang b/yang/yang-data-impl/src/test/resources/bug8713/foo.yang
new file mode 100644 (file)
index 0000000..bb65c44
--- /dev/null
@@ -0,0 +1,9 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    revision 2017-09-06;
+
+    container root {
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/builder-test/immutable-ordered-map-node.yang b/yang/yang-data-impl/src/test/resources/builder-test/immutable-ordered-map-node.yang
new file mode 100644 (file)
index 0000000..c9e7147
--- /dev/null
@@ -0,0 +1,25 @@
+module immutable-ordered-map-node {
+    yang-version 1;
+    namespace "test.namespace.builder.test";
+    prefix "iomn";
+
+    revision "2016-01-01" {
+        description "Initial revision.";
+    }
+
+    container root-container {
+        list list-ordered-by-user-with-key {
+            key "leaf-a";
+            ordered-by "user";
+
+            leaf leaf-a {
+                type string;
+            }
+        }
+
+        leaf-list leaf-list-ordered-by-user {
+            ordered-by "user";
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/case-augment-test.yang b/yang/yang-data-impl/src/test/resources/case-augment-test.yang
new file mode 100644 (file)
index 0000000..4ee9820
--- /dev/null
@@ -0,0 +1,33 @@
+module case-augment-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+    prefix "store-test";
+
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    container test {
+        choice choice1 {
+            case case1 {
+                leaf case1-leaf1 {
+                    type string;
+                }
+            }
+            case case2 {
+                leaf case2-leaf1 {
+                    type string;
+                }
+            }
+        }
+    }
+
+    augment "/test/choice1/case1" {
+        leaf case1-leaf2 {
+            type string;
+        }
+        leaf case1-leaf3 {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/case-exclusion-test.yang b/yang/yang-data-impl/src/test/resources/case-exclusion-test.yang
new file mode 100644 (file)
index 0000000..e006ad2
--- /dev/null
@@ -0,0 +1,70 @@
+module case-exclusion-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+    prefix "store-test";
+
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    container test {
+        presence true;
+        config true;
+        choice choice1 {
+            case case1 {
+                leaf case1-leaf1 {
+                    type string;
+                }
+            }
+            case case2 {
+                container case2-cont {
+                    leaf case2-leaf1 {
+                        type string;
+                    }
+                }
+            }
+        }
+
+        list outer-list {
+
+            key id;
+            leaf id {
+                type uint16;
+            }
+            choice outer-choice {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+                }
+            }
+            list inner-list {
+                key name;
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+
+            list inner-list2 {
+                key name;
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/config01.xml b/yang/yang-data-impl/src/test/resources/config01.xml
new file mode 100644 (file)
index 0000000..72804c6
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <running />
+        </target>
+        <config>
+            <top xmlns="http://example.com/schema/1.2/config">
+                <interface>
+                    <name>Ethernet0/0</name>
+                    <mtu>1500</mtu>
+                </interface>
+            </top>
+        </config>
+    </edit-config>
+</rpc>
+     
diff --git a/yang/yang-data-impl/src/test/resources/config02.xml b/yang/yang-data-impl/src/test/resources/config02.xml
new file mode 100644 (file)
index 0000000..26259a0
--- /dev/null
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<network xmlns="urn:opendaylight:controller:network">
+    <topologies>
+        <topology>
+            <topology-id>topId_01</topology-id>
+            <types></types>
+            <nodes>
+                <node>
+                    <node-id>nodeId_02</node-id>
+                    <supporting-ne>networkId_02</supporting-ne>
+                    <termination-points>
+                        <termination-point>
+                            <tp-id>tpId_03</tp-id>
+                        </termination-point>
+                    </termination-points>
+                </node>
+                <node>
+                    <node-id>nodeId_16</node-id>
+                    <supporting-ne>networkId_17</supporting-ne>
+                    <termination-points>
+                        <termination-point>
+                            <tp-id>tpId_18</tp-id>
+                        </termination-point>
+                    </termination-points>
+                </node>
+                <node>
+                    <node-id>nodeId_19</node-id>
+                    <supporting-ne>networkId_20</supporting-ne>
+                    <termination-points>
+                        <termination-point>
+                            <tp-id>tpId_18</tp-id>
+                        </termination-point>
+                        <termination-point>
+                            <tp-id>tpId_19</tp-id>
+                        </termination-point>
+                    </termination-points>
+                </node>
+            </nodes>
+            <links>
+                <link>
+                    <link-id>linkId_04</link-id>
+                    <source>
+                        <source-node>nodeId_05</source-node>
+                        <source-tp>tpId_06</source-tp>
+                    </source>
+                    <destination>
+                        <dest-node>nodeId_07</dest-node>
+                        <dest-tp>tpId_08</dest-tp>
+                    </destination>
+                </link>
+                <link>
+                    <link-id>linkId_11</link-id>
+                    <source>
+                        <source-node>nodeId_12</source-node>
+                        <source-tp>tpId_13</source-tp>
+                    </source>
+                    <destination>
+                        <dest-node>nodeId_14</dest-node>
+                        <dest-tp>tpId_15</dest-tp>
+                    </destination>
+                </link>
+            </links>
+        </topology>
+    </topologies>
+    <network-elements>
+        <network-element>
+            <element-id>ntElementId_09</element-id>
+        </network-element>
+        <network-element>
+            <element-id>ntElementId_10</element-id>
+        </network-element>
+    </network-elements>
+</network>
+
diff --git a/yang/yang-data-impl/src/test/resources/controller-network.xsd b/yang/yang-data-impl/src/test/resources/controller-network.xsd
new file mode 100644 (file)
index 0000000..d229fe9
--- /dev/null
@@ -0,0 +1,318 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns:yin="urn:ietf:params:xml:schema:yang:yin:1"
+           targetNamespace="urn:opendaylight:controller:network"
+           xmlns="urn:opendaylight:controller:network"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified"
+           version="2013-05-20"
+           xml:lang="en"
+           xmlns:topos="urn:opendaylight:controller:network"
+           xmlns:inet="urn:ietf:params:xml:ns:yang:ietf-inet-types">
+
+  <xs:import namespace="urn:ietf:params:xml:ns:yang:ietf-inet-types"
+             schemaLocation="ietf-inet-types@2010-09-24.xsd"/>
+
+  <xs:annotation>
+    <xs:documentation>
+      This schema was generated from the YANG module controller-network
+      by pyang version 1.2.
+
+      The schema describes an instance document consisting
+      of the entire configuration data store, operational
+      data, rpc operations, and notifications.
+      This schema can thus NOT be used as-is to
+      validate NETCONF PDUs.
+    </xs:documentation>
+  </xs:annotation>
+
+
+  <!-- YANG typedefs -->
+  <xs:simpleType name="topology-id">
+    <xs:restriction base="xs:string">
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="node-id">
+    <xs:restriction base="xs:string">
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="link-id">
+    <xs:restriction base="xs:string">
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="tp-id">
+    <xs:annotation>
+      <xs:documentation>
+        identifier for termination points on a port
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="xs:string">
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="tp-ref">
+    <xs:restriction base="tp-id">
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="topology-ref">
+    <xs:annotation>
+      <xs:documentation>
+        This type is used for leafs that reference topology identifier instance.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="topology-id">
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="node-ref">
+    <xs:annotation>
+      <xs:documentation>
+        This type is used for leafs that reference a node instance.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="node-id">
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="link-ref">
+    <xs:annotation>
+      <xs:documentation>
+        This type is used for leafs that reference a link instance.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="link-id">
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="network-element-ref">
+    <xs:restriction base="element-id">
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="element-id">
+    <xs:restriction base="xs:string">
+    </xs:restriction>
+  </xs:simpleType>
+
+  <xs:element name="network">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element name="topologies" minOccurs="0">
+          <xs:complexType>
+            <xs:sequence>
+              <xs:element name="topology" minOccurs="0" maxOccurs="unbounded">
+                <xs:annotation>
+                  <xs:documentation>
+
+                    This is the model of abstract topology which contains only Network
+                    Nodes and Network Links. Each topology MUST be identified by
+                    unique topology-id for reason that the store could contain many
+                    topologies.
+
+                  </xs:documentation>
+                </xs:annotation>
+                <xs:complexType>
+                  <xs:sequence>
+                    <xs:element name="topology-id"  type="topology-id">
+                      <xs:annotation>
+                        <xs:documentation>
+
+                          It is presumed that datastore will contain many topologies. To
+                          distinguish between topologies it is vital to have UNIQUE
+                          topology identifier.
+
+                        </xs:documentation>
+                      </xs:annotation>
+                    </xs:element>
+                    <xs:element name="types" minOccurs="0">
+                      <xs:annotation>
+                        <xs:documentation>
+
+                          The container for definition of topology types.
+                          The augmenting modules should add empty optional leaf 
+                          to this container to signalize topology type.
+
+                        </xs:documentation>
+                      </xs:annotation>
+                      <xs:complexType>
+                        <xs:sequence>
+                          <xs:any minOccurs="0" maxOccurs="unbounded"
+                                  namespace="##other" processContents="lax"/>
+                        </xs:sequence>
+                      </xs:complexType>
+                    </xs:element>
+                    <xs:element name="nodes" minOccurs="0">
+                      <xs:complexType>
+                        <xs:sequence>
+                          <xs:element name="node" minOccurs="0" maxOccurs="unbounded">
+                            <xs:annotation>
+                              <xs:documentation>
+                                The list of network nodes defined for topology.
+                              </xs:documentation>
+                            </xs:annotation>
+                            <xs:complexType>
+                              <xs:sequence>
+                                <xs:element name="node-id"  type="node-id">
+                                  <xs:annotation>
+                                    <xs:documentation>
+                                      The Topology identifier of network-node.
+                                    </xs:documentation>
+                                  </xs:annotation>
+                                </xs:element>
+                                <xs:element name="supporting-ne" minOccurs="0"  type="network-element-ref">
+                                </xs:element>
+                                <xs:element name="termination-points" minOccurs="0">
+                                  <xs:complexType>
+                                    <xs:sequence>
+                                      <xs:element name="termination-point" minOccurs="0" maxOccurs="unbounded">
+                                        <xs:complexType>
+                                          <xs:sequence>
+                                            <xs:element name="tp-id"  type="tp-id">
+                                            </xs:element>
+                                            <xs:any minOccurs="0" maxOccurs="unbounded"
+                                                    namespace="##other" processContents="lax"/>
+                                          </xs:sequence>
+                                        </xs:complexType>
+                                      </xs:element>
+                                      <xs:any minOccurs="0" maxOccurs="unbounded"
+                                              namespace="##other" processContents="lax"/>
+                                    </xs:sequence>
+                                  </xs:complexType>
+                                  <xs:key name="key_termination-points_node_nodes_topology_topologies_network_termination-point">
+                                    <xs:selector xpath="topos:termination-point"/>
+                                    <xs:field xpath="topos:tp-id"/>
+                                  </xs:key>
+                                </xs:element>
+                                <xs:any minOccurs="0" maxOccurs="unbounded"
+                                        namespace="##other" processContents="lax"/>
+                              </xs:sequence>
+                            </xs:complexType>
+                          </xs:element>
+                          <xs:any minOccurs="0" maxOccurs="unbounded"
+                                  namespace="##other" processContents="lax"/>
+                        </xs:sequence>
+                      </xs:complexType>
+                      <xs:key name="key_nodes_topology_topologies_network_node">
+                        <xs:selector xpath="topos:node"/>
+                        <xs:field xpath="topos:node-id"/>
+                      </xs:key>
+                    </xs:element>
+                    <xs:element name="links" minOccurs="0">
+                      <xs:complexType>
+                        <xs:sequence>
+                          <xs:element name="link" minOccurs="0" maxOccurs="unbounded">
+                            <xs:annotation>
+                              <xs:documentation>
+
+                                The Network Link which is defined by Local (Source) and
+                                Remote (Destination) Network Nodes. Every link MUST be
+                                defined either by identifier and his local and remote
+                                Network Nodes (in real applications it is common that many
+                                links are originated from one node and end up in same
+                                remote node). To ensure that we would always know to
+                                distinguish between links, every link SHOULD have
+                                identifier.
+
+                              </xs:documentation>
+                            </xs:annotation>
+                            <xs:complexType>
+                              <xs:sequence>
+                                <xs:element name="link-id"  type="link-id">
+                                </xs:element>
+                                <xs:element name="source" minOccurs="0">
+                                  <xs:complexType>
+                                    <xs:sequence>
+                                      <xs:element name="source-node" minOccurs="0"  type="node-ref">
+                                        <xs:annotation>
+                                          <xs:documentation>
+                                            Source node identifier.
+                                          </xs:documentation>
+                                        </xs:annotation>
+                                      </xs:element>
+                                      <xs:element name="source-tp" minOccurs="0"  type="tp-ref">
+                                      </xs:element>
+                                      <xs:any minOccurs="0" maxOccurs="unbounded"
+                                              namespace="##other" processContents="lax"/>
+                                    </xs:sequence>
+                                  </xs:complexType>
+                                </xs:element>
+                                <xs:element name="destination" minOccurs="0">
+                                  <xs:complexType>
+                                    <xs:sequence>
+                                      <xs:element name="dest-node" minOccurs="0"  type="node-ref">
+                                        <xs:annotation>
+                                          <xs:documentation>
+                                            Destination node identifier.
+                                          </xs:documentation>
+                                        </xs:annotation>
+                                      </xs:element>
+                                      <xs:element name="dest-tp" minOccurs="0"  type="tp-ref">
+                                      </xs:element>
+                                      <xs:any minOccurs="0" maxOccurs="unbounded"
+                                              namespace="##other" processContents="lax"/>
+                                    </xs:sequence>
+                                  </xs:complexType>
+                                </xs:element>
+                                <xs:any minOccurs="0" maxOccurs="unbounded"
+                                        namespace="##other" processContents="lax"/>
+                              </xs:sequence>
+                            </xs:complexType>
+                          </xs:element>
+                          <xs:any minOccurs="0" maxOccurs="unbounded"
+                                  namespace="##other" processContents="lax"/>
+                        </xs:sequence>
+                      </xs:complexType>
+                      <xs:key name="key_links_topology_topologies_network_link">
+                        <xs:selector xpath="topos:link"/>
+                        <xs:field xpath="topos:link-id"/>
+                      </xs:key>
+                    </xs:element>
+                    <xs:any minOccurs="0" maxOccurs="unbounded"
+                            namespace="##other" processContents="lax"/>
+                  </xs:sequence>
+                </xs:complexType>
+              </xs:element>
+              <xs:any minOccurs="0" maxOccurs="unbounded"
+                      namespace="##other" processContents="lax"/>
+            </xs:sequence>
+          </xs:complexType>
+          <xs:key name="key_topologies_network_topology">
+            <xs:selector xpath="topos:topology"/>
+            <xs:field xpath="topos:topology-id"/>
+          </xs:key>
+        </xs:element>
+        <xs:element name="network-elements" minOccurs="0">
+          <xs:complexType>
+            <xs:sequence>
+              <xs:element name="network-element" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                  <xs:sequence>
+                    <xs:element name="element-id"  type="element-id">
+                    </xs:element>
+                    <xs:any minOccurs="0" maxOccurs="unbounded"
+                            namespace="##other" processContents="lax"/>
+                  </xs:sequence>
+                </xs:complexType>
+              </xs:element>
+              <xs:any minOccurs="0" maxOccurs="unbounded"
+                      namespace="##other" processContents="lax"/>
+            </xs:sequence>
+          </xs:complexType>
+          <xs:key name="key_network-elements_network_network-element">
+            <xs:selector xpath="topos:network-element"/>
+            <xs:field xpath="topos:element-id"/>
+          </xs:key>
+        </xs:element>
+        <xs:any minOccurs="0" maxOccurs="unbounded"
+                namespace="##other" processContents="lax"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+
+</xs:schema>
diff --git a/yang/yang-data-impl/src/test/resources/controller-network.yang b/yang/yang-data-impl/src/test/resources/controller-network.yang
new file mode 100644 (file)
index 0000000..c64812a
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * 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
+ */
+module controller-network {
+    yang-version 1;
+    namespace "urn:opendaylight:controller:network";
+    prefix "topos";
+    
+    import ietf-inet-types { prefix "inet"; }
+    
+    revision 2013-05-20 {
+       description "Initial demo";
+    }
+    
+
+    
+    
+    typedef topology-id {
+        type string;
+    }
+
+    typedef node-id {
+        type string;
+    }
+
+    typedef link-id {
+        type string;
+    }
+
+    typedef tp-id {
+        type string;
+        description "identifier for termination points on a port";
+    }
+
+    typedef tp-ref {
+        type leafref {
+            path "/network/topologies/topology/nodes/node/termination-points/termination-point/tp-id";
+        }
+    }
+    typedef topology-ref {
+        type leafref {
+            path "/network/topologies/topology/topology-id";
+        }
+        description "This type is used for leafs that reference topology identifier instance.";
+        // currently not used
+    }
+
+    typedef node-ref {
+        type leafref {
+            path "/network/topologies/topology/nodes/node/node-id";
+        }
+        description "This type is used for leafs that reference a node instance.";
+    }
+
+    typedef link-ref {
+        type leafref {
+            path "/network/topologies/topology/links/link/link-id";
+        }
+        description "This type is used for leafs that reference a link instance.";
+        // currently not used
+    }
+    
+    typedef network-element-ref {
+        type leafref {
+            path "/network/network-elements/network-element/element-id";
+        }
+    }
+
+
+    typedef element-id {
+        type string;
+    }
+    
+    container network {
+        container topologies {
+            list topology {
+                description "
+                    This is the model of abstract topology which contains only Network
+                    Nodes and Network Links. Each topology MUST be identified by
+                    unique topology-id for reason that the store could contain many
+                    topologies.
+                ";
+                key "topology-id";
+                leaf topology-id {
+                    type topology-id; 
+                    description "
+                        It is presumed that datastore will contain many topologies. To
+                        distinguish between topologies it is vital to have UNIQUE
+                        topology identifier.
+                    ";
+                }
+
+                container types {
+                    description "
+                        The container for definition of topology types.
+                        The augmenting modules should add empty optional leaf 
+                        to this container to signalize topology type.
+                    ";
+                }
+
+                container nodes {
+                    list node {
+                        description "The list of network nodes defined for topology.";
+
+                        key "node-id";
+                        leaf node-id {
+                            type node-id;
+                            description "The Topology identifier of network-node.";
+                        }
+
+                        leaf supporting-ne {
+                            type network-element-ref;
+                        }
+                        
+                        container termination-points {
+                            list termination-point {
+                                key "tp-id";
+                                leaf tp-id {
+                                    type tp-id;
+                                }
+                            }
+                        }
+                    }
+                }
+        
+                container links {
+                    list link {
+                        description "
+                            The Network Link which is defined by Local (Source) and
+                            Remote (Destination) Network Nodes. Every link MUST be
+                            defined either by identifier and his local and remote
+                            Network Nodes (in real applications it is common that many
+                            links are originated from one node and end up in same
+                            remote node). To ensure that we would always know to
+                            distinguish between links, every link SHOULD have
+                            identifier.
+                        ";
+                        key "link-id";
+        
+                        leaf link-id {
+                            type link-id;
+                        }
+                        container source { 
+                            leaf source-node {
+                                type node-ref;
+                                description "Source node identifier.";
+                            }
+                            leaf source-tp {
+                                type tp-ref;
+                            }
+                        }
+                        container destination { 
+                            leaf dest-node {
+                                type node-ref;
+                                description "Destination node identifier.";
+                            }
+                            leaf dest-tp {
+                                type tp-ref;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        container network-elements {
+            config true;
+            list network-element {
+                key "element-id";
+                leaf element-id {
+                    type element-id;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/dom-serializer-test/serializer-test.yang b/yang/yang-data-impl/src/test/resources/dom-serializer-test/serializer-test.yang
new file mode 100644 (file)
index 0000000..92c9db3
--- /dev/null
@@ -0,0 +1,25 @@
+module serializer-test {
+    yang-version 1;
+    namespace "dom-serializer-test";
+    prefix "dst";
+
+    revision "2016-01-01" {
+        description "Initial revision.";
+    }
+
+    typedef int-with-range {
+        type int8 {
+            range 1..20;
+        }
+    }
+
+    container root {
+        leaf first-leaf {
+            type int-with-range;
+        }
+
+        leaf-list first-leaf-list  {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/filter-test.yang b/yang/yang-data-impl/src/test/resources/filter-test.yang
new file mode 100644 (file)
index 0000000..6df5306
--- /dev/null
@@ -0,0 +1,74 @@
+module normalization-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
+    prefix "norm-test";
+
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    grouping outer-grouping {
+    }
+
+    container test {
+        list outer-list {
+            key id;
+            leaf id {
+                type uint16;
+            }
+            choice outer-choice {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+               }
+           }
+           list inner-list {
+                key name;
+                ordered-by user;
+
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+        }
+
+        list unkeyed-list {
+            leaf name {
+                type string;
+            }
+        }
+
+        leaf-list unordered-leaf-list {
+            type string;
+        }
+
+        leaf-list ordered-leaf-list {
+            ordered-by user;
+            type string;
+        }
+
+        container outer-container {
+        }
+
+        anyxml any-xml-data;
+    }
+
+    augment /norm-test:test/norm-test:outer-container {
+
+        leaf augmented-leaf {
+           type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/foo.yang b/yang/yang-data-impl/src/test/resources/foo.yang
new file mode 100644 (file)
index 0000000..2c425b2
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace "foo";
+    prefix "foo";
+
+    container cont {
+        leaf lf1 {
+            type string;
+        }
+
+        leaf lf2 {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/ietf-inet-types@2010-09-24.xsd b/yang/yang-data-impl/src/test/resources/ietf-inet-types@2010-09-24.xsd
new file mode 100644 (file)
index 0000000..3da8ec7
--- /dev/null
@@ -0,0 +1,399 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns:yin="urn:ietf:params:xml:schema:yang:yin:1"
+           targetNamespace="urn:ietf:params:xml:ns:yang:ietf-inet-types"
+           xmlns="urn:ietf:params:xml:ns:yang:ietf-inet-types"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified"
+           version="2010-09-24"
+           xml:lang="en"
+          xmlns:inet="urn:ietf:params:xml:ns:yang:ietf-inet-types">
+
+  <xs:annotation>
+    <xs:documentation>
+      This schema was generated from the YANG module ietf-inet-types
+      by pyang version 1.2.
+
+      The schema describes an instance document consisting
+      of the entire configuration data store, operational
+      data, rpc operations, and notifications.
+      This schema can thus NOT be used as-is to
+      validate NETCONF PDUs.
+    </xs:documentation>
+  </xs:annotation>
+
+  <xs:annotation>
+    <xs:documentation>
+      This module contains a collection of generally useful derived
+      YANG data types for Internet addresses and related things.
+
+      Copyright (c) 2010 IETF Trust and the persons identified as
+      authors of the code.  All rights reserved.
+
+      Redistribution and use in source and binary forms, with or without
+      modification, is permitted pursuant to, and subject to the license
+      terms contained in, the Simplified BSD License set forth in Section
+      4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+      (http://trustee.ietf.org/license-info).
+
+      This version of this YANG module is part of RFC 6021; see
+      the RFC itself for full legal notices.
+    </xs:documentation>
+  </xs:annotation>
+
+  <!-- YANG typedefs -->
+  <xs:simpleType name="ip-version">
+    <xs:annotation>
+      <xs:documentation>
+        This value represents the version of the IP protocol.
+
+        In the value set and its semantics, this type is equivalent
+        to the InetVersion textual convention of the SMIv2.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="unknown"/>
+      <xs:enumeration value="ipv4"/>
+      <xs:enumeration value="ipv6"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="dscp">
+    <xs:annotation>
+      <xs:documentation>
+        The dscp type represents a Differentiated Services Code-Point
+        that may be used for marking packets in a traffic stream.
+
+        In the value set and its semantics, this type is equivalent
+        to the Dscp textual convention of the SMIv2.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="xs:unsignedByte">
+      <xs:minInclusive value="0"/>
+      <xs:maxInclusive value="63"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="ipv6-flow-label">
+    <xs:annotation>
+      <xs:documentation>
+        The flow-label type represents flow identifier or Flow Label
+        in an IPv6 packet header that may be used to discriminate
+        traffic flows.
+
+        In the value set and its semantics, this type is equivalent
+        to the IPv6FlowLabel textual convention of the SMIv2.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="xs:unsignedInt">
+      <xs:minInclusive value="0"/>
+      <xs:maxInclusive value="1048575"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="port-number">
+    <xs:annotation>
+      <xs:documentation>
+        The port-number type represents a 16-bit port number of an
+        Internet transport layer protocol such as UDP, TCP, DCCP, or
+        SCTP.  Port numbers are assigned by IANA.  A current list of
+        all assignments is available from &lt;http://www.iana.org/&gt;.
+
+        Note that the port number value zero is reserved by IANA.  In
+        situations where the value zero does not make sense, it can
+        be excluded by subtyping the port-number type.
+
+        In the value set and its semantics, this type is equivalent
+        to the InetPortNumber textual convention of the SMIv2.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="xs:unsignedShort">
+      <xs:minInclusive value="0"/>
+      <xs:maxInclusive value="65535"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="as-number">
+    <xs:annotation>
+      <xs:documentation>
+        The as-number type represents autonomous system numbers
+        which identify an Autonomous System (AS).  An AS is a set
+        of routers under a single technical administration, using
+        an interior gateway protocol and common metrics to route
+        packets within the AS, and using an exterior gateway
+        protocol to route packets to other ASs'.  IANA maintains
+        the AS number space and has delegated large parts to the
+        regional registries.
+
+        Autonomous system numbers were originally limited to 16
+        bits.  BGP extensions have enlarged the autonomous system
+        number space to 32 bits.  This type therefore uses an uint32
+        base type without a range restriction in order to support
+        a larger autonomous system number space.
+
+        In the value set and its semantics, this type is equivalent
+        to the InetAutonomousSystemNumber textual convention of
+        the SMIv2.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="xs:unsignedInt">
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="ip-address">
+    <xs:annotation>
+      <xs:documentation>
+        The ip-address type represents an IP address and is IP
+        version neutral.  The format of the textual representations
+        implies the IP version.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:union>
+      <xs:simpleType>
+        <xs:restriction base="ipv4-address">
+        </xs:restriction>
+      </xs:simpleType>
+      <xs:simpleType>
+        <xs:restriction base="ipv6-address">
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:union>
+  </xs:simpleType>
+  <xs:simpleType name="ipv4-address">
+    <xs:annotation>
+      <xs:documentation>
+        The ipv4-address type represents an IPv4 address in
+        dotted-quad notation.  The IPv4 address may include a zone
+        index, separated by a % sign.
+
+        The zone index is used to disambiguate identical address
+        values.  For link-local addresses, the zone index will
+        typically be the interface index number or the name of an
+        interface.  If the zone index is not present, the default
+        zone of the device will be used.
+
+        The canonical format for the zone index is the numerical
+        format
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="xs:string">
+    <xs:pattern value="(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(%[\p{N}\p{L}]+)?"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="ipv6-address">
+    <xs:annotation>
+      <xs:documentation>
+        The ipv6-address type represents an IPv6 address in full,
+        mixed, shortened, and shortened-mixed notation.  The IPv6
+        address may include a zone index, separated by a % sign.
+
+        The zone index is used to disambiguate identical address
+        values.  For link-local addresses, the zone index will
+        typically be the interface index number or the name of an
+        interface.  If the zone index is not present, the default
+        zone of the device will be used.
+
+        The canonical format of IPv6 addresses uses the compressed
+        format described in RFC 4291, Section 2.2, item 2 with the
+        following additional rules: the :: substitution must be
+        applied to the longest sequence of all-zero 16-bit chunks
+        in an IPv6 address.  If there is a tie, the first sequence
+        of all-zero 16-bit chunks is replaced by ::.  Single
+        all-zero 16-bit chunks are not compressed.  The canonical
+        format uses lowercase characters and leading zeros are
+        not allowed.  The canonical format for the zone index is
+        the numerical format as described in RFC 4007, Section
+        11.2.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="xs:string">
+    <xs:pattern value="(((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))(%[\p{N}\p{L}]+)?)|((([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)(%.+)?)"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="ip-prefix">
+    <xs:annotation>
+      <xs:documentation>
+        The ip-prefix type represents an IP prefix and is IP
+        version neutral.  The format of the textual representations
+        implies the IP version.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:union>
+      <xs:simpleType>
+        <xs:restriction base="ipv4-prefix">
+        </xs:restriction>
+      </xs:simpleType>
+      <xs:simpleType>
+        <xs:restriction base="ipv6-prefix">
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:union>
+  </xs:simpleType>
+  <xs:simpleType name="ipv4-prefix">
+    <xs:annotation>
+      <xs:documentation>
+        The ipv4-prefix type represents an IPv4 address prefix.
+        The prefix length is given by the number following the
+        slash character and must be less than or equal to 32.
+
+        A prefix length value of n corresponds to an IP address
+        mask that has n contiguous 1-bits from the most
+        significant bit (MSB) and all other bits set to 0.
+
+        The canonical format of an IPv4 prefix has all bits of
+        the IPv4 address set to zero that are not part of the
+        IPv4 prefix.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="xs:string">
+    <xs:pattern value="(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])/(([0-9])|([1-2][0-9])|(3[0-2]))"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="ipv6-prefix">
+    <xs:annotation>
+      <xs:documentation>
+        The ipv6-prefix type represents an IPv6 address prefix.
+        The prefix length is given by the number following the
+        slash character and must be less than or equal 128.
+
+        A prefix length value of n corresponds to an IP address
+        mask that has n contiguous 1-bits from the most
+        significant bit (MSB) and all other bits set to 0.
+
+        The IPv6 address should have all bits that do not belong
+        to the prefix set to zero.
+
+        The canonical format of an IPv6 prefix has all bits of
+        the IPv6 address set to zero that are not part of the
+        IPv6 prefix.  Furthermore, IPv6 address is represented
+        in the compressed format described in RFC 4291, Section
+        2.2, item 2 with the following additional rules: the ::
+        substitution must be applied to the longest sequence of
+        all-zero 16-bit chunks in an IPv6 address.  If there is
+        a tie, the first sequence of all-zero 16-bit chunks is
+        replaced by ::.  Single all-zero 16-bit chunks are not
+        compressed.  The canonical format uses lowercase
+        characters and leading zeros are not allowed.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="xs:string">
+    <xs:pattern value="(((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8]))))|((([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)(/.+))"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="domain-name">
+    <xs:annotation>
+      <xs:documentation>
+        The domain-name type represents a DNS domain name.  The
+        name SHOULD be fully qualified whenever possible.
+
+        Internet domain names are only loosely specified.  Section
+        3.5 of RFC 1034 recommends a syntax (modified in Section
+        2.1 of RFC 1123).  The pattern above is intended to allow
+        for current practice in domain name use, and some possible
+        future expansion.  It is designed to hold various types of
+        domain names, including names used for A or AAAA records
+        (host names) and other records, such as SRV records.  Note
+        that Internet host names have a stricter syntax (described
+        in RFC 952) than the DNS recommendations in RFCs 1034 and
+        1123, and that systems that want to store host names in
+        schema nodes using the domain-name type are recommended to
+        adhere to this stricter standard to ensure interoperability.
+
+        The encoding of DNS names in the DNS protocol is limited
+        to 255 characters.  Since the encoding consists of labels
+        prefixed by a length bytes and there is a trailing NULL
+        byte, only 253 characters can appear in the textual dotted
+        notation.
+
+        The description clause of schema nodes using the domain-name
+        type MUST describe when and how these names are resolved to
+        IP addresses.  Note that the resolution of a domain-name value
+        may require to query multiple DNS records (e.g., A for IPv4
+        and AAAA for IPv6).  The order of the resolution process and
+        which DNS record takes precedence can either be defined
+        explicitely or it may depend on the configuration of the
+        resolver.
+
+        Domain-name values use the US-ASCII encoding.  Their canonical
+        format uses lowercase US-ASCII characters.  Internationalized
+        domain names MUST be encoded in punycode as described in RFC
+        3492
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="t0">
+      <xs:minLength value="1"/>
+      <xs:maxLength value="253"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="host">
+    <xs:annotation>
+      <xs:documentation>
+        The host type represents either an IP address or a DNS
+        domain name.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:union>
+      <xs:simpleType>
+        <xs:restriction base="ip-address">
+        </xs:restriction>
+      </xs:simpleType>
+      <xs:simpleType>
+        <xs:restriction base="domain-name">
+        </xs:restriction>
+      </xs:simpleType>
+    </xs:union>
+  </xs:simpleType>
+  <xs:simpleType name="uri">
+    <xs:annotation>
+      <xs:documentation>
+        The uri type represents a Uniform Resource Identifier
+        (URI) as defined by STD 66.
+
+        Objects using the uri type MUST be in US-ASCII encoding,
+        and MUST be normalized as described by RFC 3986 Sections
+        6.2.1, 6.2.2.1, and 6.2.2.2.  All unnecessary
+        percent-encoding is removed, and all case-insensitive
+        characters are set to lowercase except for hexadecimal
+        digits, which are normalized to uppercase as described in
+        Section 6.2.2.1.
+
+        The purpose of this normalization is to help provide
+        unique URIs.  Note that this normalization is not
+        sufficient to provide uniqueness.  Two URIs that are
+        textually distinct after this normalization may still be
+        equivalent.
+
+        Objects using the uri type may restrict the schemes that
+        they permit.  For example, 'data:' and 'urn:' schemes
+        might not be appropriate.
+
+        A zero-length URI is not a valid URI.  This can be used to
+        express 'URI absent' where required.
+
+        In the value set and its semantics, this type is equivalent
+        to the Uri SMIv2 textual convention defined in RFC 5017.
+      </xs:documentation>
+    </xs:annotation>
+
+    <xs:restriction base="xs:string">
+    </xs:restriction>
+  </xs:simpleType>
+
+
+  <!-- locally generated simpleType helpers -->
+
+  <xs:simpleType name="t0">
+    <xs:restriction base="xs:string">
+    <xs:pattern value="((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)|\."/>
+    </xs:restriction>
+  </xs:simpleType>
+
+</xs:schema>
diff --git a/yang/yang-data-impl/src/test/resources/ietf-inet-types@2010-09-24.yang b/yang/yang-data-impl/src/test/resources/ietf-inet-types@2010-09-24.yang
new file mode 100644 (file)
index 0000000..de20feb
--- /dev/null
@@ -0,0 +1,418 @@
+ module ietf-inet-types {
+
+   namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
+   prefix "inet";
+
+   organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+   contact
+    "WG Web:   <http://tools.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+
+     WG Chair: David Partain
+               <mailto:david.partain@ericsson.com>
+
+     WG Chair: David Kessens
+               <mailto:david.kessens@nsn.com>
+
+     Editor:   Juergen Schoenwaelder
+               <mailto:j.schoenwaelder@jacobs-university.de>";
+
+   description
+    "This module contains a collection of generally useful derived
+     YANG data types for Internet addresses and related things.
+
+     Copyright (c) 2010 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or without
+     modification, is permitted pursuant to, and subject to the license
+     terms contained in, the Simplified BSD License set forth in Section
+     4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6021; see
+     the RFC itself for full legal notices.";
+
+   revision 2010-09-24 {
+     description
+      "Initial revision.";
+     reference
+      "RFC 6021: Common YANG Data Types";
+   }
+
+   /*** collection of protocol field related types ***/
+
+   typedef ip-version {
+     type enumeration {
+       enum unknown {
+         value "0";
+         description
+          "An unknown or unspecified version of the Internet protocol.";
+       }
+       enum ipv4 {
+         value "1";
+         description
+          "The IPv4 protocol as defined in RFC 791.";
+       }
+       enum ipv6 {
+         value "2";
+         description
+          "The IPv6 protocol as defined in RFC 2460.";
+       }
+     }
+     description
+      "This value represents the version of the IP protocol.
+
+       In the value set and its semantics, this type is equivalent
+       to the InetVersion textual convention of the SMIv2.";
+     reference
+      "RFC  791: Internet Protocol
+       RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+       RFC 4001: Textual Conventions for Internet Network Addresses";
+   }
+
+   typedef dscp {
+     type uint8 {
+       range "0..63";
+     }
+     description
+      "The dscp type represents a Differentiated Services Code-Point
+       that may be used for marking packets in a traffic stream.
+
+       In the value set and its semantics, this type is equivalent
+       to the Dscp textual convention of the SMIv2.";
+     reference
+      "RFC 3289: Management Information Base for the Differentiated
+                 Services Architecture
+       RFC 2474: Definition of the Differentiated Services Field
+                 (DS Field) in the IPv4 and IPv6 Headers
+       RFC 2780: IANA Allocation Guidelines For Values In
+                 the Internet Protocol and Related Headers";
+   }
+
+   typedef ipv6-flow-label {
+     type uint32 {
+       range "0..1048575";
+     }
+     description
+      "The flow-label type represents flow identifier or Flow Label
+       in an IPv6 packet header that may be used to discriminate
+       traffic flows.
+
+       In the value set and its semantics, this type is equivalent
+       to the IPv6FlowLabel textual convention of the SMIv2.";
+     reference
+      "RFC 3595: Textual Conventions for IPv6 Flow Label
+       RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
+   }
+
+   typedef port-number {
+     type uint16 {
+       range "0..65535";
+     }
+     description
+      "The port-number type represents a 16-bit port number of an
+       Internet transport layer protocol such as UDP, TCP, DCCP, or
+       SCTP.  Port numbers are assigned by IANA.  A current list of
+       all assignments is available from <http://www.iana.org/>.
+
+       Note that the port number value zero is reserved by IANA.  In
+       situations where the value zero does not make sense, it can
+       be excluded by subtyping the port-number type.
+
+       In the value set and its semantics, this type is equivalent
+       to the InetPortNumber textual convention of the SMIv2.";
+     reference
+      "RFC  768: User Datagram Protocol
+       RFC  793: Transmission Control Protocol
+       RFC 4960: Stream Control Transmission Protocol
+       RFC 4340: Datagram Congestion Control Protocol (DCCP)
+       RFC 4001: Textual Conventions for Internet Network Addresses";
+   }
+
+   /*** collection of autonomous system related types ***/
+
+   typedef as-number {
+     type uint32;
+     description
+      "The as-number type represents autonomous system numbers
+       which identify an Autonomous System (AS).  An AS is a set
+       of routers under a single technical administration, using
+       an interior gateway protocol and common metrics to route
+       packets within the AS, and using an exterior gateway
+       protocol to route packets to other ASs'.  IANA maintains
+       the AS number space and has delegated large parts to the
+       regional registries.
+
+       Autonomous system numbers were originally limited to 16
+       bits.  BGP extensions have enlarged the autonomous system
+       number space to 32 bits.  This type therefore uses an uint32
+       base type without a range restriction in order to support
+       a larger autonomous system number space.
+
+       In the value set and its semantics, this type is equivalent
+       to the InetAutonomousSystemNumber textual convention of
+       the SMIv2.";
+     reference
+      "RFC 1930: Guidelines for creation, selection, and registration
+                 of an Autonomous System (AS)
+       RFC 4271: A Border Gateway Protocol 4 (BGP-4)
+       RFC 4893: BGP Support for Four-octet AS Number Space
+       RFC 4001: Textual Conventions for Internet Network Addresses";
+   }
+
+   /*** collection of IP address and hostname related types ***/
+
+   typedef ip-address {
+     type union {
+       type inet:ipv4-address;
+       type inet:ipv6-address;
+     }
+     description
+      "The ip-address type represents an IP address and is IP
+       version neutral.  The format of the textual representations
+       implies the IP version.";
+   }
+
+   typedef ipv4-address {
+     type string {
+       pattern
+         '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+       +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+       + '(%[\p{N}\p{L}]+)?';
+     }
+     description
+       "The ipv4-address type represents an IPv4 address in
+        dotted-quad notation.  The IPv4 address may include a zone
+        index, separated by a % sign.
+
+        The zone index is used to disambiguate identical address
+        values.  For link-local addresses, the zone index will
+        typically be the interface index number or the name of an
+        interface.  If the zone index is not present, the default
+        zone of the device will be used.
+
+        The canonical format for the zone index is the numerical
+        format";
+   }
+
+   typedef ipv6-address {
+     type string {
+       pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+             + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+             + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+             + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+             + '(%[\p{N}\p{L}]+)?';
+       pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+             + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+             + '(%.+)?';
+     }
+     description
+      "The ipv6-address type represents an IPv6 address in full,
+       mixed, shortened, and shortened-mixed notation.  The IPv6
+       address may include a zone index, separated by a % sign.
+
+       The zone index is used to disambiguate identical address
+       values.  For link-local addresses, the zone index will
+       typically be the interface index number or the name of an
+       interface.  If the zone index is not present, the default
+       zone of the device will be used.
+
+       The canonical format of IPv6 addresses uses the compressed
+       format described in RFC 4291, Section 2.2, item 2 with the
+       following additional rules: the :: substitution must be
+       applied to the longest sequence of all-zero 16-bit chunks
+       in an IPv6 address.  If there is a tie, the first sequence
+       of all-zero 16-bit chunks is replaced by ::.  Single
+       all-zero 16-bit chunks are not compressed.  The canonical
+       format uses lowercase characters and leading zeros are
+       not allowed.  The canonical format for the zone index is
+       the numerical format as described in RFC 4007, Section
+       11.2.";
+     reference
+      "RFC 4291: IP Version 6 Addressing Architecture
+       RFC 4007: IPv6 Scoped Address Architecture
+       RFC 5952: A Recommendation for IPv6 Address Text Representation";
+   }
+
+   typedef ip-prefix {
+     type union {
+       type inet:ipv4-prefix;
+       type inet:ipv6-prefix;
+     }
+     description
+      "The ip-prefix type represents an IP prefix and is IP
+       version neutral.  The format of the textual representations
+       implies the IP version.";
+   }
+
+   typedef ipv4-prefix {
+     type string {
+       pattern
+          '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+        +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+        + '/(([0-9])|([1-2][0-9])|(3[0-2]))';
+     }
+     description
+      "The ipv4-prefix type represents an IPv4 address prefix.
+       The prefix length is given by the number following the
+       slash character and must be less than or equal to 32.
+
+       A prefix length value of n corresponds to an IP address
+       mask that has n contiguous 1-bits from the most
+       significant bit (MSB) and all other bits set to 0.
+
+       The canonical format of an IPv4 prefix has all bits of
+       the IPv4 address set to zero that are not part of the
+       IPv4 prefix.";
+   }
+
+   typedef ipv6-prefix {
+     type string {
+       pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+             + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+             + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+             + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+             + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
+       pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+             + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+             + '(/.+)';
+     }
+     description
+      "The ipv6-prefix type represents an IPv6 address prefix.
+       The prefix length is given by the number following the
+       slash character and must be less than or equal 128.
+
+       A prefix length value of n corresponds to an IP address
+       mask that has n contiguous 1-bits from the most
+       significant bit (MSB) and all other bits set to 0.
+
+       The IPv6 address should have all bits that do not belong
+       to the prefix set to zero.
+
+       The canonical format of an IPv6 prefix has all bits of
+       the IPv6 address set to zero that are not part of the
+       IPv6 prefix.  Furthermore, IPv6 address is represented
+       in the compressed format described in RFC 4291, Section
+       2.2, item 2 with the following additional rules: the ::
+       substitution must be applied to the longest sequence of
+       all-zero 16-bit chunks in an IPv6 address.  If there is
+       a tie, the first sequence of all-zero 16-bit chunks is
+       replaced by ::.  Single all-zero 16-bit chunks are not
+       compressed.  The canonical format uses lowercase
+       characters and leading zeros are not allowed.";
+     reference
+      "RFC 4291: IP Version 6 Addressing Architecture";
+   }
+
+   /*** collection of domain name and URI types ***/
+
+   typedef domain-name {
+     type string {
+       pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+            +  '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+            +  '|\.';
+       length "1..253";
+     }
+     description
+      "The domain-name type represents a DNS domain name.  The
+       name SHOULD be fully qualified whenever possible.
+
+       Internet domain names are only loosely specified.  Section
+       3.5 of RFC 1034 recommends a syntax (modified in Section
+       2.1 of RFC 1123).  The pattern above is intended to allow
+       for current practice in domain name use, and some possible
+       future expansion.  It is designed to hold various types of
+       domain names, including names used for A or AAAA records
+       (host names) and other records, such as SRV records.  Note
+       that Internet host names have a stricter syntax (described
+       in RFC 952) than the DNS recommendations in RFCs 1034 and
+       1123, and that systems that want to store host names in
+       schema nodes using the domain-name type are recommended to
+       adhere to this stricter standard to ensure interoperability.
+
+       The encoding of DNS names in the DNS protocol is limited
+       to 255 characters.  Since the encoding consists of labels
+       prefixed by a length bytes and there is a trailing NULL
+       byte, only 253 characters can appear in the textual dotted
+       notation.
+
+       The description clause of schema nodes using the domain-name
+       type MUST describe when and how these names are resolved to
+       IP addresses.  Note that the resolution of a domain-name value
+       may require to query multiple DNS records (e.g., A for IPv4
+       and AAAA for IPv6).  The order of the resolution process and
+       which DNS record takes precedence can either be defined
+       explicitely or it may depend on the configuration of the
+       resolver.
+
+       Domain-name values use the US-ASCII encoding.  Their canonical
+       format uses lowercase US-ASCII characters.  Internationalized
+       domain names MUST be encoded in punycode as described in RFC
+       3492";
+     reference
+      "RFC  952: DoD Internet Host Table Specification
+       RFC 1034: Domain Names - Concepts and Facilities
+       RFC 1123: Requirements for Internet Hosts -- Application
+                 and Support
+       RFC 2782: A DNS RR for specifying the location of services
+                 (DNS SRV)
+       RFC 3492: Punycode: A Bootstring encoding of Unicode for
+                 Internationalized Domain Names in Applications
+                 (IDNA)
+       RFC 5891: Internationalizing Domain Names in Applications
+                 (IDNA): Protocol";
+   }
+
+   typedef host {
+     type union {
+       type inet:ip-address;
+       type inet:domain-name;
+     }
+     description
+      "The host type represents either an IP address or a DNS
+       domain name.";
+   }
+
+   typedef uri {
+     type string;
+     description
+      "The uri type represents a Uniform Resource Identifier
+       (URI) as defined by STD 66.
+
+       Objects using the uri type MUST be in US-ASCII encoding,
+       and MUST be normalized as described by RFC 3986 Sections
+       6.2.1, 6.2.2.1, and 6.2.2.2.  All unnecessary
+       percent-encoding is removed, and all case-insensitive
+       characters are set to lowercase except for hexadecimal
+       digits, which are normalized to uppercase as described in
+       Section 6.2.2.1.
+
+       The purpose of this normalization is to help provide
+       unique URIs.  Note that this normalization is not
+       sufficient to provide uniqueness.  Two URIs that are
+       textually distinct after this normalization may still be
+       equivalent.
+
+       Objects using the uri type may restrict the schemes that
+       they permit.  For example, 'data:' and 'urn:' schemes
+       might not be appropriate.
+
+       A zero-length URI is not a valid URI.  This can be used to
+       express 'URI absent' where required.
+
+       In the value set and its semantics, this type is equivalent
+       to the Uri SMIv2 textual convention defined in RFC 5017.";
+     reference
+      "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
+       RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
+                 Group: Uniform Resource Identifiers (URIs), URLs,
+                 and Uniform Resource Names (URNs): Clarifications
+                 and Recommendations
+       RFC 5017: MIB Textual Conventions for Uniform Resource
+                 Identifiers (URIs)";
+   }
+
+ }
diff --git a/yang/yang-data-impl/src/test/resources/leafref-context-test/correct-modules/import-mod.yang b/yang/yang-data-impl/src/test/resources/leafref-context-test/correct-modules/import-mod.yang
new file mode 100644 (file)
index 0000000..826a339
--- /dev/null
@@ -0,0 +1,10 @@
+module import-mod {
+    namespace "pk.import";
+    prefix imp;
+
+    revision 2014-10-07 {
+        description
+                "Yang initial revision";
+    }
+
+}
diff --git a/yang/yang-data-impl/src/test/resources/leafref-context-test/correct-modules/leafref-test.yang b/yang/yang-data-impl/src/test/resources/leafref-context-test/correct-modules/leafref-test.yang
new file mode 100644 (file)
index 0000000..1bf8f42
--- /dev/null
@@ -0,0 +1,152 @@
+module leafref-test {
+    namespace "pk.test";
+    prefix tst;
+
+    import import-mod { prefix imp; revision-date 2014-10-07; }
+
+    revision 1999-09-09 {
+        description
+                "Yang initial revision";
+    }
+
+    container odl-contributor {
+        list contributor {
+            key "login";
+            leaf login {
+                type string;
+            }
+            leaf contributor-name {
+                type string;
+            }
+            leaf odl-project-name {
+                type leafref {
+                    path "/odl-project/project/name";
+                }
+            }
+        }
+        list noleafref-contributor {
+            leaf foo {
+                type string;
+            }
+        }
+    }
+
+    container odl-project {
+        list project {
+            key "name";
+            leaf name {
+                type string;
+            }
+            leaf project-lead {
+                type leafref {
+                    path "../../../tst:odl-contributor[imp:foo=current()/bar]/contributor[tst:odl-project-name
+                                        = current()/../imp:name][odl-project-name2 = current()/../../imp:name/tst:name2][imp:odl-project-name3
+                                        = current()/../../imp:name/imp:name2]/tst:login";
+                }
+            }
+            leaf project-lead2 {
+                type leafref {
+                    path "/odl-contributor[foo=current()/bar]/contributor[odl-project-name
+                                        = current()/../name][odl-project-name2 = current()/../../name/name2][odl-project-name3
+                                        = current()/../../name/name2]/login";
+                }
+            }
+
+            choice ch1 {
+                case c1 {
+                    choice ch2 {
+                        case c1 {
+                            leaf l1 {
+                                type leafref {
+                                    path "../../con1/l1";
+                                }
+                            }
+                        }
+                        case c2 {
+                        }
+                    }
+                }
+                case c2 {
+                }
+            }
+        }
+        list noleafref-project {
+            leaf foo {
+                type string;
+            }
+        }
+        container con1 {
+            leaf l1 {
+                type empty;
+            }
+        }
+    }
+
+    container con1 {
+        container con2 {
+            container con3 {
+                leaf l1 {
+                    type leafref {
+                        path "/odl-project/project/name";
+                    }
+                }
+                leaf l2 {
+                    type leafref {
+                        path "/odl-project/project/name";
+                    }
+                }
+                leaf l3-noleafref {
+                    type int16;
+                }
+            }
+            leaf l4 {
+                type leafref {
+                    path "/odl-project/project/name";
+                }
+            }
+            leaf l5-noleafref {
+                type int16;
+            }
+        }
+        leaf l6 {
+            type leafref {
+                path "/odl-project/project/name";
+            }
+        }
+        leaf l7-noleafref {
+            type int16;
+        }
+    }
+
+    leaf l8 {
+        type leafref {
+            path "/odl-project/project/name";
+        }
+    }
+
+    container no-leafrefcontainer {
+        list no-leafreflist {
+            leaf bar {
+                type string;
+            }
+        }
+        container no-leafrefcontainer2 {
+            leaf baz {
+                type string;
+            }
+        }
+    }
+
+    container no-leafrefcontainer2 {
+        list no-leafreflist {
+            leaf bar {
+                type string;
+            }
+        }
+        container no-leafrefcontainer2 {
+            leaf baz {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/leafref-context-test/correct-modules/leafref-test2.yang b/yang/yang-data-impl/src/test/resources/leafref-context-test/correct-modules/leafref-test2.yang
new file mode 100644 (file)
index 0000000..b707a70
--- /dev/null
@@ -0,0 +1,83 @@
+module leafref-test2 {
+    namespace "gz.test";
+    prefix "main";
+
+    revision 2015-02-13 {
+        description "Initial revision";
+    }
+
+    leaf leaf1 {
+        type int16;
+    }
+
+    leaf ref1 {
+        type leafref {
+            path "../leaf1";
+        }
+    }
+
+    typedef low_int {
+        type int8 {
+            range 1..100;
+        }
+    }
+
+    container cont1 {
+
+        list list1 {
+            leaf name {
+                type string;
+            }
+            leaf id {
+                type int32;
+            }
+            leaf value {
+                type low_int;
+            }
+            leaf external {
+                type leafref {
+                    path "../../../cont2/value";
+                }
+            }
+        }
+    }
+
+    container cont2 {
+        leaf value {
+            type decimal64 {
+                fraction-digits 4;
+            }
+        }
+    }
+
+    container cont3 {
+        container cont4 {
+            leaf l1 {
+                type leafref {
+                    path "/cont1/list1/name";
+                }
+            }
+            leaf l2 {
+                type leafref {
+                    path "../../../cont1/list1/name";
+                }
+            }
+        }
+        leaf l3 {
+            type leafref {
+                path "/cont1/list1/name";
+            }
+        }
+        leaf l4 {
+            type int32;
+        }
+    }
+
+    container cont5 {
+        leaf l5 {
+            type leafref {
+                path "/cont1/list1/name";
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/leafref-context-test/incorrect-modules/leafref-test.yang b/yang/yang-data-impl/src/test/resources/leafref-context-test/incorrect-modules/leafref-test.yang
new file mode 100644 (file)
index 0000000..ae33ffb
--- /dev/null
@@ -0,0 +1,114 @@
+module leafref-test {
+    namespace "test";
+    prefix test;
+
+    container odl-contributor {
+        list contributor {
+            key "login";
+            leaf login {
+                type string;
+            }
+            leaf contributor-name {
+                type string;
+            }
+            leaf odl-project-name {
+                type leafref {
+                    path ".../odl-project/project/name";
+                }
+            }
+        }
+        list noleafref-contributor {
+            leaf foo {
+                type string;
+            }
+        }
+    }
+
+    container odl-project {
+        list project {
+            key "name";
+            leaf name {
+                type string;
+            }
+            leaf project-lead {
+                type leafref {
+                    path "/odl-contributor/contributor/login";
+                }
+            }
+        }
+        list noleafref-project {
+            leaf foo {
+                type string;
+            }
+        }
+    }
+
+    container con1 {
+        container con2 {
+            container con3 {
+                leaf l1 {
+                    type leafref {
+                        path "/odl-project/project/name";
+                    }
+                }
+                leaf l2 {
+                    type leafref {
+                        path "/odl-project/project/name";
+                    }
+                }
+                leaf l3-noleafref {
+                    type int16;
+                }
+            }
+            leaf l4 {
+                type leafref {
+                    path "/odl-project/project/name";
+                }
+            }
+            leaf l5-noleafref {
+                type int16;
+            }
+        }
+        leaf l6 {
+            type leafref {
+                path "/odl-project/project/name";
+            }
+        }
+        leaf l7-noleafref {
+            type int16;
+        }
+    }
+
+    leaf l8 {
+        type leafref {
+            path "/odl-project/project/name";
+        }
+    }
+
+    container no-leafrefcontainer {
+        list no-leafreflist {
+            leaf bar {
+                type string;
+            }
+        }
+        container no-leafrefcontainer2 {
+            leaf baz {
+                type string;
+            }
+        }
+    }
+
+    container no-leafrefcontainer2 {
+        list no-leafreflist {
+            leaf bar {
+                type string;
+            }
+        }
+        container no-leafrefcontainer2 {
+            leaf baz {
+                type string;
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-data-impl/src/test/resources/leafref-test.yang b/yang/yang-data-impl/src/test/resources/leafref-test.yang
new file mode 100644 (file)
index 0000000..6851300
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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
+ */
+module leafref-test {
+    yang-version 1;
+    namespace "urn:opendaylight:yangtools:leafref:test";
+    prefix "lt";
+
+    revision 2014-11-04 {
+        description "Test deserialization value of leafref type.";
+    }
+
+    identity test-identity-base {
+    }
+
+    identity test-identity {
+      base lt:test-identity-base;
+    }
+
+    container interface {
+        leaf simpleValue {
+            type instance-identifier;
+        }
+    }
+
+    container cont2 {
+        container cont3 {
+            leaf leafname3 {
+                type leafref {
+                    path "../../pointToStringLeaf";
+                }
+            }
+        }
+        leaf pointToStringLeaf {
+            type leafref {
+                path "../stringleaf";
+            }
+        }
+
+        leaf point-to-identityrefleaf {
+            type leafref {
+                path "../identityrefleaf";
+            }
+        }
+
+        leaf identityrefleaf {
+            type identityref {
+              base lt:test-identity-base;
+            }
+        }
+
+        leaf stringleaf {
+            type string;
+        }
+        leaf absname {
+            type leafref {
+                path "/lt:interface/lt:simpleValue";
+            }
+        }
+        leaf relname {
+            type leafref {
+                path "../../lt:interface/lt:simpleValue";
+            }
+        }
+
+        leaf lf-with-double-point-inside {
+            type leafref {
+                path "../../lt:interface/../lt:cont2/lt:stringleaf";
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/leafref-validation/leafref-validation.yang b/yang/yang-data-impl/src/test/resources/leafref-validation/leafref-validation.yang
new file mode 100644 (file)
index 0000000..60e2df8
--- /dev/null
@@ -0,0 +1,150 @@
+module leafref-validation {
+    namespace "leafref.validation";
+    prefix val;
+
+    leaf l1 {
+        type leafref {
+            path "../l2";
+        }
+    }
+
+    leaf l2 {
+        type string;
+    }
+
+    container odl-contributor {
+        list contributor {
+            key "login";
+            leaf login {
+                type string;
+            }
+            leaf contributor-name {
+                type string;
+            }
+            leaf odl-group-id {
+                type string;
+            }
+            leaf odl-project-name {
+                type leafref {
+                    path "../../../odl-project/project/name";
+                }
+            }
+            leaf odl-project-desc {
+                type leafref {
+                    path "/odl-project/project[name = current()/../odl-project-name]/desc";
+                }
+                // type string;                                    
+            }
+        }
+        list noleafref-contributor {
+            leaf foo {
+                type string;
+            }
+        }
+    }
+
+    container odl-project {
+        list project {
+            key "name";
+            leaf name {
+                type string;
+            }
+            leaf desc {
+                type string;
+            }
+            leaf project-lead {
+                type leafref {
+                    path "../../../odl-contributor/contributor/login";
+                }
+            }
+            leaf project-owner {
+                type leafref {
+                    path "/odl-contributor/contributor/login";
+                }
+            }
+        }
+
+        choice ch1 {
+            case c1 {
+                choice ch2 {
+                    case c1 {
+                        list list-in-choice {
+                            key "list-in-choice-key";
+                            leaf list-in-choice-key {
+                                type string;
+                            }
+                            leaf leafref-in-choice {
+                                type leafref {
+                                    path "../../con1/l1";
+                                }
+                            }
+                            leaf leafref-in-choice-to-choice {
+                                type leafref {
+                                    path "../../con3/list3-in-choice/l3";
+                                }
+                            }
+                        }
+                    }
+                    case c2 {
+                    }
+                }
+            }
+            case c2 {
+            }
+        }
+
+        list noleafref-project {
+            leaf foo {
+                type string;
+            }
+        }
+        container con1 {
+            leaf l1 {
+                type string;
+            }
+        }
+
+        container con3 {
+            choice choice-in-con3 {
+                case one {
+                    list list3-in-choice {
+                        key "k";
+                        leaf k {
+                            type string;
+                        }
+                        leaf-list l3 {
+                            type string;
+                        }
+                    }
+                }
+                case two {
+                    list list3-in-choice-2 {
+                        key "l3-2";
+                        leaf l3-2 {
+                            type string;
+                        }
+                    }
+                }
+            }
+        }
+
+        leaf-list leafref-leaf-list {
+            type leafref {
+                path "../con3/list3-in-choice/k";
+            }
+        }
+    }
+
+    container no-leafrefcontainer {
+        list no-leafreflist {
+            leaf bar {
+                type string;
+            }
+        }
+        container no-leafrefcontainer2 {
+            leaf baz {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/leafref-validation/leafref-validation2.yang b/yang/yang-data-impl/src/test/resources/leafref-validation/leafref-validation2.yang
new file mode 100644 (file)
index 0000000..0a103e3
--- /dev/null
@@ -0,0 +1,52 @@
+module leafref-validation2 {
+    namespace "leafref.validation2";
+    prefix val2;
+
+    container device_types {
+        list device_type {
+            key "type";
+            leaf type {
+                type string;
+            }
+            leaf desc {
+                type string;
+            }
+        }
+    }
+
+    container devices {
+        list device {
+            key "type_text sn";
+            leaf type_text {
+                type leafref {
+                    path "/device_types/device_type/type";
+                }
+            }
+            leaf dev_desc {
+                type leafref {
+                    path "/device_types/device_type[type = current()/../type_text]/desc";
+                }
+            }
+            leaf sn {
+                type int32;
+            }
+            leaf default_ip {
+                type string;
+            }
+        }
+    }
+
+    container chips {
+        list chip {
+            key "dev_type";
+            leaf dev_type {
+                type leafref {
+                    path "/devices/device/type_text";
+                }
+            }
+            leaf chip_desc {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/leafref-validation/leafref-validation3.yang b/yang/yang-data-impl/src/test/resources/leafref-validation/leafref-validation3.yang
new file mode 100644 (file)
index 0000000..6823e10
--- /dev/null
@@ -0,0 +1,70 @@
+module leafref-validation3 {
+    namespace "leafref.validation3";
+    prefix val3;
+
+    container device_types {
+        list device_type {
+            key "type1 type2 type3";
+            leaf type1 {
+                type string;
+            }
+            leaf type2 {
+                type string;
+            }
+            leaf type3 {
+                type string;
+            }
+            leaf desc {
+                type string;
+            }
+        }
+    }
+
+    container devices {
+        list device {
+            key "type_text1 sn";
+            unique "sn";
+            leaf type_text1 {
+                type leafref {
+                    path "/device_types/device_type/type1";
+                }
+            }
+            leaf type_text2 {
+                type leafref {
+                    path "/device_types/device_type/type2";
+                }
+            }
+            leaf type_text3 {
+                type leafref {
+                    path "/device_types/device_type/type3";
+                }
+            }
+            leaf dev_desc {
+                type leafref {
+                    path "/device_types/device_type[type1 = current()/../type_text1][type2
+                                        = current()/../type_text2][type3 = current()/../type_text3]/desc";
+                }
+            }
+            leaf sn {
+                type int32;
+            }
+            leaf default_ip {
+                type string;
+            }
+        }
+    }
+
+    container chips {
+        list chip {
+            key "dev_type";
+            leaf dev_type {
+                type leafref {
+                    path "/devices/device/type_text";
+                }
+            }
+            leaf chip_desc {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/list-constraints-validation-test-model.yang b/yang/yang-data-impl/src/test/resources/list-constraints-validation-test-model.yang
new file mode 100644 (file)
index 0000000..f7b2a40
--- /dev/null
@@ -0,0 +1,47 @@
+module list-constraints-validation-test-model  {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model";
+    prefix "list-constraints-validation";
+
+    revision "2015-02-02" {
+        description "Initial revision.";
+    }
+
+    container master-container {
+        list min-max-list {
+            min-elements 2;
+            max-elements 3;
+
+            key "min-max-key-leaf";
+
+            leaf min-max-key-leaf {
+                type string;
+            }
+        }
+
+        list unbounded-list {
+            key "unbounded-key-leaf";
+
+            leaf unbounded-key-leaf {
+                type int8;
+            }
+        }
+
+        leaf-list min-max-leaf-list {
+            min-elements 1;
+            max-elements 3;
+            type string;
+        }
+
+        leaf-list unbounded-leaf-list {
+            type string;
+        }
+
+        list unkeyed-list {
+            max-elements 1;
+            leaf unkeyed-leaf {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/log4j-test.xml b/yang/yang-data-impl/src/test/resources/log4j-test.xml
new file mode 100644 (file)
index 0000000..1a64040
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+    <appender name="console" class="org.apache.log4j.ConsoleAppender">
+       <layout class="org.apache.log4j.PatternLayout">
+          <param name="ConversionPattern" value="%-6p %d{HH:mm:ss.SSS} [%t] %42.42c %x - %m%n"/>
+       </layout>
+    </appender>
+
+    <logger name="org.opendaylight.yangtools.yang.data.impl" additivity="false">
+        <level value="DEBUG" />
+        <appender-ref ref="console"/>
+    </logger>
+    <logger name="org.opendaylight.yangtools.yang.data.impl.MyNodeBuilder" additivity="false">
+        <level value="INFO" />
+        <appender-ref ref="console"/>
+    </logger>
+
+    <root>
+        <priority value="INFO"/>
+        <appender-ref ref="console" />
+    </root>
+</log4j:configuration>
diff --git a/yang/yang-data-impl/src/test/resources/mandatory-leaf-test.yang b/yang/yang-data-impl/src/test/resources/mandatory-leaf-test.yang
new file mode 100644 (file)
index 0000000..32e92e4
--- /dev/null
@@ -0,0 +1,74 @@
+module mandatory-leaf-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+    prefix "store-test";
+
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    container test {
+        presence true;
+        config true;
+        choice choice1 {
+            case case1 {
+                leaf case1-leaf1 {
+                    type string;
+                }
+            }
+            case case2 {
+                container case2-cont {
+                    leaf case2-leaf1 {
+                        mandatory true;
+                        type string;
+                    }
+                    leaf case2-leaf2 {
+                        type string;
+                    }
+                }
+            }
+        }
+
+        list outer-list {
+
+            key id;
+            leaf id {
+                type uint16;
+            }
+            choice outer-choice {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+                }
+            }
+            list inner-list {
+                key name;
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+
+            list inner-list2 {
+                key name;
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/odl-datastore-test.yang b/yang/yang-data-impl/src/test/resources/odl-datastore-test.yang
new file mode 100644 (file)
index 0000000..43143f2
--- /dev/null
@@ -0,0 +1,103 @@
+module odl-datastore-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+    prefix "store-test";
+
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    container non-presence {
+        description "Deep structure of (structural) nodes to test structural ";
+
+        leaf name {
+            type string;
+        }
+
+        choice deep-choice {
+            list a-list {
+                key "a-name";
+
+                leaf a-name {
+                    type string;
+                }
+            }
+
+            container b-container {
+                leaf b-name {
+                    type string;
+                }
+            }
+        }
+    }
+
+    container test {
+        presence true;
+        choice choice1 {
+            case case1 {
+                leaf case1-leaf1 {
+                    type string;
+                    config false;
+                }
+            }
+            case case2 {
+                container case2-cont {
+                    leaf case2-leaf1 {
+                        type string;
+                        config false;
+                    }
+                }
+            }
+        }
+
+        list outer-list {
+
+            key id;
+            leaf id {
+                type uint16;
+            }
+            choice outer-choice {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+               }
+            }
+            list inner-list {
+                config false;
+                key name;
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+
+            list inner-list2 {
+                key name;
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                    config false;
+                }
+            }
+        }
+
+        container inner-container {
+            leaf value {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/ordered-list-modification-test.yang b/yang/yang-data-impl/src/test/resources/ordered-list-modification-test.yang
new file mode 100644 (file)
index 0000000..272ef5c
--- /dev/null
@@ -0,0 +1,36 @@
+module ordered-list-modification-test {
+    namespace "ordered-list-modification-test";
+    prefix "olmt";
+
+    container parent-container {
+        container child-container {
+            list parent-ordered-list {
+                ordered-by user;
+
+                key "parent-key-leaf";
+
+                leaf parent-key-leaf {
+                    type string;
+                }
+
+                leaf parent-ordinary-leaf {
+                    type string;
+                }
+
+                list child-ordered-list {
+                    ordered-by user;
+
+                    key "child-key-leaf";
+
+                    leaf child-key-leaf {
+                        type string;
+                    }
+
+                    leaf child-ordinary-leaf {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/codec/xml/iid-test.yang b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/codec/xml/iid-test.yang
new file mode 100644 (file)
index 0000000..2265d2d
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module iid-test {
+    yang-version 1;
+    namespace "urn:opendaylight:controller:iid:test";
+    prefix "iidt";
+
+    revision 2014-07-28 {
+       description "Initial test";
+    }
+
+    container cont {
+
+        list l {
+            key "id";
+
+            leaf id {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/codec/xml/xml-doc-test.yang b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/codec/xml/xml-doc-test.yang
new file mode 100644 (file)
index 0000000..8efbea4
--- /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
+ */
+module xml-doc-test {
+    yang-version 1;
+    namespace "urn:opendaylight:controller:xml:doc:test";
+    prefix "xmldt";
+
+    revision 2014-07-28 {
+       description "Initial test";
+    }
+
+    container cont {
+
+        list l {
+            key "id";
+
+            leaf id {
+                type string;
+            }
+        }
+    }
+
+    typedef custom-instance-identifier {
+        type instance-identifier;
+    }
+
+    rpc test {
+        input {
+            leaf a {
+                type string;
+            }
+
+            leaf ref {
+                type custom-instance-identifier;
+            }
+        }
+    }
+
+
+}
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/codec/xml/xml-doc-test2.yang b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/codec/xml/xml-doc-test2.yang
new file mode 100644 (file)
index 0000000..88b8f15
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module xml-doc-test-2 {
+    yang-version 1;
+    namespace "urn:opendaylight:controller:xml:doc:test2";
+    prefix "xmldt";
+
+    revision 2014-07-28 {
+       description "Initial test";
+    }
+
+    container cont2 {
+
+        list l {
+            key "id";
+
+            leaf id {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/config02-shadow.xml b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/config02-shadow.xml
new file mode 100644 (file)
index 0000000..0025bfc
--- /dev/null
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<network xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <topologies>
+        <topology>
+            <topology-id>topId_01</topology-id>
+            <nodes>
+                <node>
+                    <node-id>nodeId_19</node-id>
+                    <supporting-ne>networkId_20</supporting-ne>
+                    <termination-points>
+                        <termination-point>
+                            <tp-id>tpId_19</tp-id>
+                        </termination-point>
+                        <termination-point>
+                            <tp-id>tpId_18</tp-id>
+                        </termination-point>
+                    </termination-points>
+                </node>
+                <node>
+                    <node-id>nodeId_16</node-id>
+                    <supporting-ne>networkId_17</supporting-ne>
+                    <termination-points>
+                        <termination-point>
+                            <tp-id>tpId_18</tp-id>
+                        </termination-point>
+                    </termination-points>
+                </node>
+                <node>
+                    <node-id>nodeId_02</node-id>
+                    <supporting-ne>networkId_02</supporting-ne>
+                    <termination-points>
+                        <termination-point>
+                            <tp-id>tpId_03</tp-id>
+                        </termination-point>
+                    </termination-points>
+                </node>
+            </nodes>
+            <links>
+                <link>
+                    <destination>
+                        <dest-tp>tpId_15</dest-tp>
+                        <dest-node>nodeId_14</dest-node>
+                    </destination>
+                    <source>
+                        <source-tp>tpId_13</source-tp>
+                        <source-node>nodeId_12</source-node>
+                    </source>
+                    <link-id>linkId_11</link-id>
+                </link>
+                <link>
+                    <destination>
+                        <dest-tp>tpId_08</dest-tp>
+                        <dest-node>nodeId_07</dest-node>
+                    </destination>
+                    <source>
+                        <source-tp>tpId_06</source-tp>
+                        <source-node>nodeId_05</source-node>
+                    </source>
+                    <link-id>linkId_04</link-id>
+                </link>
+            </links>
+        </topology>
+    </topologies>
+    <network-elements>
+        <network-element>
+            <element-id>ntElementId_10</element-id>
+        </network-element>
+        <network-element>
+            <element-id>ntElementId_09</element-id>
+        </network-element>
+    </network-elements>
+</network>
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/config02.groovy b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/config02.groovy
new file mode 100644 (file)
index 0000000..d88a047
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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
+ */
+def data = {
+ network(xmlns: 'urn:opendaylight:controller:network') {
+    topologies {
+      topology {
+        'topology-id'('topId_01')
+        
+        //types()
+        nodes {
+          node {
+            'node-id'('nodeId_02')
+            'supporting-ne'('networkId_03')
+            'termination-points' {
+              'termination-point' {
+                'tp-id'('tpId_04')
+              }
+            }
+          }
+          node {
+            'node-id'('nodeId_05')
+            'supporting-ne'('networkId_06')
+            'termination-points' {
+              'termination-point' {
+                'tp-id'('tpId_07')
+              }
+            }
+          }
+          node {
+            'node-id'('nodeId_08')
+            'supporting-ne'('networkId_09')
+            'termination-points' {
+              'termination-point' {
+                'tp-id'('tpId_10')
+              }
+              'termination-point' {
+                'tp-id'('tpId_11')
+              }
+            }
+          }
+        }
+        links {
+          link {
+            'link-id'('linkId_12')
+            source {
+              'source-node'('nodeId_13')
+              'source-tp'('tpId_13')
+            }
+            destination {
+              'dest-node'('nodeId_14')
+              'dest-tp'('tpId_14')
+            }
+          }
+          link {
+            'link-id'('linkId_15')
+            source {
+              'source-node'('nodeId_16')
+              'source-tp'('tpId_16')
+            }
+            destination {
+              'dest-node'('nodeId_17')
+              'dest-tp'('tpId_17')
+            }
+          }
+        }
+      }
+    }
+    'network-elements' {
+      'network-element' {
+        'element-id'('ntElementId_18')
+      }
+      'network-element' {
+        'element-id'('ntElementId_19')
+      }
+    }
+  }
+}
+
+System.err.println('data inited')
+
+import MyXmlGenerator
+
+xmlGen = new MyXmlGenerator()
+xmlGen.buildTree(data)
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/config02g-shadow.xml b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/config02g-shadow.xml
new file mode 100644 (file)
index 0000000..26b6a3c
--- /dev/null
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<network xmlns="urn:opendaylight:controller:network">
+    <topologies>
+        <topology>
+            <links>
+                <link>
+                    <destination>
+                        <dest-tp>tpId_17</dest-tp>
+                        <dest-node>nodeId_17</dest-node>
+                    </destination>
+                    <source>
+                        <source-tp>tpId_16</source-tp>
+                        <source-node>nodeId_16</source-node>
+                    </source>
+                    <link-id>linkId_15</link-id>
+                </link>
+                <link>
+                    <destination>
+                        <dest-tp>tpId_14</dest-tp>
+                        <dest-node>nodeId_14</dest-node>
+                    </destination>
+                    <source>
+                        <source-tp>tpId_13</source-tp>
+                        <source-node>nodeId_13</source-node>
+                    </source>
+                    <link-id>linkId_12</link-id>
+                </link>
+            </links>
+            <nodes>
+                <node>
+                    <termination-points>
+                        <termination-point>
+                            <tp-id>tpId_11</tp-id>
+                        </termination-point>
+                        <termination-point>
+                            <tp-id>tpId_10</tp-id>
+                        </termination-point>
+                    </termination-points>
+                    <supporting-ne>networkId_09</supporting-ne>
+                    <node-id>nodeId_08</node-id>
+                </node>
+                <node>
+                    <termination-points>
+                        <termination-point>
+                            <tp-id>tpId_07</tp-id>
+                        </termination-point>
+                    </termination-points>
+                    <supporting-ne>networkId_06</supporting-ne>
+                    <node-id>nodeId_05</node-id>
+                </node>
+                <node>
+                    <termination-points>
+                        <termination-point>
+                            <tp-id>tpId_04</tp-id>
+                        </termination-point>
+                    </termination-points>
+                    <supporting-ne>networkId_03</supporting-ne>
+                    <node-id>nodeId_02</node-id>
+                </node>
+            </nodes>
+            <topology-id>topId_01</topology-id>
+        </topology>
+    </topologies>
+    <network-elements>
+        <network-element>
+            <element-id>ntElementId_19</element-id>
+        </network-element>
+        <network-element>
+            <element-id>ntElementId_18</element-id>
+        </network-element>
+    </network-elements>
+</network>
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/mutableNodesConfig.xml b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/mutableNodesConfig.xml
new file mode 100644 (file)
index 0000000..9d62490
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <top>
+        <interface>
+            <mtu modifyAction="REMOVE">1501</mtu>
+            <name>Ethernet1/0</name>
+        </interface>
+        <interface modifyAction="DELETE">
+            <mtu>1500</mtu>
+            <name>Ethernet0/0</name>
+        </interface>
+    </top>
+</config>
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/augment_choice_hell.yang b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/augment_choice_hell.yang
new file mode 100644 (file)
index 0000000..d386d48
--- /dev/null
@@ -0,0 +1,143 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:test";
+    prefix "test";
+
+    organization "Cisco Systems, Inc.";
+
+    revision "2014-03-13" {
+        description
+            "Initial revision";
+    }
+
+
+    container container {
+        choice ch2{}
+        choice ch3{
+            case c3 {
+                leaf c3Leaf {
+                    type string;
+                }
+            }
+        }
+    }
+
+    augment "/container/" {
+        leaf augLeaf {
+            type string;
+        }
+    }
+
+    augment "/container/" {
+        choice ch{}
+    }
+
+    augment "/container/ch/" {
+        case c1 {
+            leaf c1Leaf {
+                type string;
+            }
+        }
+
+        leaf c12 {
+            type string;
+        }
+    }
+    augment "/container/ch/c1/" {
+        leaf c1Leaf_AnotherAugment {
+            type string;
+        }
+
+        choice deepChoice{}
+    }
+
+    augment "/container/ch3/" {
+        case c32 {
+            leaf c32Leaf {
+                type string;
+            }
+        }
+
+        leaf c34LeafS {
+            type string;
+        }
+    }
+
+
+    augment "/container/ch/c1/deepChoice/" {
+        case deepCase1 {
+            leaf deepLeafc1 {
+                type string;
+            }
+        }
+        case deepCase2 {
+            leaf deepLeafc2 {
+                type string;
+            }
+        }
+    }
+
+    augment "/container/ch2/" {
+        case c2 {
+            leaf c2Leaf {
+                type string;
+            }
+
+            choice c2DeepChoice {
+                case c2DeepChoiceCase1 {
+                    leaf c2DeepChoiceCase1Leaf1 {
+                        type string;
+                    }
+                }
+                case c2DeepChoiceCase2 {
+                    leaf c2DeepChoiceCase1Leaf2 {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+    augment "/container/ch2/" {
+        leaf c22Leaf {
+            type string;
+        }
+    }
+
+    augment "/container" {
+        /*ext:augment-identifier top-choice-augment1;*/
+        choice augment-choice1 {
+            case case1 {
+                container case1-container {
+                    leaf case1-leaf {
+                        type string;
+                    }
+                }
+            }
+
+            case case2 {
+                container case2-container {
+                    leaf case2-leaf {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+    augment "/container/augment-choice1/case1" {
+        /*ext:augment-identifier top-choice-augment2;*/
+        choice augment-choice2 {
+            case case11 {
+                container case11-choice-case-container {
+                    leaf case11-choice-case-leaf {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+
+}
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/augment_choice_hell_ok.xml b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/augment_choice_hell_ok.xml
new file mode 100644 (file)
index 0000000..c5a581c
--- /dev/null
@@ -0,0 +1,15 @@
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+
+    <c2Leaf>2</c2Leaf>
+    <c2DeepChoiceCase1Leaf2>2</c2DeepChoiceCase1Leaf2>
+    <!--<c2DeepChoiceCase1Leaf1>2</c2DeepChoiceCase1Leaf1>-->
+
+    <c3Leaf>3</c3Leaf>
+
+    <augLeaf>augment</augLeaf>
+
+    <c1Leaf>1</c1Leaf>
+    <c1Leaf_AnotherAugment>1</c1Leaf_AnotherAugment>
+    <deepLeafc1>1</deepLeafc1>
+    <!--<deepLeafc2>1</deepLeafc2>-->
+</container>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/augment_choice_hell_ok2.xml b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/augment_choice_hell_ok2.xml
new file mode 100644 (file)
index 0000000..8332d21
--- /dev/null
@@ -0,0 +1,12 @@
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+
+    <c22Leaf>2</c22Leaf>
+
+    <c34LeafS>3</c34LeafS>
+
+    <augLeaf>augment</augLeaf>
+
+    <c1Leaf>1</c1Leaf>
+    <c1Leaf_AnotherAugment>1</c1Leaf_AnotherAugment>
+    <deepLeafc2>1</deepLeafc2>
+</container>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/augment_choice_hell_ok3.xml b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/augment_choice_hell_ok3.xml
new file mode 100644 (file)
index 0000000..3a81924
--- /dev/null
@@ -0,0 +1,5 @@
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+    <case11-choice-case-container>
+        <case11-choice-case-leaf>leaf-value</case11-choice-case-leaf>
+    </case11-choice-case-container>
+</container>
\ 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-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
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/simple.xml b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/simple.xml
new file mode 100644 (file)
index 0000000..8210413
--- /dev/null
@@ -0,0 +1,75 @@
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+    <boolean>true</boolean>
+    <innerContainer>
+        <uint16>44</uint16>
+    </innerContainer>
+    <leafList>a</leafList>
+    <leafList>b</leafList>
+
+    <list>
+        <uint32InList>1</uint32InList>
+        <containerInList name="inContainer">
+            <uint32>32</uint32>
+            <uint16>16</uint16>
+            <identityr xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:b</identityr>
+            <anyX xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">direct Value</anyX>
+            <uint16-ref>16</uint16-ref>
+            <instance-id xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">/prefix:container/prefix:list[prefix:uint32InList="1"]</instance-id>
+        </containerInList>
+        <stringAugmentedToList>augmentInList</stringAugmentedToList>
+        <stringAugmentedToListInCase1>augmentInListCase1</stringAugmentedToListInCase1>
+    </list>
+    <list>
+        <uint32InList>2</uint32InList>
+        <containerInList>
+            <uint32>32</uint32>
+            <uint16>16</uint16>
+            <identityr>b</identityr>
+            <anyX>
+                <container xmlns="randomNamespace" xmlns:pref="prefixed:namespace">
+                    <pref:boolean>true</pref:boolean>
+                    <innerContainer xmlns:p="prefixed:namespace">
+                        <uint16 xmlns:pref2="prefixed:namespace">pref2:44</uint16>
+                    </innerContainer>
+                </container>
+                <container2 xmlns="randomNamespace2"/>
+            </anyX>
+        </containerInList>
+        <!--<stringAugmentedToListInCase1>augmentInListCase1</stringAugmentedToListInCase1>-->
+        <stringAugmentedToListInCase2>augmentInListCase2</stringAugmentedToListInCase2>
+    </list>
+    <list>
+        <uint32InList>4</uint32InList>
+        <containerInList>
+            <anyX>
+                <netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
+                    <schemas>
+                        <schema>
+                            <identifier>module</identifier>
+                            <version>2012-12-12</version>
+                            <format xmlns:x="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">x:yang</format>
+                        </schema>
+                    </schemas>
+                </netconf-state>
+            </anyX>
+        </containerInList>
+    </list>
+    <list>
+        <uint32InList>3</uint32InList>
+    </list>
+
+    <augmentString1>choice1Case1</augmentString1>
+    <!--<augmentString2>choice1Case1</augmentString2>-->
+    <augmentInt1>41</augmentInt1>
+    <!--<stringInAugmentedCaseInAugmentedChoice>deep</stringInAugmentedCaseInAugmentedChoice>-->
+    <stringInAugmentedCaseInAugmentedChoiceFromAugment>deep</stringInAugmentedCaseInAugmentedChoiceFromAugment>
+
+    <augmentContainer>
+        <augmentStringInaugmentContainer>choice2Case1</augmentStringInaugmentContainer>
+    </augmentContainer>
+
+    <augmentUint32>999</augmentUint32>
+
+
+
+</container>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/simple2.xml b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/simple2.xml
new file mode 100644 (file)
index 0000000..9bb8aff
--- /dev/null
@@ -0,0 +1,43 @@
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+    <augmentString1>choice1Case1</augmentString1>
+    <!--<augmentString2>choice1Case1</augmentString2>-->
+    <augmentInt1>41</augmentInt1>
+    <!--<stringInAugmentedCaseInAugmentedChoice>deep</stringInAugmentedCaseInAugmentedChoice>-->
+    <stringInAugmentedCaseInAugmentedChoice2>deep</stringInAugmentedCaseInAugmentedChoice2>
+
+    <augmentedList>
+        <augmentStringInaugmentList>aug1</augmentStringInaugmentList>
+    </augmentedList>
+
+    <augmentedList>
+        <augmentStringInaugmentList>aug2</augmentStringInaugmentList>
+        <augmentedContainerInAugmentedListInAugmentedCase>
+            <leafInAugmentedContainerInAugmentedListInAugmentedCase>
+                66
+            </leafInAugmentedContainerInAugmentedListInAugmentedCase>
+        </augmentedContainerInAugmentedListInAugmentedCase>
+
+        <augmentedListInAugmentedListInAugmentedCase>
+            <leafInAugmentedListInAugmentedListInAugmentedCase>
+                661
+            </leafInAugmentedListInAugmentedListInAugmentedCase>
+            <leafInAugmentedListInAugmentedListInAugmentedCase>
+                662
+            </leafInAugmentedListInAugmentedListInAugmentedCase>
+        </augmentedListInAugmentedListInAugmentedCase>
+
+        <augmentedListInAugmentedListInAugmentedCase>
+            <leafInAugmentedListInAugmentedListInAugmentedCase>
+                6621
+            </leafInAugmentedListInAugmentedListInAugmentedCase>
+        </augmentedListInAugmentedListInAugmentedCase>
+    </augmentedList>
+
+    <augmentedList>
+        <augmentStringInaugmentList>aug3</augmentStringInaugmentList>
+        <augmentedContainerInAugmentedListInAugmentedCase/>
+
+        <augmentedListInAugmentedListInAugmentedCase/>
+    </augmentedList>
+
+</container>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/simple_xml_with_attributes.xml b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/simple_xml_with_attributes.xml
new file mode 100644 (file)
index 0000000..740bed8
--- /dev/null
@@ -0,0 +1,11 @@
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test" name="test" xmlns:foo="http://www.foo.com/" foo:baz="baz">
+
+    <list list="on list entry">
+        <uint32InList name="test" foo:baz="baz">3</uint32InList>
+    </list>
+
+    <boolean xmlns:leaf="test:namespace:in:leaf" leaf:a="b">false</boolean>
+
+    <leafList foo:b="b">a</leafList>
+
+</container>
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/test.yang b/yang/yang-data-impl/src/test/resources/org/opendaylight/yangtools/yang/data/impl/schema/test.yang
new file mode 100644 (file)
index 0000000..b39298e
--- /dev/null
@@ -0,0 +1,249 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:test";
+    prefix "test";
+
+    organization "Cisco Systems, Inc.";
+
+    revision "2014-03-13" {
+        description
+            "Initial revision";
+    }
+
+    identity a {}
+
+    identity b {
+        base "test:a";
+    }
+
+    grouping listGroup {
+        list list {
+            key "uint32InList";
+
+            leaf uint32InList {
+                type uint32;
+            }
+
+            container containerInList{
+                leaf uint32 {
+                    type uint32;
+                }
+                leaf uint16 {
+                    type uint16;
+                }
+
+                leaf identityr {
+                    type identityref {
+                        base "test:a";
+                     }
+                }
+
+                leaf uint16-ref {
+                    type leafref {
+                        path "../uint16";
+                     }
+                }
+
+                leaf instance-id {
+                    type instance-identifier;
+                }
+
+                anyxml anyX;
+            }
+        }
+     }
+
+     grouping innerContainerGrouping {
+        container innerContainer {
+            leaf uint16 {
+                type uint16;
+            }
+
+            container innerInnerContainer {
+
+                leaf uint16 {
+                    type uint16;
+                }
+
+                leaf uint32 {
+                    type uint32;
+                }
+            }
+        }
+     }
+
+    container container {
+        leaf uint32 {
+            type uint32;
+        }
+
+        leaf decimal64 {
+            type decimal64 {
+                fraction-digits 2;
+            }
+        }
+
+        leaf boolean {
+            type boolean;
+        }
+
+        leaf binary {
+            type binary;
+        }
+
+        leaf string {
+            type string;
+        }
+
+        uses listGroup {
+            augment "list/" {
+                leaf stringAugmentedToList{
+                    type string;
+                }
+
+                choice choiceInList {
+                    case caseInList1 {
+                        leaf stringAugmentedToListInCase1 {
+                            type string;
+                        }
+                    }
+                    case caseInList2 {
+                        leaf stringAugmentedToListInCase2 {
+                            type string;
+                        }
+                    }
+                }
+            }
+        }
+
+        list directList {
+            leaf stringInDirectList {
+                type string;
+            }
+        }
+
+        uses innerContainerGrouping;
+
+        choice choice{}
+        choice choice2{}
+
+        leaf-list leafList {
+            type string;
+        }
+
+        leaf identityRef {
+            type identityref {
+                base test-identity;
+            }
+        }
+
+        /* TODO test modification with empty type
+        leaf empty {
+             type empty;
+         }
+         */
+    }
+
+    augment "/container/" {
+        leaf augmentUint32 {
+            type uint32;
+        }
+    }
+
+    augment "/container/directList/" {
+        leaf augmentedString {
+            type uint32;
+        }
+    }
+
+    augment "/container/choice/" {
+        case test-identity-augment {
+            when "/container/identityRef = 'test-identity'";
+            leaf augmentString1 {
+                type string;
+            }
+
+            leaf augmentInt1 {
+                type uint32;
+            }
+        }
+        case test-identity-augment2 {
+            when "/container/identityRef = 'test-identity2'";
+            leaf augmentString2 {
+                type string;
+            }
+
+            leaf augmentInt2 {
+                type uint32;
+            }
+        }
+    }
+
+    augment "/container/choice/test-identity-augment/" {
+
+        choice augmentedChoiceInCase {
+
+            case augmentedCaseInAugmentedChoice {
+                leaf stringInAugmentedCaseInAugmentedChoice {
+                    type string;
+                }
+            }
+
+            case augmentedCaseInAugmentedChoice2 {
+                leaf stringInAugmentedCaseInAugmentedChoice2 {
+                    type string;
+                }
+            }
+        }
+    }
+
+    augment "/container/choice/test-identity-augment/augmentedChoiceInCase/" {
+        case augmentedCaseInAugmentedChoiceFromAugment {
+            leaf stringInAugmentedCaseInAugmentedChoiceFromAugment {
+                type string;
+            }
+        }
+    }
+
+    augment "/container/choice2/" {
+        case test-identity-augment {
+            when "/container/identityRef = 'test-identity'";
+            container augmentContainer {
+                leaf augmentStringInaugmentContainer {
+                    type string;
+                }
+            }
+        }
+        case test-identity-augment2 {
+            when "/container/identityRef = 'test-identity2'";
+            list augmentedList {
+                leaf augmentStringInaugmentList {
+                    type string;
+                }
+            }
+        }
+    }
+
+
+    augment "/container/choice2/test-identity-augment2/augmentedList/" {
+
+        container augmentedContainerInAugmentedListInAugmentedCase {
+            leaf-list leafInAugmentedContainerInAugmentedListInAugmentedCase {
+                type uint32;
+            }
+        }
+
+        list augmentedListInAugmentedListInAugmentedCase {
+            leaf-list leafInAugmentedListInAugmentedListInAugmentedCase {
+                    type uint32;
+                }
+        }
+    }
+
+    identity test-identity {}
+    identity test-identity2 {
+        base test-identity;
+    }
+
+}
diff --git a/yang/yang-data-impl/src/test/resources/rpc-getDeviceEquipment.xml b/yang/yang-data-impl/src/test/resources/rpc-getDeviceEquipment.xml
new file mode 100644 (file)
index 0000000..9db40ad
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<methodCall xmlns="urn:ciscodaylight:controller:get.device.equipment">
+       <methodName>getDeviceEquipment</methodName>
+       <emptyTag />
+       <params>
+               <param>
+                       <value>
+                               <string>DeviceID123</string>
+                       </value>
+               </param>
+               <param>
+                       <value>
+                               <string>172.23.218.75</string>
+                       </value>
+               </param>
+       </params>
+</methodCall>
diff --git a/yang/yang-data-impl/src/test/resources/schema-utils-test/foo.yang b/yang/yang-data-impl/src/test/resources/schema-utils-test/foo.yang
new file mode 100644 (file)
index 0000000..abd079b
--- /dev/null
@@ -0,0 +1,39 @@
+module foo {
+    namespace my-namespace;
+    prefix p;
+    yang-version 1.1;
+
+    feature my-name;
+
+    identity my-name;
+
+    extension my-name;
+
+    typedef my-name {
+        type string;
+    }
+
+    grouping my-name {
+    }
+
+    grouping my-name-2 {
+    }
+
+    notification my-name {
+        grouping my-name-a {
+        }
+        container my-name-a {
+        }
+    }
+
+    container my-name-2 {
+        grouping my-name-b {
+        }
+        notification my-name-b {
+        }
+        grouping my-name-2-b {
+        }
+        action my-name-2-b {
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/schema-utils-test/name-conflicts.yang b/yang/yang-data-impl/src/test/resources/schema-utils-test/name-conflicts.yang
new file mode 100644 (file)
index 0000000..bc51627
--- /dev/null
@@ -0,0 +1,90 @@
+module name-conflicts {
+    namespace "my-namespace";
+    prefix nc;
+
+    grouping my-name {
+        grouping my-name-nested {
+            grouping my-name-nested2 {
+            }
+            container my-name-nested2 {
+            }
+        }
+        container my-name-nested {
+            grouping my-name-nested2 {
+            }
+            container my-name-nested2 {
+            }
+        }
+    }
+
+    container my-name {
+        grouping my-name-nested {
+            grouping my-name-nested2 {
+            }
+            container my-name-nested2 {
+            }
+        }
+        container my-name-nested {
+            grouping my-name-nested2 {
+            }
+            container my-name-nested2 {
+            }
+        }
+    }
+
+    grouping my-name-2 {
+        grouping my-name-nested {
+            grouping my-name-nested2 {
+            }
+            container my-name-nested2 {
+            }
+        }
+        container my-name-2 {
+            grouping my-name-nested2 {
+            }
+            container my-name-nested2 {
+            }
+        }
+    }
+
+    container my-name-2 {
+        grouping my-name-nested {
+            grouping my-name-nested2 {
+            }
+            container target {
+            }
+        }
+        container my-name-nested {
+            grouping my-name-nested2 {
+            }
+            container my-name-nested2 {
+            }
+        }
+    }
+
+    rpc my-name-3 {
+        input {
+            container con-1 {
+                leaf l {
+                    type string;
+                }
+            }
+        }
+        output {
+            container con-2 {
+                leaf l {
+                    type string;
+                }
+            }
+        }
+    }
+
+    grouping my-name-3 {
+        container input {
+            container con-3 {
+                container l {
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/string-pattern-checking-codec-test.yang b/yang/yang-data-impl/src/test/resources/string-pattern-checking-codec-test.yang
new file mode 100644 (file)
index 0000000..14216c2
--- /dev/null
@@ -0,0 +1,12 @@
+module string-pattern-checking-codec-test {
+    namespace "string-pattern-checking-codec-test";
+    prefix "spcct";
+
+    container test-container {
+        leaf string-leaf-with-valid-pattern {
+            type string {
+                pattern "[A-Z]+";
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/yt776/yt776.yang b/yang/yang-data-impl/src/test/resources/yt776/yt776.yang
new file mode 100644 (file)
index 0000000..5a42177
--- /dev/null
@@ -0,0 +1,44 @@
+module yt776 {
+    namespace yt776;
+    prefix yt776;
+
+    container box {
+        list object {
+            key object-id;
+
+            leaf object-id {
+                type string;
+            }
+
+            leaf-list attributes {
+                type string;
+                min-elements 1;
+                max-elements 2;
+            }
+
+            list nested {
+                key nested-attribute;
+                max-elements 1;
+
+                leaf nested-attribute {
+                    type string;
+                }
+            }
+        }
+
+        choice any-of {
+            leaf some-leaf {
+                type string;
+            }
+            list some-list {
+                key some-leaf;
+                min-elements 1;
+
+                leaf some-leaf {
+                    type string;
+                }
+            }
+        }
+    }
+}
+
diff --git a/yang/yang-data-jaxen/pom.xml b/yang/yang-data-jaxen/pom.xml
new file mode 100644 (file)
index 0000000..be42de5
--- /dev/null
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+    Copyright (c) 2015 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
+    -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-data-jaxen</artifactId>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <!-- FIXME: we have some violations here -->
+        <odlparent.spotbugs.enforce>false</odlparent.spotbugs.enforce>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jaxen</groupId>
+            <artifactId>jaxen</artifactId>
+            <version>1.1.6</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-xpath-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-util</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.kohsuke.metainf-services</groupId>
+            <artifactId>metainf-services</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-test-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.data.jaxen</Automatic-Module-Name>
+                        <Bundle-Activator>org.opendaylight.yangtools.yang.data.jaxen.osgi.Activator</Bundle-Activator>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/ConverterNamespaceContext.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/ConverterNamespaceContext.java
new file mode 100644 (file)
index 0000000..959a2b6
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 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.jaxen;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Converter;
+import org.eclipse.jdt.annotation.NonNull;
+import org.jaxen.NamespaceContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+final class ConverterNamespaceContext extends Converter<String, QNameModule> implements NamespaceContext {
+    private final @NonNull Converter<String, QNameModule> delegate;
+
+    ConverterNamespaceContext(final Converter<String, QNameModule> converter) {
+        this.delegate = requireNonNull(converter);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    protected QNameModule doForward(final String a) {
+        return delegate.convert(a);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    protected String doBackward(final QNameModule b) {
+        return delegate.reverse().convert(b);
+    }
+
+    @Override
+    public String translateNamespacePrefixToUri(final String prefix) {
+        return convert(prefix).getNamespace().toString();
+    }
+
+    @NonNull String jaxenQName(final QName qname) {
+        return reverse().convert(qname.getModule()) + ':' + qname.getLocalName();
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/ExprListener.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/ExprListener.java
new file mode 100644 (file)
index 0000000..d1c31a2
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016 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.jaxen;
+
+import java.util.Optional;
+import org.jaxen.expr.BinaryExpr;
+import org.jaxen.expr.FilterExpr;
+import org.jaxen.expr.FunctionCallExpr;
+import org.jaxen.expr.LiteralExpr;
+import org.jaxen.expr.LocationPath;
+import org.jaxen.expr.NumberExpr;
+import org.jaxen.expr.PathExpr;
+import org.jaxen.expr.UnaryExpr;
+import org.jaxen.expr.VariableReferenceExpr;
+
+abstract class ExprListener {
+    void enterBinaryExpr(final BinaryExpr expr) {
+
+    }
+
+    void exitBinaryExpr(final BinaryExpr expr) {
+
+    }
+
+    void enterFilterExpr(final FilterExpr expr) {
+
+    }
+
+    void exitFilterExpr(final FilterExpr expr) {
+
+    }
+
+    void enterFunctionCallExpr(final FunctionCallExpr expr) {
+
+    }
+
+    void exitFunctionCallExpr(final FunctionCallExpr expr) {
+
+    }
+
+    void enterNotExpr(final UnaryExpr expr) {
+
+    }
+
+    void exitNotExpr(final UnaryExpr expr) {
+
+    }
+
+    Optional<StepListener> enterLocationPath(final LocationPath path) {
+        return Optional.empty();
+    }
+
+    void exitLocationPath(final LocationPath path) {
+
+    }
+
+    void enterPathExpr(final PathExpr expr) {
+
+    }
+
+    void exitPathExpr(final PathExpr expr) {
+
+    }
+
+    void visitLiteralExpr(final LiteralExpr expr) {
+
+    }
+
+    void visitNumberExpr(final NumberExpr expr) {
+
+    }
+
+    void visitOperator(final Operator oper) {
+
+    }
+
+    void visitVariableReferenceExpr(final VariableReferenceExpr expr)  {
+
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/ExprWalker.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/ExprWalker.java
new file mode 100644 (file)
index 0000000..87bd1ec
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016 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.jaxen;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.jaxen.expr.AllNodeStep;
+import org.jaxen.expr.BinaryExpr;
+import org.jaxen.expr.CommentNodeStep;
+import org.jaxen.expr.Expr;
+import org.jaxen.expr.FilterExpr;
+import org.jaxen.expr.FunctionCallExpr;
+import org.jaxen.expr.LiteralExpr;
+import org.jaxen.expr.LocationPath;
+import org.jaxen.expr.NameStep;
+import org.jaxen.expr.NumberExpr;
+import org.jaxen.expr.PathExpr;
+import org.jaxen.expr.ProcessingInstructionNodeStep;
+import org.jaxen.expr.TextNodeStep;
+import org.jaxen.expr.UnaryExpr;
+import org.jaxen.expr.VariableReferenceExpr;
+
+final class ExprWalker {
+    private final ExprListener listener;
+
+    ExprWalker(final ExprListener listener) {
+        this.listener = requireNonNull(listener);
+    }
+
+    public void walk(final Expr expr) {
+        if (expr instanceof BinaryExpr) {
+            final BinaryExpr binary = (BinaryExpr) expr;
+            listener.enterBinaryExpr(binary);
+            walk(binary.getLHS());
+            listener.visitOperator(Operator.forString(binary.getOperator()));
+            walk(binary.getRHS());
+            listener.exitBinaryExpr(binary);
+        } else if (expr instanceof FilterExpr) {
+            final FilterExpr filter = (FilterExpr) expr;
+            listener.enterFilterExpr(filter);
+            walk(expr);
+            listener.exitFilterExpr(filter);
+        } else if (expr instanceof FunctionCallExpr) {
+            final FunctionCallExpr func = (FunctionCallExpr) expr;
+            listener.enterFunctionCallExpr(func);
+
+            for (Object arg : func.getParameters()) {
+                walk((Expr) arg);
+            }
+
+            listener.exitFunctionCallExpr(func);
+        } else if (expr instanceof LiteralExpr) {
+            listener.visitLiteralExpr((LiteralExpr) expr);
+        } else if (expr instanceof LocationPath) {
+            final LocationPath path = (LocationPath) expr;
+            final Optional<StepListener> maybeListener = listener.enterLocationPath(path);
+            if (maybeListener.isPresent()) {
+                final StepListener l = maybeListener.get();
+                for (Object step : path.getSteps()) {
+                    if (step instanceof AllNodeStep) {
+                        l.onAll((AllNodeStep) step);
+                    } else if (step instanceof CommentNodeStep) {
+                        l.onComment((CommentNodeStep) step);
+                    } else if (step instanceof NameStep) {
+                        l.onName((NameStep) step);
+                    } else if (step instanceof ProcessingInstructionNodeStep) {
+                        l.onProcessingInstruction((ProcessingInstructionNodeStep) step);
+                    } else if (step instanceof TextNodeStep) {
+                        l.onTest((TextNodeStep) step);
+                    } else {
+                        throw new IllegalArgumentException("Unsupported step " + step);
+                    }
+                }
+            }
+
+            listener.exitLocationPath(path);
+        } else if (expr instanceof NumberExpr) {
+            listener.visitNumberExpr((NumberExpr) expr);
+        } else if (expr instanceof PathExpr) {
+            final PathExpr path = (PathExpr) expr;
+            listener.enterPathExpr(path);
+            walk(path.getFilterExpr());
+            walk(path.getLocationPath());
+            listener.exitPathExpr(path);
+        } else if (expr instanceof UnaryExpr) {
+            final UnaryExpr unary = (UnaryExpr) expr;
+            listener.enterNotExpr(unary);
+            walk(unary.getExpr());
+            listener.exitNotExpr(unary);
+        } else if (expr instanceof VariableReferenceExpr) {
+            listener.visitVariableReferenceExpr((VariableReferenceExpr) expr);
+        } else {
+            throw new IllegalArgumentException("Unsupported expression " + expr);
+        }
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenDocument.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenDocument.java
new file mode 100644 (file)
index 0000000..e1058ea
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 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.jaxen;
+
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.util.AbstractSchemaContextProvider;
+
+@NonNullByDefault
+final class JaxenDocument extends AbstractSchemaContextProvider implements XPathDocument {
+    private final DataSchemaContextNode<?> schema;
+    private final NormalizedNode<?, ?> root;
+
+    JaxenDocument(final SchemaContext context, final DataSchemaContextTree tree,
+            final NormalizedNode<?, ?> root) {
+        super(context);
+        this.root = requireNonNull(root);
+        this.schema = requireNonNull(tree.getRoot().getChild(root.getIdentifier()));
+    }
+
+    @Override
+    public NormalizedNode<?, ?> getRootNode() {
+        return root;
+    }
+
+    DataSchemaContextNode<?> getSchema() {
+        return schema;
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenSchemaContext.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenSchemaContext.java
new file mode 100644 (file)
index 0000000..9683b48
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015 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.jaxen;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Converter;
+import javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.jaxen.JaxenException;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathExpression;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+@NonNullByDefault
+final class JaxenSchemaContext implements XPathSchemaContext {
+    private final DataSchemaContextTree tree;
+    private final SchemaContext context;
+
+    JaxenSchemaContext(final SchemaContext context) {
+        this.context = requireNonNull(context);
+        this.tree = DataSchemaContextTree.from(context);
+    }
+
+    @Override
+    public XPathExpression compileExpression(final SchemaPath schemaPath,
+            final Converter<String, QNameModule> prefixes, final String xpath) throws XPathExpressionException {
+        try {
+            return JaxenXPath.create(prefixes, schemaPath, xpath);
+        } catch (JaxenException e) {
+            throw new XPathExpressionException(e);
+        }
+    }
+
+    @Override
+    public XPathDocument createDocument(final NormalizedNode<?, ?> documentRoot) {
+        return new JaxenDocument(context, tree, documentRoot);
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenSchemaContextFactory.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenSchemaContextFactory.java
new file mode 100644 (file)
index 0000000..0611ca7
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.jaxen;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.kohsuke.MetaInfServices;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContextFactory;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+@MetaInfServices
+@NonNullByDefault
+public final class JaxenSchemaContextFactory implements XPathSchemaContextFactory {
+    @Override
+    public XPathSchemaContext createContext(final SchemaContext context) {
+        return new JaxenSchemaContext(context);
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenXPath.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenXPath.java
new file mode 100644 (file)
index 0000000..e44d140
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2015 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.jaxen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Converter;
+import com.google.common.base.Verify;
+import com.google.common.collect.Lists;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.List;
+import java.util.Optional;
+import javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.jaxen.JaxenException;
+import org.jaxen.JaxenHandler;
+import org.jaxen.XPathSyntaxException;
+import org.jaxen.expr.Expr;
+import org.jaxen.saxpath.SAXPathException;
+import org.jaxen.saxpath.XPathReader;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathBooleanResult;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathExpression;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathNodesetResult;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathNumberResult;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathResult;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathStringResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class JaxenXPath implements XPathExpression {
+    private static final Logger LOG = LoggerFactory.getLogger(JaxenXPath.class);
+
+    private final @NonNull Converter<String, QNameModule> converter;
+    private final @NonNull SchemaPath schemaPath;
+    private final @NonNull Expr expr;
+
+    private JaxenXPath(final @NonNull Converter<String, QNameModule> converter, final @NonNull SchemaPath schemaPath,
+            final @NonNull Expr expr) {
+        this.converter = requireNonNull(converter);
+        this.schemaPath = requireNonNull(schemaPath);
+        this.expr = requireNonNull(expr);
+    }
+
+    static @NonNull JaxenXPath create(final @NonNull Converter<String, QNameModule> converter,
+            final @NonNull SchemaPath schemaPath, final @NonNull String xpath) throws JaxenException {
+
+        final @NonNull Expr parsed;
+        try {
+            final XPathReader reader = new org.jaxen.saxpath.base.XPathReader();
+            final JaxenHandler handler = new JaxenHandler();
+            reader.setXPathHandler(handler);
+            reader.parse(xpath);
+            parsed = handler.getXPathExpr().getRootExpr();
+        } catch (org.jaxen.saxpath.XPathSyntaxException e) {
+            throw new XPathSyntaxException(e);
+        } catch (SAXPathException e) {
+            throw new JaxenException(e);
+        }
+
+        LOG.debug("Compiled {} to expression {}", xpath, parsed);
+
+        new ExprWalker(new ExprListener() {
+            // FIXME: perform expression introspection to understand things like apex, etc.
+        }).walk(parsed);
+
+        return new JaxenXPath(converter, schemaPath, parsed);
+    }
+
+    @Override
+    public Optional<? extends XPathResult<?>> evaluate(final XPathDocument document,
+            final YangInstanceIdentifier path) throws XPathExpressionException {
+        checkArgument(document instanceof JaxenDocument);
+
+        final NormalizedNodeContextSupport contextSupport = NormalizedNodeContextSupport.create(
+            (JaxenDocument)document, converter);
+
+        final Object result = evaluate(contextSupport.createContext(path));
+        if (result instanceof String) {
+            return Optional.of((XPathStringResult) () -> (String) result);
+        } else if (result instanceof Number) {
+            return Optional.of((XPathNumberResult) () -> (Number) result);
+        } else if (result instanceof Boolean) {
+            return Optional.of((XPathBooleanResult) () -> (Boolean) result);
+        } else if (result == null) {
+            return Optional.empty();
+        }
+
+        Verify.verify(result instanceof List, "Unhandled result %s", result);
+        @SuppressWarnings("unchecked")
+        final List<NormalizedNodeContext> resultList = (List<NormalizedNodeContext>) result;
+        return Optional.of((XPathNodesetResult) () -> {
+            // XXX: Will this really work, or do we need to perform deep transformation?
+            return Lists.transform(resultList,
+                context -> new SimpleImmutableEntry<>(context.getPath(), context.getNode()));
+        });
+    }
+
+    private Object evaluate(final NormalizedNodeContext context) throws XPathExpressionException {
+        final Object result;
+        try {
+            result = expr.evaluate(context);
+        } catch (JaxenException e) {
+            throw new XPathExpressionException(e);
+        }
+
+        if (result instanceof List) {
+            final List<?> list = (List<?>) result;
+            if (list.size() == 1) {
+                final Object first = list.get(0);
+                if (first instanceof String || first instanceof Number || first instanceof Boolean) {
+                    return first;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public SchemaPath getEvaluationPath() {
+        return schemaPath;
+    }
+
+    @Override
+    public SchemaPath getApexPath() {
+        // TODO: improve this
+        return SchemaPath.ROOT;
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/LeafrefXPathStringParsingPathArgumentBuilder.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/LeafrefXPathStringParsingPathArgumentBuilder.java
new file mode 100644 (file)
index 0000000..185a626
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2017 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.jaxen;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.checkerframework.checker.regex.qual.Regex;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.common.QName;
+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.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+
+@Beta
+final class LeafrefXPathStringParsingPathArgumentBuilder implements Builder<List<PathArgument>> {
+
+    private static final String UP_ONE_LEVEL = "..";
+    private static final String CURRENT_FUNCTION_INVOCATION_STR = "current()";
+
+    @Regex
+    private static final String NODE_IDENTIFIER_STR = "([A-Za-z_][A-Za-z0-9_\\.-]*:)?([A-Za-z_][A-Za-z0-9_\\.-]*)";
+
+    /**
+     * Pattern matching node-identifier YANG ABNF token.
+     */
+    private static final Pattern NODE_IDENTIFIER_PATTERN = Pattern.compile(NODE_IDENTIFIER_STR);
+
+    /**
+     * Matcher matching WSP YANG ABNF token.
+     */
+    private static final CharMatcher WSP = CharMatcher.anyOf(" \t");
+
+    /**
+     * Matcher matching IDENTIFIER first char token.
+     */
+    private static final CharMatcher IDENTIFIER_FIRST_CHAR = CharMatcher.inRange('a', 'z')
+            .or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.is('_')).precomputed();
+    /**
+     * Matcher matching IDENTIFIER token.
+     */
+    private static final CharMatcher IDENTIFIER = IDENTIFIER_FIRST_CHAR.or(CharMatcher.inRange('0', '9'))
+            .or(CharMatcher.anyOf(".-")).precomputed();
+
+    private static final Splitter SLASH_SPLITTER = Splitter.on('/');
+
+    private static final char SLASH = '/';
+    private static final char COLON = ':';
+    private static final char EQUALS = '=';
+    private static final char PRECONDITION_START = '[';
+    private static final char PRECONDITION_END = ']';
+
+    private final String xpathString;
+    private final SchemaContext schemaContext;
+    private final TypedDataSchemaNode schemaNode;
+    private final NormalizedNodeContext currentNodeCtx;
+    private final List<PathArgument> product = new ArrayList<>();
+
+    private int offset = 0;
+
+    LeafrefXPathStringParsingPathArgumentBuilder(final String xpathString, final SchemaContext schemaContext,
+            final TypedDataSchemaNode schemaNode, final NormalizedNodeContext currentNodeCtx) {
+        this.xpathString = xpathString;
+        this.schemaContext = schemaContext;
+        this.schemaNode = schemaNode;
+        this.currentNodeCtx = currentNodeCtx;
+    }
+
+    @Override
+    public List<PathArgument> build() {
+        while (!allCharactersConsumed()) {
+            product.add(computeNextArgument());
+        }
+        return ImmutableList.copyOf(product);
+    }
+
+    private PathArgument computeNextArgument() {
+        checkValid(SLASH == currentChar(), "Identifier must start with '/'.");
+        skipCurrentChar();
+        checkValid(!allCharactersConsumed(), "Identifier cannot end with '/'.");
+
+        final QName name = nextQName();
+        if (allCharactersConsumed() || SLASH == currentChar()) {
+            return new NodeIdentifier(name);
+        }
+
+        checkValid(PRECONDITION_START == currentChar(), "Last element must be identifier, predicate or '/'");
+        return computeIdentifierWithPredicate(name);
+    }
+
+    private PathArgument computeIdentifierWithPredicate(final QName name) {
+        product.add(new NodeIdentifier(name));
+
+        ImmutableMap.Builder<QName, Object> keyValues = ImmutableMap.builder();
+        while (!allCharactersConsumed() && PRECONDITION_START == currentChar()) {
+            skipCurrentChar();
+            skipWhitespaces();
+            final QName key = nextQName();
+
+            skipWhitespaces();
+            checkCurrentAndSkip(EQUALS, "Precondition must contain '='");
+            skipWhitespaces();
+            final Object keyValue = nextCurrentFunctionPathValue();
+            skipWhitespaces();
+            checkCurrentAndSkip(PRECONDITION_END, "Precondition must ends with ']'");
+
+            keyValues.put(key, keyValue);
+        }
+        return NodeIdentifierWithPredicates.of(name, keyValues.build());
+    }
+
+    private Object nextCurrentFunctionPathValue() {
+        final String xPathSubStr = xpathString.substring(offset);
+        final String pathKeyExpression = xPathSubStr.substring(0, xPathSubStr.indexOf(PRECONDITION_END));
+        final String relPathKeyExpression = pathKeyExpression.substring(CURRENT_FUNCTION_INVOCATION_STR.length());
+
+        offset += CURRENT_FUNCTION_INVOCATION_STR.length();
+        skipWhitespaces();
+        checkCurrentAndSkip(SLASH, "Expression 'current()' must be followed by slash.");
+        skipWhitespaces();
+
+        final List<String> pathComponents = SLASH_SPLITTER.trimResults().omitEmptyStrings()
+                .splitToList(relPathKeyExpression);
+        checkValid(!pathComponents.isEmpty(), "Malformed path key expression: '%s'.", pathKeyExpression);
+
+        boolean inNodeIdentifierPart = false;
+        NormalizedNodeContext nodeCtx = this.currentNodeCtx;
+        NormalizedNode<?, ?> node = null;
+        for (String pathComponent : pathComponents) {
+            final Matcher matcher = NODE_IDENTIFIER_PATTERN.matcher(pathComponent);
+            if (UP_ONE_LEVEL.equals(pathComponent)) {
+                checkValid(!inNodeIdentifierPart, "Up-one-level expression cannot follow concrete path component.");
+                nodeCtx = nodeCtx.getParent();
+                node = nodeCtx.getNode();
+                offset += UP_ONE_LEVEL.length() + 1;
+            } else if (matcher.matches()) {
+                inNodeIdentifierPart = true;
+                if (node != null && node instanceof DataContainerNode) {
+                    final DataContainerNode dcn = (DataContainerNode) node;
+                    final Optional<NormalizedNode<?, ?>> possibleChild = dcn.getChild(new NodeIdentifier(nextQName()));
+                    node = possibleChild.isPresent() ? possibleChild.get() : null;
+                }
+            } else {
+                throw new IllegalArgumentException(String.format(
+                        "Could not parse leafref path '%s'. Offset: %s : Reason: Malformed path component: '%s'.",
+                        xpathString, offset, pathComponent));
+            }
+        }
+
+        if (node != null && node instanceof LeafNode) {
+            return node.getValue();
+        }
+
+        throw new IllegalArgumentException("Could not resolve current function path value.");
+
+    }
+
+    /**
+     * Returns following QName and sets offset to end of QName.
+     *
+     * @return following QName.
+     */
+    private QName nextQName() {
+        // Consume prefix or identifier
+        final String maybePrefix = nextIdentifier();
+        final String prefix;
+        final String localName;
+        if (!allCharactersConsumed() && COLON == currentChar()) {
+            // previous token is prefix
+            prefix = maybePrefix;
+            skipCurrentChar();
+            localName = nextIdentifier();
+        } else {
+            prefix = "";
+            localName = maybePrefix;
+        }
+        return createQName(prefix, localName);
+    }
+
+    /**
+     * Returns true if all characters from input string were consumed.
+     *
+     * @return true if all characters from input string were consumed.
+     */
+    private boolean allCharactersConsumed() {
+        return offset == xpathString.length();
+    }
+
+    private QName createQName(final String prefix, final String localName) {
+        final Module module = schemaContext.findModule(schemaNode.getQName().getModule()).get();
+        if (prefix.isEmpty() || module.getPrefix().equals(prefix)) {
+            return QName.create(module.getQNameModule(), localName);
+        }
+
+        for (final ModuleImport moduleImport : module.getImports()) {
+            if (prefix.equals(moduleImport.getPrefix())) {
+                final Module importedModule = schemaContext.findModule(moduleImport.getModuleName(),
+                        moduleImport.getRevision()).get();
+                return QName.create(importedModule.getQNameModule(),localName);
+            }
+        }
+
+        throw new IllegalArgumentException(String.format("Failed to lookup a module for prefix %s", prefix));
+    }
+
+    /**
+     * Skips current char if it equals expected otherwise fails parsing.
+     *
+     * @param expected Expected character
+     * @param errorMsg Error message if {@link #currentChar()} does not match expected.
+     */
+    private void checkCurrentAndSkip(final char expected, final String errorMsg) {
+        checkValid(expected == currentChar(), errorMsg);
+        offset++;
+    }
+
+    /**
+     * Fails parsing if condition is not met.
+     *
+     * <p>
+     * In case of error provides pointer to failed leafref, offset on which failure occured with explanation.
+     *
+     * @param condition Fails parsing if {@code condition} is false
+     * @param errorMsg Error message which will be provided to user.
+     * @param attributes Message attributes
+     */
+    private void checkValid(final boolean condition, final String errorMsg, final Object... attributes) {
+        if (!condition) {
+            throw new IllegalArgumentException(String.format(
+                    "Could not parse leafref path '%s'. Offset: %s : Reason: %s", xpathString, offset,
+                    String.format(errorMsg, attributes)));
+        }
+    }
+
+    /**
+     * Returns character at current offset.
+     *
+     * @return character at current offset.
+     */
+    private char currentChar() {
+        return xpathString.charAt(offset);
+    }
+
+    /**
+     * Increments processing offset by 1.
+     */
+    private void skipCurrentChar() {
+        offset++;
+    }
+
+    /**
+     * Skip whitespace characters, sets offset to first following non-whitespace character.
+     */
+    private void skipWhitespaces() {
+        nextSequenceEnd(WSP);
+    }
+
+    /**
+     * Returns a string which matches IDENTIFIER YANG ABNF token
+     * and sets processing offset after the end of identifier.
+     *
+     * @return string which matches IDENTIFIER YANG ABNF token
+     */
+    private String nextIdentifier() {
+        int start = offset;
+        checkValid(IDENTIFIER_FIRST_CHAR.matches(currentChar()),
+                "Identifier must start with character from set 'a-zA-Z_'");
+        nextSequenceEnd(IDENTIFIER);
+        return xpathString.substring(start, offset);
+    }
+
+    private void nextSequenceEnd(final CharMatcher matcher) {
+        while (!allCharactersConsumed() && matcher.matches(xpathString.charAt(offset))) {
+            offset++;
+        }
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContext.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContext.java
new file mode 100644 (file)
index 0000000..6293ce2
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2015 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.jaxen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.jaxen.Context;
+import org.jaxen.ContextSupport;
+import org.opendaylight.yangtools.yang.common.QName;
+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.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+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.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+/**
+ * Context wrapper around a {@link NormalizedNode} for use with Jaxen. It tracks the parent node for purposes of
+ * traversing upwards the NormalizedNode tree.
+ */
+@NonNullByDefault
+final class NormalizedNodeContext extends Context {
+    private static final long serialVersionUID = 1L;
+    private final @Nullable NormalizedNodeContext parent;
+    private final DataSchemaContextNode<?> schema;
+    private final NormalizedNode<?, ?> node;
+
+    private NormalizedNodeContext(final ContextSupport contextSupport, final DataSchemaContextNode<?> schema,
+        final NormalizedNode<?, ?> node, final @Nullable NormalizedNodeContext parent) {
+        super(contextSupport);
+        this.schema = requireNonNull(schema);
+        this.node = requireNonNull(node);
+        this.parent = parent;
+
+        setNodeSet(ImmutableList.of(this));
+    }
+
+    static NormalizedNodeContext forRoot(final NormalizedNodeContextSupport contextSupport) {
+        final JaxenDocument document = contextSupport.getNavigator().getDocument();
+        return new NormalizedNodeContext(contextSupport, document.getSchema(), document.getRootNode(), null);
+    }
+
+    NormalizedNode<?, ?> getNode() {
+        return node;
+    }
+
+    @Nullable NormalizedNodeContext getParent() {
+        return parent;
+    }
+
+    YangInstanceIdentifier getPath() {
+        return (parent == null ? YangInstanceIdentifier.empty() : parent.getPath()).node(node.getIdentifier());
+    }
+
+    DataSchemaContextNode<?> getSchema() {
+        return schema;
+    }
+
+    NormalizedNodeContext createChild(final NormalizedNode<?, ?> input) {
+        DataSchemaContextNode<?> childSchema = schema.getChild(input.getIdentifier());
+        if (childSchema == null) {
+            /* This feels very much like a hack: but solves lookup of child nodes with predicates.
+             *
+             * What is happening is that a Map context gets queried for its children, which results in contexts being
+             * backed by UnorderedMapMixinContextNode, which requires us to unmask it.
+             *
+             * When the predicate is being evaluated, each child is queried for its child -- but since it is protected,
+             * we cannot find it.
+             */
+            final DataSchemaNode mySchema = schema.getDataSchemaNode();
+            if (mySchema instanceof ListSchemaNode) {
+                childSchema = verifyNotNull(schema.getChild(mySchema.getQName())).getChild(input.getIdentifier());
+            }
+        }
+
+        checkArgument(childSchema != null, "Failed to find schema for child %s", input);
+        return new NormalizedNodeContext(getContextSupport(), childSchema, input, this);
+    }
+
+    Optional<NormalizedNodeContext> findChild(final PathArgument arg) {
+        return node instanceof DataContainerNode ? ((DataContainerNode<?>)node).getChild(arg).map(this::createChild)
+                : Optional.empty();
+    }
+
+    Optional<NormalizedNodeContext> findDescendant(final YangInstanceIdentifier path) {
+        if (path.isEmpty()) {
+            return Optional.of(this);
+        }
+
+        NormalizedNodeContext ctxWalk = this;
+        NormalizedNode<?, ?> dataWalk = node;
+        for (PathArgument arg : path.getPathArguments()) {
+            checkArgument(dataWalk instanceof DataContainerNode, "Path %s refers beyond node %s", path, dataWalk);
+
+            final Optional<DataContainerChild<? extends @Nullable PathArgument, ?>> optChild =
+                    ((DataContainerNode)dataWalk).getChild(arg);
+            if (!optChild.isPresent()) {
+                return Optional.empty();
+            }
+
+            dataWalk = optChild.get();
+            ctxWalk = createChild(dataWalk);
+        }
+
+        return Optional.of(ctxWalk.createChild(dataWalk));
+    }
+
+    Iterator<NormalizedNodeContext> iterateChildren(final DataContainerNode<?> data) {
+        return Iterators.transform(((DataContainerNode<?>) node).getValue().iterator(), this::createChild);
+    }
+
+    @Nullable Iterator<NormalizedNodeContext> iterateChildrenNamed(final DataContainerNode<?> data, final QName qname) {
+        final NodeIdentifier arg = new NodeIdentifier(qname);
+        final Optional<DataContainerChild<? extends @Nullable PathArgument, ?>> maybeChild = data.getChild(arg);
+        if (!maybeChild.isPresent()) {
+            return null;
+        }
+
+        final NormalizedNode<?, ?> child = maybeChild.get();
+        final Collection<? extends NormalizedNode<?, ?>> collection;
+
+        // The child may be a structural node
+        if (child instanceof MapNode) {
+            collection = ((MapNode)child).getValue();
+        } else if (child instanceof LeafSetNode) {
+            collection = ((LeafSetNode<?>)child).getValue();
+        } else {
+            return Iterators.singletonIterator(createChild(child));
+        }
+
+        return Iterators.transform(collection.iterator(), this::createChild);
+    }
+
+    static NormalizedNodeContext cast(@Nullable final Context context) {
+        verify(context instanceof NormalizedNodeContext);
+        return (@NonNull NormalizedNodeContext) context;
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContextSupport.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeContextSupport.java
new file mode 100644 (file)
index 0000000..d9a24cb
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015 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.jaxen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Converter;
+import java.util.Optional;
+import org.jaxen.ContextSupport;
+import org.jaxen.SimpleVariableContext;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+final class NormalizedNodeContextSupport extends ContextSupport implements SchemaContextProvider {
+    private static final long serialVersionUID = 1L;
+    private final NormalizedNodeContext root;
+
+    private NormalizedNodeContextSupport(final ConverterNamespaceContext context,
+            final NormalizedNodeNavigator navigator) {
+        super(context, YangFunctionContext.getInstance(), new SimpleVariableContext(), navigator);
+        this.root = NormalizedNodeContext.forRoot(this);
+    }
+
+    static NormalizedNodeContextSupport create(final JaxenDocument document,
+            final Converter<String, QNameModule> prefixes) {
+        final ConverterNamespaceContext context = new ConverterNamespaceContext(prefixes);
+        final NormalizedNodeNavigator navigator = new NormalizedNodeNavigator(context, document);
+
+        return new NormalizedNodeContextSupport(context, navigator);
+    }
+
+    NormalizedNodeContext createContext(final YangInstanceIdentifier path) {
+        NormalizedNodeContext result = root;
+        for (PathArgument arg : path.getPathArguments()) {
+            final Optional<NormalizedNode<?, ?>> node = NormalizedNodes.getDirectChild(result.getNode(), arg);
+            checkArgument(node.isPresent(), "Node %s has no child %s", result.getNode(), arg);
+            result = result.createChild(node.get());
+        }
+        return result;
+    }
+
+    @Override
+    public NormalizedNodeNavigator getNavigator() {
+        return (NormalizedNodeNavigator) super.getNavigator();
+    }
+
+    @Override
+    public SchemaContext getSchemaContext() {
+        return getNavigator().getSchemaContext();
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeNavigator.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/NormalizedNodeNavigator.java
new file mode 100644 (file)
index 0000000..e28e564
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2015 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.jaxen;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Verify;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.UnmodifiableIterator;
+import java.util.Base64;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import org.jaxen.DefaultNavigator;
+import org.jaxen.NamedAccessNavigator;
+import org.jaxen.Navigator;
+import org.jaxen.UnsupportedAxisException;
+import org.jaxen.XPath;
+import org.jaxen.saxpath.SAXPathException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+/**
+ * A {@link Navigator} implementation for YANG XPaths instantiated on a particular root {@link NormalizedNode}.
+ */
+final class NormalizedNodeNavigator extends DefaultNavigator implements NamedAccessNavigator, SchemaContextProvider {
+    private static final long serialVersionUID = 1L;
+    private static final Joiner JOINER = Joiner.on(" ").skipNulls();
+    private final ConverterNamespaceContext namespaceContext;
+    private final JaxenDocument document;
+
+    NormalizedNodeNavigator(final ConverterNamespaceContext context, final JaxenDocument document) {
+        this.namespaceContext = requireNonNull(context);
+        this.document = document;
+    }
+
+    private static NormalizedNodeContext cast(final Object context) {
+        Verify.verify(context instanceof NormalizedNodeContext, "Unhandled context node %s", context);
+        return (NormalizedNodeContext) context;
+    }
+
+    private static NormalizedNode<?, ?> contextNode(final Object context) {
+        return cast(context).getNode();
+    }
+
+    private QName resolveQName(final NormalizedNode<?, ?> node, final String prefix, final String localName) {
+        final QNameModule module;
+        if (prefix.isEmpty()) {
+            module = node.getNodeType().getModule();
+        } else {
+            module = namespaceContext.convert(prefix);
+        }
+
+        return QName.create(module, localName);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Entry<QName, String> attribute(final Object attr) {
+        Verify.verify(attr instanceof Entry, "Unhandled attribute %s", attr);
+        return (Entry<QName, String>) attr;
+    }
+
+    @Override
+    public String getElementNamespaceUri(final Object element) {
+        return contextNode(element).getNodeType().getNamespace().toString();
+    }
+
+    @Override
+    public String getElementName(final Object element) {
+        return contextNode(element).getNodeType().getLocalName();
+    }
+
+    @Override
+    public String getElementQName(final Object element) {
+        return namespaceContext.jaxenQName(contextNode(element).getNodeType());
+    }
+
+    @Override
+    public String getAttributeNamespaceUri(final Object attr) {
+        return attribute(attr).getKey().getNamespace().toString();
+    }
+
+    @Override
+    public String getAttributeName(final Object attr) {
+        return attribute(attr).getKey().getLocalName();
+    }
+
+    @Override
+    public String getAttributeQName(final Object attr) {
+        return namespaceContext.jaxenQName(attribute(attr).getKey());
+    }
+
+    @Override
+    public NormalizedNodeContext getDocumentNode(final Object contextNode) {
+        NormalizedNodeContext ctx = cast(contextNode);
+        while (ctx.getParent() != null) {
+            ctx = ctx.getParent();
+        }
+
+        return ctx;
+    }
+
+    @Override
+    public boolean isDocument(final Object object) {
+        return cast(object).getParent() == null;
+    }
+
+    @Override
+    public boolean isElement(final Object object) {
+        return object instanceof NormalizedNodeContext;
+    }
+
+    @Override
+    public boolean isAttribute(final Object object) {
+        return object instanceof Entry;
+    }
+
+    @Override
+    public boolean isNamespace(final Object object) {
+        return false;
+    }
+
+    @Override
+    public boolean isComment(final Object object) {
+        return false;
+    }
+
+    @Override
+    public boolean isText(final Object object) {
+        return object instanceof String;
+    }
+
+    @Override
+    public boolean isProcessingInstruction(final Object object) {
+        return false;
+    }
+
+    @Override
+    public String getCommentStringValue(final Object comment) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getElementStringValue(final Object element) {
+        final NormalizedNode<?, ?> node = contextNode(element);
+        if (node instanceof LeafNode || node instanceof LeafSetEntryNode) {
+            final Object value = node.getValue();
+
+            // TODO: This is a rather poor approximation of what the codec infrastructure, but it should be sufficient
+            //       to work for now. Tracking SchemaPath will mean we will need to wrap each NormalizedNode with a
+            //       corresponding SchemaPath. That in turn would complicate this class and result in a lot of object
+            //       allocations.
+            if (value instanceof byte[]) {
+                // Binary
+                return Base64.getEncoder().encodeToString((byte[]) value);
+            }
+            if (value instanceof Set) {
+                // Bits
+                return JOINER.join((Set<?>)value);
+            }
+            if (value != null) {
+                // Everything else...
+                return String.valueOf(value);
+            }
+        }
+
+        return "";
+    }
+
+    @Override
+    public String getAttributeStringValue(final Object attr) {
+        return attribute(attr).getValue();
+    }
+
+    @Override
+    public String getNamespaceStringValue(final Object ns) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getTextStringValue(final Object text) {
+        return text.toString();
+    }
+
+    @Override
+    public String getNamespacePrefix(final Object ns) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public XPath parseXPath(final String xpath) throws SAXPathException {
+        // FIXME: need to bind YangXPath probably
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Iterator<NormalizedNodeContext> getChildAxisIterator(final Object contextNode) {
+        final NormalizedNodeContext ctx = cast(contextNode);
+        final NormalizedNode<?, ?> node = ctx.getNode();
+        return node instanceof DataContainerNode ? ctx.iterateChildren((DataContainerNode<?>) node) : null;
+    }
+
+    @Override
+    public Iterator<NormalizedNodeContext> getChildAxisIterator(final Object contextNode, final String localName,
+            final String namespacePrefix, final String namespaceURI) {
+        final NormalizedNodeContext ctx = cast(contextNode);
+        final NormalizedNode<?, ?> node = ctx.getNode();
+        return node instanceof DataContainerNode
+                ? ctx.iterateChildrenNamed((DataContainerNode<?>)node, resolveQName(node, namespacePrefix, localName))
+                        : null;
+    }
+
+    @Override
+    public Iterator<? extends Entry<?, ?>> getAttributeAxisIterator(final Object contextNode) {
+        // FIXME: how do we mix in metadata?
+        //      final NormalizedNode<?, ?> node = contextNode(contextNode);
+        //      if (node instanceof AttributesContainer) {
+        //          final Map<QName, String> attributes = ((AttributesContainer) node).getAttributes();
+        //          if (attributes.isEmpty()) {
+        //              return null;
+        //          }
+        //
+        //          return attributes.entrySet().iterator();
+        //      }
+        return null;
+    }
+
+    @Override
+    public Iterator<? extends Entry<?, ?>> getAttributeAxisIterator(final Object contextNode, final String localName,
+            final String namespacePrefix, final String namespaceURI) {
+        // FIXME: how do we mix in metadata?
+        //      final NormalizedNode<?, ?> node = contextNode(contextNode);
+        //      if (node instanceof AttributesContainer) {
+        //          final Map<QName, String> attributes = ((AttributesContainer) node).getAttributes();
+        //          if (attributes.isEmpty()) {
+        //              return null;
+        //          }
+        //
+        //          final QName qname = resolveQName(node, namespacePrefix, localName);
+        //          final String value = attributes.get(qname);
+        //          return value == null ? null : Iterators.singletonIterator(new SimpleImmutableEntry<>(qname, value));
+        //      }
+        return null;
+    }
+
+    @Override
+    public Iterator<NormalizedNodeContext> getParentAxisIterator(final Object contextNode) {
+        final NormalizedNodeContext parent = cast(contextNode).getParent();
+        return parent == null ? null : Iterators.singletonIterator(parent);
+    }
+
+    @Override
+    public Iterator<NormalizedNodeContext> getAncestorAxisIterator(final Object contextNode)
+            throws UnsupportedAxisException {
+        final NormalizedNodeContext parent = cast(contextNode).getParent();
+        return parent == null ? null : new NormalizedNodeContextIterator(parent);
+    }
+
+    @Override
+    public Iterator<NormalizedNodeContext> getSelfAxisIterator(final Object contextNode)
+            throws UnsupportedAxisException {
+        return Iterators.singletonIterator(cast(contextNode));
+    }
+
+    @Override
+    public Iterator<NormalizedNodeContext> getAncestorOrSelfAxisIterator(final Object contextNode)
+            throws UnsupportedAxisException {
+        return new NormalizedNodeContextIterator(cast(contextNode));
+    }
+
+    @Override
+    public NormalizedNodeContext getParentNode(final Object contextNode) throws UnsupportedAxisException {
+        return cast(contextNode).getParent();
+    }
+
+    @Override
+    public SchemaContext getSchemaContext() {
+        return document.getSchemaContext();
+    }
+
+    JaxenDocument getDocument() {
+        return document;
+    }
+
+    private static final class NormalizedNodeContextIterator extends UnmodifiableIterator<NormalizedNodeContext> {
+        private NormalizedNodeContext next;
+
+        NormalizedNodeContextIterator(final NormalizedNodeContext initial) {
+            this.next = requireNonNull(initial);
+        }
+
+        @Override
+        public boolean hasNext() {
+            return next != null;
+        }
+
+        @Override
+        public NormalizedNodeContext next() {
+            if (next == null) {
+                throw new NoSuchElementException();
+            }
+
+            final NormalizedNodeContext ret = next;
+            next = next.getParent();
+            return ret;
+        }
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/Operator.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/Operator.java
new file mode 100644 (file)
index 0000000..4774ef7
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 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.jaxen;
+
+enum Operator {
+    EQUALS,
+    NOT_EQUALS,
+    GREATER_THAN,
+    GREATER_THAN_EQUALS,
+    LESS_THAN,
+    LESS_THAN_EQUALS,
+    MINUS,
+    PLUS,
+    AND,
+    OR,
+    DIV,
+    MOD,
+    MULTIPLY,
+    UNION;
+
+    static Operator forString(final String str) {
+        switch (str) {
+            case "=":
+                return Operator.EQUALS;
+            case "!=":
+                return Operator.NOT_EQUALS;
+            case "-":
+                return Operator.MINUS;
+            case "+":
+                return Operator.PLUS;
+            case "and":
+                return Operator.AND;
+            case "or":
+                return Operator.OR;
+            case "div":
+                return Operator.DIV;
+            case "mod":
+                return Operator.MOD;
+            case "*":
+                return Operator.MULTIPLY;
+            case ">=":
+                return Operator.GREATER_THAN_EQUALS;
+            case ">":
+                return Operator.GREATER_THAN;
+            case "<=":
+                return Operator.LESS_THAN_EQUALS;
+            case "<":
+                return Operator.LESS_THAN;
+            case "|":
+                return Operator.UNION;
+            default:
+                throw new IllegalArgumentException("Unknown operator " + str);
+
+        }
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/StepListener.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/StepListener.java
new file mode 100644 (file)
index 0000000..3530b5e
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016 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.jaxen;
+
+import org.jaxen.expr.AllNodeStep;
+import org.jaxen.expr.CommentNodeStep;
+import org.jaxen.expr.NameStep;
+import org.jaxen.expr.ProcessingInstructionNodeStep;
+import org.jaxen.expr.TextNodeStep;
+
+abstract class StepListener {
+
+    void onAll(final AllNodeStep step) {
+
+    }
+
+    void onComment(final CommentNodeStep step) {
+
+    }
+
+    void onName(final NameStep step) {
+
+    }
+
+    void onProcessingInstruction(final ProcessingInstructionNodeStep step) {
+
+    }
+
+    void onTest(final TextNodeStep step) {
+
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/YangFunctionContext.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/YangFunctionContext.java
new file mode 100644 (file)
index 0000000..0531240
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 2015 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.jaxen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
+
+import com.google.common.base.Splitter;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.Nullable;
+import org.jaxen.Context;
+import org.jaxen.ContextSupport;
+import org.jaxen.Function;
+import org.jaxen.FunctionCallException;
+import org.jaxen.FunctionContext;
+import org.jaxen.UnresolvableException;
+import org.jaxen.XPathFunctionContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+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.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.RegexUtils;
+
+/**
+ * A {@link FunctionContext} which contains also YANG-specific functions current(), re-match(), deref(),
+ * derived-from(), derived-from-or-self(), enum-value() and bit-is-set().
+ */
+final class YangFunctionContext implements FunctionContext {
+    private static final Splitter COLON_SPLITTER = Splitter.on(':');
+    private static final Double DOUBLE_NAN = Double.NaN;
+
+    // Core XPath functions, as per http://tools.ietf.org/html/rfc6020#section-6.4.1
+    private static final FunctionContext XPATH_FUNCTION_CONTEXT = new XPathFunctionContext(false);
+
+    // Singleton instance of reuse
+    private static final YangFunctionContext INSTANCE = new YangFunctionContext();
+
+    private YangFunctionContext() {
+    }
+
+    static YangFunctionContext getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Function getFunction(final String namespaceURI, final String prefix, final String localName)
+            throws UnresolvableException {
+        if (prefix == null) {
+            switch (localName) {
+                case "bit-is-set":
+                    return YangFunctionContext::bitIsSet;
+                case "current":
+                    return YangFunctionContext::current;
+                case "deref":
+                    return YangFunctionContext::deref;
+                case "derived-from":
+                    return YangFunctionContext::derivedFrom;
+                case "derived-from-or-self":
+                    return YangFunctionContext::derivedFromOrSelf;
+                case "enum-value":
+                    return YangFunctionContext::enumValueFunction;
+                case "re-match":
+                    return YangFunctionContext::reMatch;
+                default:
+                    break;
+            }
+        }
+
+        return XPATH_FUNCTION_CONTEXT.getFunction(namespaceURI, prefix, localName);
+    }
+
+    // bit-is-set(node-set nodes, string bit-name) function as per
+    // https://tools.ietf.org/html/rfc7950#section-10.6.1
+    private static boolean bitIsSet(final Context context, final List<?> args) throws FunctionCallException {
+        if (args == null || args.size() != 1) {
+            throw new FunctionCallException("bit-is-set() takes two arguments: node-set nodes, string bit-name");
+        }
+
+        if (!(args.get(0) instanceof String)) {
+            throw new FunctionCallException("Argument bit-name of bit-is-set() function should be a String");
+        }
+
+        final String bitName = (String) args.get(0);
+
+        final NormalizedNodeContext currentNodeContext = verifyContext(context);
+        final TypedDataSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(currentNodeContext);
+
+        final TypeDefinition<?> nodeType = correspondingSchemaNode.getType();
+        if (!(nodeType instanceof BitsTypeDefinition)) {
+            return false;
+        }
+
+        final Object nodeValue = currentNodeContext.getNode().getValue();
+        if (!(nodeValue instanceof Set)) {
+            return false;
+        }
+
+        final BitsTypeDefinition bitsType = (BitsTypeDefinition) nodeType;
+        checkState(containsBit(bitsType, bitName), "Bit %s does not belong to bits %s.", bitName, bitsType);
+        return ((Set<?>)nodeValue).contains(bitName);
+    }
+
+    // current() function, as per http://tools.ietf.org/html/rfc6020#section-6.4.1
+    private static NormalizedNodeContext current(final Context context, final List<?> args)
+            throws FunctionCallException {
+        if (!args.isEmpty()) {
+            throw new FunctionCallException("current() takes no arguments.");
+        }
+
+        return verifyContext(context);
+    }
+
+    // deref(node-set nodes) function as per https://tools.ietf.org/html/rfc7950#section-10.3.1
+    private static NormalizedNode<?, ?> deref(final Context context, final List<?> args) throws FunctionCallException {
+        if (!args.isEmpty()) {
+            throw new FunctionCallException("deref() takes only one argument: node-set nodes.");
+        }
+
+        final NormalizedNodeContext currentNodeContext = verifyContext(context);
+        final TypedDataSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(currentNodeContext);
+
+        final Object nodeValue = currentNodeContext.getNode().getValue();
+        final TypeDefinition<?> type = correspondingSchemaNode.getType();
+        if (type instanceof InstanceIdentifierTypeDefinition) {
+            return nodeValue instanceof YangInstanceIdentifier
+                    ? getNodeReferencedByInstanceIdentifier((YangInstanceIdentifier) nodeValue, currentNodeContext)
+                            : null;
+        }
+        if (type instanceof LeafrefTypeDefinition) {
+            final PathExpression xpath = ((LeafrefTypeDefinition) type).getPathStatement();
+            return getNodeReferencedByLeafref(xpath, currentNodeContext, getSchemaContext(currentNodeContext),
+                correspondingSchemaNode, nodeValue);
+        }
+        return null;
+    }
+
+    // derived-from(node-set nodes, string identity) function as per https://tools.ietf.org/html/rfc7950#section-10.4.1
+    private static boolean derivedFrom(final Context context, final List<?> args) throws FunctionCallException {
+        final Entry<IdentitySchemaNode, IdentitySchemaNode> ids = commonDerivedFrom("derived-from", context, args);
+        return ids != null && isAncestorOf(ids.getKey(), ids.getValue());
+    }
+
+    // derived-from-or-self(node-set nodes, string identity) function as per
+    // https://tools.ietf.org/html/rfc7950#section-10.4.2
+    private static boolean derivedFromOrSelf(final Context context, final List<?> args) throws FunctionCallException {
+        final Entry<IdentitySchemaNode, IdentitySchemaNode> ids = commonDerivedFrom("derived-from-or-self", context,
+            args);
+        return ids != null && (ids.getValue().equals(ids.getKey()) || isAncestorOf(ids.getKey(), ids.getValue()));
+    }
+
+    private static @Nullable Entry<IdentitySchemaNode, IdentitySchemaNode> commonDerivedFrom(final String functionName,
+            final Context context, final List<?> args) throws FunctionCallException {
+        if (args == null || args.size() != 1) {
+            throw new FunctionCallException(functionName + "() takes two arguments: node-set nodes, string identity");
+        }
+        if (!(args.get(0) instanceof String)) {
+            throw new FunctionCallException("Argument 'identity' of " + functionName
+                + "() function should be a String.");
+        }
+
+        final NormalizedNodeContext currentNodeContext = verifyContext(context);
+        final TypedDataSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(currentNodeContext);
+
+        final SchemaContext schemaContext = getSchemaContext(currentNodeContext);
+        return correspondingSchemaNode.getType() instanceof IdentityrefTypeDefinition
+                && currentNodeContext.getNode().getValue() instanceof QName ? new SimpleImmutableEntry<>(
+                        getIdentitySchemaNodeFromString((String) args.get(0), schemaContext, correspondingSchemaNode),
+                        getIdentitySchemaNodeFromQName((QName) currentNodeContext.getNode().getValue(), schemaContext))
+                        : null;
+    }
+
+    // enum-value(node-set nodes) function as per https://tools.ietf.org/html/rfc7950#section-10.5.1
+    private static Object enumValueFunction(final Context context, final List<?> args) throws FunctionCallException {
+        if (!args.isEmpty()) {
+            throw new FunctionCallException("enum-value() takes one argument: node-set nodes.");
+        }
+
+        final NormalizedNodeContext currentNodeContext = verifyContext(context);
+        final TypedDataSchemaNode correspondingSchemaNode = getCorrespondingTypedSchemaNode(currentNodeContext);
+
+        final TypeDefinition<?> nodeType = correspondingSchemaNode.getType();
+        if (!(nodeType instanceof EnumTypeDefinition)) {
+            return DOUBLE_NAN;
+        }
+
+        final Object nodeValue = currentNodeContext.getNode().getValue();
+        if (!(nodeValue instanceof String)) {
+            return DOUBLE_NAN;
+        }
+
+        final EnumTypeDefinition enumerationType = (EnumTypeDefinition) nodeType;
+        final String enumName = (String) nodeValue;
+
+        return getEnumValue(enumerationType, enumName);
+    }
+
+    // re-match(string subject, string pattern) function as per https://tools.ietf.org/html/rfc7950#section-10.2.1
+    private static boolean reMatch(final Context context, final List<?> args) throws FunctionCallException {
+        if (args == null || args.size() != 2) {
+            throw new FunctionCallException("re-match() takes two arguments: string subject, string pattern.");
+        }
+        final Object subject = args.get(0);
+        if (!(subject instanceof String)) {
+            throw new FunctionCallException("First argument of re-match() should be a String.");
+        }
+        final Object pattern = args.get(1);
+        if (!(pattern instanceof String)) {
+            throw new FunctionCallException("Second argument of re-match() should be a String.");
+        }
+
+        return ((String) subject).matches(RegexUtils.getJavaRegexFromXSD((String) pattern));
+    }
+
+    private static boolean isAncestorOf(final IdentitySchemaNode identity, final IdentitySchemaNode descendant) {
+        for (IdentitySchemaNode base : descendant.getBaseIdentities()) {
+            if (identity.equals(base) || isAncestorOf(identity, base)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static IdentitySchemaNode getIdentitySchemaNodeFromQName(final QName identityQName,
+            final SchemaContext schemaContext) {
+        final Optional<Module> module = schemaContext.findModule(identityQName.getModule());
+        checkArgument(module.isPresent(), "Module for %s not found", identityQName);
+        return findIdentitySchemaNodeInModule(module.get(), identityQName);
+    }
+
+    private static IdentitySchemaNode getIdentitySchemaNodeFromString(final String identity,
+            final SchemaContext schemaContext, final TypedDataSchemaNode correspondingSchemaNode) {
+        final List<String> identityPrefixAndName = COLON_SPLITTER.splitToList(identity);
+        final Module module = schemaContext.findModule(correspondingSchemaNode.getQName().getModule()).get();
+        if (identityPrefixAndName.size() == 2) {
+            // prefix of local module
+            if (identityPrefixAndName.get(0).equals(module.getPrefix())) {
+                return findIdentitySchemaNodeInModule(module, QName.create(module.getQNameModule(),
+                        identityPrefixAndName.get(1)));
+            }
+
+            // prefix of imported module
+            for (final ModuleImport moduleImport : module.getImports()) {
+                if (identityPrefixAndName.get(0).equals(moduleImport.getPrefix())) {
+                    final Module importedModule = schemaContext.findModule(moduleImport.getModuleName(),
+                        moduleImport.getRevision()).get();
+                    return findIdentitySchemaNodeInModule(importedModule, QName.create(
+                        importedModule.getQNameModule(), identityPrefixAndName.get(1)));
+                }
+            }
+
+            throw new IllegalArgumentException(String.format("Cannot resolve prefix '%s' from identity '%s'.",
+                    identityPrefixAndName.get(0), identity));
+        }
+
+        if (identityPrefixAndName.size() == 1) {
+            // without prefix
+            return findIdentitySchemaNodeInModule(module, QName.create(module.getQNameModule(),
+                    identityPrefixAndName.get(0)));
+        }
+
+        throw new IllegalArgumentException(String.format("Malformed identity argument: %s.", identity));
+    }
+
+    private static IdentitySchemaNode findIdentitySchemaNodeInModule(final Module module, final QName identityQName) {
+        for (final IdentitySchemaNode id : module.getIdentities()) {
+            if (identityQName.equals(id.getQName())) {
+                return id;
+            }
+        }
+
+        throw new IllegalArgumentException(String.format("Identity %s does not have a corresponding"
+                    + " identity schema node in the module %s.", identityQName, module));
+    }
+
+    private static NormalizedNode<?, ?> getNodeReferencedByInstanceIdentifier(final YangInstanceIdentifier path,
+            final NormalizedNodeContext currentNodeContext) {
+        final NormalizedNodeNavigator navigator = (NormalizedNodeNavigator) currentNodeContext.getNavigator();
+        final NormalizedNode<?, ?> rootNode = navigator.getDocument().getRootNode();
+        final List<PathArgument> pathArguments = path.getPathArguments();
+        if (pathArguments.get(0).getNodeType().equals(rootNode.getNodeType())) {
+            final List<PathArgument> relPath = pathArguments.subList(1, pathArguments.size());
+            final Optional<NormalizedNode<?, ?>> possibleNode = NormalizedNodes.findNode(rootNode, relPath);
+            if (possibleNode.isPresent()) {
+                return possibleNode.get();
+            }
+        }
+
+        return null;
+    }
+
+    private static NormalizedNode<?, ?> getNodeReferencedByLeafref(final PathExpression xpath,
+            final NormalizedNodeContext currentNodeContext, final SchemaContext schemaContext,
+            final TypedDataSchemaNode correspondingSchemaNode, final Object nodeValue) {
+        final NormalizedNode<?, ?> referencedNode = xpath.isAbsolute() ? getNodeReferencedByAbsoluteLeafref(xpath,
+                currentNodeContext, schemaContext, correspondingSchemaNode) : getNodeReferencedByRelativeLeafref(xpath,
+                currentNodeContext, schemaContext, correspondingSchemaNode);
+
+        if (referencedNode instanceof LeafSetNode) {
+            return getReferencedLeafSetEntryNode((LeafSetNode<?>) referencedNode, nodeValue);
+        }
+
+        if (referencedNode instanceof LeafNode && referencedNode.getValue().equals(nodeValue)) {
+            return referencedNode;
+        }
+
+        return null;
+    }
+
+    private static NormalizedNode<?, ?> getNodeReferencedByAbsoluteLeafref(final PathExpression xpath,
+            final NormalizedNodeContext currentNodeContext, final SchemaContext schemaContext,
+            final TypedDataSchemaNode correspondingSchemaNode) {
+        final LeafrefXPathStringParsingPathArgumentBuilder builder = new LeafrefXPathStringParsingPathArgumentBuilder(
+                xpath.getOriginalString(), schemaContext, correspondingSchemaNode, currentNodeContext);
+        final List<PathArgument> pathArguments = builder.build();
+        final NormalizedNodeNavigator navigator = (NormalizedNodeNavigator) currentNodeContext.getNavigator();
+        final NormalizedNode<?, ?> rootNode = navigator.getDocument().getRootNode();
+        if (pathArguments.get(0).getNodeType().equals(rootNode.getNodeType())) {
+            final List<PathArgument> relPath = pathArguments.subList(1, pathArguments.size());
+            final Optional<NormalizedNode<?, ?>> possibleNode = NormalizedNodes.findNode(rootNode, relPath);
+            if (possibleNode.isPresent()) {
+                return possibleNode.get();
+            }
+        }
+
+        return null;
+    }
+
+    private static NormalizedNode<?, ?> getNodeReferencedByRelativeLeafref(final PathExpression xpath,
+            final NormalizedNodeContext currentNodeContext, final SchemaContext schemaContext,
+            final TypedDataSchemaNode correspondingSchemaNode) {
+        NormalizedNodeContext relativeNodeContext = currentNodeContext;
+        final StringBuilder xPathStringBuilder = new StringBuilder(xpath.getOriginalString());
+        // strip the relative path of all ../ at the beginning
+        while (xPathStringBuilder.indexOf("../") == 0) {
+            xPathStringBuilder.delete(0, 3);
+            relativeNodeContext = relativeNodeContext.getParent();
+        }
+
+        // add / to the beginning of the path so that it can be processed the same way as an absolute path
+        xPathStringBuilder.insert(0, '/');
+        final LeafrefXPathStringParsingPathArgumentBuilder builder = new LeafrefXPathStringParsingPathArgumentBuilder(
+                xPathStringBuilder.toString(), schemaContext, correspondingSchemaNode, currentNodeContext);
+        final List<PathArgument> pathArguments = builder.build();
+        final NormalizedNode<?, ?> relativeNode = relativeNodeContext.getNode();
+        final Optional<NormalizedNode<?, ?>> possibleNode = NormalizedNodes.findNode(relativeNode, pathArguments);
+        if (possibleNode.isPresent()) {
+            return possibleNode.get();
+        }
+
+        return null;
+    }
+
+    private static LeafSetEntryNode<?> getReferencedLeafSetEntryNode(final LeafSetNode<?> referencedNode,
+            final Object currentNodeValue) {
+        for (final LeafSetEntryNode<?> entryNode : referencedNode.getValue()) {
+            if (currentNodeValue.equals(entryNode.getValue())) {
+                return entryNode;
+            }
+        }
+
+        return null;
+    }
+
+
+    private static boolean containsBit(final BitsTypeDefinition bitsType, final String bitName) {
+        for (BitsTypeDefinition.Bit bit : bitsType.getBits()) {
+            if (bitName.equals(bit.getName())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private static int getEnumValue(final EnumTypeDefinition enumerationType, final String enumName) {
+        for (final EnumTypeDefinition.EnumPair enumPair : enumerationType.getValues()) {
+            if (enumName.equals(enumPair.getName())) {
+                return enumPair.getValue();
+            }
+        }
+
+        throw new IllegalStateException(String.format("Enum %s does not belong to enumeration %s.",
+                enumName, enumerationType));
+    }
+
+    private static SchemaContext getSchemaContext(final NormalizedNodeContext normalizedNodeContext) {
+        final ContextSupport contextSupport = normalizedNodeContext.getContextSupport();
+        verify(contextSupport instanceof NormalizedNodeContextSupport, "Unhandled context support %s",
+                contextSupport.getClass());
+        return ((NormalizedNodeContextSupport) contextSupport).getSchemaContext();
+    }
+
+    private static TypedDataSchemaNode getCorrespondingTypedSchemaNode(final NormalizedNodeContext currentNodeContext) {
+        final DataSchemaNode schemaNode = currentNodeContext.getSchema().getDataSchemaNode();
+        checkState(schemaNode instanceof TypedDataSchemaNode, "Node %s must be a leaf or a leaf-list.",
+                currentNodeContext.getNode());
+        return (TypedDataSchemaNode) schemaNode;
+    }
+
+    private static NormalizedNodeContext verifyContext(final Context context) {
+        verify(context instanceof NormalizedNodeContext, "Unhandled context %s", context.getClass());
+        return (NormalizedNodeContext) context;
+    }
+
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/osgi/Activator.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/osgi/Activator.java
new file mode 100644 (file)
index 0000000..2b979a8
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.jaxen.osgi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContextFactory;
+import org.opendaylight.yangtools.yang.data.jaxen.JaxenSchemaContextFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * YANG Jaxen XPath implementation activator. Publishes a {@link XPathSchemaContextFactory} implementation on bundle
+ * start.
+ *
+ * @author Robert Varga
+ */
+public final class Activator implements BundleActivator {
+    private ServiceRegistration<@NonNull XPathSchemaContextFactory> registration;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        registration = context.registerService(XPathSchemaContextFactory.class, new JaxenSchemaContextFactory(), null);
+    }
+
+    @Override
+    public void stop(final BundleContext context) throws Exception {
+        if (registration != null) {
+            registration.unregister();
+            registration = null;
+        }
+    }
+}
diff --git a/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/osgi/package-info.java b/yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/osgi/package-info.java
new file mode 100644 (file)
index 0000000..1b3732a
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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
+ */
+/**
+ * OSGi-specific Jaxen XPath implementation code. Contains activators and similar.
+ */
+package org.opendaylight.yangtools.yang.data.jaxen.osgi;
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/BitIsSetXPathFunctionTest.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/BitIsSetXPathFunctionTest.java
new file mode 100644 (file)
index 0000000..ae121dd
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2017 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.jaxen;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import java.net.URI;
+import java.util.Set;
+import org.jaxen.Context;
+import org.jaxen.Function;
+import org.jaxen.FunctionCallException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class BitIsSetXPathFunctionTest {
+
+    private static final JaxenSchemaContextFactory SCHEMA_CONTEXT_FACTORY = new JaxenSchemaContextFactory();
+    private static final QNameModule FOO_MODULE = QNameModule.create(URI.create("foo-ns"), Revision.of("2017-04-03"));
+    private static final QName MY_CONTAINER = QName.create(FOO_MODULE, "my-container");
+    private static final QName MY_LIST = QName.create(FOO_MODULE, "my-list");
+    private static final QName FLAGS = QName.create(FOO_MODULE, "flags");
+    private static final QName ORDINARY_LEAF = QName.create(FOO_MODULE, "ordinary-leaf");
+
+    @Test
+    public void testBitIsSetFunction() throws Exception {
+        final Set<String> setOfBits = ImmutableSet.of("UP", "PROMISCUOUS");
+
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(BitIsSetXPathFunctionTest.class,
+                "/yang-xpath-functions-test/bit-is-set-function/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(setOfBits));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", FOO_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToFlagsLeafNode(setOfBits));
+
+        final Function bitIsSetFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "bit-is-set");
+        boolean bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("UP"));
+        assertTrue(bitIsSetResult);
+        bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("PROMISCUOUS"));
+        assertTrue(bitIsSetResult);
+        bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("DISABLED"));
+        assertFalse(bitIsSetResult);
+    }
+
+    @Test
+    public void testInvalidTypeOfCorrespondingSchemaNode() throws Exception {
+        final Set<String> setOfBits = ImmutableSet.of("UP", "PROMISCUOUS");
+
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(BitIsSetXPathFunctionTest.class,
+                "/yang-xpath-functions-test/bit-is-set-function/foo-invalid.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(setOfBits));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", FOO_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToFlagsLeafNode(setOfBits));
+
+        final Function bitIsSetFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "bit-is-set");
+        boolean bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("UP"));
+        assertFalse(bitIsSetResult);
+    }
+
+    @Test
+    public void testInvalidNormalizedNodeValueType() throws Exception {
+        final String invalidNodeValueType = "value of invalid type";
+
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(BitIsSetXPathFunctionTest.class,
+                "/yang-xpath-functions-test/bit-is-set-function/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(
+                    invalidNodeValueType));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", FOO_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToFlagsLeafNode(invalidNodeValueType));
+
+        final Function bitIsSetFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "bit-is-set");
+        boolean bitIsSetResult = (boolean) bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("UP"));
+        assertFalse(bitIsSetResult);
+    }
+
+    @Test
+    public void shouldFailOnUnknownBitArgument() throws Exception {
+        final Set<String> setOfBits = ImmutableSet.of("UP", "PROMISCUOUS");
+
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(BitIsSetXPathFunctionTest.class,
+                "/yang-xpath-functions-test/bit-is-set-function/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(setOfBits));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", FOO_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToFlagsLeafNode(setOfBits));
+
+        final Function bitIsSetFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "bit-is-set");
+        try {
+            bitIsSetFunction.call(normalizedNodeContext, ImmutableList.of("UNKNOWN"));
+            fail("Function call should have failed on unknown bit-name argument");
+        } catch (final IllegalStateException ex) {
+            assertTrue(ex.getMessage().startsWith("Bit UNKNOWN does not belong to bits"));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidNumberOfArguments() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function bitIsSetFunction = yangFunctionContext.getFunction(null, null, "bit-is-set");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            bitIsSetFunction.call(mockedContext, ImmutableList.of("bit-a", "bit-b"));
+            fail("Function call should have failed on invalid number of arguments.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("bit-is-set() takes two arguments: node-set nodes, string bit-name", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidTypeOfArgument() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function bitIsSetFunction = yangFunctionContext.getFunction(null, null, "bit-is-set");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            bitIsSetFunction.call(mockedContext, ImmutableList.of(100));
+            fail("Function call should have failed on invalid type of the bit-name argument.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("Argument bit-name of bit-is-set() function should be a String", ex.getMessage());
+        }
+    }
+
+    private static ContainerNode buildMyContainerNode(final Object keyLeafValue) {
+        final LeafNode<?> ordinaryLeafNode = Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(ORDINARY_LEAF)).withValue("test-value").build();
+
+        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_LIST))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(MY_LIST, FLAGS, keyLeafValue))
+                        .withChild(ordinaryLeafNode).build()).build();
+
+        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(MY_CONTAINER)).withChild(myListNode).build();
+
+        return myContainerNode;
+    }
+
+    private static YangInstanceIdentifier buildPathToFlagsLeafNode(final Object keyLeafValue) {
+        final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+        final ImmutableMap<QName, Object> keys = builder.put(FLAGS, keyLeafValue).build();
+
+        final YangInstanceIdentifier path = YangInstanceIdentifier.of(MY_LIST)
+                .node(NodeIdentifierWithPredicates.of(MY_LIST, keys)).node(FLAGS);
+        return path;
+    }
+}
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/DerefXPathFunctionTest.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/DerefXPathFunctionTest.java
new file mode 100644 (file)
index 0000000..60d6667
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2017 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.jaxen;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.net.URI;
+import java.util.Map;
+import org.jaxen.Function;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.LazyLeafOperations;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class DerefXPathFunctionTest {
+
+    private static JaxenSchemaContextFactory jaxenSchemaContextFactory = new JaxenSchemaContextFactory();
+
+    private static final QNameModule FOO_MODULE = QNameModule.create(URI.create("foo-ns"), Revision.of("2017-04-03"));
+    private static final QName MY_CONTAINER = QName.create(FOO_MODULE, "my-container");
+    private static final QName MY_INNER_CONTAINER = QName.create(FOO_MODULE, "my-inner-container");
+    private static final QName MY_LIST = QName.create(FOO_MODULE, "my-list");
+    private static final QName KEY_LEAF_A = QName.create(FOO_MODULE, "key-leaf-a");
+    private static final QName KEY_LEAF_B = QName.create(FOO_MODULE, "key-leaf-b");
+    private static final QName IID_LEAF = QName.create(FOO_MODULE, "iid-leaf");
+    private static final QName REFERENCED_LEAF = QName.create(FOO_MODULE, "referenced-leaf");
+    private static final QName REFERENCED_LEAFLIST = QName.create(FOO_MODULE, "referenced-leaf-list");
+    private static final QName ABS_LEAFREF_LEAF = QName.create(FOO_MODULE, "abs-leafref-leaf");
+    private static final QName REL_LEAFREF_LEAF = QName.create(FOO_MODULE, "rel-leafref-leaf");
+    private static final QName LEAFLIST_LEAFREF_LEAF = QName.create(FOO_MODULE, "leaf-list-leafref-leaf");
+    private static final QName ORDINARY_LEAF_A = QName.create(FOO_MODULE, "ordinary-leaf-a");
+    private static final QName ORDINARY_LEAF_B = QName.create(FOO_MODULE, "ordinary-leaf-b");
+
+    @Test
+    public void testDerefFunctionForInstanceIdentifier() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(DerefXPathFunctionTest.class,
+                "/yang-xpath-functions-test/deref-function-iid/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+
+        final LeafNode<?> referencedLeafNode = Builders.leafBuilder().withNodeIdentifier(
+            new NodeIdentifier(REFERENCED_LEAF)).withValue("referenced-leaf-node-value").build();
+
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNodeForIIdTest(
+            referencedLeafNode));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", FOO_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIIdLeafNode());
+
+        final Function derefFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "deref");
+        final Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertNotNull(derefResult);
+        assertTrue(derefResult instanceof LeafNode<?>);
+        assertLeafEquals(referencedLeafNode, (LeafNode<?>) derefResult);
+    }
+
+    @Test
+    public void testDerefFunctionForLeafref() throws Exception {
+        // tests absolute and relative leafref that references a leaf node
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(DerefXPathFunctionTest.class,
+                "/yang-xpath-functions-test/deref-function-leafref/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+
+        final LeafNode<?> referencedLeafNode = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(REFERENCED_LEAF)).withValue("referenced-leaf-node-value").build();
+
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNodeForLeafrefTest(
+                referencedLeafNode));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", FOO_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final YangInstanceIdentifier absLeafrefPath = YangInstanceIdentifier.of(MY_INNER_CONTAINER)
+                .node(ABS_LEAFREF_LEAF);
+        NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(absLeafrefPath);
+
+        final Function derefFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "deref");
+        Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertNotNull(derefResult);
+        assertTrue(derefResult instanceof LeafNode<?>);
+        assertLeafEquals(referencedLeafNode, (LeafNode<?>) derefResult);
+
+        final YangInstanceIdentifier relLeafrefPath = YangInstanceIdentifier.of(MY_INNER_CONTAINER)
+                .node(REL_LEAFREF_LEAF);
+        normalizedNodeContext = normalizedNodeContextSupport.createContext(relLeafrefPath);
+
+        derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertNotNull(derefResult);
+        assertTrue(derefResult instanceof LeafNode<?>);
+        assertLeafEquals(referencedLeafNode, (LeafNode<?>) derefResult);
+    }
+
+    @Test
+    public void testDerefFunctionForLeafref2() throws Exception {
+        // tests leafref that references a leaf-list node
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(DerefXPathFunctionTest.class,
+                "/yang-xpath-functions-test/deref-function-leafref/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = jaxenSchemaContextFactory.createContext(schemaContext);
+
+        final LeafSetNode<?> referencedLeafListNode = Builders.leafSetBuilder().withNodeIdentifier(
+                new NodeIdentifier(REFERENCED_LEAFLIST))
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(REFERENCED_LEAFLIST, "referenced-node-entry-value-a"))
+                        .withValue("referenced-node-entry-value-a").build())
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(REFERENCED_LEAFLIST, "referenced-node-entry-value-b"))
+                        .withValue("referenced-node-entry-value-b").build())
+                .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue<>(REFERENCED_LEAFLIST, "referenced-node-entry-value-c"))
+                        .withValue("referenced-node-entry-value-c").build())
+                .build();
+
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNodeForLeafrefTest(
+                referencedLeafListNode));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", FOO_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final YangInstanceIdentifier leafListLeafrefPath = YangInstanceIdentifier.of(MY_INNER_CONTAINER)
+                .node(LEAFLIST_LEAFREF_LEAF);
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport
+                .createContext(leafListLeafrefPath);
+
+        final Function derefFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "deref");
+        Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertNotNull(derefResult);
+        assertTrue(derefResult instanceof NormalizedNode<?, ?>);
+
+        final LeafSetEntryNode<?> referencedLeafListNodeEntry = referencedLeafListNode.getChild(
+                new NodeWithValue<>(REFERENCED_LEAFLIST, "referenced-node-entry-value-b")).get();
+        assertSame(referencedLeafListNodeEntry, derefResult);
+    }
+
+    private static ContainerNode buildMyContainerNodeForIIdTest(final LeafNode<?> referencedLeafNode) {
+        final Map<QName, Object> keyValues = ImmutableMap.of(KEY_LEAF_A, "key-value-a", KEY_LEAF_B, "key-value-b");
+        final YangInstanceIdentifier iidPath = YangInstanceIdentifier.of(MY_CONTAINER).node(MY_LIST)
+                .node(NodeIdentifierWithPredicates.of(MY_LIST, keyValues)).node(REFERENCED_LEAF);
+
+        final LeafNode<?> iidLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(IID_LEAF))
+                .withValue(iidPath).build();
+
+        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_LIST))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(MY_LIST, keyValues))
+                        .withChild(iidLeafNode)
+                        .withChild(referencedLeafNode).build())
+                .build();
+
+        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(MY_CONTAINER)).withChild(myListNode).build();
+        return myContainerNode;
+    }
+
+    private static YangInstanceIdentifier buildPathToIIdLeafNode() {
+        final Map<QName, Object> keyValues = ImmutableMap.of(KEY_LEAF_A, "key-value-a", KEY_LEAF_B, "key-value-b");
+        final YangInstanceIdentifier path = YangInstanceIdentifier.of(MY_LIST)
+                .node(NodeIdentifierWithPredicates.of(MY_LIST, keyValues)).node(IID_LEAF);
+        return path;
+    }
+
+    // variant for a leafref that references a leaf
+    private static ContainerNode buildMyContainerNodeForLeafrefTest(final LeafNode<?> referencedLeafNode) {
+        final Map<QName, Object> keyValues = ImmutableMap.of(KEY_LEAF_A, "value-a", KEY_LEAF_B, "value-b");
+
+        final LeafNode<?> absLeafrefNode = Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(ABS_LEAFREF_LEAF))
+                .withValue("referenced-leaf-node-value").build();
+        final LeafNode<?> relLeafrefNode = Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(REL_LEAFREF_LEAF))
+                .withValue("referenced-leaf-node-value").build();
+        final LeafNode<?> ordinaryLeafANode = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(ORDINARY_LEAF_A)).withValue("value-a").build();
+        final LeafNode<?> ordinaryLeafBNode = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(ORDINARY_LEAF_B)).withValue("value-b").build();
+
+        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_LIST))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(MY_LIST, keyValues))
+                        .withChild(referencedLeafNode).build())
+                .build();
+
+        final ContainerNode myInnerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(MY_INNER_CONTAINER))
+                .withChild(absLeafrefNode)
+                .withChild(relLeafrefNode)
+                .withChild(ordinaryLeafANode)
+                .withChild(ordinaryLeafBNode).build();
+
+        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(MY_CONTAINER))
+                .withChild(myListNode)
+                .withChild(myInnerContainerNode).build();
+        return myContainerNode;
+    }
+
+    // variant for a leafref that references a leaf-list
+    private static ContainerNode buildMyContainerNodeForLeafrefTest(final LeafSetNode<?> referencedLeafListNode) {
+        final LeafNode<?> leafListLeafrefNode = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(LEAFLIST_LEAFREF_LEAF)).withValue("referenced-node-entry-value-b").build();
+
+        final LeafNode<?> ordinaryLeafANode = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(ORDINARY_LEAF_A)).withValue("value-a").build();
+        final LeafNode<?> ordinaryLeafBNode = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(ORDINARY_LEAF_B)).withValue("value-b").build();
+
+        final ContainerNode myInnerContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(MY_INNER_CONTAINER))
+                .withChild(leafListLeafrefNode)
+                .withChild(ordinaryLeafANode)
+                .withChild(ordinaryLeafBNode).build();
+
+        final Map<QName, Object> keyValues = ImmutableMap.of(KEY_LEAF_A, "value-a", KEY_LEAF_B, "value-b");
+
+        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_LIST))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(MY_LIST, keyValues))
+                        .withChild(referencedLeafListNode).build())
+                .build();
+
+        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(MY_CONTAINER))
+                .withChild(myListNode)
+                .withChild(myInnerContainerNode).build();
+        return myContainerNode;
+    }
+
+    private static void assertLeafEquals(final LeafNode<?> expected, final LeafNode<?> actual) {
+        if (LazyLeafOperations.isEnabled()) {
+            assertEquals(expected, actual);
+        } else {
+            assertSame(expected, actual);
+        }
+    }
+}
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/DerivedFromXPathFunctionTest.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/DerivedFromXPathFunctionTest.java
new file mode 100644 (file)
index 0000000..a5336f3
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2017 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.jaxen;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.net.URI;
+import org.jaxen.Context;
+import org.jaxen.Function;
+import org.jaxen.FunctionCallException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class DerivedFromXPathFunctionTest {
+
+    private static final JaxenSchemaContextFactory SCHEMA_CONTEXT_FACTORY = new JaxenSchemaContextFactory();
+    private static final QNameModule BAR_MODULE = QNameModule.create(URI.create("bar-ns"), Revision.of("2017-04-03"));
+    private static final QName MY_CONTAINER = QName.create(BAR_MODULE, "my-container");
+    private static final QName MY_LIST = QName.create(BAR_MODULE, "my-list");
+    private static final QName KEY_LEAF = QName.create(BAR_MODULE, "key-leaf");
+    private static final QName IDREF_LEAF = QName.create(BAR_MODULE, "idref-leaf");
+    private static final QName ID_C2_IDENTITY = QName.create(BAR_MODULE, "id-c2");
+
+    @Test
+    public void testDerivedFromFunction() throws Exception {
+        // also includes test for derived-from-or-self function
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(DerivedFromXPathFunctionTest.class,
+                "/yang-xpath-functions-test/derived-from-function/foo.yang",
+                "/yang-xpath-functions-test/derived-from-function/bar.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(ID_C2_IDENTITY));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("bar-prefix", BAR_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIdrefLeafNode());
+
+        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from");
+
+        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a3"));
+        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a4"));
+        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-b2"));
+        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "bar-prefix:id-b3"));
+        assertTrue(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "id-b4"));
+
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a1"));
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a2"));
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-b1"));
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-c1"));
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "bar-prefix:id-c2"));
+
+        final Function derivedFromOrSelfFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from-or-self");
+        assertTrue(getDerivedFromResult(derivedFromOrSelfFunction, normalizedNodeContext, "bar-prefix:id-c2"));
+    }
+
+    @Test
+    public void testInvalidTypeOfCorrespondingSchemaNode() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(DerivedFromXPathFunctionTest.class,
+                "/yang-xpath-functions-test/derived-from-function/bar-invalid.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(ID_C2_IDENTITY));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("bar-prefix", BAR_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIdrefLeafNode());
+
+        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from");
+
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "some-identity"));
+    }
+
+    @Test
+    public void testInvalidNormalizedNodeValueType() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(DerivedFromXPathFunctionTest.class,
+                "/yang-xpath-functions-test/derived-from-function/foo.yang",
+                "/yang-xpath-functions-test/derived-from-function/bar.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode("should be QName"));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("bar-prefix", BAR_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIdrefLeafNode());
+
+        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from");
+
+        assertFalse(getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a3"));
+    }
+
+    @Test
+    public void shouldFailOnUnknownPrefixOfIdentity() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(DerivedFromXPathFunctionTest.class,
+                "/yang-xpath-functions-test/derived-from-function/foo.yang",
+                "/yang-xpath-functions-test/derived-from-function/bar.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(ID_C2_IDENTITY));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("bar-prefix", BAR_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIdrefLeafNode());
+
+        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from");
+
+        try {
+            getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "unknown-prefix:id-a3");
+            fail("Function call should have failed on unresolved prefix of the identity argument.");
+        } catch (IllegalArgumentException ex) {
+            assertEquals("Cannot resolve prefix 'unknown-prefix' from identity 'unknown-prefix:id-a3'.",
+                ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldFailOnMalformedIdentityArgument() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(DerivedFromXPathFunctionTest.class,
+                "/yang-xpath-functions-test/derived-from-function/foo.yang",
+                "/yang-xpath-functions-test/derived-from-function/bar.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(ID_C2_IDENTITY));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("bar-prefix", BAR_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIdrefLeafNode());
+
+        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from");
+
+        try {
+            getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo:bar:id-a3");
+            fail("Function call should have failed on malformed identity argument.");
+        } catch (IllegalArgumentException ex) {
+            assertEquals("Malformed identity argument: foo:bar:id-a3.", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldFailOnUnknownIdentityArgument() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(DerivedFromXPathFunctionTest.class,
+                "/yang-xpath-functions-test/derived-from-function/foo.yang",
+                "/yang-xpath-functions-test/derived-from-function/bar.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(ID_C2_IDENTITY));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("bar-prefix", BAR_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToIdrefLeafNode());
+
+        final Function derivedFromFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "derived-from");
+
+        try {
+            getDerivedFromResult(derivedFromFunction, normalizedNodeContext, "foo-prefix:id-a333");
+            fail("Function call should have failed on unknown identity argument.");
+        } catch (IllegalArgumentException ex) {
+            assertTrue(ex.getMessage().startsWith(
+                    "Identity (foo-ns?revision=2017-04-03)id-a333 does not have a corresponding identity schema "
+                    + "node in the module"));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidNumberOfArguments() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function derivedFromFunction = yangFunctionContext.getFunction(null, null, "derived-from");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            derivedFromFunction.call(mockedContext, ImmutableList.of("some-identity", "should not be here"));
+            fail("Function call should have failed on invalid number of arguments.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("derived-from() takes two arguments: node-set nodes, string identity", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidTypeOfArgument() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function bitIsSetFunction = yangFunctionContext.getFunction(null, null, "derived-from");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            bitIsSetFunction.call(mockedContext, ImmutableList.of(100));
+            fail("Function call should have failed on invalid type of the identity argument.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("Argument 'identity' of derived-from() function should be a String.", ex.getMessage());
+        }
+    }
+
+    private static boolean getDerivedFromResult(final Function derivedFromFunction, final NormalizedNodeContext nnCtx,
+            final String identityArg) throws Exception {
+        return (boolean) derivedFromFunction.call(nnCtx, ImmutableList.of(identityArg));
+    }
+
+    private static ContainerNode buildMyContainerNode(final Object idrefLeafValue) {
+        final LeafNode<?> idrefLeafNode = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(IDREF_LEAF))
+                .withValue(idrefLeafValue).build();
+
+        final MapNode myListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_LIST))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(MY_LIST, KEY_LEAF, "key-value"))
+                        .withChild(idrefLeafNode).build()).build();
+
+        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(MY_CONTAINER)).withChild(myListNode).build();
+        return myContainerNode;
+    }
+
+    private static YangInstanceIdentifier buildPathToIdrefLeafNode() {
+        final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+        final ImmutableMap<QName, Object> keys = builder.put(KEY_LEAF, "key-value").build();
+
+        final YangInstanceIdentifier path = YangInstanceIdentifier.of(MY_LIST)
+                .node(NodeIdentifierWithPredicates.of(MY_LIST, keys)).node(IDREF_LEAF);
+        return path;
+    }
+}
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/EnumValueXPathFunctionTest.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/EnumValueXPathFunctionTest.java
new file mode 100644 (file)
index 0000000..065317e
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2017 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.jaxen;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.net.URI;
+import org.jaxen.Context;
+import org.jaxen.Function;
+import org.jaxen.FunctionCallException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class EnumValueXPathFunctionTest {
+
+    private static final JaxenSchemaContextFactory SCHEMA_CONTEXT_FACTORY = new JaxenSchemaContextFactory();
+    private static final QNameModule FOO_MODULE = QNameModule.create(URI.create("foo-ns"), Revision.of("2017-04-03"));
+    private static final QName MY_CONTAINER = QName.create(FOO_MODULE, "my-container");
+    private static final QName ALARM = QName.create(FOO_MODULE, "alarm");
+    private static final QName SEVERITY = QName.create(FOO_MODULE, "severity");
+    private static final QName ORDINARY_LEAF = QName.create(FOO_MODULE, "ordinary-leaf");
+
+    @Test
+    public void testEnumValueFunction() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(EnumValueXPathFunctionTest.class,
+                "/yang-xpath-functions-test/enum-value-function/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode("major"));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", FOO_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToSeverityLeafNode("major"));
+
+        final Function enumValueFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "enum-value");
+        final int enumValueResult = (int) enumValueFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertEquals(5, enumValueResult);
+    }
+
+    @Test
+    public void testInvalidTypeOfCorrespondingSchemaNode() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(EnumValueXPathFunctionTest.class,
+                "/yang-xpath-functions-test/enum-value-function/foo-invalid.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode("major"));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", FOO_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToSeverityLeafNode("major"));
+
+        final Function enumValueFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "enum-value");
+        final Double enumValueResult = (Double) enumValueFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertEquals(Double.NaN, enumValueResult, 0.001);
+    }
+
+    @Test
+    public void testInvalidNormalizedNodeValueType() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(EnumValueXPathFunctionTest.class,
+                "/yang-xpath-functions-test/enum-value-function/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode(100));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", FOO_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToSeverityLeafNode(100));
+
+        final Function enumValueFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "enum-value");
+        final Double enumValueResult = (Double) enumValueFunction.call(normalizedNodeContext, ImmutableList.of());
+        assertEquals(Double.NaN, enumValueResult, 0.001);
+    }
+
+    @Test
+    public void shouldFailOnUnknownEnumNodeValue() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(EnumValueXPathFunctionTest.class,
+                "/yang-xpath-functions-test/enum-value-function/foo.yang");
+        assertNotNull(schemaContext);
+
+        final XPathSchemaContext jaxenSchemaContext = SCHEMA_CONTEXT_FACTORY.createContext(schemaContext);
+        final XPathDocument jaxenDocument = jaxenSchemaContext.createDocument(buildMyContainerNode("unknown"));
+
+        final BiMap<String, QNameModule> converterBiMap = HashBiMap.create();
+        converterBiMap.put("foo-prefix", FOO_MODULE);
+
+        final NormalizedNodeContextSupport normalizedNodeContextSupport = NormalizedNodeContextSupport.create(
+                (JaxenDocument) jaxenDocument, Maps.asConverter(converterBiMap));
+
+        final NormalizedNodeContext normalizedNodeContext = normalizedNodeContextSupport.createContext(
+                buildPathToSeverityLeafNode("unknown"));
+
+        final Function enumValueFunction = normalizedNodeContextSupport.getFunctionContext()
+                .getFunction(null, null, "enum-value");
+        try {
+            enumValueFunction.call(normalizedNodeContext, ImmutableList.of());
+            fail("Function call should have failed on unknown enum node value");
+        } catch (final IllegalStateException ex) {
+            assertTrue(ex.getMessage().startsWith("Enum unknown does not belong to enumeration"));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidNumberOfArguments() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function enumValueFunction = yangFunctionContext.getFunction(null, null, "enum-value");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            enumValueFunction.call(mockedContext, ImmutableList.of("should not be here"));
+            fail("Function call should have failed on invalid number of arguments.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("enum-value() takes one argument: node-set nodes.", ex.getMessage());
+        }
+    }
+
+    private static ContainerNode buildMyContainerNode(final Object keyLeafValue) {
+        final LeafNode<?> ordinaryLeafNode = Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(ORDINARY_LEAF)).withValue("test-value").build();
+
+        final MapNode alarmListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(ALARM))
+                .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
+                        NodeIdentifierWithPredicates.of(ALARM, SEVERITY, keyLeafValue))
+                        .withChild(ordinaryLeafNode).build()).build();
+
+        final ContainerNode myContainerNode = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(MY_CONTAINER)).withChild(alarmListNode).build();
+        return myContainerNode;
+    }
+
+    private static YangInstanceIdentifier buildPathToSeverityLeafNode(final Object keyLeafValue) {
+        final ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+        final ImmutableMap<QName, Object> keys = builder.put(SEVERITY, keyLeafValue).build();
+
+        final YangInstanceIdentifier path = YangInstanceIdentifier.of(ALARM)
+                .node(NodeIdentifierWithPredicates.of(ALARM, keys)).node(SEVERITY);
+        return path;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenTest.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenTest.java
new file mode 100644 (file)
index 0000000..56e7db1
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2015 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.jaxen;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.base.Converter;
+import com.google.common.base.VerifyException;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Maps;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import javax.xml.xpath.XPathExpressionException;
+import org.jaxen.Context;
+import org.jaxen.Function;
+import org.jaxen.FunctionCallException;
+import org.jaxen.UnresolvableException;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathExpression;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathNodesetResult;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathResult;
+import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class JaxenTest {
+    private ConverterNamespaceContext convertNctx;
+    private XPathSchemaContext xpathSchemaContext;
+    private XPathDocument xpathDocument;
+    private XPathExpression xpathExpression;
+    private NormalizedNodeNavigator navigator;
+
+    private QNameModule moduleQName;
+    private QName rootQName;
+    private QName listAQName;
+    private QName listBQName;
+    private QName leafAQName;
+    private QName leafBQName;
+    private QName leafDQName;
+    private QName containerAQName;
+    private QName containerBQName;
+
+    @Before
+    public void setup() throws XPathExpressionException {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResourceDirectory("/test/documentTest");
+        assertNotNull(schemaContext);
+
+        initQNames();
+        xpathSchemaContext = new JaxenSchemaContextFactory().createContext(schemaContext);
+        assertNotNull(xpathSchemaContext);
+
+        xpathExpression = xpathSchemaContext.compileExpression(createSchemaPath(), createPrefixes(), createXPath(
+                    false));
+        assertNotNull(xpathExpression);
+
+        xpathDocument = xpathSchemaContext.createDocument(TestUtils.createNormalizedNodes());
+        assertNotNull(xpathDocument);
+        String rootNodeName = xpathDocument.getRootNode().getNodeType().getLocalName();
+        assertNotNull(rootNodeName);
+        assertEquals("root", rootNodeName);
+
+        Optional<? extends XPathResult<?>> resultExpressionEvaluate = xpathExpression
+                .evaluate(xpathDocument, createYangInstanceIdentifier(false));
+        assertNotNull(resultExpressionEvaluate);
+        assertTrue(resultExpressionEvaluate.isPresent());
+        XPathResult<?> xpathResult = resultExpressionEvaluate.get();
+        assertTrue(xpathResult instanceof XPathNodesetResult);
+        XPathNodesetResult nodeset = (XPathNodesetResult) xpathResult;
+
+        Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry = nodeset.getValue().iterator().next();
+        assertNotNull(entry);
+        assertEquals("three", entry.getValue().getValue());
+
+        convertNctx = new ConverterNamespaceContext(createPrefixes());
+        navigator = new NormalizedNodeNavigator(convertNctx, (JaxenDocument) xpathDocument);
+        assertNotNull(navigator);
+    }
+
+    @Test
+    public void testConverterNamespaceContextBackFront() {
+        assertEquals("test2", convertNctx.doBackward(moduleQName));
+        assertEquals(moduleQName, convertNctx.doForward("test2"));
+    }
+
+    @Test
+    public void testConverterNamespaceContextPrefixJaxenName() {
+        assertNotNull(rootQName);
+        assertEquals("test2:root", convertNctx.jaxenQName(rootQName));
+        String prefix = convertNctx.translateNamespacePrefixToUri("test2");
+        assertNotNull(prefix);
+        assertEquals("urn:opendaylight.test2", prefix);
+    }
+
+    @Test
+    public void testCompileExpression() {
+        assertNotNull(xpathExpression.getApexPath());
+        assertEquals(createSchemaPath(), xpathExpression.getEvaluationPath());
+    }
+
+    @Test
+    public void testJaxenXpath() throws XPathExpressionException {
+        assertNotNull(xpathExpression.evaluate(xpathDocument, createYangInstanceIdentifier(false)));
+    }
+
+    @Test
+    public void testXpathWithPredicates() throws XPathExpressionException {
+        XPathExpression xpathExpressionWithPredicates = xpathSchemaContext.compileExpression(createSchemaPath(),
+                createPrefixes(), createXPath(true));
+
+        Optional<? extends XPathResult<?>> resultExpressionEvaluate = xpathExpressionWithPredicates
+                .evaluate(xpathDocument, createYangInstanceIdentifier(true));
+        assertTrue(resultExpressionEvaluate.isPresent());
+        XPathResult<?> xpathResult = resultExpressionEvaluate.get();
+        assertTrue(xpathResult instanceof XPathNodesetResult);
+        XPathNodesetResult nodeset = (XPathNodesetResult) xpathResult;
+
+        Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry = nodeset.getValue().iterator().next();
+        assertNotNull(entry);
+        assertEquals("two", entry.getValue().getValue());
+    }
+
+    @Test(expected = VerifyException.class)
+    public void testIsMethodsInNodeNavigator() {
+        assertTrue(navigator.isDocument("test"));
+    }
+
+    @Test(expected = XPathExpressionException.class)
+    public void testCompileExpressionException() throws XPathExpressionException {
+        assertNotNull(xpathSchemaContext.compileExpression(createSchemaPath(), createPrefixes(), "/broken-path*"));
+    }
+
+    @Test(expected = UnresolvableException.class)
+    public void testYangFunctionContext() throws UnresolvableException, FunctionCallException {
+        final YangFunctionContext yangFun = YangFunctionContext.getInstance();
+        assertNotNull(yangFun);
+        final Function function = yangFun.getFunction("urn:opendaylight.test2", null, "current");
+        assertNotNull(function);
+
+        try {
+            final Context context = mock(Context.class);
+            final ArrayList<Object> list = new ArrayList<>();
+            function.call(context, list);
+            fail();
+        } catch (VerifyException e) {
+            // Expected
+        }
+
+        yangFun.getFunction("urn:opendaylight.test2", "test2", "root");
+    }
+
+    /*
+     * container-a -> container-b -> leaf-d
+     *           list-a -> list-b -> leaf-b
+     */
+    private YangInstanceIdentifier createYangInstanceIdentifier(final boolean withPredicates) {
+        YangInstanceIdentifier testYangInstanceIdentifier = YangInstanceIdentifier.of(containerAQName).node(
+                containerBQName).node(leafDQName);
+        if (withPredicates) {
+            final Map<QName, Object> keys1 = new HashMap<>();
+            keys1.put(leafAQName, "bar");
+
+            final NodeIdentifierWithPredicates mapEntryPath1 = NodeIdentifierWithPredicates.of(listAQName , keys1);
+
+            final Map<QName, Object> keys2 = new HashMap<>();
+            keys2.put(leafBQName, "two");
+
+            final NodeIdentifierWithPredicates mapEntryPath2 = NodeIdentifierWithPredicates.of(listBQName , keys2);
+
+            testYangInstanceIdentifier = YangInstanceIdentifier.of(listAQName).node(mapEntryPath1)
+                    .node(listBQName).node(mapEntryPath2).node(leafBQName);
+        }
+        return testYangInstanceIdentifier;
+    }
+
+    private static String createXPath(final boolean withPredicates) {
+        return withPredicates ? "/list-a[leaf-a='bar']/list-b[leaf-b='two']/leaf-b" : "/container-a/container-b/leaf-d";
+    }
+
+    private Converter<String, QNameModule> createPrefixes() {
+        BiMap<String, QNameModule> currentConverter = HashBiMap.create();
+        currentConverter.put("test2", moduleQName);
+
+        return Maps.asConverter(currentConverter);
+    }
+
+    // rootQName -> listAQName -> leafAQName
+    private  SchemaPath createSchemaPath() {
+        return SchemaPath.create(true, rootQName, listAQName, leafAQName);
+    }
+
+    private void initQNames() {
+        this.moduleQName = QNameModule.create(URI.create("urn:opendaylight.test2"), Revision.of("2015-08-08"));
+        this.rootQName = QName.create(moduleQName, "root");
+        this.listAQName = QName.create(moduleQName, "list-a");
+        this.listBQName = QName.create(moduleQName, "list-b");
+        this.leafAQName = QName.create(moduleQName, "leaf-a");
+        this.leafBQName = QName.create(moduleQName, "leaf-b");
+        this.leafDQName = QName.create(moduleQName, "leaf-d");
+        this.containerAQName = QName.create(moduleQName, "container-a");
+        this.containerBQName = QName.create(moduleQName, "container-b");
+    }
+}
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/ReMatchXPathFunctionTest.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/ReMatchXPathFunctionTest.java
new file mode 100644 (file)
index 0000000..27dc726
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2017 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.jaxen;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.ImmutableList;
+import org.jaxen.Context;
+import org.jaxen.Function;
+import org.jaxen.FunctionCallException;
+import org.junit.Test;
+
+public class ReMatchXPathFunctionTest {
+
+    @Test
+    public void testRematchFunction() throws Exception {
+        // re-match() uses regex processing from yang-parser-impl which has been thoroughly tested within
+        // the Bug5410Test unit test class, so here is just a basic test
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function rematchFunction = yangFunctionContext.getFunction(null, null, "re-match");
+
+        final Context mockedContext = mock(Context.class);
+
+        boolean rematchResult = (boolean) rematchFunction.call(mockedContext, ImmutableList.of("abbc", "[abc]{1,4}"));
+        assertTrue(rematchResult);
+        rematchResult = (boolean) rematchFunction.call(mockedContext, ImmutableList.of("abbcc", "[abc]{1,4}"));
+        assertFalse(rematchResult);
+    }
+
+    @Test
+    public void shouldFailOnInvalidNumberOfArguments() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function rematchFunction = yangFunctionContext.getFunction(null, null, "re-match");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            rematchFunction.call(mockedContext, ImmutableList.of("abbc", "[abc]{1,4}", "should not be here"));
+            fail("Function call should have failed on invalid number of arguments.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("re-match() takes two arguments: string subject, string pattern.", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidTypeOfArgument() throws Exception {
+        final YangFunctionContext yangFunctionContext = YangFunctionContext.getInstance();
+        final Function rematchFunction = yangFunctionContext.getFunction(null, null, "re-match");
+
+        final Context mockedContext = mock(Context.class);
+
+        try {
+            rematchFunction.call(mockedContext, ImmutableList.of(100, "[abc]{1,4}"));
+            fail("Function call should have failed on invalid type of the subject argument.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("First argument of re-match() should be a String.", ex.getMessage());
+        }
+
+        try {
+            rematchFunction.call(mockedContext, ImmutableList.of("abbc", 100));
+            fail("Function call should have failed on invalid type of the pattern argument.");
+        } catch (final FunctionCallException ex) {
+            assertEquals("Second argument of re-match() should be a String.", ex.getMessage());
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/TestUtils.java b/yang/yang-data-jaxen/src/test/java/org/opendaylight/yangtools/yang/data/jaxen/TestUtils.java
new file mode 100644 (file)
index 0000000..27aa868
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015 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.jaxen;
+
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+
+final class TestUtils {
+
+    private static final QName ROOT_QNAME = QName.create("urn:opendaylight.test2", "2015-08-08", "root");
+    private static final QName LIST_A_QNAME = QName.create(ROOT_QNAME, "list-a");
+    private static final QName LIST_B_QNAME = QName.create(ROOT_QNAME, "list-b");
+    private static final QName LEAF_A_QNAME = QName.create(ROOT_QNAME, "leaf-a");
+    private static final QName LEAF_B_QNAME = QName.create(ROOT_QNAME, "leaf-b");
+    private static final QName LEAF_C_QNAME = QName.create(ROOT_QNAME, "leaf-c");
+    private static final QName LEAF_D_QNAME = QName.create(ROOT_QNAME, "leaf-d");
+    private static final QName CONTAINER_A_QNAME = QName.create(ROOT_QNAME, "container-a");
+    private static final QName CONTAINER_B_QNAME = QName.create(ROOT_QNAME, "container-b");
+    private static final String FOO = "foo";
+    private static final String BAR = "bar";
+    private static final String WAZ = "waz";
+    private static final String ONE = "one";
+    private static final String TWO = "two";
+    private static final String THREE = "three";
+
+    private TestUtils() {
+    }
+
+    /**
+     * Returns a test document.
+     *
+     * <pre>
+     * root
+     *     leaf-c "waz"
+     *     list-a
+     *          leaf-a "foo"
+     *     list-a
+     *          leaf-a "bar"
+     *          list-b
+     *                  leaf-b "one"
+     *          list-b
+     *                  leaf-b "two"
+     *     container-a
+     *          container-b
+     *                  leaf-d "three"
+     * </pre>
+     *
+     * @return A test document instance.
+     */
+    public static NormalizedNode<?, ?> createNormalizedNodes() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ROOT_QNAME))
+                .withChild(ImmutableNodes.leafNode(LEAF_C_QNAME, WAZ))
+                .withChild(mapNodeBuilder(LIST_A_QNAME)
+                        .withChild(mapEntry(LIST_A_QNAME, LEAF_A_QNAME, FOO))
+                        .withChild(mapEntryBuilder(LIST_A_QNAME, LEAF_A_QNAME, BAR)
+                                .withChild(mapNodeBuilder(LIST_B_QNAME)
+                                        .withChild(mapEntry(LIST_B_QNAME, LEAF_B_QNAME, ONE))
+                                        .withChild(mapEntry(LIST_B_QNAME, LEAF_B_QNAME, TWO))
+                                        .build())
+                                .build())
+                        .build())
+                .withChild(ImmutableContainerNodeBuilder.create()
+                        .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CONTAINER_A_QNAME))
+                        .withChild(ImmutableContainerNodeBuilder.create()
+                            .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CONTAINER_B_QNAME))
+                                .withChild(ImmutableNodes.leafNode(LEAF_D_QNAME, THREE))
+                                .build())
+                        .build())
+                .build();
+    }
+}
diff --git a/yang/yang-data-jaxen/src/test/resources/test/documentTest/test2.yang b/yang/yang-data-jaxen/src/test/resources/test/documentTest/test2.yang
new file mode 100644 (file)
index 0000000..c8cd382
--- /dev/null
@@ -0,0 +1,58 @@
+module test2 {
+    yang-version 1;
+    namespace "urn:opendaylight.test2";
+    prefix "test2";
+
+    organization "opendaylight";
+    contact "urn:opendaylight.com";
+    description "test description";
+
+    revision "2015-08-08" {
+        reference "1st edit";
+    }
+
+    container root {
+        leaf leaf-c {
+            type string;
+        }
+
+        list list-a {
+            key "leaf-a";
+
+            leaf leaf-a {
+                type string;
+            }
+
+            choice choice-a {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+                }
+            }
+
+            list list-b {
+                key "leaf-b";
+                leaf leaf-b {
+                    type string;
+                }
+            }
+        }
+
+        container container-a {
+            container container-b {
+                leaf leaf-d {
+                    type string;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/bit-is-set-function/foo-invalid.yang b/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/bit-is-set-function/foo-invalid.yang
new file mode 100644 (file)
index 0000000..13e09e2
--- /dev/null
@@ -0,0 +1,21 @@
+module foo-invalid {
+    namespace foo-ns;
+    prefix foo-prefix;
+    yang-version 1.1;
+
+    revision 2017-04-03;
+
+    container my-container {
+        list my-list {
+            key flags;
+
+            leaf flags {
+                type int32;
+            }
+
+            leaf ordinary-leaf {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/bit-is-set-function/foo.yang b/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/bit-is-set-function/foo.yang
new file mode 100644 (file)
index 0000000..3d18861
--- /dev/null
@@ -0,0 +1,25 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prefix;
+    yang-version 1.1;
+
+    revision 2017-04-03;
+
+    container my-container {
+        list my-list {
+            key flags;
+
+            leaf flags {
+                type bits {
+                    bit UP;
+                    bit PROMISCUOUS;
+                    bit DISABLED;
+                }
+            }
+
+            leaf ordinary-leaf {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/deref-function-iid/foo.yang b/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/deref-function-iid/foo.yang
new file mode 100644 (file)
index 0000000..7dfda62
--- /dev/null
@@ -0,0 +1,29 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prefix;
+    yang-version 1.1;
+
+    revision 2017-04-03;
+
+    container my-container {
+        list my-list {
+            key "key-leaf-a key-leaf-b";
+
+            leaf key-leaf-a {
+                type string;
+            }
+
+            leaf key-leaf-b {
+                type string;
+            }
+
+            leaf iid-leaf {
+                type instance-identifier;
+            }
+
+            leaf referenced-leaf {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/deref-function-leafref/foo.yang b/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/deref-function-leafref/foo.yang
new file mode 100644 (file)
index 0000000..4b12f54
--- /dev/null
@@ -0,0 +1,60 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prefix;
+    yang-version 1.1;
+
+    revision 2017-04-03;
+
+    container my-container {
+        list my-list {
+            key "key-leaf-a key-leaf-b";
+
+            leaf key-leaf-a {
+                type string;
+            }
+
+            leaf key-leaf-b {
+                type string;
+            }
+
+            leaf referenced-leaf {
+                type string;
+            }
+
+            leaf referenced-leaf-list {
+                type string;
+            }
+        }
+
+        container my-inner-container {
+            leaf abs-leafref-leaf {
+                type leafref {
+                    path "/my-container/my-list[key-leaf-a=current()/../ordinary-leaf-a]" +
+                        "[key-leaf-b=current()/../ordinary-leaf-b]/referenced-leaf";
+                }
+            }
+
+            leaf rel-leafref-leaf {
+                type leafref {
+                    path "../../foo-prefix:my-list[foo-prefix:key-leaf-a=current()/../ordinary-leaf-a]" +
+                        "[key-leaf-b=current()/../ordinary-leaf-b]/foo-prefix:referenced-leaf";
+                }
+            }
+
+            leaf leaf-list-leafref-leaf {
+                type leafref {
+                    path "/my-container/my-list[key-leaf-a=current()/../ordinary-leaf-a]" +
+                        "[key-leaf-b=current()/../ordinary-leaf-b]/referenced-leaf-list";
+                }
+            }
+
+            leaf ordinary-leaf-a {
+                type string;
+            }
+
+            leaf ordinary-leaf-b {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/derived-from-function/bar-invalid.yang b/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/derived-from-function/bar-invalid.yang
new file mode 100644 (file)
index 0000000..c43fd47
--- /dev/null
@@ -0,0 +1,21 @@
+module bar-invalid {
+    namespace bar-ns;
+    prefix bar-prefix;
+    yang-version 1.1;
+
+    revision 2017-04-03;
+
+    container my-container {
+        list my-list {
+            key key-leaf;
+
+            leaf key-leaf {
+                type string;
+            }
+
+            leaf idref-leaf {
+                type int32;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/derived-from-function/bar.yang b/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/derived-from-function/bar.yang
new file mode 100644 (file)
index 0000000..1532fc7
--- /dev/null
@@ -0,0 +1,37 @@
+module bar {
+    namespace bar-ns;
+    prefix bar-prefix;
+    yang-version 1.1;
+
+    import foo {
+        prefix foo-prefix;
+        revision-date 2017-04-03;
+    }
+
+    revision 2017-04-03;
+
+    identity id-b3;
+    identity id-b4;
+
+    identity id-c2 {
+        base foo-prefix:id-b2;
+        base id-b3;
+        base id-b4;
+    }
+
+    container my-container {
+        list my-list {
+            key key-leaf;
+
+            leaf key-leaf {
+                type string;
+            }
+
+            leaf idref-leaf {
+                type identityref {
+                    base foo-prefix:id-a3;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/derived-from-function/foo.yang b/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/derived-from-function/foo.yang
new file mode 100644 (file)
index 0000000..7fec0c6
--- /dev/null
@@ -0,0 +1,29 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prefix;
+    yang-version 1.1;
+
+    revision 2017-04-03;
+
+    identity id-a1;
+    identity id-a2;
+    identity id-a3;
+    identity id-a4;
+
+    identity id-b1 {
+        base id-a1;
+        base id-a2;
+    }
+
+    identity id-b2 {
+        base id-a3;
+        base id-a4;
+    }
+
+    identity id-c1 {
+        base id-b1;
+        base id-b2;
+    }
+
+    container my-container {}
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/enum-value-function/foo-invalid.yang b/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/enum-value-function/foo-invalid.yang
new file mode 100644 (file)
index 0000000..aa238fe
--- /dev/null
@@ -0,0 +1,21 @@
+module foo-invalid {
+    namespace foo-ns;
+    prefix foo-prefix;
+    yang-version 1.1;
+
+    revision 2017-04-03;
+
+    container my-container {
+        list alarm {
+            key severity;
+
+            leaf ordinary-leaf {
+                type string;
+            }
+
+            leaf severity {
+                type int32;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/enum-value-function/foo.yang b/yang/yang-data-jaxen/src/test/resources/yang-xpath-functions-test/enum-value-function/foo.yang
new file mode 100644 (file)
index 0000000..40dc2fd
--- /dev/null
@@ -0,0 +1,40 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prefix;
+    yang-version 1.1;
+
+    revision 2017-04-03;
+
+    container my-container {
+        list alarm {
+            key severity;
+
+            leaf ordinary-leaf {
+                type string;
+            }
+
+            leaf severity {
+                type enumeration {
+                    enum cleared {
+                        value 1;
+                    }
+                    enum indeterminate {
+                        value 2;
+                    }
+                    enum minor {
+                        value 3;
+                    }
+                    enum warning {
+                        value 4;
+                    }
+                    enum major {
+                        value 5;
+                    }
+                    enum critical {
+                        value 6;
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-transform/pom.xml b/yang/yang-data-transform/pom.xml
new file mode 100644 (file)
index 0000000..0748617
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-data-transform</artifactId>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-impl</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.data.transform</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-data-transform/src/main/java/org/opendaylight/yangtools/transform/NormalizedNodeTransformations.java b/yang/yang-data-transform/src/main/java/org/opendaylight/yangtools/transform/NormalizedNodeTransformations.java
new file mode 100644 (file)
index 0000000..e5a8522
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015 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.transform;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import java.util.Map;
+import java.util.function.Function;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+
+@Beta
+public final class NormalizedNodeTransformations {
+
+    private NormalizedNodeTransformations() {
+        throw new UnsupportedOperationException("Utility class.");
+    }
+
+    public static NormalizedNode<?, ?> transformQNames(final NormalizedNode<?, ?> original,
+            final Function<QName, QName> mapping) {
+        NormalizedNodeResult result = new NormalizedNodeResult();
+        NormalizedNodeStreamWriter nodeWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        NormalizedNodeStreamWriter transformWriter = QNameTransformingStreamWriter.fromFunction(nodeWriter, mapping);
+        try {
+            NormalizedNodeWriter.forStreamWriter(transformWriter).write(original);
+            return result.getResult();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Returns a {@link NormalizedNode} with QNames replaced by supplied mapping.
+     *
+     * @param original Original Normalized Node
+     * @param mapping Map of QNames to transform. Not listed QNames are preserved.
+     * @return Normalized Node with replaced QNames.
+     */
+    public static NormalizedNode<?, ?> replaceQNames(final @NonNull NormalizedNode<?, ?> original,
+            final @NonNull Map<QName, QName> mapping) {
+        return transformQNames(original, new QNameReplacementFunction(mapping));
+    }
+
+    /**
+     * Returns a {@link NormalizedNode} with QNameModules replaced by supplied mapping.
+     *
+     * @param original Original Normalized Node
+     * @param mapping Map of QNameModules to transform. Not listed QNameModules are preserved.
+     * @return Normalized Node with replaced QNameModules.
+     */
+    public static NormalizedNode<?, ?> replaceQNameModules(final @NonNull NormalizedNode<?, ?> original,
+            final @NonNull Map<QNameModule, QNameModule> mapping) {
+        return transformQNames(original, new QNameModuleReplacementFunction(mapping));
+    }
+}
diff --git a/yang/yang-data-transform/src/main/java/org/opendaylight/yangtools/transform/QNameModuleReplacementFunction.java b/yang/yang-data-transform/src/main/java/org/opendaylight/yangtools/transform/QNameModuleReplacementFunction.java
new file mode 100644 (file)
index 0000000..b672e54
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 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.transform;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Map;
+import java.util.function.Function;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+class QNameModuleReplacementFunction implements Function<QName, QName> {
+    private final Map<QNameModule, QNameModule> mapping;
+
+    QNameModuleReplacementFunction(final Map<QNameModule, QNameModule> mapping) {
+        this.mapping = requireNonNull(mapping);
+    }
+
+    @Override
+    public QName apply(final QName input) {
+        QNameModule potential = mapping.get(input.getModule());
+        if (potential != null) {
+            return QName.create(potential, input.getLocalName()).intern();
+        }
+        return input;
+    }
+}
diff --git a/yang/yang-data-transform/src/main/java/org/opendaylight/yangtools/transform/QNameReplacementFunction.java b/yang/yang-data-transform/src/main/java/org/opendaylight/yangtools/transform/QNameReplacementFunction.java
new file mode 100644 (file)
index 0000000..7174472
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 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.transform;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Map;
+import java.util.function.Function;
+import org.opendaylight.yangtools.yang.common.QName;
+
+class QNameReplacementFunction implements Function<QName, QName> {
+    private final Map<QName, QName> mapping;
+
+    QNameReplacementFunction(final Map<QName, QName> mapping) {
+        this.mapping = requireNonNull(mapping);
+    }
+
+    @Override
+    public QName apply(final QName input) {
+        QName potential = mapping.get(input);
+        return potential != null ? potential : input;
+    }
+}
diff --git a/yang/yang-data-transform/src/main/java/org/opendaylight/yangtools/transform/QNameTransformingStreamWriter.java b/yang/yang-data-transform/src/main/java/org/opendaylight/yangtools/transform/QNameTransformingStreamWriter.java
new file mode 100644 (file)
index 0000000..1ff8994
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2015 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.transform;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+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.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.ForwardingNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+
+/**
+ * Stateless Normalized Node Stream Writer decorator, which performs QName translation.
+ *
+ * <p>
+ * This class serves as base for Normalized Node Stream Writer decorators with option to transform
+ * QNames by user-implemented {@link #transform(QName)} function.
+ */
+public abstract class QNameTransformingStreamWriter extends ForwardingNormalizedNodeStreamWriter {
+
+    // FIXME: Probably use loading cache to decrease memory
+
+    /**
+     * Returns decorator, which uses supplied function to transform QNames.
+     *
+     * @param delegate Underlying normalized node stream writer
+     * @param transformation Transformation function, function is required to return non-null
+     *        values.
+     * @return decorator, which uses supplied function to transform QNames.
+     */
+    public static NormalizedNodeStreamWriter fromFunction(final NormalizedNodeStreamWriter delegate,
+            final Function<QName, QName> transformation) {
+        return new QNameTransformingStreamWriter() {
+
+            @Override
+            protected NormalizedNodeStreamWriter delegate() {
+                return delegate;
+            }
+
+            @Override
+            protected QName transform(final QName key) {
+                return transformation.apply(key);
+            }
+        };
+    }
+
+    /**
+     * Returns decorator, which uses supplied map to transform QNames. QNames not present in map are left unchanged.
+     *
+     * @param delegate Underlying normalized node stream writer
+     * @param mapping Immutable map which represent mapping from original to new values.
+     * @return decorator, which uses supplied mapping to transform QNames.
+     */
+    public static NormalizedNodeStreamWriter createQNameReplacing(final NormalizedNodeStreamWriter delegate,
+            final Map<QName, QName> mapping) {
+        return fromFunction(delegate, new QNameReplacementFunction(mapping));
+    }
+
+    /**
+     * Returns decorator, which uses supplied map to transform QNameModules. QNameModules not present in map are left
+     * unchanged.
+     *
+     * @param delegate Underlying normalized node stream writer
+     * @param mapping Immutable map which represent mapping from original to new values.
+     * @return decorator, which uses supplied mapping to transform QNameModules.
+     */
+    public static NormalizedNodeStreamWriter createQNameModuleReplacing(final NormalizedNodeStreamWriter delegate,
+            final Map<QNameModule, QNameModule> mapping) {
+        return fromFunction(delegate, new QNameModuleReplacementFunction(mapping));
+    }
+
+    @Override
+    public void startLeafNode(final NodeIdentifier name) throws IOException {
+        super.startLeafNode(transform(name));
+    }
+
+    @Override
+    public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startLeafSet(transform(name), childSizeHint);
+    }
+
+    @Override
+    public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startOrderedLeafSet(transform(name), childSizeHint);
+    }
+
+    @Override
+    public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
+        super.startLeafSetEntryNode(transform(name));
+    }
+
+    @Override
+    public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startContainerNode(transform(name), childSizeHint);
+    }
+
+    @Override
+    public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startUnkeyedList(transform(name), childSizeHint);
+    }
+
+    @Override
+    public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startUnkeyedListItem(transform(name), childSizeHint);
+    }
+
+    @Override
+    public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startMapNode(transform(name), childSizeHint);
+    }
+
+    @Override
+    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+            throws IOException {
+        super.startMapEntryNode(transform(identifier), childSizeHint);
+    }
+
+    @Override
+    public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startOrderedMapNode(transform(name), childSizeHint);
+    }
+
+    @Override
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startChoiceNode(transform(name), childSizeHint);
+    }
+
+    @Override
+    public void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
+        super.startAugmentationNode(transform(identifier));
+    }
+
+    @Override
+    public boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+        return super.startAnyxmlNode(transform(name), objectModel);
+    }
+
+    @Override
+    public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        super.startYangModeledAnyXmlNode(transform(name), childSizeHint);
+    }
+
+    /**
+     * Transforms a QName to new mapping.
+     *
+     * <p>
+     * NOTE: If QName should be unchanged implementation needs to return original QName.
+     *
+     * @param key QName to transform.
+     * @return Returns new value of QName.
+     */
+    protected abstract @NonNull QName transform(@NonNull QName key);
+
+    private NodeIdentifier transform(final NodeIdentifier name) {
+        final QName original = name.getNodeType();
+        final QName transformed = transform(original);
+        return transformed == original ? name : new NodeIdentifier(transformed);
+    }
+
+    private <T> NodeWithValue<T> transform(final NodeWithValue<T> name) {
+        final QName original = name.getNodeType();
+        final QName transformed = transform(original);
+        return transformed == original ? name : new NodeWithValue<>(transformed, name.getValue());
+    }
+
+    private AugmentationIdentifier transform(final AugmentationIdentifier identifier) {
+        ImmutableSet.Builder<QName> builder = ImmutableSet.builder();
+        for (QName original : identifier.getPossibleChildNames()) {
+            builder.add(transform(original));
+        }
+        return new AugmentationIdentifier(builder.build());
+    }
+
+    private NodeIdentifierWithPredicates transform(final NodeIdentifierWithPredicates identifier) {
+        Map<QName, Object> keyValues = new HashMap<>();
+        for (Map.Entry<QName, Object> original : identifier.entrySet()) {
+            keyValues.put(transform(original.getKey()), original.getValue());
+        }
+        return NodeIdentifierWithPredicates.of(transform(identifier.getNodeType()), keyValues);
+    }
+}
diff --git a/yang/yang-data-util/pom.xml b/yang/yang-data-util/pom.xml
new file mode 100644 (file)
index 0000000..3a208a1
--- /dev/null
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-data-util</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odlext-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-util</artifactId>
+        </dependency>
+
+        <!-- Test dependencies -->
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-test-util</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.data.util</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractInteriorContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractInteriorContextNode.java
new file mode 100644 (file)
index 0000000..0ec3baf
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class AbstractInteriorContextNode<T extends PathArgument> extends
+        DataSchemaContextNode<T> {
+
+    protected AbstractInteriorContextNode(final T identifier, final DataSchemaNode schema) {
+        super(identifier, schema);
+    }
+
+    @Override
+    public boolean isLeaf() {
+        return false;
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractLeafContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractLeafContextNode.java
new file mode 100644 (file)
index 0000000..7acdbad
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.util;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class AbstractLeafContextNode<T extends PathArgument, S extends DataSchemaNode>
+        extends DataSchemaContextNode<T> {
+    AbstractLeafContextNode(T identifier, S schema) {
+        super(identifier, schema);
+    }
+
+    @Override
+    public final DataSchemaContextNode<?> getChild(final PathArgument child) {
+        return null;
+    }
+
+    @Override
+    public final DataSchemaContextNode<?> getChild(final QName child) {
+        return null;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractLeafNodeContext.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractLeafNodeContext.java
new file mode 100644 (file)
index 0000000..773eea9
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class AbstractLeafNodeContext<T extends PathArgument, S extends DataSchemaNode>
+        extends AbstractLeafContextNode<T, S> {
+
+    AbstractLeafNodeContext(final T identifier, final S potential) {
+        super(identifier, potential);
+    }
+
+    @Override
+    public final boolean isLeaf() {
+        return true;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractMixinContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractMixinContextNode.java
new file mode 100644 (file)
index 0000000..3806612
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class AbstractMixinContextNode<T extends PathArgument> extends
+        AbstractInteriorContextNode<T> {
+
+    protected AbstractMixinContextNode(final T identifier, final DataSchemaNode schema) {
+        super(identifier, schema);
+    }
+
+    @Override
+    public final boolean isMixin() {
+        return true;
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractModuleStringIdentityrefCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractModuleStringIdentityrefCodec.java
new file mode 100644 (file)
index 0000000..d2de311
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+@Beta
+public abstract class AbstractModuleStringIdentityrefCodec extends AbstractStringIdentityrefCodec {
+    /**
+     * Resolve a string prefix into the corresponding module.
+     *
+     * @param prefix Prefix
+     * @return module mapped to prefix, or null if the module cannot be resolved
+     */
+    protected abstract @Nullable Module moduleForPrefix(@NonNull String prefix);
+
+    @Override
+    protected final QName createQName(final String prefix, final String localName) {
+        final Module module = moduleForPrefix(prefix);
+        checkArgument(module != null, "Failed to lookup prefix %s", prefix);
+
+        final QName qname = QName.create(module.getQNameModule(), localName);
+        for (IdentitySchemaNode identity : module.getIdentities()) {
+            if (qname.equals(identity.getQName())) {
+                return identity.getQName();
+            }
+        }
+
+        throw new IllegalArgumentException("Failed to find identity matching " + qname);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractModuleStringInstanceIdentifierCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractModuleStringInstanceIdentifierCodec.java
new file mode 100644 (file)
index 0000000..847aa68
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+/**
+ * Extension of {@link AbstractStringInstanceIdentifierCodec}, which instantiates
+ * QNames by first resolving the namespace and then looking the target namespace
+ * in the list of currently-subscribed modules.
+ */
+@Beta
+public abstract class AbstractModuleStringInstanceIdentifierCodec extends AbstractStringInstanceIdentifierCodec {
+    /**
+     * Resolve a string prefix into the corresponding module.
+     *
+     * @param prefix Prefix
+     * @return module mapped to prefix, or null if the module cannot be resolved
+     */
+    protected abstract @Nullable Module moduleForPrefix(@NonNull String prefix);
+
+    @Override
+    protected final QName createQName(final String prefix, final String localName) {
+        final Module module = moduleForPrefix(prefix);
+        checkArgument(module != null, "Failed to lookup prefix %s", prefix);
+        return QName.create(module.getQNameModule(), localName);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractMountPointDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractMountPointDataWithSchema.java
new file mode 100644 (file)
index 0000000..94f18ef
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.util;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * A {@link CompositeNodeDataWithSchema} which can hold mount-point data. This data is manipulated through
+ * {@link #getMountPointData(MountPointIdentifier, MountPointContextFactory)}.
+ */
+@Beta
+public abstract class AbstractMountPointDataWithSchema<T extends DataSchemaNode>
+        extends CompositeNodeDataWithSchema<T> {
+    private MountPointData mountedData;
+
+    AbstractMountPointDataWithSchema(final T schema) {
+        super(schema);
+    }
+
+    @Override
+    public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+            throws IOException {
+        super.write(writer, metaWriter);
+        if (mountedData != null) {
+            mountedData.write(writer);
+        }
+    }
+
+    public final MountPointData getMountPointData(final MountPointIdentifier label,
+            final MountPointContextFactory factory) {
+        if (mountedData != null) {
+            final MountPointIdentifier existing = mountedData.getIdentifier();
+            checkState(label.equals(existing), "Mismatched mount label {}, already have {}", label, existing);
+        } else {
+            mountedData = new MountPointData(label, factory);
+        }
+        return mountedData;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractNamespaceCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractNamespaceCodec.java
new file mode 100644 (file)
index 0000000..d92c9eb
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Splitter;
+import java.net.URI;
+import java.util.Iterator;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.AbstractIllegalArgumentCodec;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+abstract class AbstractNamespaceCodec<T> extends AbstractIllegalArgumentCodec<String, T> {
+    private static final Splitter COLON_SPLITTER = Splitter.on(':');
+
+    /**
+     * Return string prefix for a particular namespace, allocating a new one if necessary.
+     *
+     * @param namespace Namespace to map
+     * @return Allocated unique prefix, or null if the prefix cannot be mapped.
+     */
+    protected abstract @Nullable String prefixForNamespace(@NonNull URI namespace);
+
+    /**
+     * Create a QName for a prefix and local name.
+     *
+     * @param prefix Prefix for namespace
+     * @param localName local name
+     * @return QName
+     * @throws IllegalArgumentException if the prefix cannot be resolved
+     */
+    protected abstract @Nullable QName createQName(@NonNull String prefix, @NonNull String localName);
+
+    private static String getIdAndPrefixAsStr(final String pathPart) {
+        int predicateStartIndex = pathPart.indexOf('[');
+        return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
+    }
+
+    protected final @NonNull StringBuilder appendQName(final StringBuilder sb, final QName qname) {
+        final String prefix = prefixForNamespace(qname.getNamespace());
+        checkArgument(prefix != null, "Failed to map QName %s to prefix", qname);
+        return sb.append(prefix).append(':').append(qname.getLocalName());
+    }
+
+    /**
+     * Append a QName, potentially taking into account last QNameModule encountered in the serialized path.
+     *
+     * @param sb target StringBuilder
+     * @param qname QName to append
+     * @param lastModule last QNameModule encountered, may be null
+     * @return target StringBuilder
+     */
+    protected StringBuilder appendQName(final StringBuilder sb, final QName qname,
+            final @Nullable QNameModule lastModule) {
+        // Covers XML and uncompressed JSON codec
+        return appendQName(sb, qname);
+    }
+
+    protected final QName parseQName(final String str) {
+        final String xPathPartTrimmed = getIdAndPrefixAsStr(str).trim();
+        final Iterator<String> it = COLON_SPLITTER.split(xPathPartTrimmed).iterator();
+
+        // Empty string
+        if (!it.hasNext()) {
+            return null;
+        }
+
+
+        final String first = it.next().trim();
+        if (first.isEmpty()) {
+            return null;
+        }
+
+        final String identifier;
+        final String prefix;
+        if (it.hasNext()) {
+            // It is "prefix:value"
+            prefix = first;
+            identifier = it.next().trim();
+        } else {
+            prefix = "";
+            identifier = first;
+        }
+        if (identifier.isEmpty()) {
+            return null;
+        }
+
+        return createQName(prefix, identifier);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..2dc4291
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * Utility abstract class for tracking parser state, as needed by StAX-like parser.
+ * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
+ */
+@Beta
+public abstract class AbstractNodeDataWithSchema<T extends DataSchemaNode> {
+    private final T schema;
+    private ImmutableMap<QName, Object> attributes;
+
+    public AbstractNodeDataWithSchema(final T schema) {
+        this.schema = requireNonNull(schema);
+    }
+
+    /**
+     * Return the associated schema node.
+     *
+     * @return Associated schema node.
+     */
+    public final @NonNull T getSchema() {
+        return schema;
+    }
+
+    /**
+     * Set the associated attributes.
+     *
+     * @param attributes parsed attributes
+     */
+    public final void setAttributes(final ImmutableMap<QName, Object> attributes) {
+        checkState(this.attributes == null, "Node '%s' has already set its attributes to %s.", getSchema().getQName(),
+                this.attributes);
+        this.attributes = attributes;
+    }
+
+    /**
+     * Return the associated attributes.
+     *
+     * @return associated attributes
+     */
+    public final ImmutableMap<QName, Object> getAttributes() {
+        return attributes;
+    }
+
+    /**
+     * Emit this node's events into the specified writer.
+     *
+     * @param writer Target writer
+     * @throws IOException reported when thrown by the writer.
+     */
+    public final void write(final NormalizedNodeStreamWriter writer) throws IOException {
+        write(writer, writer.getExtensions().getInstance(StreamWriterMetadataExtension.class));
+    }
+
+    protected abstract void write(NormalizedNodeStreamWriter writer,
+            @Nullable StreamWriterMetadataExtension metaWriter) throws IOException;
+
+    protected final NodeIdentifier provideNodeIdentifier() {
+        return NodeIdentifier.create(schema.getQName());
+    }
+
+    protected final void writeMetadata(final StreamWriterMetadataExtension metaWriter) throws IOException {
+        if (metaWriter != null && attributes != null && !attributes.isEmpty()) {
+            metaWriter.metadata(attributes);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(schema);
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final AbstractNodeDataWithSchema<?> other = (AbstractNodeDataWithSchema<?>) obj;
+        return schema.equals(other.schema);
+    }
+
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractOpaqueContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractOpaqueContextNode.java
new file mode 100644 (file)
index 0000000..857486b
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.util;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class AbstractOpaqueContextNode<S extends DataSchemaNode> extends AbstractLeafContextNode<NodeIdentifier, S> {
+    AbstractOpaqueContextNode(S schema) {
+        super(NodeIdentifier.create(schema.getQName()), schema);
+    }
+
+    @Override
+    public final boolean isLeaf() {
+        return false;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringIdentityrefCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringIdentityrefCodec.java
new file mode 100644 (file)
index 0000000..0f8a852
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.util;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.codec.IdentityrefCodec;
+
+/**
+ * Abstract utility class for representations which encode Identityref as a
+ * prefix:name tuple. Typical uses are RESTCONF/JSON (module:name) and XML (prefix:name).
+ */
+@Beta
+public abstract class AbstractStringIdentityrefCodec extends AbstractNamespaceCodec<QName>
+        implements IdentityrefCodec<String> {
+    @Override
+    protected final String serializeImpl(final QName data) {
+        return appendQName(new StringBuilder(), data).toString();
+    }
+
+    @Override
+    protected final QName deserializeImpl(final String data) {
+        return parseQName(data);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringInstanceIdentifierCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringInstanceIdentifierCodec.java
new file mode 100644 (file)
index 0000000..02a2455
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Map.Entry;
+import javax.xml.XMLConstants;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.codec.InstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * Abstract utility class for representations which encode {@link YangInstanceIdentifier} as a
+ * prefix:name tuple. Typical uses are RESTCONF/JSON (module:name) and XML (prefix:name).
+ */
+@Beta
+public abstract class AbstractStringInstanceIdentifierCodec extends AbstractNamespaceCodec<YangInstanceIdentifier>
+        implements InstanceIdentifierCodec<String> {
+    @Override
+    protected final String serializeImpl(final YangInstanceIdentifier data) {
+        final StringBuilder sb = new StringBuilder();
+        DataSchemaContextNode<?> current = getDataContextTree().getRoot();
+        QNameModule lastModule = null;
+        for (PathArgument arg : data.getPathArguments()) {
+            current = current.getChild(arg);
+            checkArgument(current != null, "Invalid input %s: schema for argument %s (after %s) not found", data, arg,
+                    sb);
+
+            if (current.isMixin()) {
+                /*
+                 * XML/YANG instance identifier does not have concept
+                 * of augmentation identifier, or list as whole which
+                 * identifies a mixin (same as the parent element),
+                 * so we can safely ignore it if it is part of path
+                 * (since child node) is identified in same fashion.
+                 */
+                continue;
+            }
+
+            final QName qname = arg.getNodeType();
+            sb.append('/');
+            appendQName(sb, qname, lastModule);
+            lastModule = qname.getModule();
+
+            if (arg instanceof NodeIdentifierWithPredicates) {
+                for (Entry<QName, Object> entry : ((NodeIdentifierWithPredicates) arg).entrySet()) {
+                    appendQName(sb.append('['), entry.getKey(), lastModule);
+                    sb.append("='").append(String.valueOf(entry.getValue())).append("']");
+                }
+            } else if (arg instanceof NodeWithValue) {
+                sb.append("[.='").append(((NodeWithValue<?>) arg).getValue()).append("']");
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Returns DataSchemaContextTree associated with SchemaContext for which
+     * serialization / deserialization occurs.
+     *
+     * <p>
+     * Implementations MUST provide non-null Data Tree context, in order
+     * for correct serialization / deserialization of PathArguments,
+     * since XML representation does not have Augmentation arguments
+     * and does not provide path arguments for cases.
+     *
+     * <p>
+     * This effectively means same input XPath representation of Path Argument
+     * may result in different YangInstanceIdentifiers if models are different
+     * in uses of choices and cases.
+     *
+     * @return DataSchemaContextTree associated with SchemaContext for which
+     *         serialization / deserialization occurs.
+     */
+    protected abstract @NonNull DataSchemaContextTree getDataContextTree();
+
+    protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final String value) {
+        return value;
+    }
+
+    @Override
+    protected final YangInstanceIdentifier deserializeImpl(final String data) {
+        XpathStringParsingPathArgumentBuilder builder = new XpathStringParsingPathArgumentBuilder(this,
+            requireNonNull(data));
+        return YangInstanceIdentifier.create(builder.build());
+    }
+
+    /**
+     * Create QName from unprefixed name, potentially taking last QNameModule encountered into account.
+     *
+     * @param lastModule last QNameModule encountered, potentially null
+     * @param localName Local name string
+     * @return A newly-created QName
+     */
+    protected QName createQName(final @Nullable QNameModule lastModule, final String localName) {
+        // This implementation handles both XML encoding, where we follow XML namespace rules and old JSON encoding,
+        // which is the same thing: always encode prefixes
+        return createQName(XMLConstants.DEFAULT_NS_PREFIX, localName);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringUnionCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringUnionCodec.java
new file mode 100644 (file)
index 0000000..bb67be3
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2016 Intel Corporation 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.yangtools.concepts.AbstractIllegalArgumentCodec;
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractStringUnionCodec extends AbstractIllegalArgumentCodec<String, Object> {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractStringUnionCodec.class);
+
+    protected final DataSchemaNode schema;
+    protected final UnionTypeDefinition typeDefinition;
+
+    protected AbstractStringUnionCodec(final DataSchemaNode schema, final UnionTypeDefinition typeDefinition) {
+        this.schema = requireNonNull(schema);
+        this.typeDefinition = requireNonNull(typeDefinition);
+    }
+
+    protected abstract IllegalArgumentCodec<String, Object> codecFor(TypeDefinition<?> type);
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    protected Object deserializeImpl(final String stringRepresentation) {
+        Object returnValue = null;
+        for (final TypeDefinition<?> type : typeDefinition.getTypes()) {
+            IllegalArgumentCodec<String, Object> codec = codecFor(type);
+            if (codec == null) {
+                /*
+                 * This is a type for which we have no codec (eg identity ref) so we'll say it's valid
+                 */
+                returnValue = stringRepresentation;
+                continue;
+            }
+            try {
+                // FIXME: this hunting is a bit inefficient: we probably want to use Optional or something
+                return codec.deserialize(stringRepresentation);
+            } catch (final IllegalArgumentException e) {
+                LOG.debug("Value {} did not match representation for {}", stringRepresentation, type, e);
+                // invalid - try the next union type.
+            }
+        }
+        if (returnValue == null) {
+            throw new IllegalArgumentException("Invalid value \"" + stringRepresentation + "\" for union type.");
+        }
+        return returnValue;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:illegalCatch")
+    protected final String serializeImpl(final Object data) {
+        for (final TypeDefinition<?> type : typeDefinition.getTypes()) {
+            IllegalArgumentCodec<String, Object> codec = codecFor(type);
+            if (codec == null) {
+                LOG.debug("no codec found for {}", type);
+                continue;
+            }
+            try {
+                return codec.serialize(data);
+            } catch (final IllegalArgumentException e) {
+                LOG.debug("Data {} did not match for {}", data, type, e);
+                // invalid - try the next union type.
+            }
+        }
+        throw new IllegalArgumentException("Invalid data \"" + data + "\" for union type.");
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnyXmlContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnyXmlContextNode.java
new file mode 100644 (file)
index 0000000..94ec33e
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+
+final class AnyXmlContextNode extends AbstractOpaqueContextNode<AnyxmlSchemaNode> {
+    AnyXmlContextNode(final AnyxmlSchemaNode schema) {
+        super(schema);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnyXmlNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnyXmlNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..9620765
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import java.io.IOException;
+import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+
+/**
+ * Utility class used for tracking parser state as needed by a StAX-like parser.
+ * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
+ *
+ * <p>
+ * Represents a YANG anyxml node.
+ */
+public class AnyXmlNodeDataWithSchema extends SimpleNodeDataWithSchema<AnyxmlSchemaNode> {
+
+    public AnyXmlNodeDataWithSchema(final AnyxmlSchemaNode dataSchemaNode) {
+        super(dataSchemaNode);
+    }
+
+    @Override
+    public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+            throws IOException {
+        writer.nextDataSchemaNode(getSchema());
+        if (writer.startAnyxmlNode(provideNodeIdentifier(), DOMSource.class)) {
+            writeMetadata(metaWriter);
+            writer.domSourceValue((DOMSource) getValue());
+            writer.endNode();
+        }
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnydataContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnydataContextNode.java
new file mode 100644 (file)
index 0000000..6cd9e8e
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.util;
+
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+
+final class AnydataContextNode extends AbstractOpaqueContextNode<AnydataSchemaNode> {
+    AnydataContextNode(final AnydataSchemaNode schema) {
+        super(schema);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnydataNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnydataNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..762b084
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+
+@Beta
+public class AnydataNodeDataWithSchema extends SimpleNodeDataWithSchema<AnydataSchemaNode> {
+    private Class<?> objectModel;
+
+    public AnydataNodeDataWithSchema(final AnydataSchemaNode dataSchemaNode) {
+        super(dataSchemaNode);
+    }
+
+    public AnydataNodeDataWithSchema(final AnydataSchemaNode dataSchemaNode, final Class<?> objectModel) {
+        super(dataSchemaNode);
+        this.objectModel = requireNonNull(objectModel);
+    }
+
+    @Override
+    public Object getValue() {
+        return getObjectModel().cast(super.getValue());
+    }
+
+    @Override
+    public void setValue(final Object value) {
+        final Class<?> clazz = getObjectModel();
+        checkArgument(clazz.isInstance(value), "Value %s is not compatible with %s", clazz);
+        super.setValue(value);
+    }
+
+    @Override
+    protected void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+            throws IOException {
+        writer.nextDataSchemaNode(getSchema());
+        if (writer.startAnydataNode(provideNodeIdentifier(), getObjectModel())) {
+            writer.scalarValue(getValue());
+            writer.endNode();
+        }
+    }
+
+    public final @NonNull Class<?> getObjectModel() {
+        checkState(objectModel != null, "Object model not set");
+        return objectModel;
+    }
+
+    public void setObjectModel(final Class<?> newObjectModel) {
+        checkState(objectModel == null, "Object model already set to %s", objectModel);
+        objectModel = requireNonNull(newObjectModel);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AugmentationContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AugmentationContextNode.java
new file mode 100644 (file)
index 0000000..15fb6a2
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
+
+final class AugmentationContextNode extends DataContainerContextNode<AugmentationIdentifier> {
+    AugmentationContextNode(final AugmentationSchemaNode augmentation, final DataNodeContainer schema) {
+        super(augmentationIdentifierFrom(augmentation), EffectiveAugmentationSchema.create(augmentation, schema), null);
+    }
+
+    @Override
+    public boolean isMixin() {
+        return true;
+    }
+
+    @Override
+    protected DataSchemaContextNode<?> fromLocalSchemaAndQName(final DataNodeContainer schema, final QName child) {
+        final DataSchemaNode result = findChildSchemaNode(schema, child);
+        // We try to look up if this node was added by augmentation
+        if (schema instanceof DataSchemaNode && result.isAugmenting()) {
+            return fromAugmentation(schema, (AugmentationTarget) schema, result);
+        }
+        return fromDataSchemaNode(result);
+    }
+
+    @Override
+    protected Set<QName> getQNameIdentifiers() {
+        return getIdentifier().getPossibleChildNames();
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CaseNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CaseNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..3d7ddd3
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+
+class CaseNodeDataWithSchema extends CompositeNodeDataWithSchema<CaseSchemaNode> {
+    CaseNodeDataWithSchema(final CaseSchemaNode schema) {
+        super(schema);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ChoiceNodeContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ChoiceNodeContextNode.java
new file mode 100644 (file)
index 0000000..efa06b9
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import com.google.common.collect.ImmutableMap;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class ChoiceNodeContextNode extends AbstractMixinContextNode<NodeIdentifier> {
+
+    private final ImmutableMap<QName, DataSchemaContextNode<?>> byQName;
+    private final ImmutableMap<PathArgument, DataSchemaContextNode<?>> byArg;
+
+    protected ChoiceNodeContextNode(final ChoiceSchemaNode schema) {
+        super(NodeIdentifier.create(schema.getQName()), schema);
+        ImmutableMap.Builder<QName, DataSchemaContextNode<?>> byQNameBuilder = ImmutableMap.builder();
+        ImmutableMap.Builder<PathArgument, DataSchemaContextNode<?>> byArgBuilder = ImmutableMap.builder();
+
+        for (CaseSchemaNode caze : schema.getCases().values()) {
+            for (DataSchemaNode cazeChild : caze.getChildNodes()) {
+                DataSchemaContextNode<?> childOp = fromDataSchemaNode(cazeChild);
+                byArgBuilder.put(childOp.getIdentifier(), childOp);
+                for (QName qname : childOp.getQNameIdentifiers()) {
+                    byQNameBuilder.put(qname, childOp);
+                }
+            }
+        }
+        byQName = byQNameBuilder.build();
+        byArg = byArgBuilder.build();
+    }
+
+    @Override
+    public DataSchemaContextNode<?> getChild(final PathArgument child) {
+        return byArg.get(child);
+    }
+
+    @Override
+    public DataSchemaContextNode<?> getChild(final QName child) {
+        return byQName.get(child);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ChoiceNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ChoiceNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..47e9081
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import java.io.IOException;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * childs - empty augment - only one element can be.
+ */
+class ChoiceNodeDataWithSchema extends CompositeNodeDataWithSchema<ChoiceSchemaNode> {
+    private CaseNodeDataWithSchema caseNodeDataWithSchema;
+
+    ChoiceNodeDataWithSchema(final ChoiceSchemaNode schema) {
+        super(schema);
+    }
+
+    @Override
+    protected CaseNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) {
+        CaseNodeDataWithSchema newChild = new CaseNodeDataWithSchema((CaseSchemaNode) schema);
+        caseNodeDataWithSchema = newChild;
+        addCompositeChild(newChild);
+        return newChild;
+    }
+
+    public CaseNodeDataWithSchema getCase() {
+        return caseNodeDataWithSchema;
+    }
+
+    @Override
+    public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+            throws IOException {
+        writer.nextDataSchemaNode(getSchema());
+        writer.startChoiceNode(provideNodeIdentifier(), childSizeHint());
+        super.write(writer, metaWriter);
+        writer.endNode();
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CompositeNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CompositeNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..ef4faae
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map.Entry;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+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;
+
+/**
+ * Utility class used for tracking parser state as needed by a StAX-like parser.
+ * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
+ *
+ * <p>
+ * Represents a node which is composed of multiple simpler nodes.
+ */
+public class CompositeNodeDataWithSchema<T extends DataSchemaNode> extends AbstractNodeDataWithSchema<T> {
+    /**
+     * nodes which were added to schema via augmentation and are present in data input.
+     */
+    private final Multimap<AugmentationSchemaNode, AbstractNodeDataWithSchema<?>> augmentationsToChild =
+        ArrayListMultimap.create();
+
+    /**
+     * remaining data nodes (which aren't added via augment). Every of one them should have the same QName.
+     */
+    private final List<AbstractNodeDataWithSchema<?>> children = new ArrayList<>();
+
+    public CompositeNodeDataWithSchema(final T schema) {
+        super(schema);
+    }
+
+    private AbstractNodeDataWithSchema<?> addChild(final DataSchemaNode schema) {
+        AbstractNodeDataWithSchema<?> newChild = addSimpleChild(schema);
+        return newChild == null ? addCompositeChild(schema) : newChild;
+    }
+
+    public void addChild(final AbstractNodeDataWithSchema<?> newChild) {
+        children.add(newChild);
+    }
+
+    public AbstractNodeDataWithSchema<?> addChild(final Deque<DataSchemaNode> schemas) {
+        checkArgument(!schemas.isEmpty(), "Expecting at least one schema");
+
+        // Pop the first node...
+        final DataSchemaNode schema = schemas.pop();
+        if (schemas.isEmpty()) {
+            // Simple, direct node
+            return addChild(schema);
+        }
+
+        // The choice/case mess, reuse what we already popped
+        final DataSchemaNode choiceCandidate = schema;
+        checkArgument(choiceCandidate instanceof ChoiceSchemaNode, "Expected node of type ChoiceNode but was %s",
+            choiceCandidate.getClass());
+        final ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) choiceCandidate;
+
+        final DataSchemaNode caseCandidate = schemas.pop();
+        checkArgument(caseCandidate instanceof CaseSchemaNode, "Expected node of type ChoiceCaseNode but was %s",
+            caseCandidate.getClass());
+        final CaseSchemaNode caseNode = (CaseSchemaNode) caseCandidate;
+
+        AugmentationSchemaNode augSchema = null;
+        if (choiceCandidate.isAugmenting()) {
+            augSchema = findCorrespondingAugment(getSchema(), choiceCandidate);
+        }
+
+        // looking for existing choice
+        final Collection<AbstractNodeDataWithSchema<?>> childNodes;
+        if (augSchema != null) {
+            childNodes = augmentationsToChild.get(augSchema);
+        } else {
+            childNodes = children;
+        }
+
+        CompositeNodeDataWithSchema<?> caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate);
+        if (caseNodeDataWithSchema == null) {
+            ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode);
+            childNodes.add(choiceNodeDataWithSchema);
+            caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode);
+        }
+
+        return caseNodeDataWithSchema.addChild(schemas);
+    }
+
+    private AbstractNodeDataWithSchema<?> addSimpleChild(final DataSchemaNode schema) {
+        SimpleNodeDataWithSchema<?> newChild = null;
+        if (schema instanceof LeafSchemaNode) {
+            newChild = new LeafNodeDataWithSchema((LeafSchemaNode) schema);
+        } else if (schema instanceof AnyxmlSchemaNode) {
+            // YangModeledAnyxmlSchemaNode is handled by addCompositeChild method.
+            if (schema instanceof YangModeledAnyxmlSchemaNode) {
+                return null;
+            }
+            newChild = new AnyXmlNodeDataWithSchema((AnyxmlSchemaNode) schema);
+        } else if (schema instanceof AnydataSchemaNode) {
+            newChild = new AnydataNodeDataWithSchema((AnydataSchemaNode) schema);
+        } else {
+            return null;
+        }
+
+        AugmentationSchemaNode augSchema = null;
+        if (schema.isAugmenting()) {
+            augSchema = findCorrespondingAugment(getSchema(), schema);
+        }
+        if (augSchema != null) {
+            augmentationsToChild.put(augSchema, newChild);
+        } else {
+            addChild(newChild);
+        }
+        return newChild;
+    }
+
+    private static CaseNodeDataWithSchema findChoice(final Collection<AbstractNodeDataWithSchema<?>> childNodes,
+            final DataSchemaNode choiceCandidate, final DataSchemaNode caseCandidate) {
+        if (childNodes != null) {
+            for (AbstractNodeDataWithSchema<?> nodeDataWithSchema : childNodes) {
+                if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema
+                        && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) {
+                    CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase();
+
+                    checkArgument(casePrevious.getSchema().getQName().equals(caseCandidate.getQName()),
+                        "Data from case %s are specified but other data from case %s were specified earlier."
+                        + " Data aren't from the same case.", caseCandidate.getQName(),
+                        casePrevious.getSchema().getQName());
+
+                    return casePrevious;
+                }
+            }
+        }
+        return null;
+    }
+
+    AbstractNodeDataWithSchema<?> addCompositeChild(final DataSchemaNode schema) {
+        final CompositeNodeDataWithSchema<?> newChild;
+
+        if (schema instanceof ListSchemaNode) {
+            newChild = new ListNodeDataWithSchema((ListSchemaNode) schema);
+        } else if (schema instanceof LeafListSchemaNode) {
+            newChild = new LeafListNodeDataWithSchema((LeafListSchemaNode) schema);
+        } else if (schema instanceof ContainerSchemaNode) {
+            newChild = new ContainerNodeDataWithSchema((ContainerSchemaNode) schema);
+        } else if (schema instanceof YangModeledAnyxmlSchemaNode) {
+            newChild = new YangModeledAnyXmlNodeDataWithSchema((YangModeledAnyxmlSchemaNode)schema);
+        } else {
+            newChild = new CompositeNodeDataWithSchema<>(schema);
+        }
+
+        addCompositeChild(newChild);
+        return newChild;
+    }
+
+    void addCompositeChild(final CompositeNodeDataWithSchema<?> newChild) {
+        AugmentationSchemaNode augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema());
+        if (augSchema != null) {
+            augmentationsToChild.put(augSchema, newChild);
+        } else {
+            addChild(newChild);
+        }
+    }
+
+    /**
+     * Return a hint about how may children we are going to generate.
+     * @return Size of currently-present node list.
+     */
+    protected final int childSizeHint() {
+        return children.size();
+    }
+
+    @Override
+    public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+            throws IOException {
+        for (AbstractNodeDataWithSchema<?> child : children) {
+            child.write(writer, metaWriter);
+        }
+        for (Entry<AugmentationSchemaNode, Collection<AbstractNodeDataWithSchema<?>>> augmentationToChild
+                : augmentationsToChild.asMap().entrySet()) {
+            final Collection<AbstractNodeDataWithSchema<?>> childsFromAgumentation = augmentationToChild.getValue();
+            if (!childsFromAgumentation.isEmpty()) {
+                // FIXME: can we get the augmentation schema?
+                writer.startAugmentationNode(DataSchemaContextNode.augmentationIdentifierFrom(
+                    augmentationToChild.getKey()));
+
+                for (AbstractNodeDataWithSchema<?> nodeDataWithSchema : childsFromAgumentation) {
+                    nodeDataWithSchema.write(writer, metaWriter);
+                }
+
+                writer.endNode();
+            }
+        }
+    }
+
+    /**
+     * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such
+     * node is found then it is returned, else null.
+     *
+     * @param parent parent node
+     * @param child child node
+     * @return augmentation schema
+     */
+    private static AugmentationSchemaNode findCorrespondingAugment(final DataSchemaNode parent,
+            final DataSchemaNode child) {
+        if (parent instanceof AugmentationTarget && !(parent instanceof ChoiceSchemaNode)) {
+            for (AugmentationSchemaNode augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
+                DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
+                if (childInAugmentation != null) {
+                    return augmentation;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerContextNode.java
new file mode 100644 (file)
index 0000000..a7a6492
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+
+final class ContainerContextNode extends DataContainerContextNode<NodeIdentifier> {
+
+    protected ContainerContextNode(final ContainerSchemaNode schema) {
+        super(NodeIdentifier.create(schema.getQName()), schema, schema);
+    }
+
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..e70d278
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import java.io.IOException;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+
+/**
+ * Utility class used for tracking parser state as needed by a StAX-like parser.
+ * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
+ *
+ * <p>
+ * Represents a YANG container node.
+ */
+public class ContainerNodeDataWithSchema extends AbstractMountPointDataWithSchema<ContainerSchemaNode> {
+
+    public ContainerNodeDataWithSchema(final ContainerSchemaNode schema) {
+        super(schema);
+    }
+
+    @Override
+    public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+            throws IOException {
+        writer.nextDataSchemaNode(getSchema());
+
+        writer.startContainerNode(provideNodeIdentifier(), childSizeHint());
+        writeMetadata(metaWriter);
+
+        super.write(writer, metaWriter);
+        writer.endNode();
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerSchemaNodes.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerSchemaNodes.java
new file mode 100644 (file)
index 0000000..588c81a
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2016 ZTE, 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+/**
+ * yang-data-util
+ * org.opendaylight.yangtools.yang.data.util
+ * Utility class for taking notification or rpc as ContainerSchemaNode.
+ * @author <a href="mailto:geng.xingyuan@zte.com.cn">Geng Xingyuan</a>
+ */
+public final class ContainerSchemaNodes {
+
+    private ContainerSchemaNodes() {
+    }
+
+    @Beta
+    public static ContainerSchemaNode forNotification(final NotificationDefinition notification) {
+        return new NotificationContainerSchemaNode(notification);
+    }
+
+    @Beta
+    public static ContainerSchemaNode forRPC(final RpcDefinition rpc) {
+        return new RpcContainerSchemaNode(rpc);
+    }
+
+    private abstract static class AbstractContainerSchemaNode implements ContainerSchemaNode {
+
+        private final SchemaNode schemaNode;
+
+        private AbstractContainerSchemaNode(final SchemaNode schemaNode) {
+            this.schemaNode = schemaNode;
+        }
+
+        @Override
+        public boolean isPresenceContainer() {
+            return false;
+        }
+
+        @Override
+        public Set<UsesNode> getUses() {
+            return ImmutableSet.of();
+        }
+
+        @Override
+        public boolean isAugmenting() {
+            return false;
+        }
+
+        @Override
+        public boolean isConfiguration() {
+            return false;
+        }
+
+        @Override
+        public QName getQName() {
+            return schemaNode.getQName();
+        }
+
+        @Override
+        public SchemaPath getPath() {
+            return schemaNode.getPath();
+        }
+
+        @Override
+        public Optional<String> getDescription() {
+            return schemaNode.getDescription();
+        }
+
+        @Override
+        public Optional<String> getReference() {
+            return schemaNode.getReference();
+        }
+
+        @Override
+        public Status getStatus() {
+            return schemaNode.getStatus();
+        }
+
+        @Override
+        public Collection<MustDefinition> getMustConstraints() {
+            return ImmutableList.of();
+        }
+
+        @Override
+        public Optional<RevisionAwareXPath> getWhenCondition() {
+            return Optional.empty();
+        }
+    }
+
+    private static final class RpcContainerSchemaNode extends AbstractContainerSchemaNode {
+
+        private final RpcDefinition rpcDefinition;
+
+        private RpcContainerSchemaNode(final RpcDefinition rpcDefinition) {
+            super(rpcDefinition);
+            this.rpcDefinition = rpcDefinition;
+        }
+
+        @Override
+        public Set<GroupingDefinition> getGroupings() {
+            return rpcDefinition.getGroupings();
+        }
+
+        @Override
+        public Set<TypeDefinition<?>> getTypeDefinitions() {
+            return rpcDefinition.getTypeDefinitions();
+        }
+
+        @Override
+        public Set<AugmentationSchemaNode> getAvailableAugmentations() {
+            return ImmutableSet.of();
+        }
+
+        @Override
+        public Collection<DataSchemaNode> getChildNodes() {
+            final ContainerSchemaNode input = rpcDefinition.getInput();
+            final ContainerSchemaNode output = rpcDefinition.getOutput();
+            if (input == null && output == null) {
+                return ImmutableList.of();
+            } else if (input != null && output != null) {
+                return ImmutableList.of(input,output);
+            } else if (input != null) {
+                return ImmutableList.of(input);
+            } else {
+                return ImmutableList.of(output);
+            }
+        }
+
+        @Override
+        public Optional<DataSchemaNode> findDataChildByName(final QName name) {
+            switch (name.getLocalName()) {
+                case "input":
+                    return Optional.of(rpcDefinition.getInput());
+                case "output":
+                    return Optional.of(rpcDefinition.getOutput());
+                default:
+                    return Optional.empty();
+            }
+        }
+
+        @Deprecated
+        @Override
+        public boolean isAddedByUses() {
+            return false;
+        }
+
+        @Override
+        public Set<ActionDefinition> getActions() {
+            return ImmutableSet.of();
+        }
+
+        @Override
+        public Set<NotificationDefinition> getNotifications() {
+            return ImmutableSet.of();
+        }
+    }
+
+    private static final class NotificationContainerSchemaNode extends AbstractContainerSchemaNode {
+
+        private final NotificationDefinition notification;
+        private final ImmutableMap<QName, DataSchemaNode> mapNodes;
+
+        private NotificationContainerSchemaNode(final NotificationDefinition notification) {
+            super(notification);
+            this.notification = notification;
+            mapNodes = Maps.uniqueIndex(notification.getChildNodes(), DataSchemaNode::getQName);
+        }
+
+        @Override
+        public Set<NotificationDefinition> getNotifications() {
+            return ImmutableSet.of(notification);
+        }
+
+        @Override
+        public Set<AugmentationSchemaNode> getAvailableAugmentations() {
+            return notification.getAvailableAugmentations();
+        }
+
+        @Override
+        public Set<TypeDefinition<?>> getTypeDefinitions() {
+            return notification.getTypeDefinitions();
+        }
+
+        @Override
+        public Collection<DataSchemaNode> getChildNodes() {
+            return notification.getChildNodes();
+        }
+
+        @Override
+        public Set<GroupingDefinition> getGroupings() {
+            return notification.getGroupings();
+        }
+
+        @Override
+        public Optional<DataSchemaNode> findDataChildByName(final QName name) {
+            return Optional.ofNullable(mapNodes.get(requireNonNull(name)));
+        }
+
+        @Override
+        @Deprecated
+        public boolean isAddedByUses() {
+            //FIXME: reference to https://bugs.opendaylight.org/show_bug.cgi?id=6897
+            return false;
+        }
+
+        @Override
+        public Set<ActionDefinition> getActions() {
+            return ImmutableSet.of();
+        }
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/DataContainerContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/DataContainerContextNode.java
new file mode 100644 (file)
index 0000000..51aa4e2
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class DataContainerContextNode<T extends PathArgument> extends
+        AbstractInteriorContextNode<T> {
+
+    private final DataNodeContainer schema;
+    private final Map<QName, DataSchemaContextNode<?>> byQName;
+    private final Map<PathArgument, DataSchemaContextNode<?>> byArg;
+
+    protected DataContainerContextNode(final T identifier, final DataNodeContainer schema,
+            final DataSchemaNode node) {
+        super(identifier, node);
+        this.schema = schema;
+        this.byArg = new ConcurrentHashMap<>();
+        this.byQName = new ConcurrentHashMap<>();
+    }
+
+    @Override
+    public DataSchemaContextNode<?> getChild(final PathArgument child) {
+        DataSchemaContextNode<?> potential = byArg.get(child);
+        if (potential != null) {
+            return potential;
+        }
+        potential = fromLocalSchema(child);
+        return register(potential);
+    }
+
+    @Override
+    public DataSchemaContextNode<?> getChild(final QName child) {
+        DataSchemaContextNode<?> potential = byQName.get(child);
+        if (potential != null) {
+            return potential;
+        }
+        potential = fromLocalSchemaAndQName(schema, child);
+        return register(potential);
+    }
+
+    private DataSchemaContextNode<?> fromLocalSchema(final PathArgument child) {
+        if (child instanceof AugmentationIdentifier) {
+            return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
+                    .iterator().next());
+        }
+        return fromSchemaAndQNameChecked(schema, child.getNodeType());
+    }
+
+    protected DataSchemaContextNode<?> fromLocalSchemaAndQName(final DataNodeContainer schema2, final QName child) {
+        return fromSchemaAndQNameChecked(schema2, child);
+    }
+
+    private DataSchemaContextNode<?> register(final DataSchemaContextNode<?> potential) {
+        if (potential != null) {
+            byArg.put(potential.getIdentifier(), potential);
+            for (QName qname : potential.getQNameIdentifiers()) {
+                byQName.put(qname, potential);
+            }
+        }
+        return potential;
+    }
+
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/DataSchemaContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/DataSchemaContextNode.java
new file mode 100644 (file)
index 0000000..3b83de3
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+/**
+ * Schema derived data providing necessary information for mapping between
+ * {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode} and serialization format defined in RFC6020,
+ * since the mapping is not one-to-one.
+ *
+ * @param <T> Path Argument type
+ */
+public abstract class DataSchemaContextNode<T extends PathArgument> extends AbstractIdentifiable<T> {
+    private final DataSchemaNode dataSchemaNode;
+
+    protected DataSchemaContextNode(final T identifier, final SchemaNode schema) {
+        super(identifier);
+        if (schema instanceof DataSchemaNode) {
+            this.dataSchemaNode = (DataSchemaNode) schema;
+        } else {
+            this.dataSchemaNode = null;
+        }
+    }
+
+    public boolean isMixin() {
+        return false;
+    }
+
+    public boolean isKeyedEntry() {
+        return false;
+    }
+
+    public abstract boolean isLeaf();
+
+    protected Set<QName> getQNameIdentifiers() {
+        return ImmutableSet.of(getIdentifier().getNodeType());
+    }
+
+    /**
+     * Find a child node identifier by its {@link PathArgument}.
+     *
+     * @param child Child path argument
+     * @return A child node, or null if not found
+     */
+    public abstract @Nullable DataSchemaContextNode<?> getChild(PathArgument child);
+
+    public abstract @Nullable DataSchemaContextNode<?> getChild(QName child);
+
+    public @Nullable DataSchemaNode getDataSchemaNode() {
+        return dataSchemaNode;
+    }
+
+    /**
+     * Find a child node as identified by a {@link YangInstanceIdentifier} relative to this node.
+     *
+     * @param path Path towards the child node
+     * @return Child node if present, or empty when corresponding child is not found.
+     * @throws NullPointerException if {@code path} is null
+     */
+    public final @NonNull Optional<@NonNull DataSchemaContextNode<?>> findChild(
+            final @NonNull YangInstanceIdentifier path) {
+        DataSchemaContextNode<?> currentOp = this;
+        for (PathArgument arg : path.getPathArguments()) {
+            currentOp = currentOp.getChild(arg);
+            if (currentOp == null) {
+                return Optional.empty();
+            }
+        }
+        return Optional.of(currentOp);
+    }
+
+    static DataSchemaNode findChildSchemaNode(final DataNodeContainer parent, final QName child) {
+        final DataSchemaNode potential = parent.getDataChildByName(child);
+        return potential == null ? findChoice(Iterables.filter(parent.getChildNodes(), ChoiceSchemaNode.class), child)
+                : potential;
+    }
+
+    static DataSchemaContextNode<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) {
+        final DataSchemaNode result = findChildSchemaNode(schema, child);
+        // We try to look up if this node was added by augmentation
+        if (result != null && schema instanceof DataSchemaNode && result.isAugmenting()) {
+            return fromAugmentation(schema, (AugmentationTarget) schema, result);
+        }
+        return fromDataSchemaNode(result);
+    }
+
+    // FIXME: this looks like it should be a Predicate on a stream with findFirst()
+    private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
+        for (ChoiceSchemaNode choice : choices) {
+            // FIXME: this looks weird: what are we looking for again?
+            for (CaseSchemaNode caze : choice.getCases().values()) {
+                if (findChildSchemaNode(caze, child) != null) {
+                    return choice;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Create AugmentationIdentifier from an AugmentationSchemaNode.
+     *
+     * @param schema Augmentation schema
+     * @return AugmentationIdentifier for the schema
+     * @throws NullPointerException if {@code schema} is null
+     */
+    public static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchemaNode schema) {
+        return new AugmentationIdentifier(schema.getChildNodes().stream().map(DataSchemaNode::getQName)
+            .collect(Collectors.toSet()));
+    }
+
+    /**
+     * Returns a DataContextNodeOperation for provided child node
+     *
+     * <p>
+     * If supplied child is added by Augmentation this operation returns a
+     * DataContextNodeOperation for augmentation, otherwise returns a
+     * DataContextNodeOperation for child as call for
+     * {@link #fromDataSchemaNode(DataSchemaNode)}.
+     */
+    static @Nullable DataSchemaContextNode<?> fromAugmentation(final DataNodeContainer parent,
+            final AugmentationTarget parentAug, final DataSchemaNode child) {
+        for (AugmentationSchemaNode aug : parentAug.getAvailableAugmentations()) {
+            if (aug.findDataChildByName(child.getQName()).isPresent()) {
+                return new AugmentationContextNode(aug, parent);
+            }
+        }
+        return fromDataSchemaNode(child);
+    }
+
+    public static @Nullable DataSchemaContextNode<?> fromDataSchemaNode(final DataSchemaNode potential) {
+        if (potential instanceof ContainerSchemaNode) {
+            return new ContainerContextNode((ContainerSchemaNode) potential);
+        } else if (potential instanceof ListSchemaNode) {
+            return fromListSchemaNode((ListSchemaNode) potential);
+        } else if (potential instanceof LeafSchemaNode) {
+            return new LeafContextNode((LeafSchemaNode) potential);
+        } else if (potential instanceof ChoiceSchemaNode) {
+            return new ChoiceNodeContextNode((ChoiceSchemaNode) potential);
+        } else if (potential instanceof LeafListSchemaNode) {
+            return fromLeafListSchemaNode((LeafListSchemaNode) potential);
+        } else if (potential instanceof AnydataSchemaNode) {
+            return new AnydataContextNode((AnydataSchemaNode) potential);
+        } else if (potential instanceof AnyxmlSchemaNode) {
+            return new AnyXmlContextNode((AnyxmlSchemaNode) potential);
+        }
+        return null;
+    }
+
+    private static DataSchemaContextNode<?> fromListSchemaNode(final ListSchemaNode potential) {
+        List<QName> keyDefinition = potential.getKeyDefinition();
+        if (keyDefinition == null || keyDefinition.isEmpty()) {
+            return new UnkeyedListMixinContextNode(potential);
+        }
+        if (potential.isUserOrdered()) {
+            return new OrderedMapMixinContextNode(potential);
+        }
+        return new UnorderedMapMixinContextNode(potential);
+    }
+
+    private static DataSchemaContextNode<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
+        if (potential.isUserOrdered()) {
+            return new OrderedLeafListMixinContextNode(potential);
+        }
+        return new UnorderedLeafListMixinContextNode(potential);
+    }
+
+    public static DataSchemaContextNode<?> from(final SchemaContext ctx) {
+        return new ContainerContextNode(ctx);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/DataSchemaContextTree.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/DataSchemaContextTree.java
new file mode 100644 (file)
index 0000000..33235a1
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Semantic tree binding a {@link SchemaContext} to a {@link NormalizedNode} tree. Since the layout of the schema
+ * and data has differences, the mapping is not trivial -- which is where this class comes in.
+ *
+ * @author Robert Varga
+ */
+// FIXME: 5.0.0: @NonNullByDefault
+public final class DataSchemaContextTree {
+    private static final LoadingCache<SchemaContext, DataSchemaContextTree> TREES = CacheBuilder.newBuilder()
+            .weakKeys().weakValues().build(new CacheLoader<SchemaContext, DataSchemaContextTree>() {
+                @Override
+                public DataSchemaContextTree load(final SchemaContext key) throws Exception {
+                    return new DataSchemaContextTree(key);
+                }
+            });
+
+    private final DataSchemaContextNode<?> root;
+
+    private DataSchemaContextTree(final SchemaContext ctx) {
+        root = DataSchemaContextNode.from(ctx);
+    }
+
+    public static @NonNull DataSchemaContextTree from(final @NonNull SchemaContext ctx) {
+        return TREES.getUnchecked(ctx);
+    }
+
+    /**
+     * Find a child node as identified by an absolute {@link YangInstanceIdentifier}.
+     *
+     * @param path Path towards the child node
+     * @return Child node if present, or empty when corresponding child is not found.
+     * @throws NullPointerException if {@code path} is null
+     */
+    public @NonNull Optional<@NonNull DataSchemaContextNode<?>> findChild(final @NonNull YangInstanceIdentifier path) {
+        return getRoot().findChild(path);
+    }
+
+    /**
+     * Get a child node as identified by an absolute {@link YangInstanceIdentifier}.
+     *
+     * @param path Path towards the child node
+     * @return Child node if present, or null when corresponding child is not found.
+     * @throws NullPointerException if {@code path} is null
+     *
+     * @deprecated Use {@link #findChild(YangInstanceIdentifier)} instead.
+     */
+    @Deprecated(forRemoval = true)
+    public @Nullable DataSchemaContextNode<?> getChild(final YangInstanceIdentifier path) {
+        DataSchemaContextNode<?> currentOp = root;
+        for (PathArgument arg : path.getPathArguments()) {
+            currentOp = currentOp.getChild(arg);
+            if (currentOp == null) {
+                return null;
+            }
+        }
+        return currentOp;
+    }
+
+    public DataSchemaContextNode<?> getRoot() {
+        return root;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ImmutableNormalizedAnydata.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ImmutableNormalizedAnydata.java
new file mode 100644 (file)
index 0000000..5168ea9
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedAnydata;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.util.AbstractSchemaContextProvider;
+
+@Beta
+@NonNullByDefault
+public class ImmutableNormalizedAnydata extends AbstractSchemaContextProvider implements NormalizedAnydata {
+    private final DataSchemaNode contextNode;
+    private final NormalizedNode<?, ?> data;
+
+    public ImmutableNormalizedAnydata(final SchemaContext schemaContext, final DataSchemaNode contextNode,
+            final NormalizedNode<?, ?> data) {
+        super(schemaContext);
+        this.contextNode = requireNonNull(contextNode);
+        this.data = requireNonNull(data);
+    }
+
+    @Override
+    public final DataSchemaNode getContextNode() {
+        return contextNode;
+    }
+
+    @Override
+    public final NormalizedNode<?, ?> getData() {
+        return data;
+    }
+
+    @Override
+    public final int hashCode() {
+        return super.hashCode();
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        return super.equals(obj);
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper).add("node", contextNode).add("data", data);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafContextNode.java
new file mode 100644 (file)
index 0000000..67cf08a
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+
+final class LeafContextNode extends AbstractLeafNodeContext<NodeIdentifier, LeafSchemaNode> {
+    LeafContextNode(final LeafSchemaNode potential) {
+        super(NodeIdentifier.create(potential.getQName()), potential);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafInterner.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafInterner.java
new file mode 100644 (file)
index 0000000..9464b4c
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for sharing instances of {@link LeafNode}s which have low cardinality-- e.g. those which hold
+ * boolean or enumeration values. Instances containing attributes are not interned.
+ *
+ * <p>
+ * Such objects have cardinality which is capped at the product of QNAMES * TYPE_CARDINALITY, where QNAMES is the total
+ * number of different QNames where the type is used and TYPE_CARDINALITY is the number of possible values for the type.
+ * Boolean has cardinality of 2, enumerations have cardinality equal to the number of enum statements.
+ *
+ * <p>
+ * The theory here is that we tend to have a large number (100K+) of entries in a few places, which could end up hogging
+ * the heap retained via the DataTree with duplicate objects (same QName, same value, different object). Using this
+ * utility, such objects will end up reusing the same object, preventing this overhead.
+ */
+@Beta
+public final class LeafInterner {
+    private static final Logger LOG = LoggerFactory.getLogger(LeafInterner.class);
+    private static final Interner<Object> INTERNER = Interners.newWeakInterner();
+
+    private LeafInterner() {
+        throw new UnsupportedOperationException();
+    }
+
+    private static <T extends LeafNode<?>> T intern(final T sample) {
+        @SuppressWarnings("unchecked")
+        final T ret = (T) INTERNER.intern(sample);
+        LOG.trace("Interned object {} to {}", sample, ret);
+        return ret;
+    }
+
+    /**
+     * Return a {@link LeafInterner} for a particular schema. Interner instances must not be reused for leaves of
+     * different types, otherwise they may produce unexpected results.
+     *
+     * @param schema The leaf node's schema
+     * @return An interner instance, if applicable
+     */
+    public static <T extends LeafNode<?>> @NonNull Optional<Interner<T>> forSchema(
+            final @Nullable LeafSchemaNode schema) {
+        if (schema != null && isLowCardinality(schema.getType())) {
+            return Optional.of(LeafInterner::intern);
+        }
+
+        return Optional.empty();
+    }
+
+    private static boolean isLowCardinality(final TypeDefinition<?> type) {
+        return type instanceof BooleanTypeDefinition
+                || type instanceof EmptyTypeDefinition
+                || type instanceof EnumTypeDefinition
+                || type instanceof IdentityrefTypeDefinition
+                || type instanceof Int8TypeDefinition
+                || type instanceof Uint8TypeDefinition;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListEntryContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListEntryContextNode.java
new file mode 100644 (file)
index 0000000..2cd1eb8
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+final class LeafListEntryContextNode extends AbstractLeafNodeContext<NodeWithValue<?>, LeafListSchemaNode> {
+    LeafListEntryContextNode(final LeafListSchemaNode potential) {
+        super(new NodeWithValue<>(potential.getQName(), null), potential);
+    }
+
+    @Override
+    public boolean isKeyedEntry() {
+        return true;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListEntryNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListEntryNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..48ab633
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import java.io.IOException;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+/**
+ * Utility class used for tracking parser state as needed by a StAX-like parser.
+ * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
+ *
+ * <p>
+ * Represents a YANG leaf-list entry node.
+ */
+public class LeafListEntryNodeDataWithSchema extends SimpleNodeDataWithSchema<LeafListSchemaNode> {
+    public LeafListEntryNodeDataWithSchema(final LeafListSchemaNode dataSchemaNode) {
+        super(dataSchemaNode);
+    }
+
+    @Override
+    public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+            throws IOException {
+        writer.nextDataSchemaNode(getSchema());
+
+        writer.startLeafSetEntryNode(new NodeWithValue<>(getSchema().getQName(), getValue()));
+        writeMetadata(metaWriter);
+        writer.scalarValue(getValue());
+        writer.endNode();
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..9636ec0
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import java.io.IOException;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+/**
+ * Utility class used for tracking parser state as needed by a StAX-like parser.
+ * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
+ *
+ * <p>
+ * Represents a YANG leaf-list node.
+ */
+public class LeafListNodeDataWithSchema extends CompositeNodeDataWithSchema<LeafListSchemaNode> {
+    public LeafListNodeDataWithSchema(final LeafListSchemaNode schema) {
+        super(schema);
+    }
+
+    @Override
+    public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+            throws IOException {
+        final LeafListSchemaNode schema = getSchema();
+        writer.nextDataSchemaNode(schema);
+        if (schema.isUserOrdered()) {
+            writer.startOrderedLeafSet(provideNodeIdentifier(), childSizeHint());
+        } else {
+            writer.startLeafSet(provideNodeIdentifier(), childSizeHint());
+        }
+        super.write(writer, metaWriter);
+        writer.endNode();
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..8af9143
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import java.io.IOException;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+
+/**
+ * Utility class used for tracking parser state as needed by a StAX-like parser.
+ * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
+ *
+ * <p>
+ * Represents a YANG leaf node.
+ */
+public class LeafNodeDataWithSchema extends SimpleNodeDataWithSchema<LeafSchemaNode> {
+    public LeafNodeDataWithSchema(final LeafSchemaNode schema) {
+        super(schema);
+    }
+
+    @Override
+    public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+            throws IOException {
+        writer.nextDataSchemaNode(getSchema());
+
+        writer.startLeafNode(provideNodeIdentifier());
+        writeMetadata(metaWriter);
+        writer.scalarValue(getValue());
+        writer.endNode();
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafsetEntryInterner.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafsetEntryInterner.java
new file mode 100644 (file)
index 0000000..b43662a
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for sharing instances of {@link LeafSetEntryNode}s which have low cardinality -- e.g. those which hold
+ * boolean or enumeration values. Instances containing attributes are not interned.
+ *
+ * <p>
+ * Such objects have cardinality which is capped at the product of QNAMES * TYPE_CARDINALITY, where QNAMES is the total
+ * number of different QNames where the type is used and TYPE_CARDINALITY is the number of possible values for the type.
+ * Boolean has cardinality of 2, enumerations have cardinality equal to the number of enum statements.
+ *
+ * <p>
+ * The theory here is that we tend to have a large number (100K+) of entries in a few places, which could end up hogging
+ * the heap retained via the DataTree with duplicate objects (same QName, same value, different object). Using this
+ * utility, such objects will end up reusing the same object, preventing this overhead.
+ */
+@Beta
+public final class LeafsetEntryInterner {
+    private static final Logger LOG = LoggerFactory.getLogger(LeafsetEntryInterner.class);
+    private static final LeafsetEntryInterner INSTANCE = new LeafsetEntryInterner();
+    private static final Interner<Object> INTERNER = Interners.newWeakInterner();
+
+    private LeafsetEntryInterner() {
+
+    }
+
+    @SuppressWarnings("static-method")
+    public <T extends LeafSetEntryNode<?>> @NonNull T intern(final @NonNull T sample) {
+        /*
+         * We do not perform type checks here as they are implied by #forSchema(LeafListSchemaNode). Any misuse can
+         * result in inappropriate candidates being interned, but the alternative would be quite a bit slower.
+         */
+        @SuppressWarnings("unchecked")
+        final T ret = (T) INTERNER.intern(sample);
+        LOG.trace("Interned object {} to {}", sample, ret);
+        return ret;
+    }
+
+    /**
+     * Return a {@link LeafsetEntryInterner} for a particular schema. Interner instances must be used only for leafset
+     * entries for that particular schema, otherwise they may produce unexpected results.
+     *
+     * @param schema Schema of the parent leaf set
+     * @return An interner instance, or null if the leafset's type should not be interned.
+     */
+    public static @Nullable LeafsetEntryInterner forSchema(final @Nullable LeafListSchemaNode schema) {
+        if (schema != null) {
+            final TypeDefinition<?> type = schema.getType();
+            if (type instanceof BooleanTypeDefinition || type instanceof EnumTypeDefinition
+                    || type instanceof IdentityrefTypeDefinition) {
+                return INSTANCE;
+            }
+        }
+        return null;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListEntryNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListEntryNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..1f0e12b
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import static com.google.common.base.Verify.verify;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.util.ImmutableMapTemplate;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+/**
+ * Utility class used for tracking parser state as needed by a StAX-like parser.
+ * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
+ *
+ * <p>
+ * Represents a YANG list entry node.
+ */
+public abstract class ListEntryNodeDataWithSchema extends AbstractMountPointDataWithSchema<ListSchemaNode> {
+    private static final class Keyed extends ListEntryNodeDataWithSchema {
+        private final Map<QName, SimpleNodeDataWithSchema<?>> keyValues = new HashMap<>();
+        // This template results in Maps in schema definition order
+        private final ImmutableMapTemplate<QName> predicateTemplate;
+
+        Keyed(final ListSchemaNode schema, final List<QName> keyDef) {
+            super(schema);
+            predicateTemplate = ImmutableMapTemplate.ordered(keyDef);
+        }
+
+        @Override
+        public void addChild(final AbstractNodeDataWithSchema<?> newChild) {
+            final DataSchemaNode childSchema = newChild.getSchema();
+            if (childSchema instanceof LeafSchemaNode) {
+                final QName childName = childSchema.getQName();
+                if (predicateTemplate.keySet().contains(childName)) {
+                    verify(newChild instanceof SimpleNodeDataWithSchema);
+                    keyValues.put(childName, (SimpleNodeDataWithSchema<?>)newChild);
+                }
+            }
+            super.addChild(newChild);
+        }
+
+        @Override
+        public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+                throws IOException {
+            writer.nextDataSchemaNode(getSchema());
+            final NodeIdentifierWithPredicates identifier = NodeIdentifierWithPredicates.of(getSchema().getQName(),
+                predicateTemplate.instantiateTransformed(keyValues, (key, node) -> node.getValue()));
+
+            writer.startMapEntryNode(identifier, childSizeHint());
+            writeMetadata(metaWriter);
+            super.write(writer, metaWriter);
+            writer.endNode();
+        }
+    }
+
+    private static final class Unkeyed extends ListEntryNodeDataWithSchema {
+        Unkeyed(final ListSchemaNode schema) {
+            super(schema);
+        }
+
+        @Override
+        public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+                throws IOException {
+            writer.nextDataSchemaNode(getSchema());
+            writer.startUnkeyedListItem(provideNodeIdentifier(), childSizeHint());
+            super.write(writer, metaWriter);
+            writer.endNode();
+        }
+    }
+
+    ListEntryNodeDataWithSchema(final ListSchemaNode schema) {
+        super(schema);
+    }
+
+    public static ListEntryNodeDataWithSchema forSchema(final ListSchemaNode schema) {
+        final List<QName> keyDef = schema.getKeyDefinition();
+        return keyDef.isEmpty() ? new Unkeyed(schema) :  new Keyed(schema, keyDef);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListItemContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListItemContextNode.java
new file mode 100644 (file)
index 0000000..3465f56
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+final class ListItemContextNode extends
+        DataContainerContextNode<NodeIdentifierWithPredicates> {
+
+
+    protected ListItemContextNode(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
+        super(identifier, schema, schema);
+    }
+
+    @Override
+    public boolean isKeyedEntry() {
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..3e22edf
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import java.io.IOException;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+/**
+ * Utility class used for tracking parser state as needed by a StAX-like parser.
+ * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
+ *
+ * <p>
+ * Represents a YANG list node.
+ */
+public class ListNodeDataWithSchema extends CompositeNodeDataWithSchema<ListSchemaNode> {
+
+    public ListNodeDataWithSchema(final ListSchemaNode schema) {
+        super(schema);
+    }
+
+    @Override
+    public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+            throws IOException {
+        final ListSchemaNode schema = getSchema();
+        writer.nextDataSchemaNode(schema);
+        if (schema.getKeyDefinition().isEmpty()) {
+            writer.startUnkeyedList(provideNodeIdentifier(), childSizeHint());
+        } else if (schema.isUserOrdered()) {
+            writer.startOrderedMapNode(provideNodeIdentifier(), childSizeHint());
+        } else {
+            writer.startMapNode(provideNodeIdentifier(), childSizeHint());
+        }
+        super.write(writer, metaWriter);
+        writer.endNode();
+    }
+
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ModuleStringIdentityrefCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ModuleStringIdentityrefCodec.java
new file mode 100644 (file)
index 0000000..9a22d7c
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 Intel Corporation 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.net.URI;
+import java.util.Iterator;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+/**
+ * Base class for implementing identityref codecs on based on module names.
+ */
+@Beta
+public abstract class ModuleStringIdentityrefCodec extends AbstractModuleStringIdentityrefCodec
+        implements SchemaContextProvider {
+    private final SchemaContext context;
+    private final QNameModule parentModule;
+
+    protected ModuleStringIdentityrefCodec(final @NonNull SchemaContext context,
+            final @NonNull QNameModule parentModule) {
+        this.context = requireNonNull(context);
+        this.parentModule = requireNonNull(parentModule);
+    }
+
+    @Override
+    public final SchemaContext getSchemaContext() {
+        return context;
+    }
+
+    protected final QNameModule getParentModule() {
+        return parentModule;
+    }
+
+    @Override
+    protected String prefixForNamespace(final URI namespace) {
+        final Iterator<Module> modules = context.findModules(namespace).iterator();
+        return modules.hasNext() ? modules.next().getName() : null;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/MountPointData.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/MountPointData.java
new file mode 100644 (file)
index 0000000..aaae6bc
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.util;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointChild;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.rfc8528.data.api.StreamWriterMountPointExtension;
+import org.opendaylight.yangtools.rfc8528.data.api.YangLibraryConstants.ContainerName;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * YANG Schema Mount-supported data attached to either a {@code list} item or a {@code container}.
+ */
+@Beta
+public final class MountPointData extends AbstractIdentifiable<MountPointIdentifier> {
+    private static final Logger LOG = LoggerFactory.getLogger(MountPointData.class);
+
+    private final Map<ContainerName, MountPointChild> yangLib = new EnumMap<>(ContainerName.class);
+    private final List<MountPointChild> children = new ArrayList<>();
+    private final MountPointContextFactory contextFactory;
+
+    private MountPointChild schemaMounts;
+
+    MountPointData(final MountPointIdentifier mountId, final MountPointContextFactory contextFactory) {
+        super(mountId);
+        this.contextFactory = requireNonNull(contextFactory);
+    }
+
+    public void setContainer(final @NonNull ContainerName containerName, final @NonNull MountPointChild data) {
+        final MountPointChild prev = yangLib.putIfAbsent(containerName, requireNonNull(data));
+        checkState(prev == null, "Attempted to duplicate container %s data %s with %s", containerName, prev, data);
+        addChild(data);
+    }
+
+    public void setSchemaMounts(final @NonNull MountPointChild data) {
+        checkState(schemaMounts == null, "Attempted to reset schema-mounts from %s to %s", schemaMounts, data);
+        schemaMounts = requireNonNull(data);
+        addChild(data);
+    }
+
+    public void addChild(final @NonNull MountPointChild data) {
+        children.add(requireNonNull(data));
+    }
+
+    void write(final @NonNull NormalizedNodeStreamWriter writer) throws IOException {
+        final StreamWriterMountPointExtension mountWriter = writer.getExtensions()
+            .getInstance(StreamWriterMountPointExtension.class);
+        if (mountWriter == null) {
+            LOG.debug("Writer {} does not support mount points, ignoring data in {}", writer, getIdentifier());
+            return;
+        }
+
+        final MountPointContext mountCtx;
+        try {
+            mountCtx = contextFactory.createContext(yangLib, schemaMounts);
+        } catch (YangParserException e) {
+            throw new IOException("Failed to resolve mount point " + getIdentifier(), e);
+        }
+        try (NormalizedNodeStreamWriter nestedWriter = mountWriter.startMountPoint(getIdentifier(), mountCtx)) {
+            for (MountPointChild child : children) {
+                child.writeTo(nestedWriter, mountCtx);
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/OperationAsContainer.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/OperationAsContainer.java
new file mode 100644 (file)
index 0000000..f58816c
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ForwardingObject;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+@Beta
+public class OperationAsContainer extends ForwardingObject implements ContainerSchemaNode, OperationDefinition {
+    private final @NonNull OperationDefinition delegate;
+
+    OperationAsContainer(final OperationDefinition parentNode) {
+        delegate = requireNonNull(parentNode);
+    }
+
+    public static @NonNull OperationAsContainer of(final OperationDefinition delegate) {
+        return new OperationAsContainer(delegate);
+    }
+
+    @Override
+    protected final @NonNull OperationDefinition delegate() {
+        return delegate;
+    }
+
+    @Override
+    public final Optional<String> getDescription() {
+        return delegate.getDescription();
+    }
+
+    @Override
+    public final Optional<String> getReference() {
+        return delegate.getReference();
+    }
+
+    @Override
+    public final Set<TypeDefinition<?>> getTypeDefinitions() {
+        return delegate.getTypeDefinitions();
+    }
+
+    @Override
+    public final Set<GroupingDefinition> getGroupings() {
+        return delegate.getGroupings();
+    }
+
+    @Override
+    public final Status getStatus() {
+        return delegate.getStatus();
+    }
+
+    @Override
+    public final ContainerSchemaNode getInput() {
+        return delegate.getInput();
+    }
+
+    @Override
+    public final ContainerSchemaNode getOutput() {
+        return delegate.getOutput();
+    }
+
+    @Override
+    public final QName getQName() {
+        return delegate.getQName();
+    }
+
+    @Override
+    public final SchemaPath getPath() {
+        return delegate.getPath();
+    }
+
+    @Override
+    public final Optional<DataSchemaNode> findDataChildByName(final QName name) {
+        if (!name.getModule().equals(getQName().getModule())) {
+            return Optional.empty();
+        }
+
+        switch (name.getLocalName()) {
+            case "input":
+                return Optional.of(delegate.getInput());
+            case "output":
+                return Optional.of(delegate.getOutput());
+            default:
+                return Optional.empty();
+        }
+    }
+
+    @Override
+    public Set<UsesNode> getUses() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public final Set<AugmentationSchemaNode> getAvailableAugmentations() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public final boolean isPresenceContainer() {
+        return false;
+    }
+
+    @Override
+    public final Collection<DataSchemaNode> getChildNodes() {
+        final List<DataSchemaNode> ret = new ArrayList<>();
+        final ContainerSchemaNode input = getInput();
+        final ContainerSchemaNode output = getOutput();
+        if (input != null) {
+            ret.add(input);
+        }
+        if (output != null) {
+            ret.add(output);
+        }
+        return ret;
+    }
+
+    @Deprecated
+    @Override
+    public final boolean isAugmenting() {
+        return false;
+    }
+
+    @Deprecated
+    @Override
+    public final boolean isAddedByUses() {
+        return false;
+    }
+
+    @Override
+    public final boolean isConfiguration() {
+        return false;
+    }
+
+    @Override
+    public final Set<ActionDefinition> getActions() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Optional<ActionDefinition> findAction(final QName qname) {
+        requireNonNull(qname);
+        return Optional.empty();
+    }
+
+    @Override
+    public final Set<NotificationDefinition> getNotifications() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Optional<NotificationDefinition> findNotification(final QName qname) {
+        requireNonNull(qname);
+        return Optional.empty();
+    }
+
+    @Override
+    public final Collection<MustDefinition> getMustConstraints() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public final Optional<RevisionAwareXPath> getWhenCondition() {
+        return Optional.empty();
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/OrderedLeafListMixinContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/OrderedLeafListMixinContextNode.java
new file mode 100644 (file)
index 0000000..83936c7
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+final class OrderedLeafListMixinContextNode extends UnorderedLeafListMixinContextNode {
+    OrderedLeafListMixinContextNode(final LeafListSchemaNode potential) {
+        super(potential);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/OrderedMapMixinContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/OrderedMapMixinContextNode.java
new file mode 100644 (file)
index 0000000..3770aa4
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+final class OrderedMapMixinContextNode extends UnorderedMapMixinContextNode {
+    OrderedMapMixinContextNode(final ListSchemaNode list) {
+        super(list);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ParserStreamUtils.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ParserStreamUtils.java
new file mode 100644 (file)
index 0000000..ce1efd9
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import java.net.URI;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+public final class ParserStreamUtils {
+
+    private ParserStreamUtils() {
+        throw new UnsupportedOperationException("Utility class should not be instantiated.");
+    }
+
+    /**
+     * Returns stack of schema nodes via which it was necessary to pass to get schema node with specified
+     * {@code childName} and {@code namespace}.
+     *
+     * @return stack of schema nodes via which it was passed through. If found schema node is direct child then stack
+     *         contains only one node. If it is found under choice and case then stack should contains 2*n+1 element
+     *         (where n is number of choices through it was passed)
+     */
+    public static Deque<DataSchemaNode> findSchemaNodeByNameAndNamespace(final DataSchemaNode dataSchemaNode,
+                                                                   final String childName, final URI namespace) {
+        final Deque<DataSchemaNode> result = new ArrayDeque<>();
+        final List<ChoiceSchemaNode> childChoices = new ArrayList<>();
+        DataSchemaNode potentialChildNode = null;
+        if (dataSchemaNode instanceof DataNodeContainer) {
+            for (final DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
+                if (childNode instanceof ChoiceSchemaNode) {
+                    childChoices.add((ChoiceSchemaNode) childNode);
+                } else {
+                    final QName childQName = childNode.getQName();
+                    if (childQName.getLocalName().equals(childName) && childQName.getNamespace().equals(namespace)
+                            && (potentialChildNode == null || Revision.compare(childQName.getRevision(),
+                                potentialChildNode.getQName().getRevision()) > 0)) {
+                        potentialChildNode = childNode;
+                    }
+                }
+            }
+        }
+        if (potentialChildNode != null) {
+            result.push(potentialChildNode);
+            return result;
+        }
+
+        // try to find data schema node in choice (looking for first match)
+        for (final ChoiceSchemaNode choiceNode : childChoices) {
+            for (final CaseSchemaNode concreteCase : choiceNode.getCases().values()) {
+                final Deque<DataSchemaNode> resultFromRecursion = findSchemaNodeByNameAndNamespace(concreteCase,
+                        childName, namespace);
+                if (!resultFromRecursion.isEmpty()) {
+                    resultFromRecursion.push(concreteCase);
+                    resultFromRecursion.push(choiceNode);
+                    return resultFromRecursion;
+                }
+            }
+        }
+        return result;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/SimpleNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/SimpleNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..f9267ce
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * Utility class used for tracking parser state as needed by a StAX-like parser.
+ * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
+ *
+ * <p>
+ * Represents a simple node with value (anyxml, leaf, leaf-list entry).
+ */
+public abstract class SimpleNodeDataWithSchema<T extends DataSchemaNode> extends AbstractNodeDataWithSchema<T> {
+
+    private Object value;
+
+    public SimpleNodeDataWithSchema(final T dataSchemaNode) {
+        super(dataSchemaNode);
+    }
+
+    public void setValue(final Object value) {
+        this.value = requireNonNull(value);
+    }
+
+    public Object getValue() {
+        return value;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/SingleChildDataNodeContainer.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/SingleChildDataNodeContainer.java
new file mode 100644 (file)
index 0000000..f1a7f79
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+/**
+ * Utility DataNodeContainer containing only the specified node.
+ */
+@Beta
+public final class SingleChildDataNodeContainer implements DataNodeContainer {
+    private final DataSchemaNode child;
+
+    public SingleChildDataNodeContainer(final DataSchemaNode child) {
+        this.child = requireNonNull(child);
+    }
+
+    @Override
+    public Optional<DataSchemaNode> findDataChildByName(final QName name) {
+        return child.getQName().equals(name) ? Optional.of(child) : Optional.empty();
+    }
+
+    @Override
+    public Collection<DataSchemaNode> getChildNodes() {
+        return ImmutableSet.of(child);
+    }
+
+    @Override
+    public Set<TypeDefinition<?>> getTypeDefinitions() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Set<GroupingDefinition> getGroupings() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Set<UsesNode> getUses() {
+        return ImmutableSet.of();
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/UnkeyedListItemContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/UnkeyedListItemContextNode.java
new file mode 100644 (file)
index 0000000..0817d5b
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+final class UnkeyedListItemContextNode extends DataContainerContextNode<NodeIdentifier> {
+
+    protected UnkeyedListItemContextNode(final ListSchemaNode schema) {
+        super(NodeIdentifier.create(schema.getQName()), schema, schema);
+    }
+
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/UnkeyedListMixinContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/UnkeyedListMixinContextNode.java
new file mode 100644 (file)
index 0000000..507d611
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+class UnkeyedListMixinContextNode extends AbstractMixinContextNode<NodeIdentifier> {
+
+    private final UnkeyedListItemContextNode innerNode;
+
+    UnkeyedListMixinContextNode(final ListSchemaNode list) {
+        super(NodeIdentifier.create(list.getQName()), list);
+        this.innerNode = new UnkeyedListItemContextNode(list);
+    }
+
+    @Override
+    public DataSchemaContextNode<?> getChild(final PathArgument child) {
+        if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+            return innerNode;
+        }
+        return null;
+    }
+
+    @Override
+    public DataSchemaContextNode<?> getChild(final QName child) {
+        if (getIdentifier().getNodeType().equals(child)) {
+            return innerNode;
+        }
+        return null;
+    }
+
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/UnorderedLeafListMixinContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/UnorderedLeafListMixinContextNode.java
new file mode 100644 (file)
index 0000000..c6907f3
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+class UnorderedLeafListMixinContextNode extends AbstractMixinContextNode<NodeIdentifier> {
+
+    private final DataSchemaContextNode<?> innerOp;
+
+    UnorderedLeafListMixinContextNode(final LeafListSchemaNode potential) {
+        super(NodeIdentifier.create(potential.getQName()), potential);
+        innerOp = new LeafListEntryContextNode(potential);
+    }
+
+    @Override
+    public DataSchemaContextNode<?> getChild(final PathArgument child) {
+        if (child instanceof NodeWithValue) {
+            return innerOp;
+        }
+        return null;
+    }
+
+    @Override
+    public DataSchemaContextNode<?> getChild(final QName child) {
+        if (getIdentifier().getNodeType().equals(child)) {
+            return innerOp;
+        }
+        return null;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/UnorderedMapMixinContextNode.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/UnorderedMapMixinContextNode.java
new file mode 100644 (file)
index 0000000..5e36250
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import org.opendaylight.yangtools.yang.common.QName;
+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.model.api.ListSchemaNode;
+
+class UnorderedMapMixinContextNode extends AbstractMixinContextNode<NodeIdentifier> {
+    private final ListItemContextNode innerNode;
+
+    UnorderedMapMixinContextNode(final ListSchemaNode list) {
+        super(NodeIdentifier.create(list.getQName()), list);
+        this.innerNode = new ListItemContextNode(NodeIdentifierWithPredicates.of(list.getQName()), list);
+    }
+
+    @Override
+    public DataSchemaContextNode<?> getChild(final PathArgument child) {
+        if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+            return innerNode;
+        }
+        return null;
+    }
+
+    @Override
+    public DataSchemaContextNode<?> getChild(final QName child) {
+        if (getIdentifier().getNodeType().equals(child)) {
+            return innerNode;
+        }
+        return null;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/XpathStringParsingPathArgumentBuilder.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/XpathStringParsingPathArgumentBuilder.java
new file mode 100644 (file)
index 0000000..c1ee5c2
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * Iterator which lazily parses {@link PathArgument} from string representation.
+ *
+ * <p>
+ * Note that invocation of {@link #hasNext()} or {@link #next()} may result in
+ * throwing of {@link IllegalArgumentException} if underlying string representation
+ * is not correctly serialized or does not represent instance identifier valid
+ * for associated schema context.
+ */
+final class XpathStringParsingPathArgumentBuilder implements Builder<List<PathArgument>> {
+
+    /**
+     * Matcher matching WSP YANG ABNF token.
+     */
+    private static final CharMatcher WSP = CharMatcher.anyOf(" \t");
+
+    /**
+     * Matcher matching IDENTIFIER first char token.
+     */
+    private static final CharMatcher IDENTIFIER_FIRST_CHAR = CharMatcher.inRange('a', 'z')
+            .or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.is('_')).precomputed();
+
+    /**
+     * Matcher matching IDENTIFIER token.
+     */
+    private static final CharMatcher IDENTIFIER = IDENTIFIER_FIRST_CHAR.or(CharMatcher.inRange('0', '9'))
+            .or(CharMatcher.anyOf(".-")).precomputed();
+
+    private static final CharMatcher QUOTE = CharMatcher.anyOf("'\"");
+
+    private static final char SLASH = '/';
+    private static final char COLON = ':';
+    private static final char DOT = '.';
+    private static final char EQUALS = '=';
+    private static final char PRECONDITION_START = '[';
+    private static final char PRECONDITION_END = ']';
+
+    private final List<PathArgument> product = new ArrayList<>();
+    private final AbstractStringInstanceIdentifierCodec codec;
+    private final String data;
+
+    private DataSchemaContextNode<?> current;
+    private QNameModule lastModule;
+    private int offset;
+
+    XpathStringParsingPathArgumentBuilder(final AbstractStringInstanceIdentifierCodec codec, final String data) {
+        this.codec = requireNonNull(codec);
+        this.data = requireNonNull(data);
+        this.current = codec.getDataContextTree().getRoot();
+        this.offset = 0;
+    }
+
+    @Override
+    public List<PathArgument> build() {
+        while (!allCharactersConsumed()) {
+            product.add(computeNextArgument());
+        }
+        return ImmutableList.copyOf(product);
+    }
+
+    private PathArgument computeNextArgument() {
+        checkValid(SLASH == currentChar(), "Identifier must start with '/'.");
+        skipCurrentChar();
+        checkValid(!allCharactersConsumed(), "Identifier cannot end with '/'.");
+        final QName name = nextQName();
+        // Memoize module
+        lastModule = name.getModule();
+        if (allCharactersConsumed() || SLASH == currentChar()) {
+            return computeIdentifier(name);
+        }
+
+        checkValid(PRECONDITION_START == currentChar(), "Last element must be identifier, predicate or '/'");
+        return computeIdentifierWithPredicate(name);
+    }
+
+    private DataSchemaContextNode<?> nextContextNode(final QName name) {
+        current = current.getChild(name);
+        checkValid(current != null, "%s is not correct schema node identifier.",name);
+        while (current.isMixin()) {
+            product.add(current.getIdentifier());
+            current = current.getChild(name);
+        }
+        return current;
+    }
+
+    /**
+     * Creates path argument with predicates and sets offset
+     * to end of path argument.
+     *
+     * {@code
+     *     predicate = "[" *WSP (predicate-expr / pos) *WSP "]"
+     *     predicate-expr = (node-identifier / ".") *WSP "=" *WSP
+     *          ((DQUOTE string DQUOTE) /
+     *           (SQUOTE string SQUOTE))
+     *     pos = non-negative-integer-value
+     * }
+     *
+     * @param name QName of node, for which predicates are computed.
+     * @return PathArgument representing node selection with predictes
+     */
+    private PathArgument computeIdentifierWithPredicate(final QName name) {
+        DataSchemaContextNode<?> currentNode = nextContextNode(name);
+        checkValid(currentNode.isKeyedEntry(), "Entry %s does not allow specifying predicates.", name);
+
+        ImmutableMap.Builder<QName,Object> keyValues = ImmutableMap.builder();
+        while (!allCharactersConsumed() && PRECONDITION_START == currentChar()) {
+            skipCurrentChar();
+            skipWhitespaces();
+            final QName key;
+            if (DOT == currentChar()) {
+                key = null;
+                skipCurrentChar();
+            } else {
+                key = nextQName();
+            }
+            skipWhitespaces();
+            checkCurrentAndSkip(EQUALS, "Precondition must contain '='");
+            skipWhitespaces();
+            final String keyValue = nextQuotedValue();
+            skipWhitespaces();
+            checkCurrentAndSkip(PRECONDITION_END, "Precondition must ends with ']'");
+
+            // Break-out from method for leaf-list case
+            if (key == null && currentNode.isLeaf()) {
+                checkValid(offset == data.length(), "Leaf argument must be last argument of instance identifier.");
+                return new NodeWithValue<>(name, keyValue);
+            }
+            final DataSchemaContextNode<?> keyNode = currentNode.getChild(key);
+            checkValid(keyNode != null, "%s is not correct schema node identifier.", key);
+            final Object value = codec.deserializeKeyValue(keyNode.getDataSchemaNode(), keyValue);
+            keyValues.put(key, value);
+        }
+        return NodeIdentifierWithPredicates.of(name, keyValues.build());
+    }
+
+
+    private PathArgument computeIdentifier(final QName name) {
+        DataSchemaContextNode<?> currentNode = nextContextNode(name);
+        checkValid(!currentNode.isKeyedEntry(), "Entry %s requires key or value predicate to be present", name);
+        return currentNode.getIdentifier();
+    }
+
+    /**
+     * Returns following QName and sets offset to end of QName.
+     *
+     * @return following QName.
+     */
+    private QName nextQName() {
+        // Consume prefix or identifier
+        final String maybePrefix = nextIdentifier();
+        if (!allCharactersConsumed() && COLON == currentChar()) {
+            // previous token is prefix
+            skipCurrentChar();
+            return codec.createQName(maybePrefix, nextIdentifier());
+        }
+
+        return codec.createQName(lastModule, maybePrefix);
+    }
+
+    /**
+     * Returns true if all characters from input string were consumed.
+     *
+     * @return true if all characters from input string were consumed.
+     */
+    private boolean allCharactersConsumed() {
+        return offset == data.length();
+    }
+
+    /**
+     * Skips current char if it equals expected otherwise fails parsing.
+     *
+     * @param expected Expected character
+     * @param errorMsg Error message if {@link #currentChar()} does not match expected.
+     */
+    private void checkCurrentAndSkip(final char expected, final String errorMsg) {
+        checkValid(expected == currentChar(), errorMsg);
+        offset++;
+    }
+
+    /**
+     * Fails parsing if a condition is not met.
+     *
+     * <p>
+     * In case of error provides pointer to failed instance identifier,
+     * offset on which failure occurred with explanation.
+     *
+     * @param condition Fails parsing if {@code condition} is false
+     * @param errorMsg Error message which will be provided to user.
+     */
+    private void checkValid(final boolean condition, final String errorMsg, final Object... attributes) {
+        if (!condition) {
+            throw new IllegalArgumentException(String.format(
+                "Could not parse Instance Identifier '%s'. Offset: %s : Reason: %s", data, offset,
+                String.format(errorMsg, attributes)));
+        }
+    }
+
+    /**
+     * Returns following value of quoted literal (without quotes) and sets offset after literal.
+     *
+     * @return String literal
+     */
+    private String nextQuotedValue() {
+        final char quoteChar = currentChar();
+        checkValid(QUOTE.matches(quoteChar), "Value must be qoute escaped with ''' or '\"'.");
+        skipCurrentChar();
+        final int valueStart = offset;
+        final int endQoute = data.indexOf(quoteChar, offset);
+        final String value = data.substring(valueStart, endQoute);
+        offset = endQoute;
+        skipCurrentChar();
+        return value;
+    }
+
+    /**
+     * Returns character at current offset.
+     *
+     * @return character at current offset.
+     */
+    private char currentChar() {
+        return data.charAt(offset);
+    }
+
+    /**
+     * Increases processing offset by 1.
+     */
+    private void skipCurrentChar() {
+        offset++;
+    }
+
+    /**
+     * Skip whitespace characters, sets offset to first following non-whitespace character.
+     */
+    private void skipWhitespaces() {
+        nextSequenceEnd(WSP);
+    }
+
+    /**
+     * Returns string which matches IDENTIFIER YANG ABNF token
+     * and sets processing offset after end of identifier.
+     *
+     * @return string which matches IDENTIFIER YANG ABNF token
+     */
+    private String nextIdentifier() {
+        checkValid(IDENTIFIER_FIRST_CHAR.matches(currentChar()),
+            "Identifier must start with character from set 'a-zA-Z_'");
+        final int start = offset;
+        nextSequenceEnd(IDENTIFIER);
+        return data.substring(start, offset);
+    }
+
+    private void nextSequenceEnd(final CharMatcher matcher) {
+        while (!allCharactersConsumed() && matcher.matches(data.charAt(offset))) {
+            offset++;
+        }
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/YangModeledAnyXmlNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/YangModeledAnyXmlNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..c7815f0
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import java.io.IOException;
+import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode;
+import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+
+/**
+ * Utility class used for tracking parser state as needed by a StAX-like parser.
+ * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson.
+ *
+ * <p>
+ * Represents a YANG-modeled-anyxml node.
+ */
+public final class YangModeledAnyXmlNodeDataWithSchema
+        extends CompositeNodeDataWithSchema<YangModeledAnyxmlSchemaNode> {
+
+    public YangModeledAnyXmlNodeDataWithSchema(final YangModeledAnyxmlSchemaNode yangModeledAnyXml) {
+        super(yangModeledAnyXml);
+    }
+
+    @Override
+    public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter)
+            throws IOException {
+        writer.nextDataSchemaNode(getSchema());
+
+        writer.startYangModeledAnyXmlNode(provideNodeIdentifier(), childSizeHint());
+        writeMetadata(metaWriter);
+        super.write(writer, metaWriter);
+        writer.endNode();
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/AbstractCodecFactory.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/AbstractCodecFactory.java
new file mode 100644 (file)
index 0000000..0aab521
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.util.codec;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeAware;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnknownTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.AbstractSchemaContextProvider;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A type-to-codec factory base class with logic to efficiently lookup and cache codec instances,
+ * also dealing with union type composition. This class is thread-safe as long as its underlying {@link CodecCache}
+ * is thread-safe
+ *
+ * @param <T> Codec type
+ * @author Robert Varga
+ */
+public abstract class AbstractCodecFactory<T extends TypeAwareCodec<?, ?, ?>> extends AbstractSchemaContextProvider {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractCodecFactory.class);
+
+    private final @NonNull CodecCache<T> cache;
+
+    protected AbstractCodecFactory(final @NonNull SchemaContext schemaContext, final @NonNull CodecCache<T> cache) {
+        super(schemaContext);
+        this.cache = requireNonNull(cache);
+    }
+
+    public final <S extends TypeAware & SchemaNode> @NonNull T codecFor(final S schema) {
+        /*
+         * There are many trade-offs to be made here. We need the common case being as fast as possible while reusing
+         * codecs as much as possible.
+         *
+         * This gives us essentially four classes of codecs:
+         * - simple codecs, which are based on the type definition only
+         * - complex codecs, which depend on both type definition and the leaf
+         * - null codec, which does not depend on anything
+         * - instance identifier codec, which is based on namespace mapping
+         *
+         * We assume prevalence is in above order and that caching is effective.
+         */
+        final TypeDefinition<?> type = schema.getType();
+        T ret = cache.lookupSimple(type);
+        if (ret != null) {
+            LOG.trace("Type {} hit simple {}", type, ret);
+            return ret;
+        }
+        ret = cache.lookupComplex(schema);
+        if (ret != null) {
+            LOG.trace("Type {} hit complex {}", type, ret);
+            return ret;
+        }
+
+        // Dealing with simple types first...
+        ret = getSimpleCodecFor(type);
+        if (ret != null) {
+            LOG.trace("Type {} miss simple {}", type, ret);
+            return ret;
+        }
+
+        // ... and complex types afterwards
+        ret = createComplexCodecFor(schema, type);
+        LOG.trace("Type {} miss complex {}", type, ret);
+        return cache.getComplex(schema, ret);
+    }
+
+    protected abstract T binaryCodec(BinaryTypeDefinition type);
+
+    protected abstract T booleanCodec(BooleanTypeDefinition type);
+
+    protected abstract T bitsCodec(BitsTypeDefinition type);
+
+    protected abstract T emptyCodec(EmptyTypeDefinition type);
+
+    protected abstract T enumCodec(EnumTypeDefinition type);
+
+    protected abstract T identityRefCodec(IdentityrefTypeDefinition type, QNameModule module);
+
+    // FIXME: there really are two favors, as 'require-instance true' needs to be validated. In order to deal
+    //        with that, though, we need access to the current data store.
+    protected abstract T instanceIdentifierCodec(InstanceIdentifierTypeDefinition type);
+
+    protected abstract T int8Codec(Int8TypeDefinition type);
+
+    protected abstract T int16Codec(Int16TypeDefinition type);
+
+    protected abstract T int32Codec(Int32TypeDefinition type);
+
+    protected abstract T int64Codec(Int64TypeDefinition type);
+
+    protected abstract T decimalCodec(DecimalTypeDefinition type);
+
+    protected abstract T stringCodec(StringTypeDefinition type);
+
+    protected abstract T uint8Codec(Uint8TypeDefinition type);
+
+    protected abstract T uint16Codec(Uint16TypeDefinition type);
+
+    protected abstract T uint32Codec(Uint32TypeDefinition type);
+
+    protected abstract T uint64Codec(Uint64TypeDefinition type);
+
+    protected abstract T unionCodec(UnionTypeDefinition type, List<T> codecs);
+
+    protected abstract T unknownCodec(UnknownTypeDefinition type);
+
+    private T getSimpleCodecFor(final TypeDefinition<?> type) {
+        // These types are expected to be fully-shared
+        if (type instanceof EmptyTypeDefinition) {
+            return emptyCodec((EmptyTypeDefinition) type);
+        } else if (type instanceof UnknownTypeDefinition) {
+            return unknownCodec((UnknownTypeDefinition) type);
+        }
+
+        // Now deal with simple types. Note we consider union composed of purely simple types a simple type itself.
+        // The checks here are optimized for common types.
+        final T ret;
+        if (type instanceof StringTypeDefinition) {
+            ret = stringCodec((StringTypeDefinition) type);
+        } else if (type instanceof Int8TypeDefinition) {
+            ret = int8Codec((Int8TypeDefinition) type);
+        } else if (type instanceof Int16TypeDefinition) {
+            ret = int16Codec((Int16TypeDefinition) type);
+        } else if (type instanceof Int32TypeDefinition) {
+            ret = int32Codec((Int32TypeDefinition) type);
+        } else if (type instanceof Int64TypeDefinition) {
+            ret = int64Codec((Int64TypeDefinition) type);
+        } else if (type instanceof Uint8TypeDefinition) {
+            ret = uint8Codec((Uint8TypeDefinition) type);
+        } else if (type instanceof Uint16TypeDefinition) {
+            ret = uint16Codec((Uint16TypeDefinition) type);
+        } else if (type instanceof Uint32TypeDefinition) {
+            ret = uint32Codec((Uint32TypeDefinition) type);
+        } else if (type instanceof Uint64TypeDefinition) {
+            ret = uint64Codec((Uint64TypeDefinition) type);
+        } else if (type instanceof BooleanTypeDefinition) {
+            ret = booleanCodec((BooleanTypeDefinition) type);
+        } else if (type instanceof DecimalTypeDefinition) {
+            ret = decimalCodec((DecimalTypeDefinition) type);
+        } else if (type instanceof EnumTypeDefinition) {
+            ret = enumCodec((EnumTypeDefinition) type);
+        } else if (type instanceof BitsTypeDefinition) {
+            ret = bitsCodec((BitsTypeDefinition) type);
+        } else if (type instanceof UnionTypeDefinition) {
+            final UnionTypeDefinition union = (UnionTypeDefinition) type;
+            if (!isSimpleUnion(union)) {
+                return null;
+            }
+            ret = createSimpleUnion(union);
+        } else if (type instanceof BinaryTypeDefinition) {
+            ret = binaryCodec((BinaryTypeDefinition) type);
+        } else if (type instanceof InstanceIdentifierTypeDefinition) {
+            return instanceIdentifierCodec((InstanceIdentifierTypeDefinition) type);
+        } else {
+            return null;
+        }
+
+        return cache.getSimple(type, verifyNotNull(ret));
+    }
+
+    private static boolean isSimpleUnion(final UnionTypeDefinition union) {
+        for (TypeDefinition<?> t : union.getTypes()) {
+            if (t instanceof IdentityrefTypeDefinition || t instanceof LeafrefTypeDefinition
+                    || t instanceof UnionTypeDefinition && !isSimpleUnion((UnionTypeDefinition) t)) {
+                LOG.debug("Type {} has non-simple subtype", t);
+                return false;
+            }
+        }
+
+        LOG.debug("Type {} is simple", union);
+        return true;
+    }
+
+    private T createComplexCodecFor(final SchemaNode schema, final TypeDefinition<?> type) {
+        if (type instanceof UnionTypeDefinition) {
+            return createComplexUnion(schema, (UnionTypeDefinition) type);
+        } else if (type instanceof LeafrefTypeDefinition) {
+            final TypeDefinition<?> target = SchemaContextUtil.getBaseTypeForLeafRef((LeafrefTypeDefinition) type,
+                getSchemaContext(), schema);
+            verifyNotNull(target, "Unable to find base type for leafref node %s type %s.", schema.getPath(),
+                    target);
+
+            final T ret = getSimpleCodecFor(target);
+            return ret != null ? ret : createComplexCodecFor(schema, target);
+        } else if (type instanceof IdentityrefTypeDefinition) {
+            return identityRefCodec((IdentityrefTypeDefinition) type, schema.getQName().getModule());
+        } else {
+            throw new IllegalArgumentException("Unsupported type " + type);
+        }
+    }
+
+    private T createSimpleUnion(final UnionTypeDefinition union) {
+        final List<TypeDefinition<?>> types = union.getTypes();
+        final List<T> codecs = new ArrayList<>(types.size());
+
+        for (TypeDefinition<?> type : types) {
+            T codec = cache.lookupSimple(type);
+            if (codec == null) {
+                codec = verifyNotNull(getSimpleCodecFor(type), "Type %s did not resolve to a simple codec", type);
+            }
+
+            codecs.add(codec);
+        }
+
+        return unionCodec(union, codecs);
+    }
+
+    private T createComplexUnion(final SchemaNode schema, final UnionTypeDefinition union) {
+        final List<TypeDefinition<?>> types = union.getTypes();
+        final List<T> codecs = new ArrayList<>(types.size());
+
+        for (TypeDefinition<?> type : types) {
+            T codec = cache.lookupSimple(type);
+            if (codec == null) {
+                codec = getSimpleCodecFor(type);
+                if (codec == null) {
+                    codec = createComplexCodecFor(schema, type);
+                }
+            }
+
+            codecs.add(verifyNotNull(codec, "Schema %s subtype %s has no codec", schema, type));
+        }
+
+        return unionCodec(union, codecs);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/CodecCache.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/CodecCache.java
new file mode 100644 (file)
index 0000000..0c6e504
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.util.codec;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeAware;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Abstract cache for codecs.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Codec type
+ */
+@Beta
+public abstract class CodecCache<T> {
+    /**
+     * Lookup a complex codec for schema node.
+     *
+     * @param schema Schema node
+     * @param <S> schema node type
+     * @return Cached codec, or null if no codec is cached.
+     */
+    abstract <S extends SchemaNode & TypeAware> @Nullable T lookupComplex(S schema);
+
+    /**
+     * Lookup a simple codec for a type definition.
+     *
+     * @param type Type definition
+     * @return Cached codec, or null if no codec is cached.
+     */
+    abstract @Nullable T lookupSimple(TypeDefinition<?> type);
+
+    /**
+     * Lookup-or-store a complex codec for a particular schema node.
+     *
+     * @param schema Schema node
+     * @param codec Codec to cache
+     * @param <S> schema node type
+     * @return Codec instance, either already-cached, or the codec presented as argument.
+     */
+    abstract <S extends SchemaNode & TypeAware> @NonNull T getComplex(S schema, T codec);
+
+    /**
+     * Lookup-or-store a simple codec for a particular schema node.
+     *
+     * @param type Type definition
+     * @param codec Codec to cache
+     * @return Codec instance, either already-cached, or the codec presented as argument.
+     */
+    abstract @NonNull T getSimple(TypeDefinition<?> type, T codec);
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/IdentityCodecUtil.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/IdentityCodecUtil.java
new file mode 100644 (file)
index 0000000..0587d10
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.util.codec;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import java.util.function.Function;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Utility methods for implementing string-to-identity codecs.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public final class IdentityCodecUtil {
+
+    private IdentityCodecUtil() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Parse a string into a QName using specified prefix-to-QNameModule mapping function, interpreting the result
+     * as an IdentitySchemaNode existing in specified SchemaContext.
+     *
+     * @param value string value to parse
+     * @param schemaContext Parent schema context
+     * @param prefixToModule prefix-to-QNameModule mapping function
+     * @return Corresponding IdentitySchemaNode.
+     * @throws IllegalArgumentException if the value is invalid or does not refer to an existing identity
+     */
+    public static IdentitySchemaNode parseIdentity(final String value, final SchemaContext schemaContext,
+            final Function<String, QNameModule> prefixToModule) {
+        final QName qname = QNameCodecUtil.decodeQName(value, prefixToModule);
+        final Optional<Module> optModule = schemaContext.findModule(qname.getModule());
+        checkState(optModule.isPresent(), "Parsed QName %s refers to a non-existent module", qname);
+
+        for (IdentitySchemaNode identity : optModule.get().getIdentities()) {
+            if (qname.equals(identity.getQName())) {
+                return identity;
+            }
+        }
+
+        throw new IllegalArgumentException("Parsed QName " + qname + " does not refer to a valid identity");
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/LazyCodecCache.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/LazyCodecCache.java
new file mode 100644 (file)
index 0000000..70cfd8e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.util.codec;
+
+import com.google.common.annotations.Beta;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeAware;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Lazily-populated CodecCache. This is a non-thread-safe factory, which performs caching of codecs. It is most
+ * appropriate for one-off encodings of repetitive data.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class LazyCodecCache<T> extends CodecCache<T> {
+    private final Map<SchemaNode, T> complexCodecs = new IdentityHashMap<>();
+    private final Map<TypeDefinition<?>, T> simpleCodecs = new IdentityHashMap<>();
+
+    @Override
+    <S extends SchemaNode & TypeAware> T getComplex(final S schema, final T codec) {
+        return complexCodecs.computeIfAbsent(schema, any -> codec);
+    }
+
+    @Override
+    <S extends SchemaNode & TypeAware> T lookupComplex(final S schema) {
+        return complexCodecs.get(schema);
+    }
+
+    @Override
+    T lookupSimple(final TypeDefinition<?> type) {
+        return simpleCodecs.get(type);
+    }
+
+    @Override
+    T getSimple(final TypeDefinition<?> type, final T codec) {
+        return simpleCodecs.computeIfAbsent(type, any -> codec);
+    }
+
+    public PrecomputedCodecCache<T> toPrecomputed() {
+        return new PrecomputedCodecCache<>(simpleCodecs, complexCodecs);
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/NoopCodecCache.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/NoopCodecCache.java
new file mode 100644 (file)
index 0000000..d65874c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.util.codec;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeAware;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * A no-operation codec cache. This class is thread-safe.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Codec type
+ */
+@Beta
+public final class NoopCodecCache<T> extends CodecCache<T> {
+    private static final NoopCodecCache<?> INSTANCE = new NoopCodecCache<>();
+
+    private NoopCodecCache() {
+        // Hidden
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> NoopCodecCache<T> getInstance() {
+        return (NoopCodecCache<T>) INSTANCE;
+    }
+
+    @Override
+    <S extends SchemaNode & TypeAware> T lookupComplex(final S schema) {
+        return null;
+    }
+
+    @Override
+    T lookupSimple(final TypeDefinition<?> type) {
+        return null;
+    }
+
+    @Override
+    T getSimple(final TypeDefinition<?> type, final T codec) {
+        return codec;
+    }
+
+    @Override
+    <S extends SchemaNode & TypeAware> T getComplex(final S schema, final T codec) {
+        return codec;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/PrecomputedCodecCache.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/PrecomputedCodecCache.java
new file mode 100644 (file)
index 0000000..4c81f7d
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.util.codec;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeAware;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Pre-computed thread-safe CodecCache. All possible codecs are created upfront at instantiation time, after which they
+ * are available for the cost of a constant lookup.
+ *
+ * <p>
+ * Instantiation needs to occur through {@link LazyCodecCache#toPrecomputed()} after the lazy cache has been fully
+ * populated.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class PrecomputedCodecCache<T> extends CodecCache<T> {
+    private final Map<TypeDefinition<?>, T> simpleCodecs;
+    private final Map<SchemaNode, T> complexCodecs;
+
+    PrecomputedCodecCache(final Map<TypeDefinition<?>, T> simpleCodecs, final Map<SchemaNode, T> complexCodecs) {
+        this.simpleCodecs = requireNonNull(simpleCodecs);
+        this.complexCodecs = requireNonNull(complexCodecs);
+    }
+
+    @Override
+    <S extends SchemaNode & TypeAware> T lookupComplex(final S schema) {
+        final T ret = complexCodecs.get(schema);
+        checkArgument(ret != null, "No codec available for schema %s", schema);
+        return ret;
+    }
+
+    @Override
+    T lookupSimple(final TypeDefinition<?> type) {
+        return simpleCodecs.get(type);
+    }
+
+    @Override
+    <S extends SchemaNode & TypeAware> T getComplex(final S schema, final T codec) {
+        throw new IllegalStateException("Uncached codec for " + schema);
+    }
+
+    @Override
+    T getSimple(final TypeDefinition<?> type, final T codec) {
+        throw new IllegalStateException("Uncached codec for " + type);
+    }
+
+    public int complexSize() {
+        return complexCodecs.size();
+    }
+
+    public int simpleSize() {
+        return simpleCodecs.size();
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/QNameCodecUtil.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/QNameCodecUtil.java
new file mode 100644 (file)
index 0000000..3b48442
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.util.codec;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Splitter;
+import java.util.Iterator;
+import java.util.function.Function;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+/**
+ * Utility methods for parsing and writing QNames.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class QNameCodecUtil {
+    private static final Splitter COLON_SPLITTER = Splitter.on(':').trimResults();
+
+    private QNameCodecUtil() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static QName decodeQName(final String str, final Function<String, QNameModule> prefixToModule) {
+        final Iterator<String> it = COLON_SPLITTER.split(str).iterator();
+
+        // Empty string
+        final String identifier;
+        final String prefix;
+        if (it.hasNext()) {
+            final String first = it.next();
+            if (it.hasNext()) {
+                // It is "prefix:value"
+                prefix = first;
+                identifier = it.next();
+                checkArgument(!it.hasNext(), "Malformed QName '%s'", str);
+            } else {
+                prefix = "";
+                identifier = first;
+            }
+        } else {
+            prefix = "";
+            identifier = "";
+        }
+
+        final QNameModule module = prefixToModule.apply(prefix);
+        checkArgument(module != null, "Cannot resolve prefix '%s' from %s", prefix, str);
+        return QName.create(module, identifier);
+    }
+
+    public static String encodeQName(final QName qname, final Function<QNameModule, String> moduleToPrefix) {
+        final String prefix = moduleToPrefix.apply(qname.getModule());
+        checkArgument(prefix != null, "Cannot allocated prefix for %s", qname);
+        return prefix.isEmpty() ? qname.getLocalName() : prefix + ":" + qname.getLocalName();
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/SharedCodecCache.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/SharedCodecCache.java
new file mode 100644 (file)
index 0000000..d0bd3f3
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.util.codec;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Throwables;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeAware;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * A thread-safe lazily-populated codec cache. Instances are cached in an internal weak/soft cache.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class SharedCodecCache<T> extends CodecCache<T> {
+    // Weak keys to force identity lookup
+    // Soft values to keep unreferenced codecs around for a bit, but eventually we want them to go away
+    private final Cache<TypeDefinition<?>, T> simpleCodecs = CacheBuilder.newBuilder().weakKeys().softValues().build();
+    private final Cache<SchemaNode, T> complexCodecs = CacheBuilder.newBuilder().weakKeys().softValues().build();
+
+    @Override
+    public <S extends SchemaNode & TypeAware> T lookupComplex(final S schema) {
+        return complexCodecs.getIfPresent(schema);
+    }
+
+    @Override
+    T lookupSimple(final TypeDefinition<?> type) {
+        return simpleCodecs.getIfPresent(type);
+    }
+
+    @Override
+    <S extends SchemaNode & TypeAware> T getComplex(final S schema, final T codec) {
+        try {
+            return complexCodecs.get(schema, () -> codec);
+        } catch (ExecutionException e) {
+            final Throwable cause = e.getCause();
+            Throwables.throwIfUnchecked(cause);
+            throw new IllegalStateException(e);
+        }
+    }
+
+    @Override
+    T getSimple(final TypeDefinition<?> type, final T codec) {
+        try {
+            return simpleCodecs.get(type, () -> codec);
+        } catch (ExecutionException e) {
+            final Throwable cause = e.getCause();
+            Throwables.throwIfUnchecked(cause);
+            throw new IllegalStateException(e);
+        }
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/TypeAwareCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/TypeAwareCodec.java
new file mode 100644 (file)
index 0000000..d72b42b
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.util.codec;
+
+import com.google.common.annotations.Beta;
+
+/**
+  * A codec, which knows what the native representation for a particular data type is. It knows how to convert a native
+  * value to and from string representation based on some additional input or output context.
+  *
+  * @author Robert Varga
+  *
+  * @param <T> Data value type
+  * @param <I> Input context type
+  * @param <O> Output context type
+  */
+@Beta
+public interface TypeAwareCodec<T, I, O> {
+    /**
+     * Return the data type class.
+     *
+     * @return Data type class
+     */
+    Class<T> getDataType();
+
+    /**
+     * Parse a String representation into its native format.
+     *
+     * @param ctx Parse context
+     * @param str String representation
+     * @return Value in native format
+     * @throws IllegalArgumentException if the value does not parse or pass type validation
+     */
+    T parseValue(I ctx, String str);
+
+    /**
+     * Serialize specified value with specified JsonWriter.
+     *
+     * @param ctx Write context
+     * @param value Value in native format
+     * @throws Exception if the write fails
+     */
+    void writeValue(O ctx, T value) throws Exception;
+}
diff --git a/yang/yang-data-util/src/test/java/org/opendaylight/yangtools/yang/data/util/codec/DataSchemaContextTreeTest.java b/yang/yang-data-util/src/test/java/org/opendaylight/yangtools/yang/data/util/codec/DataSchemaContextTreeTest.java
new file mode 100644 (file)
index 0000000..cca8dcd
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.util.codec;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.Optional;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class DataSchemaContextTreeTest {
+    private static final QNameModule MODULE = QNameModule.create(URI.create("dataschemacontext"));
+    private static final QName FOO = QName.create(MODULE, "foo");
+    private static final QName BAR = QName.create(MODULE, "bar");
+    private static final QName BAZ = QName.create(MODULE, "baz");
+    private static DataSchemaContextTree CONTEXT;
+
+    @BeforeClass
+    public static void init() {
+        CONTEXT = DataSchemaContextTree.from(YangParserTestUtils.parseYangResource("/dataschemacontext.yang"));
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        CONTEXT = null;
+    }
+
+    @Test
+    public void testCorrectInput() {
+        assertTrue(CONTEXT.findChild(YangInstanceIdentifier.of(FOO)).isPresent());
+        assertTrue(CONTEXT.findChild(YangInstanceIdentifier.of(FOO).node(BAR)).isPresent());
+        assertTrue(CONTEXT.findChild(YangInstanceIdentifier.of(FOO).node(BAR).node(BAZ)).isPresent());
+
+        assertNotNull(CONTEXT.getChild(YangInstanceIdentifier.of(FOO)));
+        assertNotNull(CONTEXT.getChild(YangInstanceIdentifier.of(FOO).node(BAR)));
+        assertNotNull(CONTEXT.getChild(YangInstanceIdentifier.of(FOO).node(BAR).node(BAZ)));
+    }
+
+    @Test
+    public void testSimpleBad() {
+        assertEquals(Optional.empty(), CONTEXT.findChild(YangInstanceIdentifier.of(BAR)));
+        assertNull(CONTEXT.getChild(YangInstanceIdentifier.of(BAR)));
+    }
+
+    @Test
+    public void testNestedBad() {
+        assertEquals(Optional.empty(), CONTEXT.findChild(YangInstanceIdentifier.of(BAR).node(BAZ)));
+        assertNull(CONTEXT.getChild(YangInstanceIdentifier.of(BAR).node(BAZ)));
+    }
+}
diff --git a/yang/yang-data-util/src/test/java/org/opendaylight/yangtools/yang/data/util/codec/IdentityCodecUtilTest.java b/yang/yang-data-util/src/test/java/org/opendaylight/yangtools/yang/data/util/codec/IdentityCodecUtilTest.java
new file mode 100644 (file)
index 0000000..0212361
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.util.codec;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.junit.Assert.assertNotNull;
+
+import java.net.URI;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class IdentityCodecUtilTest {
+    private static final QNameModule MODULE = QNameModule.create(URI.create("yangtools846"));
+    private static SchemaContext SCHEMA_CONTEXT;
+
+    @BeforeClass
+    public static void init() {
+        SCHEMA_CONTEXT = YangParserTestUtils.parseYangResource("/yangtools846.yang");
+    }
+
+    @AfterClass
+    public static void cleanup() {
+        SCHEMA_CONTEXT = null;
+    }
+
+    @Test
+    public void testCorrectInput() {
+        assertNotNull(IdentityCodecUtil.parseIdentity("yt846:foo", SCHEMA_CONTEXT,
+            IdentityCodecUtilTest::resolvePrefix));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNonExistent() {
+        IdentityCodecUtil.parseIdentity("yt846:bar", SCHEMA_CONTEXT, IdentityCodecUtilTest::resolvePrefix);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testEmptyLocalName() {
+        IdentityCodecUtil.parseIdentity("yt846:", SCHEMA_CONTEXT, IdentityCodecUtilTest::resolvePrefix);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testEmptyString() {
+        IdentityCodecUtil.parseIdentity("", SCHEMA_CONTEXT, IdentityCodecUtilTest::resolvePrefix);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testNoPrefix() {
+        IdentityCodecUtil.parseIdentity("foo", SCHEMA_CONTEXT, IdentityCodecUtilTest::resolvePrefix);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testEmptyPrefix() {
+        IdentityCodecUtil.parseIdentity(":foo", SCHEMA_CONTEXT, IdentityCodecUtilTest::resolvePrefix);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testColon() {
+        IdentityCodecUtil.parseIdentity(":", SCHEMA_CONTEXT, IdentityCodecUtilTest::resolvePrefix);
+    }
+
+
+    private static QNameModule resolvePrefix(final String prefix) {
+        // TODO: QNameCodecUtil should deal with some of the malformed stuff here by throwing IAE. We throw an ISE
+        //       to discern what is happening.
+        checkState("yt846".equals(prefix), "Unexpected prefix %s", prefix);
+        return MODULE;
+    }
+}
diff --git a/yang/yang-data-util/src/test/resources/dataschemacontext.yang b/yang/yang-data-util/src/test/resources/dataschemacontext.yang
new file mode 100644 (file)
index 0000000..a8ec4cf
--- /dev/null
@@ -0,0 +1,12 @@
+module dataschemacontext {
+    namespace "dataschemacontext";
+    prefix dsc;
+
+    container foo {
+        choice bar {
+            leaf baz {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-data-util/src/test/resources/yangtools846.yang b/yang/yang-data-util/src/test/resources/yangtools846.yang
new file mode 100644 (file)
index 0000000..4e577c8
--- /dev/null
@@ -0,0 +1,7 @@
+module yangtools846 {
+    namespace "yangtools846";
+    prefix yt846;
+
+    identity foo;
+}
+
diff --git a/yang/yang-data-xpath-api/pom.xml b/yang/yang-data-xpath-api/pom.xml
new file mode 100644 (file)
index 0000000..71c707d
--- /dev/null
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-data-xpath-api</artifactId>
+    <packaging>bundle</packaging>
+    <version>0.2.3-SNAPSHOT</version>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.yangtools</groupId>
+          <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.yangtools</groupId>
+          <artifactId>yang-xpath-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.data.xpath.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-data-xpath-api/src/main/java/org/opendaylight/yangtools/yang/data/api/xpath/DataTreeCandidateDocument.java b/yang/yang-data-xpath-api/src/main/java/org/opendaylight/yangtools/yang/data/api/xpath/DataTreeCandidateDocument.java
new file mode 100644 (file)
index 0000000..90d70cc
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.xpath;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+/**
+ * An {@link XPathDocument} bound to a {@link DataTreeCandidate}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface DataTreeCandidateDocument extends XPathDocument<DataTreeCandidate> {
+
+}
diff --git a/yang/yang-data-xpath-api/src/main/java/org/opendaylight/yangtools/yang/data/api/xpath/NormalizedNodeDocument.java b/yang/yang-data-xpath-api/src/main/java/org/opendaylight/yangtools/yang/data/api/xpath/NormalizedNodeDocument.java
new file mode 100644 (file)
index 0000000..239cc1b
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.xpath;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * An {@link XPathDocument} bound to a {@link NormalizedNode} tree.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface NormalizedNodeDocument extends XPathDocument<NormalizedNode<?, ?>> {
+
+}
diff --git a/yang/yang-data-xpath-api/src/main/java/org/opendaylight/yangtools/yang/data/api/xpath/XPathDocument.java b/yang/yang-data-xpath-api/src/main/java/org/opendaylight/yangtools/yang/data/api/xpath/XPathDocument.java
new file mode 100644 (file)
index 0000000..4614732
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.xpath;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * The notion of a W3C documented bound to a specific object model. This binding is expressed in terms of the document
+ * exposing a root node from the object model.
+ *
+ * <p>
+ * This interface is not meant to be used directly. Refer to its specializations like {@link NormalizedNodeDocument}
+ * and {@link DataTreeCandidateDocument}.
+ *
+ * @param <T> Object model node type
+ * @author Robert Varga
+ */
+@Beta
+public interface XPathDocument<T> {
+    /**
+     * Return the root node of this document.
+     *
+     * @return This document's root node.
+     */
+    @NonNull T getRootNode();
+}
diff --git a/yang/yang-data-xpath-api/src/main/java/org/opendaylight/yangtools/yang/data/api/xpath/package-info.java b/yang/yang-data-xpath-api/src/main/java/org/opendaylight/yangtools/yang/data/api/xpath/package-info.java
new file mode 100644 (file)
index 0000000..973da3e
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+ */
+/**
+ * Experimental support for evaluating {@link org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression}s on top
+ * of {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode} trees.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.yang.data.api.xpath;
\ No newline at end of file
diff --git a/yang/yang-maven-plugin-it/.gitignore b/yang/yang-maven-plugin-it/.gitignore
new file mode 100644 (file)
index 0000000..ba40a30
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# 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
+#
+log.txt
diff --git a/yang/yang-maven-plugin-it/pom.xml b/yang/yang-maven-plugin-it/pom.xml
new file mode 100644 (file)
index 0000000..a71ebd8
--- /dev/null
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <groupId>org.opendaylight.odlparent</groupId>
+        <artifactId>odlparent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>yang-maven-plugin-it</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.maven.shared</groupId>
+            <artifactId>maven-verifier</artifactId>
+            <version>1.6</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.code.findbugs</groupId>
+                    <!-- Use com.google.code.findbugs:annotations instead of jsr305 -->
+                    <artifactId>jsr305</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+      <testResources>
+        <testResource>
+          <directory>${basedir}/src/test/resources</directory>
+          <filtering>true</filtering>
+        </testResource>
+      </testResources>
+        <plugins>
+          <plugin>
+            <artifactId>maven-resources-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>default-testResources</id>
+                <phase>process-test-resources</phase>
+                <goals>
+                  <goal>testResources</goal>
+                </goals>
+                <configuration>
+                  <useDefaultDelimiters>false</useDefaultDelimiters>
+                  <delimiters>
+                    <delimiter>@</delimiter>
+                  </delimiters>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+            <plugin>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>integration-test</goal>
+                            <goal>verify</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>properties-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>write-project-properties</goal>
+                        </goals>
+                        <configuration>
+                            <outputFile>${project.build.directory}/it-project.properties</outputFile>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-help-plugin</artifactId>
+                <configuration>
+                    <output>${project.build.directory}/effective-settings.xml</output>
+                </configuration>
+                <executions>
+                    <execution>
+                      <phase>pre-integration-test</phase>
+                      <goals>
+                          <goal>effective-settings</goal>
+                      </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/it/YangToSourcesPluginTestIT.java b/yang/yang-maven-plugin-it/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/it/YangToSourcesPluginTestIT.java
new file mode 100644 (file)
index 0000000..6a60736
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * 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.yang2sources.plugin.it;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.io.Resources;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.Properties;
+import org.apache.maven.it.VerificationException;
+import org.apache.maven.it.Verifier;
+import org.junit.Test;
+
+public class YangToSourcesPluginTestIT {
+
+    // TODO Test yang files in transitive dependencies
+
+    @Test
+    public void testYangRootNotExist() throws Exception {
+        setUp("test-parent/YangRootNotExist/", false)
+            .verifyTextInLog("[WARNING] yang-to-sources: YANG source directory");
+    }
+
+    @Test
+    public void testCorrect() throws VerificationException, URISyntaxException, IOException {
+        verifyCorrectLog(setUp("test-parent/Correct/", false));
+    }
+
+    @Test
+    public void testAdditionalConfiguration() throws Exception {
+        Verifier vrf = setUp("test-parent/AdditionalConfig/", false);
+        vrf.verifyTextInLog("[DEBUG] yang-to-sources: Additional configuration picked up for : "
+                + "org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl: "
+                + "{nm1=abcd=a.b.c.d, nm2=abcd2=a.b.c.d.2}");
+        vrf.verifyTextInLog("[DEBUG] yang-to-sources: Additional configuration picked up for : "
+                + "org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl: {c1=config}");
+        vrf.verifyTextInLog("[DEBUG] yang-to-sources: YANG files marked as resources:");
+        vrf.verifyTextInLog(Joiner.on(File.separator).join(Arrays.asList("target", "generated-sources", "spi"))
+                + " marked as resources for generator: org.opendaylight.yangtools.yang2sources.spi."
+                + "CodeGeneratorTestImpl");
+    }
+
+    @Test
+    public void testMissingYangInDep() throws Exception {
+        try {
+            setUp("test-parent/MissingYangInDep/", false);
+            fail("Verification exception should have been thrown");
+        } catch (VerificationException e) {
+            assertVerificationException(e, "Imported module [unknownDep] was not found.");
+        }
+    }
+
+    static void verifyCorrectLog(final Verifier vrf) throws VerificationException {
+        vrf.verifyErrorFreeLog();
+        vrf.verifyTextInLog("[INFO] yang-to-sources: Project model files found: ");
+        vrf.verifyTextInLog("[INFO] yang-to-sources: Code generator instantiated from "
+                + "org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl");
+        vrf.verifyTextInLog("[INFO] yang-to-sources: Sources generated by "
+                + "org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl: 0");
+    }
+
+    @Test
+    public void testNoGenerators() throws Exception {
+        Verifier vrf = setUp("test-parent/NoGenerators/", false);
+        vrf.verifyErrorFreeLog();
+        vrf.verifyTextInLog("[WARNING] yang-to-sources: No code generators provided");
+    }
+
+    @Test
+    public void testInvalidVersion() throws Exception {
+        Verifier vrf = setUp("test-parent/InvalidVersion/", false);
+        vrf.verifyErrorFreeLog();
+        vrf.verifyTextInLog("[WARNING] yang-to-sources: Dependency resolution conflict:");
+    }
+
+    @Test
+    public void testUnknownGenerator() throws Exception {
+        Verifier vrf = setUp("test-parent/UnknownGenerator/", true);
+        vrf.verifyTextInLog("[INFO] yang-to-sources: Code generator instantiated from "
+                + "org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl");
+        vrf.verifyTextInLog("Failed to instantiate code generator unknown");
+        vrf.verifyTextInLog("java.lang.ClassNotFoundException: unknown");
+    }
+
+    @Test
+    public void testNoYangFiles() throws Exception {
+        setUp("test-parent/NoYangFiles/", false).verifyTextInLog("[INFO] yang-to-sources: No input files found");
+    }
+
+    static void assertVerificationException(final VerificationException ex, final String string) {
+        assertThat(ex.getMessage(), containsString(string));
+    }
+
+    static Verifier setUp(final String project, final boolean ignoreF)
+            throws VerificationException, URISyntaxException, IOException {
+        final URL path = YangToSourcesPluginTestIT.class.getResource("/" + project + "pom.xml");
+        final Verifier verifier = new Verifier(new File(path.toURI()).getParent());
+        if (ignoreF) {
+            verifier.addCliOption("-fn");
+        }
+
+        final Optional<String> maybeSettings = getEffectiveSettingsXML();
+        if (maybeSettings.isPresent()) {
+            verifier.addCliOption("-gs");
+            verifier.addCliOption(maybeSettings.get());
+        }
+        verifier.setMavenDebug(true);
+        verifier.executeGoal("generate-sources");
+        return verifier;
+    }
+
+    @Test
+    public void testNoOutputDir() throws Exception {
+        verifyCorrectLog(YangToSourcesPluginTestIT.setUp("test-parent/NoOutputDir/", false));
+    }
+
+    @Test
+    public void testFindResourceOnCp() throws Exception {
+        Verifier v1 = setUp("test-parent/GenerateTest1/", false);
+        v1.executeGoal("clean");
+        v1.executeGoal("package");
+
+        String buildDir = getMavenBuildDirectory(v1);
+        v1.assertFilePresent(buildDir + "/classes/META-INF/yang/types1@2013-02-27.yang");
+        v1.assertFilePresent(buildDir + "/classes/META-INF/yang/types2@2013-02-27.yang");
+        v1.assertFilePresent(buildDir + "/classes/META-INF/yang/types3@2013-02-27.yang");
+
+        Verifier v2 = setUp("test-parent/GenerateTest2/", false);
+        v2.executeGoal("clean");
+        v2.executeGoal("package");
+
+        buildDir = getMavenBuildDirectory(v2);
+        v2.assertFilePresent(buildDir + "/classes/META-INF/yang/private@2013-02-27.yang");
+        v2.assertFileNotPresent(buildDir + "/classes/META-INF/yang/types1@2013-02-27.yang");
+        v2.assertFileNotPresent(buildDir + "/classes/META-INF/yang/types2@2013-02-27.yang");
+        v2.assertFileNotPresent(buildDir + "/classes/META-INF/yang/types3@2013-02-27.yang");
+    }
+
+    private static String getMavenBuildDirectory(final Verifier verifier) throws IOException {
+        final Properties sp = new Properties();
+        final Path path = new File(verifier.getBasedir() + "/it-project.properties").toPath();
+        try (InputStream is = Files.newInputStream(path)) {
+            sp.load(is);
+        }
+        return sp.getProperty("target.dir");
+    }
+
+    private static Optional<String> getEffectiveSettingsXML() throws URISyntaxException, VerificationException,
+            IOException {
+        final URL path = Resources.getResource(YangToSourcesPluginTestIT.class, "/test-parent/pom.xml");
+        final File buildDir = new File(path.toURI()).getParentFile().getParentFile().getParentFile();
+        final File effectiveSettingsXML = new File(buildDir, "effective-settings.xml");
+        if (effectiveSettingsXML.exists()) {
+            return Optional.of(effectiveSettingsXML.getAbsolutePath());
+        }
+
+        fail(effectiveSettingsXML.getAbsolutePath());
+        return Optional.empty();
+    }
+}
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/AdditionalConfig/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/AdditionalConfig/pom.xml
new file mode 100644 (file)
index 0000000..f740ad2
--- /dev/null
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>additional-config</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-maven-plugin-spi</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>../files</yangFilesRootDir>
+                            <inspectDependencies>false</inspectDependencies>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl
+                                    </codeGeneratorClass>
+                                    <additionalConfiguration>
+                                        <nm1>abcd=a.b.c.d</nm1>
+                                        <nm2>abcd2=a.b.c.d.2</nm2>
+                                    </additionalConfiguration>
+                                    <resourceBaseDir>/target/resourcesGenerated</resourceBaseDir>
+                                    <outputBaseDir>
+                                        target/AdditionalConfig
+                                    </outputBaseDir>
+                                </generator>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl
+                                    </codeGeneratorClass>
+                                    <additionalConfiguration>
+                                        <c1>config</c1>
+                                    </additionalConfiguration>
+                                    <outputBaseDir>
+                                        target/AdditionalConfig
+                                    </outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                        </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>properties-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/Correct/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/Correct/pom.xml
new file mode 100644 (file)
index 0000000..c01e196
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>correct</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-maven-plugin-spi</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>../files</yangFilesRootDir>
+                            <inspectDependencies>false</inspectDependencies>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        target/correct
+                                    </outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                        </configuration>
+                    </execution>
+                </executions>
+
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/pom.xml
new file mode 100644 (file)
index 0000000..1051fcb
--- /dev/null
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>generator-test1</artifactId>
+
+    <properties>
+        <target.dir>${project.build.directory}</target.dir>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>${basedir}/src/main/resources</yangFilesRootDir>
+                            <inspectDependencies>false</inspectDependencies>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        target/GenerateTest1
+                                    </outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                        </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>properties-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/src/main/resources/testfile1.yang b/yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/src/main/resources/testfile1.yang
new file mode 100644 (file)
index 0000000..dd50655
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module types1 {
+    yang-version 1;
+    namespace "urn:simple.container.demo.types1";
+    prefix "t1";
+
+    import types2 {
+        prefix "data";
+        revision-date 2013-02-27;
+    }
+
+   import types3 {
+        prefix "t3";
+        revision-date 2013-02-27;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+}
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/src/main/resources/testfile2.yang b/yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/src/main/resources/testfile2.yang
new file mode 100644 (file)
index 0000000..9eec4b4
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module types2 {
+    yang-version 1;
+    namespace "urn:simple.types.data.demo.types2";
+    prefix "t2";
+    
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+    
+    description "This is types-data test description";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+    
+    typedef my-base-int32-type {
+        type int32 {
+            range "2..20";
+        }
+    }
+
+    typedef my-type1 {
+        type my-base-int32-type {
+            range "11..max";
+        }
+        units "mile";
+        default "11";
+    }
+    
+    typedef my-custom-string {
+        type string {
+            pattern "[a-k]*";
+            length "5..11";
+        }
+    }
+    
+    typedef my-string-type {
+        type my-custom-string {
+            length "5..10";
+        }
+    }
+    
+    typedef my-string-type2 {
+        type my-string-type {
+            pattern "[b-u]*";
+        }
+    }
+
+    typedef my-string-type-ext {
+        type my-string-type2 {
+            pattern "[e-z]*";
+        }
+    }
+
+    typedef my-int-type {
+        type int32 {
+            range "10..20";
+        }
+    }
+    
+    typedef my-int-type2 {
+        type my-int-type {
+            range "12..18";
+        }
+    }
+    
+    typedef my-int-type-ext {
+        type my-int-type2 {
+            range "14..16";
+        }
+    }
+    
+    typedef my-decimal-type {
+        type decimal64 {
+            fraction-digits 6;
+        }
+    }
+    
+    typedef my-decimal-type-ext {
+        type decimal64 {
+            fraction-digits 5;
+        }
+    }
+
+    typedef my-union {
+       type union {
+               type int16 {
+                       range "1..100";
+               }
+               type int32;
+       }
+    }
+
+    typedef my-union-ext {
+       type my-union;
+    }
+
+    typedef nested-union1 {
+       type nested-union2;
+    }
+
+    typedef nested-union2 {
+       type union {
+               type my-union-ext;
+               type string;
+       }
+    }
+
+    leaf if-name {
+        type leafref {
+            path "/interface/name";
+        }
+    }
+     
+    leaf name {
+        type string;
+    }
+     
+    leaf nested-type-leaf {
+        type my-type1;
+    }
+    
+    extension c-define {
+        description
+        "Takes as argument a name string.
+        Makes the code generator use the given name in the
+        #define.";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+    
+    container system {
+        leaf user {
+            type string;
+        }
+    }
+    
+    grouping target {
+        leaf address {
+            type string;
+            description "Target IP address";
+        }
+        container port {
+            description "Target port container";
+        }
+    }
+
+    container peer {
+        container destination {
+            uses target {
+                refine address {
+                    default "1.2.3.4";
+                }
+                refine port {
+                    description "new port description updated by refine";
+                }
+            }
+        }
+    }
+    
+    container interfaces {
+         list ifEntry {
+             key "ifIndex";
+
+             leaf ifIndex {
+                 type uint32;
+                 units minutes;
+             }
+             
+             leaf ifMtu {
+                 type int32;
+             }
+         }
+    }
+
+}
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/src/main/resources/testfile3.yang b/yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest1/src/main/resources/testfile3.yang
new file mode 100644 (file)
index 0000000..12901ad
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module types3 {
+    yang-version 1;
+    namespace "urn:simple.container.demo.types3";
+    prefix "t3";
+
+    import types2 {
+        prefix "data";
+        revision-date 2013-02-27;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+}
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest2/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest2/pom.xml
new file mode 100644 (file)
index 0000000..359a870
--- /dev/null
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>generator-test2</artifactId>
+
+    <properties>
+        <target.dir>${project.build.directory}</target.dir>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>generator-test1</artifactId>
+            <version>${project.version}</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/../GenerateTest1/target/generator-test1-${project.version}.jar</systemPath>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>${project.basedir}/yang</yangFilesRootDir>
+                            <inspectDependencies>true</inspectDependencies>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        outDir/
+                                    </outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                        </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>properties-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest2/yang/private.yang b/yang/yang-maven-plugin-it/src/test/resources/test-parent/GenerateTest2/yang/private.yang
new file mode 100644 (file)
index 0000000..8c15c78
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module private {
+    yang-version 1;
+    namespace "urn:simple.container.demo";
+    prefix "p";
+
+    import types2 {
+        prefix "data";
+        revision-date 2013-02-27;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+    
+
+}
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/Generator/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/Generator/pom.xml
new file mode 100644 (file)
index 0000000..0fa70ed
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>generator</artifactId>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>../files</yangFilesRootDir>
+                            <inspectDependencies>false</inspectDependencies>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        outDir/
+                                    </outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                        </configuration>
+                    </execution>
+                </executions>
+
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/InvalidVersion/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/InvalidVersion/pom.xml
new file mode 100644 (file)
index 0000000..bc2e724
--- /dev/null
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>invalid-version</artifactId>
+
+    <!-- Testing dependency -->
+    <!-- yang-common dependency added here only for purpose of 'artifact version 
+        check' test: version of yang-common artifact has to be different than one defined 
+        in yang-maven-plugin to make test pass -->
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+            <version>0.6.1</version>
+        </dependency>
+    </dependencies>
+    <!-- End of Testing dependency -->
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>../files</yangFilesRootDir>
+                            <inspectDependencies>false</inspectDependencies>
+                            <codeGenerators>
+                            </codeGenerators>
+                            <resourceProviders>
+                                <provider>
+                                    <resourceProviderClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.ResourceProviderTestImpl
+                                    </resourceProviderClass>
+                                    <outputBaseDir>
+                                        outDir/
+                                    </outputBaseDir>
+                                </provider>
+                            </resourceProviders>
+                        </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/MissingYangInDep/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/MissingYangInDep/pom.xml
new file mode 100644 (file)
index 0000000..2933592
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>missing-yang-in-dep</artifactId>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>${project.basedir}/yang</yangFilesRootDir>
+                            <inspectDependencies>true</inspectDependencies>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        outDir/
+                                    </outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                            <resourceProviders>
+                                <provider>
+                                    <resourceProviderClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.ResourceProviderTestImpl
+                                    </resourceProviderClass>
+                                    <outputBaseDir>
+                                        outDir/
+                                    </outputBaseDir>
+                                </provider>
+                            </resourceProviders>
+                        </configuration>
+                    </execution>
+                </executions>
+
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/MissingYangInDep/yang/private.yang b/yang/yang-maven-plugin-it/src/test/resources/test-parent/MissingYangInDep/yang/private.yang
new file mode 100644 (file)
index 0000000..66f1553
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module private {
+    yang-version 1;
+    namespace "urn:simple.container.demo";
+    prefix "p";
+
+    import unknownDep {
+        prefix "data";
+        revision-date 2013-02-27;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+    
+
+}
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/NoGenerators/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/NoGenerators/pom.xml
new file mode 100644 (file)
index 0000000..e5340fe
--- /dev/null
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>no-generators</artifactId>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration combine.self="override">
+                            <yangFilesRootDir>../files</yangFilesRootDir>
+                            <inspectDependencies>false</inspectDependencies>
+                            <codeGenerators>
+                            </codeGenerators>
+                            <resourceProviders>
+                                <provider>
+                                    <resourceProviderClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.ResourceProviderTestImpl
+                                    </resourceProviderClass>
+                                    <outputBaseDir>
+                                        outDir/
+                                    </outputBaseDir>
+                                </provider>
+                            </resourceProviders>
+                        </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/NoOutputDir/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/NoOutputDir/pom.xml
new file mode 100644 (file)
index 0000000..e8e9d4a
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>no-output-dir</artifactId>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>../files</yangFilesRootDir>
+                            <inspectDependencies>false</inspectDependencies>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        target/NoOutputDir
+                                    </outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                        </configuration>
+                    </execution>
+                </executions>
+
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/NoYangFiles/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/NoYangFiles/pom.xml
new file mode 100644 (file)
index 0000000..480b644
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>no-yang-files</artifactId>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>${basedir}</yangFilesRootDir>
+                            <inspectDependencies>false</inspectDependencies>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        /outDir/
+                                    </outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                        </configuration>
+                    </execution>
+                </executions>
+
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/UnknownGenerator/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/UnknownGenerator/pom.xml
new file mode 100644 (file)
index 0000000..3b4a0ce
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>unknown-generator</artifactId>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>../files</yangFilesRootDir>
+                            <inspectDependencies>false</inspectDependencies>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        /outDir/
+                                    </outputBaseDir>
+                                </generator>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        unknown
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        /outDir/
+                                    </outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                        </configuration>
+                    </execution>
+                </executions>
+
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/YangRootNotExist/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/YangRootNotExist/pom.xml
new file mode 100644 (file)
index 0000000..bb968be
--- /dev/null
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>test-parent</artifactId>
+        <version>@project.version@</version>
+    </parent>
+
+    <artifactId>yang-root-not-exist</artifactId>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>unknown</yangFilesRootDir>
+                            <inspectDependencies>false</inspectDependencies>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        /outDir/
+                                    </outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/files/testfile1.yang b/yang/yang-maven-plugin-it/src/test/resources/test-parent/files/testfile1.yang
new file mode 100644 (file)
index 0000000..dd50655
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module types1 {
+    yang-version 1;
+    namespace "urn:simple.container.demo.types1";
+    prefix "t1";
+
+    import types2 {
+        prefix "data";
+        revision-date 2013-02-27;
+    }
+
+   import types3 {
+        prefix "t3";
+        revision-date 2013-02-27;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+}
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/files/testfile2.yang b/yang/yang-maven-plugin-it/src/test/resources/test-parent/files/testfile2.yang
new file mode 100644 (file)
index 0000000..f856acb
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module types2 {
+    yang-version 1;
+    namespace "urn:simple.types.data.demo.types2";
+    prefix "t2";
+    
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+    
+    description "This is types-data test description";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+}
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/files/testfile3.yang b/yang/yang-maven-plugin-it/src/test/resources/test-parent/files/testfile3.yang
new file mode 100644 (file)
index 0000000..12901ad
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module types3 {
+    yang-version 1;
+    namespace "urn:simple.container.demo.types3";
+    prefix "t3";
+
+    import types2 {
+        prefix "data";
+        revision-date 2013-02-27;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+}
diff --git a/yang/yang-maven-plugin-it/src/test/resources/test-parent/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/test-parent/pom.xml
new file mode 100644 (file)
index 0000000..0924704
--- /dev/null
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>test-parent</artifactId>
+    <version>@project.version@</version>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>additional-config</module>
+        <module>correct</module>
+        <module>generate-test1</module>
+        <module>generate-test2</module>
+        <module>generator</module>
+        <module>invalid-version</module>
+        <module>missing-yang-in-dep</module>
+        <module>naming-conflict</module>
+        <module>no-generators</module>
+        <module>no-output-dir</module>
+        <module>no-yang-files</module>
+        <module>unknown-generator</module>
+        <module>yang-root-not-exist</module>
+    </modules>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>properties-maven-plugin</artifactId>
+                    <version>1.0.0</version>
+                    <executions>
+                        <execution>
+                            <phase>generate-resources</phase>
+                            <goals>
+                                <goal>write-project-properties</goal>
+                            </goals>
+                            <configuration>
+                                <outputFile>it-project.properties</outputFile>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+</project>
diff --git a/yang/yang-maven-plugin-spi/pom.xml b/yang/yang-maven-plugin-spi/pom.xml
new file mode 100644 (file)
index 0000000..92ce5ed
--- /dev/null
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <groupId>org.opendaylight.odlparent</groupId>
+        <artifactId>odlparent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>yang-maven-plugin-spi</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-plugin-api</artifactId>
+            <version>3.3.9</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+            <version>3.3.9</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.sonatype.plexus</groupId>
+            <artifactId>plexus-build-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-maven-plugin-spi/src/main/java/org/opendaylight/yangtools/yang2sources/spi/BasicCodeGenerator.java b/yang/yang-maven-plugin-spi/src/main/java/org/opendaylight/yangtools/yang2sources/spi/BasicCodeGenerator.java
new file mode 100644 (file)
index 0000000..d669509
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015 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.yang2sources.spi;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+/**
+ * Maven 3.1.x and newer uses SLF4J internally, which means we do not need to pass a logger instance around.
+ */
+public interface BasicCodeGenerator {
+    enum ImportResolutionMode {
+        /**
+         * Standard, RFC6020 and RFC7950 compliant mode. Imports are satisfied by exact revision match (if specified),
+         * or by latest available revision.
+         */
+        REVISION_EXACT_OR_LATEST,
+        /**
+         * Semantic version based mode. Imports which specify a semantic version (via the OpenConfig extension) will
+         * be satisfied by module which exports the latest compatible revision. Imports which do not specify semantic
+         * version will be resolved just as they would be via {@link #REVISION_EXACT_OR_LATEST}.
+         */
+        SEMVER_LATEST,
+    }
+
+    /**
+     * Generate sources from provided {@link EffectiveModelContext}.
+     *
+     * @param context
+     *            parsed from YANG files
+     * @param outputBaseDir
+     *            expected output directory for generated sources configured by
+     *            user
+     * @param currentModules
+     *            YANG modules parsed from yangFilesRootDir
+     * @param moduleResourcePathResolver
+     *            Function converting a local module to the packaged resource path
+     * @return collection of files that were generated from schema context
+     */
+    Collection<File> generateSources(EffectiveModelContext context, File outputBaseDir, Set<Module> currentModules,
+            Function<Module, Optional<String>> moduleResourcePathResolver) throws IOException;
+
+    /**
+     * Provided map contains all configuration that was set in pom for code
+     * generator in additionalConfiguration tag.
+     */
+    void setAdditionalConfig(Map<String, String> additionalConfiguration);
+
+    /**
+     * Provided folder is marked as resources and its content will be packaged
+     * in resulting jar. Feel free to add necessary resources.
+     */
+    void setResourceBaseDir(File resourceBaseDir);
+
+    /**
+     * Indicate import resolution mode this code generator requires. Default implementation indicates
+     * {@link ImportResolutionMode#REVISION_EXACT_OR_LATEST}.
+     *
+     * @return Required import resolution mode, null if the code generator does not care.
+     */
+    // FIXME: This is not really extensible, we should be returning a collection of acceptable modes, or have some sort
+    //        of two-step negotiation protocol:
+    //        - Optional<ImportResolutionMode> suggestImportResolutionMode();
+    //        - boolean isImportResolutionModeAcceptable(ImportResolutionMode);
+    //        Let's go with something hacky until we figure out exact requirements.
+    default ImportResolutionMode getImportResolutionMode() {
+        return ImportResolutionMode.REVISION_EXACT_OR_LATEST;
+    }
+}
diff --git a/yang/yang-maven-plugin-spi/src/main/java/org/opendaylight/yangtools/yang2sources/spi/BuildContextAware.java b/yang/yang-maven-plugin-spi/src/main/java/org/opendaylight/yangtools/yang2sources/spi/BuildContextAware.java
new file mode 100644 (file)
index 0000000..0a3a6ea
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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.yang2sources.spi;
+
+import org.sonatype.plexus.build.incremental.BuildContext;
+
+/**
+ * Interface implemented by CodeGenerator implementations which are integrated
+ * with BuildContext. These will have the build context injected before any
+ * attempt is made to generate files and should interact with the reactor solely
+ * through it.
+ */
+public interface BuildContextAware {
+    /**
+     * Set the build context to be used during the lifetime of this reactor.
+     *
+     * @param buildContext current build context reference.
+     */
+    void setBuildContext(BuildContext buildContext);
+}
diff --git a/yang/yang-maven-plugin-spi/src/main/java/org/opendaylight/yangtools/yang2sources/spi/MavenProjectAware.java b/yang/yang-maven-plugin-spi/src/main/java/org/opendaylight/yangtools/yang2sources/spi/MavenProjectAware.java
new file mode 100644 (file)
index 0000000..2787ed5
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.yang2sources.spi;
+
+import org.apache.maven.project.MavenProject;
+
+/**
+ * Bridge for plugins which need access to the underlying maven project.
+ */
+public interface MavenProjectAware {
+    /**
+     * Provided maven project object. Any additional information about current
+     * maven project can be accessed from it.
+     */
+    void setMavenProject(MavenProject project);
+}
diff --git a/yang/yang-maven-plugin-spi/src/test/java/org/opendaylight/yangtools/yang2sources/spi/CodeGeneratorTestImpl.java b/yang/yang-maven-plugin-spi/src/test/java/org/opendaylight/yangtools/yang2sources/spi/CodeGeneratorTestImpl.java
new file mode 100644 (file)
index 0000000..1dea842
--- /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.yang2sources.spi;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import org.apache.maven.project.MavenProject;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CodeGeneratorTestImpl implements BasicCodeGenerator, MavenProjectAware {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CodeGeneratorTestImpl.class);
+
+    @Override
+    public Collection<File> generateSources(final EffectiveModelContext context, final File outputBaseDir,
+            final Set<Module> currentModules, final Function<Module, Optional<String>> moduleResourcePathResolver)
+                    throws IOException {
+        LOG.debug("{} generateSources:context: {}", getClass().getCanonicalName(), context);
+        LOG.debug("{} generateSources:outputBaseDir: {}", getClass().getCanonicalName(), outputBaseDir);
+        LOG.debug("{} generateSources:currentModules: {}", getClass().getCanonicalName(), currentModules);
+        return null;
+    }
+
+    @Override
+    public void setAdditionalConfig(final Map<String, String> additionalConfiguration) {
+        LOG.debug("{} additionalConfig: {}", getClass().getCanonicalName(), additionalConfiguration);
+    }
+
+    @Override
+    public void setResourceBaseDir(final File resourceBaseDir) {
+        LOG.debug("{} resourceBaseDir: {}", getClass().getCanonicalName(), resourceBaseDir);
+    }
+
+    @Override
+    public void setMavenProject(final MavenProject project) {
+        LOG.debug("{} maven project: {}", getClass().getCanonicalName(), project);
+    }
+}
diff --git a/yang/yang-maven-plugin/pom.xml b/yang/yang-maven-plugin/pom.xml
new file mode 100644 (file)
index 0000000..d14e47a
--- /dev/null
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <groupId>org.opendaylight.odlparent</groupId>
+        <artifactId>odlparent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>yang-maven-plugin</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <packaging>maven-plugin</packaging>
+    <description>
+        This plugin is a wrapper for "yang to source code" generation.
+        It can be configured by a set of third-party code generators and resource providers.
+        For further info see available goals.
+        Sample usage:
+
+        TODO: add sample usage when finished
+    </description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+            <version>3.3.9</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-plugin-api</artifactId>
+            <version>3.3.9</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.plugin-tools</groupId>
+            <artifactId>maven-plugin-annotations</artifactId>
+            <version>3.4</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-artifact</artifactId>
+            <version>3.3.9</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-test-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-maven-plugin-spi</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.sonatype.plexus</groupId>
+            <artifactId>plexus-build-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-maven-plugin-spi</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-plugin-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </reporting>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <supportedProjectTypes>
+                        <supportedProjectType>maven-plugin</supportedProjectType>
+                    </supportedProjectTypes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-plugin-plugin</artifactId>
+                <configuration>
+                    <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>mojo-descriptor</id>
+                        <goals>
+                            <goal>descriptor</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ConfigArg.java b/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ConfigArg.java
new file mode 100644 (file)
index 0000000..5e6bb11
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.yang2sources.plugin;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.maven.project.MavenProject;
+
+/**
+ * Base complex configuration arguments.
+ */
+public abstract class ConfigArg {
+    private final File outputBaseDir;
+
+    public ConfigArg(final String outputBaseDir) {
+        this.outputBaseDir = outputBaseDir == null ? null : new File(outputBaseDir);
+    }
+
+    public File getOutputBaseDir(final MavenProject project) {
+        if (outputBaseDir == null) {
+            return null;
+        }
+        return outputBaseDir.isAbsolute() ? outputBaseDir : new File(project.getBasedir(), outputBaseDir.getPath());
+    }
+
+    public abstract void check();
+
+    /**
+     * Configuration argument for code generator class and output directory.
+     */
+    public static final class CodeGeneratorArg extends ConfigArg {
+
+        private final Map<String, String> additionalConfiguration = new HashMap<>();
+
+        private String codeGeneratorClass;
+        private File resourceBaseDir;
+
+        public CodeGeneratorArg() {
+            super(null);
+        }
+
+        public CodeGeneratorArg(final String codeGeneratorClass) {
+            this(codeGeneratorClass, null);
+        }
+
+        public CodeGeneratorArg(final String codeGeneratorClass, final String outputBaseDir) {
+            super(outputBaseDir);
+            this.codeGeneratorClass = codeGeneratorClass;
+        }
+
+        public CodeGeneratorArg(final String codeGeneratorClass, final String outputBaseDir,
+                final String resourceBaseDir) {
+            super(outputBaseDir);
+            this.codeGeneratorClass = codeGeneratorClass;
+            this.resourceBaseDir = new File(resourceBaseDir);
+        }
+
+        @Override
+        public void check() {
+            requireNonNull(codeGeneratorClass, "codeGeneratorClass for CodeGenerator cannot be null");
+        }
+
+        public String getCodeGeneratorClass() {
+            return codeGeneratorClass;
+        }
+
+        public File getResourceBaseDir(final MavenProject project) {
+            if (resourceBaseDir == null) {
+                // if it has not been set, use a default (correctly dealing with target/ VS target-ide)
+                return new GeneratedDirectories(project).getYangServicesDir();
+            }
+
+            return resourceBaseDir.isAbsolute() ? resourceBaseDir
+                        : new File(project.getBasedir(), resourceBaseDir.getPath());
+        }
+
+        public Map<String, String> getAdditionalConfiguration() {
+            return additionalConfiguration;
+        }
+    }
+}
diff --git a/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ContextHolder.java b/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ContextHolder.java
new file mode 100644 (file)
index 0000000..83d1c4c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.yang2sources.plugin;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Optional;
+import java.util.Set;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+final class ContextHolder implements Immutable {
+    private final EffectiveModelContext context;
+    private final Set<Module> modules;
+    private final Set<SourceIdentifier> sources;
+
+    ContextHolder(final EffectiveModelContext context, final Set<Module> modules, final Set<SourceIdentifier> sources) {
+        this.context = requireNonNull(context);
+        this.modules = ImmutableSet.copyOf(modules);
+        this.sources = ImmutableSet.copyOf(sources);
+    }
+
+    EffectiveModelContext getContext() {
+        return context;
+    }
+
+    Set<Module> getYangModules() {
+        return modules;
+    }
+
+    Optional<String> moduleToResourcePath(final Module mod) {
+        final SourceIdentifier id = Util.moduleToIdentifier(mod);
+        return sources.contains(id)
+                ? Optional.of("/" + YangToSourcesProcessor.META_INF_YANG_STRING_JAR + "/" + id.toYangFilename())
+                        : Optional.empty();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/GeneratedDirectories.java b/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/GeneratedDirectories.java
new file mode 100644 (file)
index 0000000..4937124
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2017 Red Hat, 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.yang2sources.plugin;
+
+import java.io.File;
+import org.apache.maven.project.MavenProject;
+
+/**
+ * Utility to obtain the correct path to generated directories.
+ * It's important that this does not hard-code "target/" anywhere, but uses
+ * ${project.build.directory}/, to make target-ide/ possible.
+ *
+ * @author Michael Vorburger.ch
+ */
+class GeneratedDirectories {
+
+    private final File targetGeneratedSources;
+
+    GeneratedDirectories(MavenProject project) {
+        this.targetGeneratedSources = new File(project.getBuild().getDirectory(), "generated-sources");
+    }
+
+    public File getYangServicesDir() {
+        return new File(targetGeneratedSources, "spi");
+    }
+
+    public File getYangDir() {
+        return new File(targetGeneratedSources, "yang");
+    }
+
+}
diff --git a/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ProcessorModuleReactor.java b/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ProcessorModuleReactor.java
new file mode 100644 (file)
index 0000000..62aea1b
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.yang2sources.plugin;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import com.google.common.io.CharStreams;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An incremental state reactor. Allows resolution of a SchemaContext based on a set of sources.
+ *
+ * @author Robert Varga
+ */
+final class ProcessorModuleReactor {
+    private static final Logger LOG = LoggerFactory.getLogger(ProcessorModuleReactor.class);
+
+    private final Map<SourceIdentifier, YangTextSchemaSource> modelsInProject;
+    private final Collection<ScannedDependency> dependencies;
+    private final YangParser parser;
+
+    ProcessorModuleReactor(final YangParser parser, final Collection<YangTextSchemaSource> modelsInProject,
+        final Collection<ScannedDependency> dependencies) {
+        this.parser = requireNonNull(parser);
+        this.modelsInProject = Maps.uniqueIndex(modelsInProject, YangTextSchemaSource::getIdentifier);
+        this.dependencies = ImmutableList.copyOf(dependencies);
+    }
+
+    ContextHolder toContext() throws IOException, YangParserException {
+        for (YangTextSchemaSource source : toUniqueSources(dependencies)) {
+            // This source is coming from a dependency:
+            // - its identifier should be accurate, as it should have been processed into a file with accurate name
+            // - it is not required to be parsed, hence we add it just as a library source
+            parser.addLibSource(source);
+        }
+
+        final EffectiveModelContext schemaContext = verifyNotNull(parser.buildEffectiveModel());
+
+        final Set<Module> modules = new HashSet<>();
+        for (Module module : schemaContext.getModules()) {
+            final SourceIdentifier modId = Util.moduleToIdentifier(module);
+            LOG.debug("Looking for source {}", modId);
+            if (modelsInProject.containsKey(modId)) {
+                LOG.debug("Module {} belongs to current project", module);
+                modules.add(module);
+
+                for (Module sub : module.getSubmodules()) {
+                    final SourceIdentifier subId = Util.moduleToIdentifier(sub);
+                    if (!modelsInProject.containsKey(subId)) {
+                        LOG.warn("Submodule {} not found in input files", sub);
+                    }
+                }
+            }
+        }
+
+        return new ContextHolder(schemaContext, modules, modelsInProject.keySet());
+    }
+
+    Collection<YangTextSchemaSource> getModelsInProject() {
+        return modelsInProject.values();
+    }
+
+    @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE",
+        justification = "https://github.com/spotbugs/spotbugs/issues/600")
+    private static Collection<YangTextSchemaSource> toUniqueSources(final Collection<ScannedDependency> dependencies)
+            throws IOException {
+        final Map<String, YangTextSchemaSource> byContent = new HashMap<>();
+
+        for (ScannedDependency dependency : dependencies) {
+            for (YangTextSchemaSource s : dependency.sources()) {
+                try (Reader reader = s.asCharSource(StandardCharsets.UTF_8).openStream()) {
+                    final String contents = CharStreams.toString(reader);
+                    byContent.putIfAbsent(contents, s);
+                }
+            }
+        }
+        return byContent.values();
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("sources", modelsInProject.keySet()).add("parser", parser)
+                .toString();
+    }
+}
diff --git a/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ScannedDependency.java b/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/ScannedDependency.java
new file mode 100644 (file)
index 0000000..cab0d26
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.yang2sources.plugin;
+
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.common.YangConstants.RFC6020_YANG_FILE_EXTENSION;
+import static org.opendaylight.yangtools.yang2sources.plugin.YangToSourcesProcessor.META_INF_YANG_STRING;
+import static org.opendaylight.yangtools.yang2sources.plugin.YangToSourcesProcessor.META_INF_YANG_STRING_JAR;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.ByteSource;
+import com.google.common.io.ByteStreams;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NonNullByDefault
+abstract class ScannedDependency {
+    private static final class Single extends ScannedDependency {
+
+        Single(final File file) {
+            super(file);
+        }
+
+        @Override
+        Collection<YangTextSchemaSource> sources() {
+            return ImmutableList.of(YangTextSchemaSource.forFile(file()));
+        }
+    }
+
+    private static final class Zip extends ScannedDependency {
+        private final Set<String> entryNames;
+
+        Zip(final File file, final Collection<String> entryNames) {
+            super(file);
+            this.entryNames = ImmutableSet.copyOf(entryNames);
+        }
+
+        @Override
+        Collection<YangTextSchemaSource> sources() throws IOException {
+            final Collection<YangTextSchemaSource> result = new ArrayList<>(entryNames.size());
+
+            try (ZipFile zip = new ZipFile(file())) {
+                for (String entryName : entryNames) {
+                    final ZipEntry entry = requireNonNull(zip.getEntry(entryName));
+
+                    result.add(YangTextSchemaSource.delegateForByteSource(
+                        entryName.substring(entryName.lastIndexOf('/') + 1),
+                        ByteSource.wrap(ByteStreams.toByteArray(zip.getInputStream(entry)))));
+                }
+            }
+
+            return result;
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(ScannedDependency.class);
+
+    private final File file;
+
+    ScannedDependency(final File file) {
+        this.file = requireNonNull(file);
+    }
+
+    static Collection<ScannedDependency> scanDependencies(final MavenProject project) throws IOException {
+        final Collection<File> filesOnCp = Util.getClassPath(project);
+        LOG.debug("{} Searching for YANG files in dependencies: {}", YangToSourcesProcessor.LOG_PREFIX, filesOnCp);
+        LOG.debug("{} Searching for YANG files in {} dependencies", YangToSourcesProcessor.LOG_PREFIX,
+            filesOnCp.size());
+
+        final List<ScannedDependency> result = new ArrayList<>();
+        for (File file : filesOnCp) {
+            // is it jar file or directory?
+            if (file.isDirectory()) {
+                final File yangDir = new File(file, META_INF_YANG_STRING);
+                if (yangDir.exists() && yangDir.isDirectory()) {
+                    result.addAll(scanDirectory(yangDir));
+                }
+            } else {
+                result.addAll(scanZipFile(file));
+            }
+        }
+        return result;
+    }
+
+    private static Collection<ScannedDependency> scanDirectory(final File yangDir) {
+        return Arrays.stream(yangDir.listFiles(
+            (dir, name) -> name.endsWith(RFC6020_YANG_FILE_EXTENSION) && new File(dir, name).isFile()))
+                .map(Single::new).collect(ImmutableList.toImmutableList());
+    }
+
+    private static Collection<ScannedDependency> scanZipFile(final File zipFile) throws IOException {
+        final Collection<String> entryNames;
+        try (ZipFile zip = new ZipFile(zipFile)) {
+            entryNames = zip.stream().filter(entry -> {
+                final String entryName = entry.getName();
+                return entryName.startsWith(META_INF_YANG_STRING_JAR) && !entry.isDirectory()
+                        && entryName.endsWith(RFC6020_YANG_FILE_EXTENSION);
+            }).map(ZipEntry::getName).collect(ImmutableList.toImmutableList());
+        }
+
+        return entryNames.isEmpty() ? ImmutableList.of() : ImmutableList.of(new Zip(zipFile, entryNames));
+    }
+
+    final File file() {
+        return file;
+    }
+
+    abstract Collection<YangTextSchemaSource> sources() throws IOException;
+}
diff --git a/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/Util.java b/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/Util.java
new file mode 100644 (file)
index 0000000..c2c5252
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * 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.yang2sources.plugin;
+
+import static org.opendaylight.yangtools.yang2sources.plugin.YangToSourcesProcessor.LOG_PREFIX;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
+import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.repository.RepositorySystem;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class Util {
+
+    /**
+     * It isn't desirable to create instances of this class.
+     */
+    private Util() {
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(Util.class);
+
+    static List<File> getClassPath(final MavenProject project) {
+        final List<File> dependencies = new ArrayList<>();
+        for (Artifact element : project.getArtifacts()) {
+            File asFile = element.getFile();
+            if (isJar(asFile) || asFile.isDirectory()) {
+                dependencies.add(asFile);
+            }
+        }
+        return dependencies;
+    }
+
+    /**
+     * Read current project dependencies and check if it don't grab incorrect
+     * artifacts versions which could be in conflict with plugin dependencies.
+     *
+     * @param project
+     *            current project
+     * @param repoSystem
+     *            repository system
+     * @param localRepo
+     *            local repository
+     * @param remoteRepos
+     *            remote repositories
+     */
+    static void checkClasspath(final MavenProject project, final RepositorySystem repoSystem,
+            final ArtifactRepository localRepo, final List<ArtifactRepository> remoteRepos) {
+        Plugin plugin = project.getPlugin(YangToSourcesMojo.PLUGIN_NAME);
+        if (plugin == null) {
+            LOG.warn("{} {} not found, dependencies version check skipped", LOG_PREFIX, YangToSourcesMojo.PLUGIN_NAME);
+        } else {
+            Map<Artifact, Collection<Artifact>> pluginDependencies = new HashMap<>();
+            getPluginTransitiveDependencies(plugin, pluginDependencies, repoSystem, localRepo, remoteRepos);
+
+            Set<Artifact> projectDependencies = project.getDependencyArtifacts();
+            for (Map.Entry<Artifact, Collection<Artifact>> entry : pluginDependencies.entrySet()) {
+                checkArtifact(entry.getKey(), projectDependencies);
+                for (Artifact dependency : entry.getValue()) {
+                    checkArtifact(dependency, projectDependencies);
+                }
+            }
+        }
+    }
+
+    /**
+     * Read transitive dependencies of given plugin and store them in map.
+     *
+     * @param plugin
+     *            plugin to read
+     * @param map
+     *            map, where founded transitive dependencies will be stored
+     * @param repoSystem
+     *            repository system
+     * @param localRepository
+     *            local repository
+     * @param remoteRepos
+     *            list of remote repositories
+     */
+    private static void getPluginTransitiveDependencies(final Plugin plugin,
+            final Map<Artifact, Collection<Artifact>> map, final RepositorySystem repoSystem,
+            final ArtifactRepository localRepository, final List<ArtifactRepository> remoteRepos) {
+
+        List<Dependency> pluginDependencies = plugin.getDependencies();
+        for (Dependency dep : pluginDependencies) {
+            Artifact artifact = repoSystem.createDependencyArtifact(dep);
+
+            ArtifactResolutionRequest request = new ArtifactResolutionRequest();
+            request.setArtifact(artifact);
+            request.setResolveTransitively(true);
+            request.setLocalRepository(localRepository);
+            request.setRemoteRepositories(remoteRepos);
+
+            ArtifactResolutionResult result = repoSystem.resolve(request);
+            Set<Artifact> pluginDependencyDependencies = result.getArtifacts();
+            map.put(artifact, pluginDependencyDependencies);
+        }
+    }
+
+    /**
+     * Check artifact against collection of dependencies. If collection contains
+     * artifact with same groupId and artifactId, but different version, logs a
+     * warning.
+     *
+     * @param artifact
+     *            artifact to check
+     * @param dependencies
+     *            collection of dependencies
+     */
+    private static void checkArtifact(final Artifact artifact, final Collection<Artifact> dependencies) {
+        for (org.apache.maven.artifact.Artifact d : dependencies) {
+            if (artifact.getGroupId().equals(d.getGroupId()) && artifact.getArtifactId().equals(d.getArtifactId())) {
+                if (!artifact.getVersion().equals(d.getVersion())) {
+                    LOG.warn("{} Dependency resolution conflict:", LOG_PREFIX);
+                    LOG.warn("{} '{}' dependency [{}] has different version than one declared in current project [{}]"
+                            + ". It is recommended to fix this problem because it may cause compilation errors.",
+                            LOG_PREFIX, YangToSourcesMojo.PLUGIN_NAME, artifact, d);
+                }
+            }
+        }
+    }
+
+    private static boolean isJar(final File element) {
+        return element.isFile() && element.getName().endsWith(".jar");
+    }
+
+    static SourceIdentifier moduleToIdentifier(final Module module) {
+        return RevisionSourceIdentifier.create(module.getName(), module.getRevision());
+    }
+}
diff --git a/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangProvider.java b/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangProvider.java
new file mode 100644 (file)
index 0000000..9d4c972
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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.yang2sources.plugin;
+
+import com.google.common.io.Files;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import org.apache.maven.model.Resource;
+import org.apache.maven.project.MavenProject;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class YangProvider {
+    private static final class Default extends YangProvider {
+        private static final Logger LOG = LoggerFactory.getLogger(Default.class);
+
+        @Override
+        void addYangsToMetaInf(final MavenProject project, final Collection<YangTextSchemaSource> modelsInProject)
+                throws IOException {
+
+            final File generatedYangDir = new GeneratedDirectories(project).getYangDir();
+            LOG.debug("Generated dir {}", generatedYangDir);
+
+            // copy project's src/main/yang/*.yang to ${project.builddir}/generated-sources/yang/META-INF/yang/
+            // This honors setups like a Eclipse-profile derived one
+            final File withMetaInf = new File(generatedYangDir, YangToSourcesProcessor.META_INF_YANG_STRING);
+
+            for (YangTextSchemaSource source : modelsInProject) {
+                final String fileName = source.getIdentifier().toYangFilename();
+                final File file = new File(withMetaInf, fileName);
+                Files.createParentDirs(file);
+
+                source.copyTo(Files.asByteSink(file));
+                LOG.debug("Created file {} for {}", file, source.getIdentifier());
+            }
+
+            setResource(generatedYangDir, project);
+            LOG.debug("{} YANG files marked as resources: {}", YangToSourcesProcessor.LOG_PREFIX, generatedYangDir);
+        }
+    }
+
+    private static final YangProvider DEFAULT = new Default();
+
+    static YangProvider getInstance() {
+        return DEFAULT;
+    }
+
+    abstract void addYangsToMetaInf(MavenProject project, Collection<YangTextSchemaSource> modelsInProject)
+            throws IOException;
+
+    static void setResource(final File targetYangDir, final MavenProject project) {
+        Resource res = new Resource();
+        res.setDirectory(targetYangDir.getPath());
+        project.addResource(res);
+    }
+}
diff --git a/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangSourceFromDependency.java b/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangSourceFromDependency.java
new file mode 100644 (file)
index 0000000..009382b
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 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.yang2sources.plugin;
+
+import com.google.common.io.ByteSource;
+
+abstract class YangSourceFromDependency extends ByteSource {
+
+    /**
+     * Return a description for this dependency, to be used for error output when
+     * working with the dependency.
+     */
+    abstract String getDescription();
+
+    @Override
+    public final String toString() {
+        return getDescription();
+    }
+
+}
diff --git a/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesMojo.java b/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesMojo.java
new file mode 100644 (file)
index 0000000..a2d83c1
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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.yang2sources.plugin;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.repository.RepositorySystem;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
+import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
+import org.sonatype.plexus.build.incremental.BuildContext;
+
+/**
+ * Generate sources from yang files using user provided set of
+ * {@link BasicCodeGenerator}s. Steps of this process:
+ * <ol>
+ * <li>List yang files from {@link #yangFilesRootDir}</li>
+ * <li>Process yang files using Yang Parser</li>
+ * <li>For each {@link BasicCodeGenerator} from {@link #codeGenerators}:
+ * <ol>
+ * <li>Instantiate using default constructor</li>
+ * <li>Call {@link BasicCodeGenerator#generateSources(EffectiveModelContext, File, Set, Function)}</li>
+ * </ol></li>
+ * </ol>
+ */
+@Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES,
+    requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true)
+public final class YangToSourcesMojo extends AbstractMojo {
+    public static final String PLUGIN_NAME = "org.opendaylight.yangtools:yang-maven-plugin";
+
+    /**
+     * Classes implementing {@link BasicCodeGenerator} interface. An instance will be
+     * created out of every class using default constructor. Method {@link
+     * BasicCodeGenerator#generateSources(SchemaContext, File, Set)} will be called on every instance.
+     */
+    @Parameter(required = false)
+    private CodeGeneratorArg[] codeGenerators;
+
+    /**
+     * Source directory that will be recursively searched for yang files (ending
+     * with .yang suffix).
+     */
+    @Parameter(required = false)
+    // defaults to ${basedir}/src/main/yang
+    private String yangFilesRootDir;
+
+    @Parameter(required = false)
+    private String[] excludeFiles;
+
+    @Parameter(property = "project", required = true, readonly = true)
+    private MavenProject project;
+
+    @Parameter(property = "inspectDependencies")
+    private boolean inspectDependencies;
+
+    @Component
+    @VisibleForTesting
+    BuildContext buildContext;
+
+    private YangToSourcesProcessor yangToSourcesProcessor;
+
+    @Component
+    private RepositorySystem repoSystem;
+
+    @Parameter(readonly = true, defaultValue = "${localRepository}")
+    private ArtifactRepository localRepository;
+
+    @Parameter(readonly = true, defaultValue = "${project.remoteArtifactRepositories}")
+    private List<ArtifactRepository> remoteRepos;
+
+    // When set to "true", then the execution of the plugin is disabled
+    @Parameter(property = "yang.skip")
+    private String yangSkip;
+
+    public YangToSourcesMojo() {
+
+    }
+
+    @VisibleForTesting
+    YangToSourcesMojo(final YangToSourcesProcessor processor) {
+        this.yangToSourcesProcessor = processor;
+    }
+
+    public void setProject(final MavenProject project) {
+        this.project = project;
+    }
+
+    @Override
+    @SuppressFBWarnings(value = "UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", justification = "yangFilesRootDir")
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        Util.checkClasspath(project, repoSystem, localRepository, remoteRepos);
+
+        if (yangToSourcesProcessor == null) {
+            List<CodeGeneratorArg> codeGeneratorArgs = processCodeGenerators(codeGenerators);
+
+            // defaults to ${basedir}/src/main/yang
+            File yangFilesRootFile = processYangFilesRootDir(yangFilesRootDir, project.getBasedir());
+            Collection<File> excludedFiles = processExcludeFiles(excludeFiles, yangFilesRootFile);
+
+            yangToSourcesProcessor = new YangToSourcesProcessor(buildContext, yangFilesRootFile,
+                    excludedFiles, codeGeneratorArgs, project, inspectDependencies);
+        }
+        yangToSourcesProcessor.conditionalExecute("true".equals(yangSkip));
+    }
+
+    private static List<CodeGeneratorArg> processCodeGenerators(final CodeGeneratorArg[] codeGenerators) {
+        List<CodeGeneratorArg> codeGeneratorArgs;
+        if (codeGenerators == null) {
+            codeGeneratorArgs = Collections.emptyList();
+        } else {
+            codeGeneratorArgs = Arrays.asList(codeGenerators);
+        }
+        return codeGeneratorArgs;
+    }
+
+    private static File processYangFilesRootDir(final String yangFilesRootDir, final File baseDir) {
+        File yangFilesRootFile;
+        if (yangFilesRootDir == null) {
+            yangFilesRootFile = new File(baseDir, "src" + File.separator + "main" + File.separator + "yang");
+        } else {
+            File file = new File(yangFilesRootDir);
+            if (file.isAbsolute()) {
+                yangFilesRootFile = file;
+            } else {
+                yangFilesRootFile = new File(baseDir, file.getPath());
+            }
+        }
+        return yangFilesRootFile;
+    }
+
+    private static Collection<File> processExcludeFiles(final String[] excludeFiles, final File baseDir) {
+        if (excludeFiles == null) {
+            return ImmutableList.of();
+        }
+
+        return Collections2.transform(Arrays.asList(excludeFiles), f -> new File(baseDir, f));
+    }
+
+}
diff --git a/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java b/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java
new file mode 100644 (file)
index 0000000..2879f03
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * 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.yang2sources.plugin;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.project.MavenProject;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
+import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
+import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
+import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator.ImportResolutionMode;
+import org.opendaylight.yangtools.yang2sources.spi.BuildContextAware;
+import org.opendaylight.yangtools.yang2sources.spi.MavenProjectAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonatype.plexus.build.incremental.BuildContext;
+import org.sonatype.plexus.build.incremental.DefaultBuildContext;
+
+class YangToSourcesProcessor {
+    private static final Logger LOG = LoggerFactory.getLogger(YangToSourcesProcessor.class);
+    private static final YangParserFactory DEFAULT_PARSER_FACTORY;
+
+    static {
+        final Iterator<YangParserFactory> it = ServiceLoader.load(YangParserFactory.class).iterator();
+        checkState(it.hasNext(), "Failed to find a YangParserFactory implementation");
+        DEFAULT_PARSER_FACTORY = it.next();
+    }
+
+    static final String LOG_PREFIX = "yang-to-sources:";
+    private static final String META_INF_STR = "META-INF";
+    private static final String YANG_STR = "yang";
+
+    static final String META_INF_YANG_STRING = META_INF_STR + File.separator + YANG_STR;
+    static final String META_INF_YANG_STRING_JAR = META_INF_STR + "/" + YANG_STR;
+    static final String META_INF_YANG_SERVICES_STRING_JAR = META_INF_STR + "/" + "services";
+
+    private final YangParserFactory parserFactory;
+    private final File yangFilesRootDir;
+    private final Set<File> excludedFiles;
+    private final List<CodeGeneratorArg> codeGeneratorArgs;
+    private final MavenProject project;
+    private final boolean inspectDependencies;
+    private final BuildContext buildContext;
+    private final YangProvider yangProvider;
+
+    private YangToSourcesProcessor(final BuildContext buildContext, final File yangFilesRootDir,
+            final Collection<File> excludedFiles, final List<CodeGeneratorArg> codeGenerators,
+            final MavenProject project, final boolean inspectDependencies, final YangProvider yangProvider) {
+        this.buildContext = requireNonNull(buildContext, "buildContext");
+        this.yangFilesRootDir = requireNonNull(yangFilesRootDir, "yangFilesRootDir");
+        this.excludedFiles = ImmutableSet.copyOf(excludedFiles);
+        this.codeGeneratorArgs = ImmutableList.copyOf(codeGenerators);
+        this.project = requireNonNull(project);
+        this.inspectDependencies = inspectDependencies;
+        this.yangProvider = requireNonNull(yangProvider);
+        this.parserFactory = DEFAULT_PARSER_FACTORY;
+    }
+
+    @VisibleForTesting
+    YangToSourcesProcessor(final File yangFilesRootDir, final Collection<File> excludedFiles,
+            final List<CodeGeneratorArg> codeGenerators, final MavenProject project, final boolean inspectDependencies,
+            final YangProvider yangProvider) {
+        this(new DefaultBuildContext(), yangFilesRootDir, excludedFiles, codeGenerators, project,
+                inspectDependencies, yangProvider);
+    }
+
+    YangToSourcesProcessor(final BuildContext buildContext, final File yangFilesRootDir,
+                final Collection<File> excludedFiles, final List<CodeGeneratorArg> codeGenerators,
+                final MavenProject project, final boolean inspectDependencies) {
+        this(buildContext, yangFilesRootDir, excludedFiles, codeGenerators, project, inspectDependencies,
+            YangProvider.getInstance());
+    }
+
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        conditionalExecute(false);
+    }
+
+    void conditionalExecute(final boolean skip) throws MojoExecutionException, MojoFailureException {
+        /*
+         * Collect all files which affect YANG context. This includes all
+         * files in current project and optionally any jars/files in the
+         * dependencies.
+         */
+        final List<File> yangFilesInProject;
+        try {
+            yangFilesInProject = listFiles(yangFilesRootDir, excludedFiles);
+        } catch (IOException e) {
+            throw new MojoFailureException("Failed to list project files", e);
+        }
+
+        if (yangFilesInProject.isEmpty()) {
+            // No files to process, skip.
+            LOG.info("{} No input files found", LOG_PREFIX);
+            return;
+        }
+
+        // We need to instantiate all code generators to determine required import resolution mode
+        final List<Entry<CodeGeneratorArg, BasicCodeGenerator>> codeGenerators = instantiateGenerators();
+        final StatementParserMode importMode = determineRequiredImportMode(codeGenerators);
+        final Optional<ProcessorModuleReactor> optReactor = createReactor(importMode, yangFilesInProject);
+        if (!optReactor.isPresent()) {
+            return;
+        }
+
+        final ProcessorModuleReactor reactor = optReactor.get();
+        if (!skip) {
+            final Stopwatch watch = Stopwatch.createStarted();
+            final ContextHolder holder;
+
+            try {
+                holder = reactor.toContext();
+            } catch (YangParserException e) {
+                throw new MojoFailureException("Failed to process reactor " + reactor, e);
+            } catch (IOException e) {
+                throw new MojoExecutionException("Failed to read reactor " + reactor, e);
+            }
+
+            LOG.info("{} {} YANG models processed in {}", LOG_PREFIX, holder.getContext().getModules().size(), watch);
+            generateSources(holder, codeGenerators);
+        } else {
+            LOG.info("{} Skipping YANG code generation because property yang.skip is true", LOG_PREFIX);
+        }
+
+        // add META_INF/yang
+        final Collection<YangTextSchemaSource> models = reactor.getModelsInProject();
+        try {
+            yangProvider.addYangsToMetaInf(project, models);
+        } catch (IOException e) {
+            throw new MojoExecutionException("Failed write model files for " + models, e);
+        }
+
+        // add META_INF/services
+        File generatedServicesDir = new GeneratedDirectories(project).getYangServicesDir();
+        YangProvider.setResource(generatedServicesDir, project);
+        LOG.debug("{} Yang services files from: {} marked as resources: {}", LOG_PREFIX, generatedServicesDir,
+            META_INF_YANG_SERVICES_STRING_JAR);
+    }
+
+    private static StatementParserMode determineRequiredImportMode(
+            final Collection<Entry<CodeGeneratorArg, BasicCodeGenerator>> codeGenerators) throws MojoFailureException {
+        ImportResolutionMode requestedMode = null;
+        BasicCodeGenerator requestingGenerator = null;
+
+        for (Entry<CodeGeneratorArg, BasicCodeGenerator> entry : codeGenerators) {
+            final BasicCodeGenerator generator = entry.getValue();
+            final ImportResolutionMode mode = generator.getImportResolutionMode();
+            if (mode == null) {
+                // the generator does not care about the mode
+                continue;
+            }
+
+            if (requestedMode == null) {
+                // No mode yet, we have just determined it
+                requestedMode = mode;
+                requestingGenerator = generator;
+                continue;
+            }
+
+            if (mode != requestedMode) {
+                throw new MojoFailureException(String.format(
+                    "Import resolution mode conflict between %s (%s) and %s (%s)", requestingGenerator, requestedMode,
+                    generator, mode));
+            }
+        }
+
+        if (requestedMode == null) {
+            return StatementParserMode.DEFAULT_MODE;
+        }
+        switch (requestedMode) {
+            case REVISION_EXACT_OR_LATEST:
+                return StatementParserMode.DEFAULT_MODE;
+            case SEMVER_LATEST:
+                return StatementParserMode.SEMVER_MODE;
+            default:
+                throw new IllegalStateException("Unhandled import resolution mode " + requestedMode);
+        }
+    }
+
+    private List<Entry<CodeGeneratorArg, BasicCodeGenerator>> instantiateGenerators() throws MojoExecutionException {
+        final List<Entry<CodeGeneratorArg, BasicCodeGenerator>> generators = new ArrayList<>(codeGeneratorArgs.size());
+        for (CodeGeneratorArg arg : codeGeneratorArgs) {
+            arg.check();
+
+            final BasicCodeGenerator generator;
+            try {
+                generator = getInstance(arg.getCodeGeneratorClass(), BasicCodeGenerator.class);
+            } catch (ReflectiveOperationException e) {
+                throw new MojoExecutionException("Failed to instantiate code generator "
+                        + arg.getCodeGeneratorClass(), e);
+            }
+
+            LOG.info("{} Code generator instantiated from {}", LOG_PREFIX, arg.getCodeGeneratorClass());
+            generators.add(new SimpleImmutableEntry<>(arg, generator));
+        }
+
+        return generators;
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private Optional<ProcessorModuleReactor> createReactor(final StatementParserMode parserMode,
+            final List<File> yangFilesInProject) throws MojoExecutionException {
+        LOG.info("{} Inspecting {}", LOG_PREFIX, yangFilesRootDir);
+
+        try {
+            final Collection<File> allFiles = new ArrayList<>(yangFilesInProject);
+            final Collection<ScannedDependency> dependencies;
+            if (inspectDependencies) {
+                dependencies = new ArrayList<>();
+                final Stopwatch watch = Stopwatch.createStarted();
+                ScannedDependency.scanDependencies(project).forEach(dep -> {
+                    allFiles.add(dep.file());
+                    dependencies.add(dep);
+                });
+                LOG.info("{} Found {} dependencies in {}", LOG_PREFIX, dependencies.size(), watch);
+            } else {
+                dependencies = ImmutableList.of();
+            }
+
+            /*
+             * Check if any of the listed files changed. If no changes occurred,
+             * simply return null, which indicates and of execution.
+             */
+            if (!allFiles.stream().anyMatch(buildContext::hasDelta)) {
+                LOG.info("{} None of {} input files changed", LOG_PREFIX, allFiles.size());
+                return Optional.empty();
+            }
+
+            final YangParser parser = parserFactory.createParser(parserMode);
+            final List<YangTextSchemaSource> sourcesInProject = new ArrayList<>(yangFilesInProject.size());
+            for (final File f : yangFilesInProject) {
+                final YangTextSchemaSource textSource = YangTextSchemaSource.forFile(f);
+                final ASTSchemaSource astSource = TextToASTTransformer.transformText(textSource);
+
+                parser.addSource(astSource);
+
+                if (!astSource.getIdentifier().equals(textSource.getIdentifier())) {
+                    // AST indicates a different source identifier, make sure we use that
+                    sourcesInProject.add(YangTextSchemaSource.delegateForByteSource(astSource.getIdentifier(),
+                        textSource));
+                } else {
+                    sourcesInProject.add(textSource);
+                }
+            }
+
+            LOG.debug("Found project files: {}", yangFilesInProject);
+            LOG.info("{} Project model files found: {}", LOG_PREFIX, yangFilesInProject.size());
+
+            final ProcessorModuleReactor reactor = new ProcessorModuleReactor(parser, sourcesInProject, dependencies);
+            LOG.debug("Initialized reactor {} with {}", reactor, yangFilesInProject);
+            return Optional.of(reactor);
+        } catch (IOException | SchemaSourceException | YangSyntaxErrorException | RuntimeException e) {
+            // MojoExecutionException is thrown since execution cannot continue
+            LOG.error("{} Unable to parse YANG files from {}", LOG_PREFIX, yangFilesRootDir, e);
+            Throwable rootCause = Throwables.getRootCause(e);
+            throw new MojoExecutionException(LOG_PREFIX + " Unable to parse YANG files from " + yangFilesRootDir,
+                rootCause);
+        }
+    }
+
+    private static List<File> listFiles(final File root, final Collection<File> excludedFiles)
+            throws IOException {
+        if (!root.isDirectory()) {
+            LOG.warn("{} YANG source directory {} not found. No code will be generated.", LOG_PREFIX, root);
+            return ImmutableList.of();
+        }
+
+        return Files.walk(root.toPath()).map(Path::toFile).filter(File::isFile).filter(f -> {
+            if (excludedFiles.contains(f)) {
+                LOG.info("{} YANG file excluded {}", LOG_PREFIX, f);
+                return false;
+            }
+            return true;
+        }).filter(f -> f.getName().endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION)).collect(Collectors.toList());
+    }
+
+    /**
+     * Call generate on every generator from plugin configuration.
+     */
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private void generateSources(final ContextHolder context,
+            final Collection<Entry<CodeGeneratorArg, BasicCodeGenerator>> generators) throws MojoFailureException {
+        if (generators.isEmpty()) {
+            LOG.warn("{} No code generators provided", LOG_PREFIX);
+            return;
+        }
+
+        final Map<String, String> thrown = new HashMap<>();
+        for (Entry<CodeGeneratorArg, BasicCodeGenerator> entry : generators) {
+            final String codeGeneratorClass = entry.getKey().getCodeGeneratorClass();
+
+            try {
+                generateSourcesWithOneGenerator(context, entry.getKey(), entry.getValue());
+            } catch (Exception e) {
+                // try other generators, exception will be thrown after
+                LOG.error("{} Unable to generate sources with {} generator", LOG_PREFIX, codeGeneratorClass, e);
+                thrown.put(codeGeneratorClass, e.getClass().getCanonicalName());
+            }
+        }
+
+        if (!thrown.isEmpty()) {
+            LOG.error("{} One or more code generators failed, including failed list(generatorClass=exception) {}",
+                LOG_PREFIX, thrown);
+            throw new MojoFailureException(LOG_PREFIX
+                + " One or more code generators failed, including failed list(generatorClass=exception) " + thrown);
+        }
+    }
+
+    /**
+     * Complete initialization of a code generator and invoke it.
+     */
+    private void generateSourcesWithOneGenerator(final ContextHolder context, final CodeGeneratorArg codeGeneratorCfg,
+            final BasicCodeGenerator codeGenerator) throws IOException {
+
+        final File outputDir = requireNonNull(codeGeneratorCfg.getOutputBaseDir(project),
+            "outputBaseDir is null. Please provide a valid outputBaseDir value in pom.xml");
+
+        project.addCompileSourceRoot(outputDir.getAbsolutePath());
+
+        LOG.info("{} Sources will be generated to {}", LOG_PREFIX, outputDir);
+        LOG.debug("{} Project root dir is {}", LOG_PREFIX, project.getBasedir());
+        LOG.debug("{} Additional configuration picked up for : {}: {}", LOG_PREFIX, codeGeneratorCfg
+                        .getCodeGeneratorClass(), codeGeneratorCfg.getAdditionalConfiguration());
+
+        if (codeGenerator instanceof BuildContextAware) {
+            ((BuildContextAware)codeGenerator).setBuildContext(buildContext);
+        }
+        if (codeGenerator instanceof MavenProjectAware) {
+            ((MavenProjectAware)codeGenerator).setMavenProject(project);
+        }
+        codeGenerator.setAdditionalConfig(codeGeneratorCfg.getAdditionalConfiguration());
+
+        File resourceBaseDir = codeGeneratorCfg.getResourceBaseDir(project);
+        YangProvider.setResource(resourceBaseDir, project);
+        codeGenerator.setResourceBaseDir(resourceBaseDir);
+        LOG.debug("{} Folder: {} marked as resources for generator: {}", LOG_PREFIX, resourceBaseDir,
+                codeGeneratorCfg.getCodeGeneratorClass());
+
+        if (outputDir.exists()) {
+            Files.walk(outputDir.toPath()).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
+            LOG.info("{} Succesfully deleted output directory {}", LOG_PREFIX, outputDir);
+        }
+        final Stopwatch watch = Stopwatch.createStarted();
+        Collection<File> generated = codeGenerator.generateSources(context.getContext(), outputDir,
+            context.getYangModules(), context::moduleToResourcePath);
+
+        LOG.debug("{} Sources generated by {}: {}", LOG_PREFIX, codeGeneratorCfg.getCodeGeneratorClass(), generated);
+        LOG.info("{} Sources generated by {}: {} in {}", LOG_PREFIX, codeGeneratorCfg.getCodeGeneratorClass(),
+                generated == null ? 0 : generated.size(), watch);
+    }
+
+    /**
+     * Instantiate object from fully qualified class name.
+     */
+    private static <T> T getInstance(final String codeGeneratorClass, final Class<T> baseType)
+            throws ReflectiveOperationException {
+        final Class<?> clazz = Class.forName(codeGeneratorClass);
+        checkArgument(baseType.isAssignableFrom(clazz), "Code generator %s has to implement %s", clazz, baseType);
+        return baseType.cast(clazz.getConstructor().newInstance());
+    }
+}
diff --git a/yang/yang-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml b/yang/yang-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml
new file mode 100644 (file)
index 0000000..681d336
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2017 Pantheon Technologies, s.r.o. 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
+-->
+<lifecycleMappingMetadata>
+    <pluginExecutions>
+        <pluginExecution>
+            <pluginExecutionFilter>
+                <goals>
+                    <goal>generate-sources</goal>
+                </goals>
+            </pluginExecutionFilter>
+            <action>
+                <execute>
+                    <runOnIncremental>false</runOnIncremental>
+                    <runOnConfiguration>true</runOnConfiguration>
+                </execute>
+            </action>
+        </pluginExecution>
+    </pluginExecutions>
+</lifecycleMappingMetadata>
diff --git a/yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/GenerateSourcesTest.java b/yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/GenerateSourcesTest.java
new file mode 100644 (file)
index 0000000..491a518
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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.yang2sources.plugin;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import org.apache.maven.model.Build;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.project.MavenProject;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
+import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
+import org.opendaylight.yangtools.yang2sources.spi.MavenProjectAware;
+
+public class GenerateSourcesTest {
+
+    private String yang;
+    private YangToSourcesMojo mojo;
+    private File outDir;
+
+    @Mock
+    private MavenProject project;
+
+    @Mock
+    private Build build;
+
+    @Mock
+    private Plugin plugin;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        yang = new File(getClass().getResource("/yang/mock.yang").toURI()).getParent();
+        outDir = new File("/outputDir");
+        final YangProvider mock = mock(YangProvider.class);
+        doNothing().when(mock).addYangsToMetaInf(any(MavenProject.class), any(Collection.class));
+
+        final YangToSourcesProcessor processor = new YangToSourcesProcessor(new File(this.yang), ImmutableList.of(),
+                ImmutableList.of(new CodeGeneratorArg(GeneratorMock.class.getName(), "outputDir")), this.project, false,
+                mock);
+        this.mojo = new YangToSourcesMojo(processor);
+        doReturn(new File("")).when(this.project).getBasedir();
+        doReturn("target/").when(this.build).getDirectory();
+        doReturn(this.build).when(this.project).getBuild();
+        doReturn(Collections.emptyList()).when(this.plugin).getDependencies();
+        doReturn(this.plugin).when(this.project).getPlugin(YangToSourcesMojo.PLUGIN_NAME);
+        this.mojo.setProject(this.project);
+    }
+
+    @Test
+    public void test() throws Exception {
+        mojo.execute();
+        assertEquals(outDir, GeneratorMock.outputDir);
+        assertEquals(project, GeneratorMock.project);
+        assertTrue(GeneratorMock.additionalCfg.isEmpty());
+        assertThat(GeneratorMock.resourceBaseDir.toString(), containsString("target" + File.separator
+                + "generated-sources" + File.separator + "spi"));
+    }
+
+    public static class GeneratorMock implements BasicCodeGenerator, MavenProjectAware {
+        private static int called = 0;
+        private static File outputDir;
+        private static Map<String, String> additionalCfg;
+        private static File resourceBaseDir;
+        private static MavenProject project;
+
+        @Override
+        public void setAdditionalConfig(final Map<String, String> additionalConfiguration) {
+            GeneratorMock.additionalCfg = additionalConfiguration;
+        }
+
+        @Override
+        public void setResourceBaseDir(final File resourceBaseDir) {
+            GeneratorMock.resourceBaseDir = resourceBaseDir;
+
+        }
+
+        @Override
+        public void setMavenProject(final MavenProject mavenProject) {
+            GeneratorMock.project = mavenProject;
+        }
+
+        @Override
+        public Collection<File> generateSources(final EffectiveModelContext context, final File outputBaseDir,
+                final Set<Module> currentModules, final Function<Module, Optional<String>> moduleResourcePathResolver)
+                        throws IOException {
+            called++;
+            outputDir = outputBaseDir;
+            return new ArrayList<>();
+        }
+    }
+}
diff --git a/yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/ScannedDependencyTest.java b/yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/ScannedDependencyTest.java
new file mode 100644 (file)
index 0000000..cd95324
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.yang2sources.plugin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.project.MavenProject;
+import org.junit.Test;
+
+public class ScannedDependencyTest {
+
+    @Test
+    public void findYangFilesInDependenciesAsStream() throws Exception {
+        final MavenProject project = mock(MavenProject.class);
+        prepareProject(project);
+
+        final Collection<ScannedDependency> yangzip = ScannedDependency.scanDependencies(project);
+        assertNotNull(yangzip);
+        assertEquals(2, yangzip.size());
+    }
+
+    @Test
+    public void findYangFilesInDependencies() throws Exception {
+        final MavenProject project = mock(MavenProject.class);
+        prepareProject(project);
+
+        final Collection<ScannedDependency> files = ScannedDependency.scanDependencies(project);
+        assertNotNull(files);
+        assertEquals(2, files.size());
+    }
+
+    private static void prepareProject(final MavenProject project) throws Exception {
+        final Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
+        final File testFile2 = new File(ScannedDependencyTest.class.getResource("/").getPath(), "test.jar");
+        final JarOutputStream target = new JarOutputStream(new FileOutputStream(testFile2), manifest);
+        addSourceFileToTargetJar(new File(ScannedDependencyTest.class.getResource("/tests/META-INF").getPath()),
+            target);
+        target.close();
+
+        final Artifact artifact = mock(Artifact.class);
+        when(artifact.getFile()).thenReturn(new File(ScannedDependencyTest.class.getResource("/tests").toURI()));
+
+        final Artifact artifact2 = mock(Artifact.class);
+        when(artifact2.getFile()).thenReturn(testFile2);
+        when(project.getArtifacts()).thenReturn(ImmutableSet.of(artifact, artifact2));
+    }
+
+    private static void addSourceFileToTargetJar(final File source, final JarOutputStream target) throws IOException {
+        BufferedInputStream in = null;
+        try {
+            if (source.isDirectory()) {
+                String name = source.getPath().replace("\\", "/");
+                if (!name.isEmpty()) {
+                    if (!name.endsWith("/")) {
+                        name += "/";
+                    }
+                    final JarEntry entry = new JarEntry(name);
+                    entry.setTime(source.lastModified());
+                    target.putNextEntry(entry);
+                    target.closeEntry();
+                }
+                for (final File nestedFile : source.listFiles()) {
+                    addSourceFileToTargetJar(nestedFile, target);
+                }
+                return;
+            }
+
+            final JarEntry entry = new JarEntry(source.getPath().replace("\\", "/"));
+            entry.setTime(source.lastModified());
+            target.putNextEntry(entry);
+            in = new BufferedInputStream(new FileInputStream(source));
+
+            final byte[] buffer = new byte[1024];
+            while (true) {
+                final int count = in.read(buffer);
+                if (count == -1) {
+                    break;
+                }
+                target.write(buffer, 0, count);
+            }
+            target.closeEntry();
+        } finally {
+            if (in != null) {
+                in.close();
+            }
+        }
+    }
+}
diff --git a/yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/UtilTest.java b/yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/UtilTest.java
new file mode 100644 (file)
index 0000000..bfb822d
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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.yang2sources.plugin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
+import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.repository.RepositorySystem;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+@RunWith(MockitoJUnitRunner.class)
+public class UtilTest {
+
+    @Test
+    public void getClassPathTest() {
+        final MavenProject project = mock(MavenProject.class);
+        final File file = mock(File.class);
+        final File file2 = mock(File.class);
+        final Artifact artifact = mock(Artifact.class);
+        final Artifact artifact2 = mock(Artifact.class);
+
+        final Set<Artifact> artifacts = new HashSet<>();
+        artifacts.add(artifact);
+        artifacts.add(artifact2);
+
+        when(project.getArtifacts()).thenReturn(artifacts);
+        when(artifact.getFile()).thenReturn(file);
+        when(file.isFile()).thenReturn(true);
+        when(file.getName()).thenReturn("iamjar.jar");
+        when(artifact2.getFile()).thenReturn(file2);
+        when(file2.isDirectory()).thenReturn(true);
+
+        final List<File> files = Util.getClassPath(project);
+        assertEquals(2, files.size());
+        assertTrue(files.contains(file) && files.contains(file2));
+    }
+
+    @Test
+    public void checkClasspathTest() throws Exception {
+        final MavenProject project = mock(MavenProject.class);
+        final Plugin plugin = mock(Plugin.class);
+        final RepositorySystem repoSystem = mock(RepositorySystem.class);
+        final ArtifactRepository localRepo = mock(ArtifactRepository.class);
+        final ArtifactResolutionResult artifactResolResult = mock(ArtifactResolutionResult.class);
+        final Artifact artifact = mock(Artifact.class);
+        final Dependency dep = mock(Dependency.class);
+
+        final List<ArtifactRepository> remoteRepos = new ArrayList<>();
+        remoteRepos.add(localRepo);
+
+        final Set<Artifact> artifacts = new HashSet<>();
+        artifacts.add(artifact);
+
+        final List<Dependency> listDepcy = new ArrayList<>();
+        listDepcy.add(dep);
+
+        when(project.getPlugin(anyString())).thenReturn(plugin);
+        when(plugin.getDependencies()).thenReturn(listDepcy);
+        when(artifact.getArtifactId()).thenReturn("artifactId");
+        when(artifact.getGroupId()).thenReturn("groupId");
+        when(artifact.getVersion()).thenReturn("SNAPSHOT");
+        when(repoSystem.createDependencyArtifact(dep)).thenReturn(artifact);
+        when(repoSystem.resolve(any(ArtifactResolutionRequest.class))).thenReturn(artifactResolResult);
+        when(artifactResolResult.getArtifacts()).thenReturn(artifacts);
+        when(project.getDependencyArtifacts()).thenReturn(artifacts);
+
+        Util.checkClasspath(project, repoSystem, localRepo, remoteRepos);
+        assertEquals(1, artifacts.size());
+        assertEquals(1, remoteRepos.size());
+        assertEquals(1, listDepcy.size());
+    }
+
+    @Test
+    public void contextHolderTest() throws Exception {
+        final EffectiveModelContext context = YangParserTestUtils.parseYangResources(getClass(), "/test.yang",
+            "/test2.yang");
+        final Set<Module> yangModules = new HashSet<>();
+        final ContextHolder cxH = new ContextHolder(context, yangModules, ImmutableSet.of());
+        assertEquals(context, cxH.getContext());
+        assertEquals(yangModules, cxH.getYangModules());
+    }
+}
diff --git a/yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesMojoTest.java b/yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesMojoTest.java
new file mode 100644 (file)
index 0000000..43faea0
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016 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.yang2sources.plugin;
+
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.maven.model.Build;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.project.MavenProject;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
+import org.opendaylight.yangtools.yang2sources.plugin.GenerateSourcesTest.GeneratorMock;
+import org.sonatype.plexus.build.incremental.DefaultBuildContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class YangToSourcesMojoTest {
+
+    private YangToSourcesMojo mojo;
+
+    @Mock
+    private MavenProject project;
+
+    @Mock
+    private Plugin plugin;
+
+    private YangToSourcesProcessor proc;
+
+    @Test
+    public void yangToSourceMojoTest() throws Exception {
+        Mockito.when(this.project.getPlugin(YangToSourcesMojo.PLUGIN_NAME)).thenReturn(this.plugin);
+
+        this.mojo = new YangToSourcesMojo();
+        this.mojo.setProject(this.project);
+        this.mojo.buildContext = new DefaultBuildContext();
+        this.mojo.execute();
+        Assert.assertNotNull(this.mojo);
+
+        final YangToSourcesProcessor processor = Mockito.mock(YangToSourcesProcessor.class);
+        this.mojo = new YangToSourcesMojo(processor);
+        this.mojo.setProject(this.project);
+        this.mojo.execute();
+        Mockito.verify(processor).conditionalExecute(false);
+    }
+
+    @Test
+    public void test() throws Exception {
+        prepareProcessor();
+        Assert.assertNotNull(this.proc);
+        this.mojo = new YangToSourcesMojo(this.proc);
+        this.mojo.setProject(this.project);
+        this.mojo.execute();
+        Assert.assertNotNull(this.mojo);
+    }
+
+    private void prepareProcessor() {
+        final File file = new File(getClass().getResource("/yang").getFile());
+        final File excludedYang = new File(getClass().getResource("/yang/excluded-file.yang").getFile());
+        final String path = file.getPath();
+        final List<CodeGeneratorArg> codeGenerators = new ArrayList<>();
+        final CodeGeneratorArg codeGeneratorArg = new CodeGeneratorArg(GeneratorMock.class.getName(),
+                "target/YangToSourcesMojoTest-outputBaseDir");
+        codeGenerators.add(codeGeneratorArg);
+        final MavenProject mvnProject = Mockito.mock(MavenProject.class);
+        final Build build = new Build();
+        Mockito.when(mvnProject.getBuild()).thenReturn(build);
+        final boolean dependencies = true;
+        this.proc = new YangToSourcesProcessor(file, ImmutableList.of(excludedYang), codeGenerators,
+                mvnProject, dependencies, YangProvider.getInstance());
+    }
+}
diff --git a/yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessorTest.java b/yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessorTest.java
new file mode 100644 (file)
index 0000000..2250ed4
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 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.yang2sources.plugin;
+
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.util.List;
+import org.apache.maven.model.Build;
+import org.apache.maven.project.MavenProject;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
+import org.opendaylight.yangtools.yang2sources.plugin.GenerateSourcesTest.GeneratorMock;
+
+@RunWith(MockitoJUnitRunner.Silent.class)
+public class YangToSourcesProcessorTest {
+
+    private final File buildContext = Mockito.mock(File.class);
+    private final List<File> yangFilesRootDir = ImmutableList.of(buildContext);
+    private final MavenProject project = Mockito.mock(MavenProject.class);
+    private final boolean dep = false;
+    private final YangProvider inspectDependencies = Mockito.mock(YangProvider.class);
+
+    @Test
+    public void yangToSourcesProcessotTest() {
+        Mockito.when(this.buildContext.getPath()).thenReturn("path");
+        YangToSourcesProcessor processor = new YangToSourcesProcessor(buildContext, yangFilesRootDir,
+            ImmutableList.of(), project, dep, inspectDependencies);
+        Assert.assertNotNull(processor);
+
+        processor = new YangToSourcesProcessor(buildContext, yangFilesRootDir, ImmutableList.of(), project, dep,
+            inspectDependencies);
+        Assert.assertNotNull(processor);
+    }
+
+    @Test
+    public void test() throws Exception {
+        final File file = new File(getClass().getResource("/yang").getFile());
+        final File excludedYang = new File(getClass().getResource("/yang/excluded-file.yang").getFile());
+        final String path = file.getPath();
+        final CodeGeneratorArg codeGeneratorArg = new CodeGeneratorArg(GeneratorMock.class.getName(),
+                "target/YangToSourcesProcessorTest-outputBaseDir");
+        final List<CodeGeneratorArg> codeGenerators = ImmutableList.of(codeGeneratorArg);
+        final MavenProject mvnProject = Mockito.mock(MavenProject.class);
+        final Build build = new Build();
+        Mockito.when(mvnProject.getBuild()).thenReturn(build);
+        final boolean dependencies = true;
+        final YangToSourcesProcessor proc = new YangToSourcesProcessor(file, ImmutableList.of(excludedYang),
+            codeGenerators, mvnProject, dependencies, YangProvider.getInstance());
+        Assert.assertNotNull(proc);
+        proc.execute();
+    }
+
+}
diff --git a/yang/yang-maven-plugin/src/test/resources/test.yang b/yang/yang-maven-plugin/src/test/resources/test.yang
new file mode 100644 (file)
index 0000000..a01d7dd
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module test {
+    yang-version 1;
+    namespace "urn:opendaylight:test";
+    prefix "test";
+
+    organization "Cisco Systems, Inc.";
+
+    revision "2014-03-13" {
+        description
+            "Initial revision";
+    }
+
+    container cont{
+        leaf l {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-maven-plugin/src/test/resources/test2.yang b/yang/yang-maven-plugin/src/test/resources/test2.yang
new file mode 100644 (file)
index 0000000..913cca8
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module test2 {
+    yang-version 1;
+    namespace "urn:opendaylight:test2";
+    prefix "test";
+
+    organization "Cisco Systems, Inc.";
+
+    revision "2014-03-13" {
+        description
+            "Initial revision";
+    }
+
+    container cont2{
+        leaf l {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-maven-plugin/src/test/resources/tests/META-INF/yang/test.yang b/yang/yang-maven-plugin/src/test/resources/tests/META-INF/yang/test.yang
new file mode 100644 (file)
index 0000000..a01d7dd
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module test {
+    yang-version 1;
+    namespace "urn:opendaylight:test";
+    prefix "test";
+
+    organization "Cisco Systems, Inc.";
+
+    revision "2014-03-13" {
+        description
+            "Initial revision";
+    }
+
+    container cont{
+        leaf l {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-maven-plugin/src/test/resources/tests/META-INF/yang/test2.yang b/yang/yang-maven-plugin/src/test/resources/tests/META-INF/yang/test2.yang
new file mode 100644 (file)
index 0000000..913cca8
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module test2 {
+    yang-version 1;
+    namespace "urn:opendaylight:test2";
+    prefix "test";
+
+    organization "Cisco Systems, Inc.";
+
+    revision "2014-03-13" {
+        description
+            "Initial revision";
+    }
+
+    container cont2{
+        leaf l {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-maven-plugin/src/test/resources/yang-source-from-file.yang b/yang/yang-maven-plugin/src/test/resources/yang-source-from-file.yang
new file mode 100644 (file)
index 0000000..688c5b1
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+ module yang-source-from-file {
+
+   namespace "yang:source:from:file";
+   prefix "ysff";
+
+   revision 2016-02-12 {
+     description
+      "Initial revision.";
+   }
+
+   container cont{
+     leaf l{
+       type string;
+     }
+   }
+
+ }
diff --git a/yang/yang-maven-plugin/src/test/resources/yang/excluded-file.yang b/yang/yang-maven-plugin/src/test/resources/yang/excluded-file.yang
new file mode 100644 (file)
index 0000000..682142e
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+ module excluded-file {
+
+   namespace "excluded:file";
+   prefix "ef";
+
+   revision 2016-02-12 {
+     description
+      "Initial revision.";
+   }
+
+   container cont{
+     leaf l{
+       type string;
+     }
+   }
+
+ }
diff --git a/yang/yang-maven-plugin/src/test/resources/yang/mock.yang b/yang/yang-maven-plugin/src/test/resources/yang/mock.yang
new file mode 100644 (file)
index 0000000..5c1602d
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+ module mock {
+
+   namespace "a:b:c";
+   prefix "m";
+
+   revision 2010-09-24 {
+     description
+      "Initial revision.";
+   }
+
+ }
diff --git a/yang/yang-model-api/pom.xml b/yang/yang-model-api/pom.xml
new file mode 100644 (file)
index 0000000..8429627
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-model-api</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-xpath-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.model.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ActionDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ActionDefinition.java
new file mode 100644 (file)
index 0000000..f304b2d
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016 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.api;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Represents YANG action statement.
+ *
+ * <p>
+ * The "action" statement is used to define an operation connected to a
+ * specific container or list data node.  It takes one argument, which
+ * is an identifier, followed by a block of substatements that holds
+ * detailed action information.  The argument is the name of the action.
+ */
+@Beta
+public interface ActionDefinition extends OperationDefinition, CopyableNode {
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ActionNodeContainer.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ActionNodeContainer.java
new file mode 100644 (file)
index 0000000..759a6be
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Node which can contain action nodes.
+ */
+@Beta
+public interface ActionNodeContainer {
+    /**
+     * Return the set of actions.
+     *
+     * @return set of action nodes
+     */
+    @NonNull Set<ActionDefinition> getActions();
+
+    /**
+     * Find an action based on its QName. Default implementation searches the set returned by {@link #getActions()}.
+     *
+     * @param qname Action's QName
+     * @return Action definition, if found
+     * @throws NullPointerException if qname is null
+     */
+    default Optional<ActionDefinition> findAction(final QName qname) {
+        requireNonNull(qname);
+        for (ActionDefinition action : getActions()) {
+            if (qname.equals(action.getQName())) {
+                return Optional.of(action);
+            }
+        }
+        return Optional.empty();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AddedByUsesAware.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AddedByUsesAware.java
new file mode 100644 (file)
index 0000000..94a91bd
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api;
+
+/**
+ * Trait interface for {@link SchemaNode}s, which have the {@link #isAddedByUses()} method.
+ *
+ * @deprecated This interface relates to declared model rather than to effective mode and as such should not
+ *             exist. It is provided to provide common method definition and eash migration of users.
+ */
+@Deprecated
+public interface AddedByUsesAware {
+    /**
+     * Returns <code>true</code> if this node was added by uses statement,
+     * otherwise returns <code>false</code>.
+     *
+     * @return <code>true</code> if this node was added by uses statement,
+     *         otherwise returns <code>false</code>
+     */
+    boolean isAddedByUses();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AnydataSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AnydataSchemaNode.java
new file mode 100644 (file)
index 0000000..2e5fc2e
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016 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.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+
+/**
+ * The "anydata" statement defines an interior node in the schema tree. It takes one argument, which is an identifier,
+ * followed by a block of substatements that holds detailed anydata information.
+ *
+ * <p>
+ * The "anydata" statement is used to represent an unknown set of nodes that can be modeled with YANG, except anyxml,
+ * but for which the data model is not known at module design time.  It is possible, though not required, for the data
+ * model for anydata content to become known through protocol signaling or other means that are outside the scope
+ * of this document.
+ */
+@Beta
+public interface AnydataSchemaNode extends DataSchemaNode, MandatoryAware, MustConstraintAware {
+    /**
+     * Schema of data.
+     *
+     * @return schema of contained data or empty if it is not provided
+     */
+    Optional<ContainerSchemaNode> getDataSchema();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AnyxmlSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AnyxmlSchemaNode.java
new file mode 100644 (file)
index 0000000..37a8595
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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.model.api;
+
+/**
+ * The "anyxml" interface defines an interior node in the schema tree. It takes one argument, which is an identifier
+ * represented by QName inherited from {@link SchemaNode}, followed by a block of substatements that holds detailed
+ * anyxml information. The substatements are defined in {@link DataSchemaNode}.
+ *
+ * <p>
+ * This interface was modeled according to definition in
+ * <a href="https://tools.ietf.org/html/rfc6020#section-7.10">[RFC-6020] The anyxml Statement</a>
+ */
+public interface AnyxmlSchemaNode extends DataSchemaNode, MandatoryAware, MustConstraintAware {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AugmentationSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AugmentationSchemaNode.java
new file mode 100644 (file)
index 0000000..dcb64f7
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.model.api;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+
+/**
+ * AugmentationSchema represents augment definition. The "augment" statement allows a module or submodule to add
+ * to the schema tree defined in an external module, or the current module and its submodules, and to add to the nodes
+ * from a grouping in a "uses" statement.
+ */
+public interface AugmentationSchemaNode extends DataNodeContainer, NotificationNodeContainer, ActionNodeContainer,
+        WhenConditionAware, WithStatus {
+    /**
+     * Returns augmentation schema path.
+     *
+     * @return SchemaPath that identifies a node in the schema tree. This node
+     *         is called the augment's target node. The target node MUST be
+     *         either a container, list, choice, case, input, output, or
+     *         notification node. It is augmented with the nodes defined as
+     *         child nodes of this AugmentationSchema.
+     */
+    SchemaPath getTargetPath();
+
+    /**
+     * Returns Augmentation Definition from which this augmentation is derived
+     * if augmentation was added transitively via augmented uses.
+     *
+     * @return Augmentation Definition from which this augmentation is derived
+     *         if augmentation was added transitively via augmented uses.
+     */
+    Optional<AugmentationSchemaNode> getOriginalDefinition();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AugmentationTarget.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/AugmentationTarget.java
new file mode 100644 (file)
index 0000000..e30273c
--- /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.model.api;
+
+import java.util.Set;
+
+/**
+ * Interface for all nodes which are possible targets of augmentation. The
+ * target node of augmentation MUST be either a container, list, choice, case,
+ * input, output, or notification node.
+ */
+public interface AugmentationTarget {
+    /**
+     * Returns augmentations targeting this element.
+     *
+     * @return set of augmentations targeting this element.
+     */
+    Set<AugmentationSchemaNode> getAvailableAugmentations();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/CaseSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/CaseSchemaNode.java
new file mode 100644 (file)
index 0000000..b90cd74
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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.model.api;
+
+/**
+ * The CaseSchemaNode interface is used to define branches of the ChoiceSchemaNode.
+ */
+public interface CaseSchemaNode extends DataSchemaNode, DataNodeContainer, AugmentationTarget {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ChoiceSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ChoiceSchemaNode.java
new file mode 100644 (file)
index 0000000..0e472fe
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2015 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.Optional;
+import java.util.SortedMap;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * A ChoiceSchemaNode defines a set of alternatives. It consists of a number of branches defined as
+ * ChoiceCaseSchemaNode objects.
+ */
+public interface ChoiceSchemaNode extends DataSchemaNode, AugmentationTarget, MandatoryAware {
+    /**
+     * Returns cases of choice, keyed by their {@link SchemaNode#getQName()}. Returned map does not contain null keys
+     * nor values.
+     *
+     * @return set of ChoiceCaseNode objects defined in this node which represents set of arguments of the YANG
+     *         <code>case</code> substatement of the <code>choice</code> statement.
+     */
+    SortedMap<QName, CaseSchemaNode> getCases();
+
+    /**
+     * Returns the concrete case according to specified Q name.
+     *
+     * @param qname
+     *            QName of sought Choice Case Node
+     * @return child case node of this Choice if child with given name is present, empty otherwise.
+     * @throws NullPointerException if qname is null
+     */
+    default Optional<CaseSchemaNode> findCase(final QName qname) {
+        return Optional.ofNullable(getCases().get(requireNonNull(qname)));
+    }
+
+    /**
+     * Returns the concrete cases according to specified name, disregarding their namespace.
+     *
+     * @param localname
+     *            local name of sought child as String
+     * @return child case nodes matching specified local name, empty list if no match is found.
+     * @throws NullPointerException if localname is null
+     */
+    @Beta
+    default List<CaseSchemaNode> findCaseNodes(final String localname) {
+        return getCases().values().stream().filter(node -> localname.equals(node.getQName().getLocalName()))
+                .collect(ImmutableList.toImmutableList());
+    }
+
+    /**
+     * Find a specific data schema child, if present. This method searches among its {@link CaseSchemaNode}s,
+     * potentially recursing to nested choices.
+     *
+     * @param qname
+     *            QName of sought data schema node
+     * @return Matching node, or empty if no match is found
+     * @throws NullPointerException if qname is null
+     */
+    @Beta
+    default Optional<DataSchemaNode> findDataSchemaChild(final QName qname) {
+        requireNonNull(qname);
+        for (CaseSchemaNode caseNode : getCases().values()) {
+            final Optional<DataSchemaNode> child = caseNode.findDataChildByName(qname);
+            if (child.isPresent()) {
+                return child;
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    /**
+     * Returns the concrete case according to specified QName.
+     *
+     * @param qname
+     *            QName of sought Choice Case Node
+     * @return child case node of this Choice if child with given name is present, <code>null</code> otherwise.
+     *
+     * @deprecated Use either {@code getCases().get(name)} or #findCase(QName)
+     */
+    @Deprecated
+    default CaseSchemaNode getCaseNodeByName(final QName qname) {
+        return getCases().get(qname);
+    }
+
+    /**
+     * Returns name of case which is in the choice specified as default.
+     *
+     * @return string with the name of case which is specified in the argument of the YANG <code>default</code>
+     *         substatement of <code>choice</code> statement.
+     */
+    Optional<CaseSchemaNode> getDefaultCase();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ConstraintMetaDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ConstraintMetaDefinition.java
new file mode 100644 (file)
index 0000000..a028513
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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.model.api;
+
+import java.util.Optional;
+
+/**
+ * Contains methods which retrieve values for description, error message, error app tag and reference (to some external
+ * definition, resource or similar).
+ */
+public interface ConstraintMetaDefinition extends DocumentedNode {
+
+    /**
+     * Returns the value of the argument of YANG <code>error-app-tag</code> keyword.
+     *
+     * @return string with the application tag, or empty if it was not provided.
+     */
+    Optional<String> getErrorAppTag();
+
+    /**
+     * Returns the value of the argument of YANG <code>error-message</code> keyword.
+     *
+     * @return string with the error message, or empty if it was not provided.
+     */
+    Optional<String> getErrorMessage();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ContainerSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ContainerSchemaNode.java
new file mode 100644 (file)
index 0000000..3142bb1
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.model.api;
+
+/**
+ * The ContainerSchemaNode is used to define an interior data node in the schema
+ * tree. There are two styles of containers, those that exist only for
+ * organizing the hierarchy of data nodes, and those whose presence in the
+ * configuration has an explicit meaning.
+ */
+public interface ContainerSchemaNode extends DataNodeContainer,
+        AugmentationTarget, DataSchemaNode, NotificationNodeContainer, ActionNodeContainer, MustConstraintAware {
+    /**
+     * Returns true if this container is marked as presence.
+     *
+     * @return true, if presence of this container has an explicit meaning,
+     *         false otherwise
+     */
+    boolean isPresenceContainer();
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/CopyableNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/CopyableNode.java
new file mode 100644 (file)
index 0000000..b7f38a8
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017 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.api;
+
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+/**
+ * Represents a node that can be added by uses or by augmentation.
+ *
+ * @deprecated Aside from the deprecated {@link AddedByUsesAware} contract, this interface adds only a trait related
+ *             to now how we arrived at this effective node. Users who need to know this information should really be
+ *             looking at the {@link DeclaredStatement} world, which holds the original node definition.
+ */
+@Deprecated
+public interface CopyableNode extends AddedByUsesAware {
+    /**
+     * Returns <code>true</code> if this node was added by augmentation,
+     * otherwise returns <code>false</code>.
+     *
+     * @return <code>true</code> if this node was added by augmentation,
+     *         otherwise returns <code>false</code>
+     */
+    boolean isAugmenting();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DataNodeContainer.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DataNodeContainer.java
new file mode 100644 (file)
index 0000000..845a318
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * 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.model.api;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Node which can contains other nodes.
+ */
+public interface DataNodeContainer {
+    /**
+     * Returns set of all newly defined types within this DataNodeContainer.
+     *
+     * @return typedef statements in lexicographical order
+     */
+    Set<TypeDefinition<?>> getTypeDefinitions();
+
+    /**
+     * Returns set of all child nodes defined within this DataNodeContainer. Although the return type is a collection,
+     * each node is guaranteed to be present at most once.
+     *
+     * <p>
+     * Note that the nodes returned are <strong>NOT</strong> {@code data nodes}, but rather {@link DataSchemaNode}s,
+     * hence {@link ChoiceSchemaNode} and {@link CaseSchemaNode} are present instead of their children. This
+     * is consistent with {@code schema tree}.
+     *
+     * @return child nodes in lexicographical order
+     */
+    Collection<DataSchemaNode> getChildNodes();
+
+    /**
+     * Returns set of all groupings defined within this DataNodeContainer.
+     *
+     * @return grouping statements in lexicographical order
+     */
+    Set<GroupingDefinition> getGroupings();
+
+    /**
+     * Returns the child node corresponding to the specified name.
+     *
+     * <p>
+     * Note that the nodes searched are <strong>NOT</strong> {@code data nodes}, but rather {@link DataSchemaNode}s,
+     * hence {@link ChoiceSchemaNode} and {@link CaseSchemaNode} are returned instead of their matching children. This
+     * is consistent with {@code schema tree}.
+     *
+     * @param name QName of child
+     * @return child node of this DataNodeContainer if child with given name is present, null otherwise
+     * @deprecated Use {@link #findDataChildByName(QName)} instead.
+     * @throws NullPointerException if {@code name} is null
+     */
+    @Deprecated
+    default @Nullable DataSchemaNode getDataChildByName(final QName name) {
+        return findDataChildByName(name).orElse(null);
+    }
+
+    /**
+     * Returns the child node corresponding to the specified name.
+     *
+     * <p>
+     * Note that the nodes searched are <strong>NOT</strong> {@code data nodes}, but rather {@link DataSchemaNode}s,
+     * hence {@link ChoiceSchemaNode} and {@link CaseSchemaNode} are returned instead of their matching children.
+     *
+     * @param name QName of child
+     * @return child node of this DataNodeContainer if child with given name is present, empty otherwise
+     * @throws NullPointerException if {@code name} is null
+     */
+    Optional<DataSchemaNode> findDataChildByName(QName name);
+
+    /**
+     * Returns the child node corresponding to the specified name.
+     *
+     * <p>
+     * Note that the nodes searched are <strong>NOT</strong> {@code data nodes}, but rather {@link DataSchemaNode}s,
+     * hence {@link ChoiceSchemaNode} and {@link CaseSchemaNode} are returned instead of their matching children.
+     *
+     * @param first QName of first child
+     * @param others QNames of subsequent children
+     * @return child node of this DataNodeContainer if child with given name is present, empty otherwise
+     * @throws NullPointerException if any argument is null
+     */
+    default Optional<DataSchemaNode> findDataChildByName(final QName first, final QName... others) {
+        Optional<DataSchemaNode> optCurrent = findDataChildByName(first);
+        for (QName qname : others) {
+            if (optCurrent.isPresent()) {
+                final DataSchemaNode current = optCurrent.get();
+                if (current instanceof DataNodeContainer) {
+                    optCurrent = ((DataNodeContainer) current).findDataChildByName(qname);
+                    continue;
+                }
+            }
+
+            return Optional.empty();
+        }
+        return optCurrent;
+    }
+
+    /**
+     * Returns grouping nodes used ny this container.
+     *
+     * @return Set of all uses nodes defined within this DataNodeContainer
+     */
+    Set<UsesNode> getUses();
+
+    /**
+     * Returns a {@code data node} identified by a QName. This method is distinct from
+     * {@link #findDataChildByName(QName)} in that it skips over {@link ChoiceSchemaNode}s and {@link CaseSchemaNode}s,
+     * hence mirroring layout of the {@code data tree}, not {@code schema tree}.
+     *
+     * @param name QName identifier of the data node
+     * @return Direct or indirect child of this DataNodeContainer which is a {@code data node}, empty otherwise
+     * @throws NullPointerException if {@code name} is null
+     */
+    @Beta
+    default Optional<DataSchemaNode> findDataTreeChild(final QName name) {
+        // First we try to find a direct child and check if it is a data node (as per RFC7950)
+        final Optional<DataSchemaNode> optDataChild = findDataChildByName(name);
+        if (HelperMethods.isDataNode(optDataChild)) {
+            return optDataChild;
+        }
+
+        // There either is no such node present, or there are Choice/CaseSchemaNodes with the same name involved,
+        // hence we have to resort to a full search.
+        for (DataSchemaNode child : getChildNodes()) {
+            if (child instanceof ChoiceSchemaNode) {
+                for (CaseSchemaNode choiceCase : ((ChoiceSchemaNode) child).getCases().values()) {
+                    final Optional<DataSchemaNode> caseChild = choiceCase.findDataTreeChild(name);
+                    if (caseChild.isPresent()) {
+                        return caseChild;
+                    }
+                }
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    /**
+     * Returns a {@code data node} identified by a series of QNames. This is equivalent to incrementally calling
+     * {@link #findDataTreeChild(QName)}.
+     *
+     * @param path Series of QNames towards identifying the requested data node
+     * @return Direct or indirect child of this DataNodeContainer which is a {@code data node}, empty otherwise
+     * @throws IllegalArgumentException if {@code path} is determined to go beyond a not-container-nor-list node.
+     * @throws NoSuchElementException if {@code path} is empty
+     * @throws NullPointerException if {@code path} is null or contains a null
+     */
+    @Beta
+    default Optional<DataSchemaNode> findDataTreeChild(final QName... path) {
+        return findDataTreeChild(Arrays.asList(path));
+    }
+
+    /**
+     * Returns a {@code data node} identified by a series of QNames. This is equivalent to incrementally calling
+     * {@link #findDataTreeChild(QName)}.
+     *
+     * @param path Series of QNames towards identifying the requested data node
+     * @return Direct or indirect child of this DataNodeContainer which is a {@code data node}, empty otherwise
+     * @throws IllegalArgumentException if {@code path} is determined to go beyond a not-container-nor-list node.
+     * @throws NoSuchElementException if {@code path} is empty
+     * @throws NullPointerException if {@code path} is null or contains a null
+     */
+    @Beta
+    default Optional<DataSchemaNode> findDataTreeChild(final Iterable<QName> path) {
+        final Iterator<QName> it = path.iterator();
+        DataNodeContainer parent = this;
+        do {
+            final Optional<DataSchemaNode> optChild = parent.findDataTreeChild(requireNonNull(it.next()));
+            if (optChild.isEmpty() || !it.hasNext()) {
+                return optChild;
+            }
+
+            final DataSchemaNode child = optChild.get();
+            checkArgument(child instanceof DataNodeContainer, "Path %s extends beyond terminal child %s", path, child);
+            parent = (DataNodeContainer) child;
+        } while (true);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DataSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DataSchemaNode.java
new file mode 100644 (file)
index 0000000..cf9e60f
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.model.api;
+
+/**
+ * Data Schema Node represents abstract supertype from which all data tree definitions are derived. Unlike what
+ * the name would suggest, this interface corresponds more to RFC7950 {@code data definition statement} than to
+ * {@code data node}, yet it notably does not include {@link UsesNode} and {@link AugmentationSchemaNode}, which are
+ * resolved separately.
+ *
+ * <p>
+ * Common interface is composed of {@link #isConfiguration()}, governing validity in config/operation data stores
+ * and {@link WhenConditionAware} mixin, which governs validity based on other document data.
+ *
+ * @see ContainerSchemaNode
+ * @see ListSchemaNode
+ * @see LeafListSchemaNode
+ * @see ChoiceSchemaNode
+ * @see CaseSchemaNode
+ * @see LeafSchemaNode
+ * @see AnyxmlSchemaNode
+ * @see AnydataSchemaNode
+ */
+public interface DataSchemaNode extends SchemaNode, CopyableNode, WhenConditionAware {
+    /**
+     * Returns <code>true</code> if the data represents configuration data,
+     * otherwise returns <code>false</code>.
+     *
+     * @return <code>true</code> if the data represents configuration data,
+     *         otherwise returns <code>false</code>
+     */
+    boolean isConfiguration();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DerivableSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DerivableSchemaNode.java
new file mode 100644 (file)
index 0000000..2aaa58f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.api;
+
+import java.util.Optional;
+
+/**
+ * Schema Node which may be derived from other schema node
+ * using augmentation or uses statement.
+ *
+ */
+public interface DerivableSchemaNode extends DataSchemaNode {
+
+    /**
+     * If this node is added by uses, returns original node definition from
+     * grouping where it was defined.
+     *
+     * @return original node definition from grouping if this node is added by
+     *         uses, Optional.absent otherwise
+     */
+    Optional<? extends SchemaNode> getOriginal();
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DeviateDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DeviateDefinition.java
new file mode 100644 (file)
index 0000000..d77c03b
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2016 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.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Interface describing YANG 'deviate' statement.
+ *
+ * <p>
+ * The 'deviate' statement defines how the device's implementation of
+ * the target node deviates from its original definition.
+ * The argument is one of the strings "not-supported", "add", "replace", or "delete".
+ */
+@Beta
+public interface DeviateDefinition {
+    /**
+     * Return deviation kind.
+     *
+     * @return enum which describes the type of this deviate statement
+     */
+    DeviateKind getDeviateType();
+
+    /**
+     * Returns deviated config value.
+     *
+     * @return value of the deviated config statement or null if it is not deviated
+     */
+    Boolean getDeviatedConfig();
+
+    /**
+     * Returns deviated default value.
+     *
+     * @return value of the deviated default statement or null if it is not deviated
+     */
+    String getDeviatedDefault();
+
+    /**
+     * Returns deviated mandatory value.
+     *
+     * @return value of the deviated mandatory statement or null if it is not deviated
+     */
+    Boolean getDeviatedMandatory();
+
+    /**
+     * Returns deviated max-elements.
+     *
+     * @return value of the deviated max-elements statement or null if it is not deviated
+     */
+    Integer getDeviatedMaxElements();
+
+    /**
+     * Returns deviated min-elements.
+     *
+     * @return value of the deviated min-elements statement or null if it is not deviated
+     */
+    Integer getDeviatedMinElements();
+
+    /**
+     * Returns deviated must statements.
+     *
+     * @return set of the deviated must statements
+     */
+    Set<MustDefinition> getDeviatedMusts();
+
+    /**
+     * Returns deviated type statement.
+     *
+     * @return deviated type statement or null if it is not deviated
+     */
+    TypeDefinition<?> getDeviatedType();
+
+    /**
+     * Returns deviated unique statements.
+     *
+     * @return collection of the deviated unique statements
+     */
+    Collection<UniqueConstraint> getDeviatedUniques();
+
+    /**
+     * Returns deviated units statement.
+     *
+     * @return value of the deviated units statement or null if it is not deviated
+     */
+    String getDeviatedUnits();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DeviateKind.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DeviateKind.java
new file mode 100644 (file)
index 0000000..d7a7280
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Enum describing YANG deviation 'deviate' statement. It defines how the
+ * device's implementation of the target node deviates from its original
+ * definition.
+ */
+public enum DeviateKind {
+
+    NOT_SUPPORTED("not-supported"), ADD("add"), REPLACE("replace"), DELETE("delete");
+
+    private final String keyword;
+
+    DeviateKind(final String keyword) {
+        this.keyword = requireNonNull(keyword);
+    }
+
+    /**
+     * Returns the YANG keyword corresponding to this object.
+     *
+     * @return String that corresponds to the yang keyword.
+     */
+    public String getKeyword() {
+        return keyword;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Deviation.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Deviation.java
new file mode 100644 (file)
index 0000000..408a172
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.model.api;
+
+import java.util.List;
+
+/**
+ * Interface describing YANG 'deviation' statement.
+ *
+ * <p>
+ * The 'deviation' statement defines a hierarchy of a module that the device does not implement faithfully. Deviations
+ * define the way a device deviate from a standard.
+ */
+public interface Deviation extends DocumentedNode {
+
+    /**
+     * Returns target schema path.
+     *
+     * @return SchemaPath that identifies the node in the schema tree where a deviation from the module occurs.
+     */
+    SchemaPath getTargetPath();
+
+    /**
+     * Returns deviate children.
+     *
+     * @return List of all deviate statements defined in this deviation.
+     */
+    List<DeviateDefinition> getDeviates();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DocumentedNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DocumentedNode.java
new file mode 100644 (file)
index 0000000..d6c387a
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.api;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Node which can have documentation assigned.
+ */
+public interface DocumentedNode {
+    /**
+     * Returns the value of the argument of YANG <code>description</code> keyword.
+     *
+     * @return string with the description, or empty if description was not provided.
+     */
+    Optional<String> getDescription();
+
+    /**
+     * Returns the value of the argument of YANG <code>reference</code> keyword.
+     *
+     * @return string with reference to some other document, or empty if reference was not provided.
+     */
+    Optional<String> getReference();
+
+    /**
+     * Returns unknown schema nodes which belongs to this instance. Default implementation returns an empty list.
+     *
+     * @return list of unknown schema nodes defined under this node.
+     */
+    default @NonNull List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return ImmutableList.of();
+    }
+
+    interface WithStatus extends DocumentedNode {
+        /**
+         * Returns status of the instance of the type <code>SchemaNode</code>.
+         *
+         * @return status of this node which represents the argument of the YANG
+         *         <code>status</code> substatement
+         */
+        @NonNull Status getStatus();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/EffectiveModelContext.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/EffectiveModelContext.java
new file mode 100644 (file)
index 0000000..855184b
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+
+/**
+ * {@link EffectiveStatement}-based result of YANG parser compilation. Unlike a SchemaContext, which it extends,
+ * it gives access to individual {@link ModuleEffectiveStatement}s that comprise it.
+ *
+ * @author Robert Varga
+ */
+@Beta
+// FIXME: 5.0.0: evaluate if we still need to extend SchemaContext here
+public interface EffectiveModelContext extends SchemaContext {
+
+    Map<QNameModule, ModuleEffectiveStatement> getModuleStatements();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/EffectiveModelContextListener.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/EffectiveModelContextListener.java
new file mode 100644 (file)
index 0000000..a16f8d4
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.api;
+
+import com.google.common.annotations.Beta;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import java.util.EventListener;
+
+/**
+ * Interface for listeners interested in updates of an EffectiveModelContext.
+ */
+@Beta
+public interface EffectiveModelContextListener extends EventListener {
+    /**
+     * Invoked when the model context changes.
+     *
+     * @param newModelContext New model context being installed
+     */
+    void onModelContextUpdated(@NonNull EffectiveModelContext newModelContext);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/EffectiveModelContextProvider.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/EffectiveModelContextProvider.java
new file mode 100644 (file)
index 0000000..fa6421a
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.api;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * A static provider of an {@link EffectiveModelContext}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface EffectiveModelContextProvider extends SchemaContextProvider {
+    /**
+     * Return the {@link EffectiveModelContext} attached to this object.
+     *
+     * @return An EffectiveModelContext instance.
+     * @throws IllegalStateException if the context is not available.
+     */
+    @NonNull EffectiveModelContext getEffectiveModelContext();
+
+    @Override
+    default SchemaContext getSchemaContext() {
+        return getEffectiveModelContext();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ElementCountConstraint.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ElementCountConstraint.java
new file mode 100644 (file)
index 0000000..9a2147f
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * 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.model.api;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Contains method which returns various data constraints for a list-like YANG element
+ * (e.g. min or max number of elements).
+ */
+@Beta
+public abstract class ElementCountConstraint {
+    private static final class Min extends ElementCountConstraint {
+        private final int minElements;
+
+        Min(final int minElements) {
+            checkArgument(minElements >= 0);
+            this.minElements = minElements;
+        }
+
+        @Override
+        public Integer getMinElements() {
+            return minElements;
+        }
+
+        @Override
+        public Integer getMaxElements() {
+            return null;
+        }
+    }
+
+    private static final class Max extends ElementCountConstraint {
+        private final int maxElements;
+
+        Max(final int maxElements) {
+            checkArgument(maxElements >= 0);
+            this.maxElements = maxElements;
+        }
+
+        @Override
+        public Integer getMinElements() {
+            return null;
+        }
+
+        @Override
+        public Integer getMaxElements() {
+            return maxElements;
+        }
+    }
+
+    private static final class MinMax extends ElementCountConstraint {
+        private final int minElements;
+        private final int maxElements;
+
+        MinMax(final int minElements, final int maxElements) {
+            checkArgument(minElements >= 0);
+            checkArgument(maxElements >= 0);
+            checkArgument(minElements <= maxElements);
+            this.minElements = minElements;
+            this.maxElements = maxElements;
+        }
+
+        @Override
+        public Integer getMinElements() {
+            return minElements;
+        }
+
+        @Override
+        public Integer getMaxElements() {
+            return maxElements;
+        }
+    }
+
+    ElementCountConstraint() {
+        // Hidden on purpose
+    }
+
+    public static @NonNull ElementCountConstraint atLeast(final int minElements) {
+        return new Min(minElements);
+    }
+
+    public static @NonNull ElementCountConstraint atMost(final int maxElements) {
+        return new Max(maxElements);
+    }
+
+    public static @NonNull ElementCountConstraint inRange(final int minElements, final int maxElements) {
+        return new MinMax(minElements, maxElements);
+    }
+
+    public static @NonNull Optional<ElementCountConstraint> forNullable(final @Nullable Integer minElements,
+            final @Nullable Integer maxElements) {
+        if (minElements == null) {
+            return maxElements != null ? Optional.of(new Max(maxElements)) : Optional.empty();
+        }
+
+        return Optional.of(maxElements != null ? new MinMax(minElements, maxElements) : new Min(minElements));
+    }
+
+    /**
+     * Returns the minimum required number of data elements for node where this
+     * constraint is specified.
+     *
+     * <p>
+     * The returning value equals to value of the argument of the
+     * <b>min-elements</b> YANG substatement.
+     * It is used with YANG statements leaf-list, list, deviate.
+     *
+     * @return integer with minimal number of elements, or null if no minimum is defined
+     */
+    public abstract @Nullable Integer getMinElements();
+
+    /**
+     * Returns the maximum admissible number of data elements for node where
+     * this constraint is specified.
+     *
+     * <p>
+     * The returning value equals to value of the argument of the
+     * <b>max-elements</b> YANG substatement.
+     * It is used with YANG statements leaf-list, list, deviate.
+     *
+     * @return integer with maximum number of elements, or null if no maximum is defined
+     */
+    public abstract @Nullable Integer getMaxElements();
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(getMinElements(), getMaxElements());
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof ElementCountConstraint)) {
+            return false;
+        }
+        final ElementCountConstraint other = (ElementCountConstraint) obj;
+        return Objects.equals(getMinElements(), other.getMinElements())
+                && Objects.equals(getMaxElements(), other.getMaxElements());
+    }
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(ElementCountConstraint.class).omitNullValues()
+                .add("minElements", getMinElements())
+                .add("maxElements", getMaxElements()).toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ElementCountConstraintAware.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ElementCountConstraintAware.java
new file mode 100644 (file)
index 0000000..2d32f4d
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+
+/**
+ * Common interface for list-like nodes, which can optionally have constraints on the number of direct children.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface ElementCountConstraintAware {
+    /**
+     * Return the constraint on the number of child nodes.
+     *
+     * @return the constraint on the number of child nodes, if applicable.
+     */
+    Optional<ElementCountConstraint> getElementCountConstraint();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ExtensionDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ExtensionDefinition.java
new file mode 100644 (file)
index 0000000..965ae93
--- /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.yang.model.api;
+
+/**
+ * Contains the methods for getting the data which are part of the YANG
+ * <code>extensoion</code> statement.
+ */
+public interface ExtensionDefinition extends SchemaNode {
+    /**
+     * Returns the <code>String</code> that is the name of argument to the
+     * Keyword. If no argument statement is present the method will return
+     * <code>null</code> <br>
+     * The argument statement is defined in <a
+     * href="https://tools.ietf.org/html/rfc6020#section-7.17.2">[RFC-6020] The
+     * argument Statement</a>
+     *
+     * @return the <code>String</code> that is the name of argument to the
+     *         Keyword. If no argument statement is present the method will
+     *         return <code>null</code>
+     */
+    String getArgument();
+
+    /**
+     * This statement indicates if the argument is mapped to an XML element in
+     * YIN or to an XML attribute.<br>
+     * By contract if implementation of ExtensionDefinition does not specify the
+     * yin-element statement the return value is by default set to
+     * <code>false</code>
+     *
+     * <p>
+     * For more specific definition please look into <a
+     * href="https://tools.ietf.org/html/rfc6020#section-7.17.2.2">[RFC-6020]
+     * The yin-element Statement</a>
+     *
+     * @return <code>true</code> if the argument is mapped to an XML element in
+     *         YIN or returns <code>false</code> if the argument is mapped to an
+     *         XML attribute.
+     */
+    boolean isYinElement();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/FeatureDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/FeatureDefinition.java
new file mode 100644 (file)
index 0000000..1a3d7d1
--- /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.model.api;
+
+/**
+ * Interface describing YANG 'feature' statement.
+ * <p>
+ * The feature statement is used to define a mechanism by which portions of the
+ * schema are marked as conditional. A feature name can later be referenced
+ * using the 'if-feature' statement.
+ * </p>
+ */
+public interface FeatureDefinition extends SchemaNode {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/GroupingDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/GroupingDefinition.java
new file mode 100644 (file)
index 0000000..9258675
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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.model.api;
+
+/**
+ * Interface describing YANG 'grouping' statement.
+ *
+ * <p>
+ * It is used to define a reusable block of nodes, which may be used locally in
+ * the module, in modules that include it, and by other modules that import from it.
+ *
+ * <p>
+ * Note: this interface extends {@link AddedByUsesAware}, but this contradicts the javadoc of {@link #isAddedByUses()},
+ *       as groupings can never be encountered in 'data schema node' context. It is their children, which are data
+ *       schema node, but those really are instantiated and typically differ in {@link #getQName()}'s namespace.
+ */
+public interface GroupingDefinition extends DataNodeContainer, SchemaNode, NotificationNodeContainer,
+       ActionNodeContainer, AddedByUsesAware {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/HelperMethods.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/HelperMethods.java
new file mode 100644 (file)
index 0000000..3323cc9
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Package-internal helper methods for use in interface default methods.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault
+final class HelperMethods {
+    private HelperMethods() {
+
+    }
+
+    static boolean isDataNode(final Optional<DataSchemaNode> optNode) {
+        return optNode.isPresent() && isDataNode(optNode.get());
+    }
+
+    private static boolean isDataNode(final DataSchemaNode node) {
+        return node instanceof ContainerSchemaNode || node instanceof LeafSchemaNode
+                || node instanceof LeafListSchemaNode || node instanceof ListSchemaNode
+                || node instanceof AnydataSchemaNode || node instanceof AnyxmlSchemaNode;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/IdentitySchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/IdentitySchemaNode.java
new file mode 100644 (file)
index 0000000..42ae973
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.model.api;
+
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Interface describing YANG 'identity' statement.
+ *
+ * <p>
+ * The 'identity' statement is used to define a new globally unique, abstract, and untyped identity. Its only purpose
+ * is to denote its name, semantics, and existence. The built-in datatype "identityref" can be used to reference
+ * identities within a data model.
+ */
+public interface IdentitySchemaNode extends SchemaNode {
+    /**
+     * Return base identities of this identity. The semantics of differ between RFC6020 and RFC7950 here. YANG 1.0
+     * uses single inheritance, where there can be 0..1 base identities. YANG 1.1 uses multiple inheritance, where
+     * there can be 0..N base identities.
+     *
+     * <p>
+     * Callers should be prepared to handle multiple base identities.
+     *
+     * @return set of existing identities from which the new identity is derived or an empty Set if the identity is
+     *         a root identity.
+     */
+    @NonNull Set<IdentitySchemaNode> getBaseIdentities();
+
+    /**
+     * Get identities derived from this identity.
+     *
+     * @return collection of identities derived from this identity
+     */
+    @NonNull Set<IdentitySchemaNode> getDerivedIdentities();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/LeafListSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/LeafListSchemaNode.java
new file mode 100644 (file)
index 0000000..e165045
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.model.api;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Interface describing YANG 'leaf-list' statement.
+ */
+public interface LeafListSchemaNode extends TypedDataSchemaNode, MustConstraintAware, ElementCountConstraintAware {
+    /**
+     * YANG 'ordered-by' statement. It defines whether the order of entries
+     * within this leaf-list are determined by the user or the system. If not
+     * present, default is false.
+     *
+     * @return true if ordered-by argument is "user", false otherwise
+     */
+    boolean isUserOrdered();
+
+    /**
+     * Return the default value of this leaf-list, as per the rules outlined in
+     * <a href="https://tools.ietf.org/html/rfc7950#section-7.7.4">Section 7.4.4 of RFC7950</a>. RFC6020 does not
+     * allow for default value of leaf-list, hence the returned list will be empty.
+     *
+     * @return Ordered list of Strings which specify the default values of this leaf-list
+     */
+    @NonNull Collection<? extends Object> getDefaults();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/LeafSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/LeafSchemaNode.java
new file mode 100644 (file)
index 0000000..1c6d6d3
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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.model.api;
+
+/**
+ * Interface describing YANG <code>leaf</code> statement.
+ *
+ * <p>
+ * The 'leaf' statement is used to define a leaf node in the schema tree.
+ *
+ * <p>
+ * Since we are presenting the effective model of the world, the information dictated by 'default' and 'units'
+ * substatements is captured in the type returned via {@link #getType()}.
+ */
+public interface LeafSchemaNode extends TypedDataSchemaNode, MandatoryAware, MustConstraintAware {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ListSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ListSchemaNode.java
new file mode 100644 (file)
index 0000000..e4cb9ba
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.model.api;
+
+import java.util.Collection;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Interface describing YANG 'list' statement.
+ *
+ * <p>
+ * The 'list' statement is used to define an interior data node in the schema tree. A list entry is uniquely identified
+ * by the values of the list's keys, if defined.
+ */
+public interface ListSchemaNode extends DataNodeContainer, AugmentationTarget, DataSchemaNode,
+        NotificationNodeContainer, ActionNodeContainer, ElementCountConstraintAware, MustConstraintAware {
+    /**
+     * Returns the list of leaf identifiers.
+     *
+     * @return List of QNames of leaf identifiers of this list, empty if the list has no keys.
+     */
+    @NonNull List<QName> getKeyDefinition();
+
+    /**
+     * YANG 'ordered-by' statement. It defines whether the order of entries within a list are determined by the user
+     * or the system. If not present, default is false.
+     *
+     * @return true if ordered-by argument is "user", false otherwise
+     */
+    boolean isUserOrdered();
+
+    /**
+     * Returns unique constraints.
+     *
+     * @return Collection of unique constraints of this list schema node
+     */
+    @NonNull Collection<UniqueConstraint> getUniqueConstraints();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/MandatoryAware.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/MandatoryAware.java
new file mode 100644 (file)
index 0000000..14ad2b3
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api;
+
+/**
+ * Mix-in interface for {@link SchemaNode}s which can have a {@code mandatory} statement.
+ *
+ * @author Robert Varga
+ */
+public interface MandatoryAware {
+
+    /**
+     * Return whether this node is mandatory or not. Note this reflects the declared model, as defined by 'mandatory'
+     * statement, not the effective model. This notably means this attribute does not mirror the definition of
+     * {@code mandatory node} as per <a href="https://tools.ietf.org/html/rfc7950#page-14">RFC7950 Terminology</a>.
+     *
+     * @return True if this node is marked as mandatory.
+     */
+    boolean isMandatory();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Module.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Module.java
new file mode 100644 (file)
index 0000000..0355ca5
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * 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.model.api;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+
+/**
+ * This interface contains the methods for getting the data from the YANG module.
+ */
+public interface Module extends DataNodeContainer, DocumentedNode, Immutable, NotificationNodeContainer,
+        NamespaceRevisionAware {
+    /**
+     * Returns the name of the module which is specified as argument of YANG {@code module} statement.
+     *
+     * @return string with the name of the module
+     */
+    String getName();
+
+    /**
+     * Returns a {@link QNameModule}, which contains the namespace and the revision of the module.
+     *
+     * @return QNameModule identifier.
+     */
+    QNameModule getQNameModule();
+
+    /**
+     * Returns the namespace of the module which is specified as argument of YANG {@code namespace}
+     * keyword. If you need both namespace and revision, please consider using {@link #getQNameModule()}.
+     *
+     * @return URI format of the namespace of the module
+     */
+    @Override
+    default URI getNamespace() {
+        return getQNameModule().getNamespace();
+    }
+
+    /**
+     * Returns the revision date for the module. If you need both namespace and
+     * revision, please consider using {@link #getQNameModule()}.
+     *
+     * @return date of the module revision which is specified as argument of YANG {@code revison} statement
+     */
+    @Override
+    default Optional<Revision> getRevision() {
+        return getQNameModule().getRevision();
+    }
+
+    /**
+     * Returns the semantic version of YANG module. If the semantic version is not specified, default semantic version
+     * of module is returned.
+     *
+     * @return SemVer semantic version of YANG module which is specified as argument of
+     *         {@code (urn:opendaylight:yang:extension:semantic-version?revision=2016-02-02)semantic-version} statement
+     */
+    Optional<SemVer> getSemanticVersion();
+
+    /**
+     * Returns the prefix of the module.
+     *
+     * @return string with the module prefix which is specified as argument of YANG {@code prefix} statement
+     */
+    String getPrefix();
+
+    /**
+     * Returns the YANG version.
+     *
+     * @return YANG version of this module.
+     */
+    YangVersion getYangVersion();
+
+    /**
+     * Returns the module organization.
+     *
+     * @return string with the name of the organization specified in the module as the argument of YANG
+     *         {@code organization} statement
+     */
+    Optional<String> getOrganization();
+
+    /**
+     * Returns the module contact.
+     *
+     * <p>
+     * The contact represents the person or persons to whom technical queries concerning this module should be sent,
+     * such as their name, postal address, telephone number, and electronic mail address.
+     *
+     * @return string with the contact data specified in the module as the argument of YANG {@code contact} statement
+     */
+    Optional<String> getContact();
+
+    /**
+     * Returns imports which represents YANG modules which are imported to this module via {@code import} statement.
+     *
+     * @return set of module imports which are specified in the module as the argument of YANG {@code import}
+     *         statements.
+     */
+    Set<ModuleImport> getImports();
+
+    Set<Module> getSubmodules();
+
+    /**
+     * Returns {@link FeatureDefinition} instances which contain data from {@code feature} statements defined in the
+     * module.
+     *
+     * <p>
+     * The feature is used to define a mechanism by which portions of the schema are marked as conditional.
+     *
+     * @return feature statements in lexicographical order which are specified in the module as the argument of YANG
+     *         {@code feature} statements.
+     */
+    Set<FeatureDefinition> getFeatures();
+
+    /**
+     * Returns {@link AugmentationSchemaNode} instances which contain data from {@code augment} statements defined
+     * in the module.
+     *
+     * @return set of the augmentation schema instances which are specified in the module as YANG {@code augment}
+     *         statement and are lexicographically ordered
+     */
+    Set<AugmentationSchemaNode> getAugmentations();
+
+    /**
+     * Returns {@link RpcDefinition} instances which contain data from {@code rpc} statements defined in the module.
+     *
+     * @return set of the RPC definition instances which are specified in the module as YANG {@code rpc} statements and
+     *         are lexicographicaly ordered
+     */
+    Set<RpcDefinition> getRpcs();
+
+    /**
+     * Returns {@link Deviation} instances which contain data from {@code deviation} statements defined in the module.
+     *
+     * @return set of the deviation instances
+     */
+    Set<Deviation> getDeviations();
+
+    /**
+     * Returns {@link IdentitySchemaNode} instances which contain data from {@code identity} statements defined in the
+     * module.
+     *
+     * @return set of identity schema node instances which are specified in the module as YANG {@code identity}
+     *         statements and are lexicographically ordered
+     */
+    Set<IdentitySchemaNode> getIdentities();
+
+    /**
+     * Returns {@link ExtensionDefinition} instances which contain data from {@code extension} statements defined in
+     * the module.
+     *
+     * @return set of extension definition instances which are specified in the module as YANG {@code extension}
+     *         statements and are lexicographically ordered
+     */
+    List<ExtensionDefinition> getExtensionSchemaNodes();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ModuleImport.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ModuleImport.java
new file mode 100644 (file)
index 0000000..43976a8
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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.model.api;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.common.Revision;
+
+/**
+ * Interface describing YANG 'import' statement.
+ *
+ * <p>
+ * The import statement makes definitions from one module available inside another module or submodule.
+ */
+// FIXME: 5.0.0 this class is a leak of the declared world into the effective one. In effective world, all nodes form
+//        a tree, which consists of multiple (mostly) QName-navigated namespaces. As such module imports contribute
+//        only a prefix/QNameModule mapping to the effective world and hence should be mapped that way:
+//        - Module exposes String->QNameModule mapping
+public interface ModuleImport extends DocumentedNode {
+    /**
+     * Returns the name of the module to import.
+     *
+     * @return Name of the module to import
+     */
+    String getModuleName();
+
+    /**
+     * Returns the module revision to import. May be null.
+     *
+     * @return Revision of module to import
+     */
+    Optional<Revision> getRevision();
+
+    /**
+     * Returns the semantic version to import.
+     *
+     * @return Semantic version of module to import
+     */
+    Optional<SemVer> getSemanticVersion();
+
+    /**
+     * Returns the prefix associated with the imported module.
+     *
+     * @return Prefix used to point to imported module
+     */
+    String getPrefix();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/MustConstraintAware.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/MustConstraintAware.java
new file mode 100644 (file)
index 0000000..609ff1f
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api;
+
+import java.util.Collection;
+
+/**
+ * Mix-in interface for nodes which can define must constraints.
+ *
+ * @author Robert Varga
+ */
+public interface MustConstraintAware {
+    /**
+     * Specifies the rules which the node which contains <code>must</code> YANG substatement has to match.
+     *
+     * @return collection of <code>MustDefinition</code> (XPath) instances which represents the concrete data
+     *         constraints
+     */
+    Collection<MustDefinition> getMustConstraints();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/MustDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/MustDefinition.java
new file mode 100644 (file)
index 0000000..9d3480e
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.model.api;
+
+/**
+ * Contains methods for accessing constraint declaration for valid data in form
+ * of XPath expressions.<br>
+ * <br>
+ * <i>YANG example:<br>
+ * <code>must "ifType != 'ethernet' or (ifType = 'ethernet' and ifMTU = 1500)";</code>
+ * </i>
+ */
+public interface MustDefinition extends ConstraintMetaDefinition {
+
+    /**
+     * Returns XPath expression which contains constraint.
+     *
+     * @return XPath expression which represents the value of the argument of
+     *         the <code>must</code> YANG substatement
+     */
+    RevisionAwareXPath getXpath();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NamespaceRevisionAware.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NamespaceRevisionAware.java
new file mode 100644 (file)
index 0000000..7e905f5
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * 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.model.api;
+
+import java.net.URI;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.Revision;
+
+public interface NamespaceRevisionAware {
+
+    URI getNamespace();
+
+    Optional<Revision> getRevision();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NotificationDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NotificationDefinition.java
new file mode 100644 (file)
index 0000000..ad3577e
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * 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.model.api;
+
+/**
+ * Interface describing YANG 'notification' statement. The notification
+ * statement is used to define a NETCONF notification.
+ */
+public interface NotificationDefinition extends SchemaNode, DataNodeContainer, AugmentationTarget, CopyableNode,
+        MustConstraintAware {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NotificationNodeContainer.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NotificationNodeContainer.java
new file mode 100644 (file)
index 0000000..8a0d099
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface NotificationNodeContainer {
+    /**
+     * Return the set of notifications in this container, keyed by QName. RFC7950 specifies that
+     * {@link AugmentationSchemaNode}s, {@link GroupingDefinition}s, {@link ListSchemaNode}s and
+     * {@link ContainerSchemaNode}s can also contain {@link NotificationDefinition}s.
+      *
+     * @return set of notification nodes
+     */
+    @NonNull Set<NotificationDefinition> getNotifications();
+
+    /**
+     * Find a notification based on its QName. Default implementation searches the set returned by
+     * {@link #getNotifications()}.
+     *
+     * @param qname Notification QName
+     * @return Notification definition, if found
+     * @throws NullPointerException if qname is null
+     */
+    default Optional<NotificationDefinition> findNotification(final QName qname) {
+        requireNonNull(qname);
+        for (NotificationDefinition notif : getNotifications()) {
+            if (qname.equals(notif.getQName())) {
+                return Optional.of(notif);
+            }
+        }
+        return Optional.empty();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/OperationDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/OperationDefinition.java
new file mode 100644 (file)
index 0000000..4f285d8
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 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.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Set;
+
+/**
+ * Common interface for an operation.
+ */
+@Beta
+public interface OperationDefinition extends SchemaNode {
+    /**
+     * Returns the set of type definitions declared under this operation.
+     *
+     * @return Set of type definitions declared under this operation.
+     */
+    Set<TypeDefinition<?>> getTypeDefinitions();
+
+    /**
+     * Returns the set of grouping statements declared under this operation.
+     *
+     * @return Set of grouping statements declared under this operation.
+     */
+    Set<GroupingDefinition> getGroupings();
+
+    /**
+     * Returns definition of input parameters for this operation.
+     *
+     * @return Definition of input parameters for this operation.
+     *         The substatements of input define nodes under the operation's input node.
+     */
+    ContainerSchemaNode getInput();
+
+    /**
+     * Returns definition of output parameters for this operation.
+     *
+     * @return Definition of output parameters for this operation. The
+     *         substatements of output define nodes under the operation's output node.
+     */
+    ContainerSchemaNode getOutput();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/PathExpression.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/PathExpression.java
new file mode 100644 (file)
index 0000000..fef07e7
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.api;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
+import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
+
+/**
+ * An expression as defined in <a href="https://tools.ietf.org/html/rfc7950#section-9.9.2">RFC7950 Section 9.9.2</a>,
+ * i.e. the argument of a {@code path} statement.
+ *
+ * <p>
+ * Semantically a {@link PathExpression} is similar to a {@link YangXPathExpression} with guarantees around what
+ * subexpressions it can contain:
+ * <ul>
+ * <li>the root expression must be a {@link YangLocationPath}</li>
+ * <li>it can contain steps only along {@link YangXPathAxis#CHILD} and {@link YangXPathAxis#PARENT} axis</li>
+ * <li>all steps along {@link YangXPathAxis#CHILD} axis are {@link QNameStep}</li>
+ * <li>the only function invocation is {@link YangFunction#CURRENT}</li>
+ * <li>only {@link YangBinaryOperator#EQUALS} is allowed</li>
+ * <li>no literals nor numbers are allowed</li>
+ * <li>all qualified node identifiers must me resolved</li>
+ * </ul>
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public interface PathExpression extends Immutable {
+    /**
+     * Returns the path expression formatted string as is defined in model. For example:
+     * {@code /prefix:container/prefix:container::cond[when()=foo]/prefix:leaf}
+     *
+     * @return the path expression formatted string as is defined in model.
+     */
+    String getOriginalString();
+
+    /**
+     * Return the {@link YangLocationPath} of this expression.
+     *
+     * @return The location path
+     * @throws UnsupportedOperationException if the implementation has not parsed the string. Implementations are
+     *         strongly encouraged to perform proper parsing.
+     */
+    YangLocationPath getLocation();
+
+    /**
+     * Returns <code>true</code> if the XPapth starts in root of YANG model, otherwise returns <code>false</code>.
+     *
+     * @return <code>true</code> if the XPapth starts in root of YANG model, otherwise returns <code>false</code>
+     */
+    default boolean isAbsolute() {
+        return getLocation().isAbsolute();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/RevisionAwareXPath.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/RevisionAwareXPath.java
new file mode 100644 (file)
index 0000000..46a1e99
--- /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.yang.model.api;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression.QualifiedBound;
+
+/**
+ * Contains methods for getting data (concrete XPath) and metadata (is XPath absolute) from XPath instance.
+ */
+// FIXME: 5.0.0: find a better name for this interface
+public interface RevisionAwareXPath {
+    /**
+     * Returns <code>true</code> if the XPapth starts in root of Yang model, otherwise returns <code>false</code>.
+     *
+     * @return <code>true</code> if the XPapth starts in root of Yang model, otherwise returns <code>false</code>
+     */
+    boolean isAbsolute();
+
+    /**
+     * Returns the XPath formatted string as is defined in model. For example:
+     * /prefix:container/prefix:container::cond[when()=foo]/prefix:leaf
+     *
+     * @return the XPath formatted string as is defined in model.
+     */
+    @NonNull String getOriginalString();
+
+    @Beta
+    // FIXME: 5.0.0: integrate this interface into RevisionAwareXPath
+    interface WithExpression extends RevisionAwareXPath {
+        /**
+         * Return the {@link YangXPathExpression} of this XPath. The expression is required to be at least
+         * Qualified-bound.
+         *
+         * @return The location path
+         */
+        @NonNull QualifiedBound getXPathExpression();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/RpcDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/RpcDefinition.java
new file mode 100644 (file)
index 0000000..42b49cc
--- /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.model.api;
+
+/**
+ * Interface describing YANG 'rpc' statement.
+ *
+ * <p>
+ * The rpc statement defines an rpc node in the schema tree. Under the rpc node,
+ * a schema node with the name 'input', and a schema node with the name 'output'
+ * are also defined.
+ */
+public interface RpcDefinition extends OperationDefinition {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaContext.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaContext.java
new file mode 100644 (file)
index 0000000..9ef5971
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * 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.model.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+
+/**
+ * The interface represents static view of compiled yang files,
+ * contains the methods for obtaining all the top level context
+ * data (data from all modules) like YANG notifications, extensions,
+ * operations...
+ * Instances MUST be immutable and thus usage in multi threaded
+ * environment is safe.
+ */
+// FIXME: 5.0.0: ContainerSchemaNode is far too broad. A combination of DataNodeContainer, NotificationNodeContainer
+//               and possibly DataSchemaNode would reflect SchemaContext traits better.
+// FIXME: 5.0.0: consider deprecating this class in favor of EffectiveModelContext
+public interface SchemaContext extends ContainerSchemaNode, Immutable {
+    /**
+     * QName of NETCONF top-level data node.
+     */
+    @NonNull QName NAME = QName.create(URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"), "data").intern();
+
+    /**
+     * Returns data schema node instances which represents direct subnodes (like
+     * leaf, leaf-list, list, container) in all YANG modules in the context.
+     *
+     * @return set of <code>DataSchemaNode</code> instances which represents
+     *         YANG data nodes at the module top level
+     */
+    Set<DataSchemaNode> getDataDefinitions();
+
+    /**
+     * Returns modules which are part of the schema context. Returned set is required to have its iteration ordered
+     * by module revision, so that if modules are filtered by {@link Module#getName()} or {@link Module#getNamespace()},
+     * modules having the same attribute are encountered newest revision first.
+     *
+     * @return set of the modules which belong to the schema context
+     */
+    Set<Module> getModules();
+
+    /**
+     * Returns rpc definition instances which are defined as the direct
+     * subelements in all YANG modules in the context.
+     *
+     * @return set of <code>RpcDefinition</code> instances which represents
+     *         nodes defined via <code>rpc</code> YANG keyword
+     */
+    Set<RpcDefinition> getOperations();
+
+    /**
+     * Returns extension definition instances which are defined as the direct
+     * subelements in all YANG modules in the context.
+     *
+     * @return set of <code>ExtensionDefinition</code> instances which
+     *         represents nodes defined via <code>extension</code> YANG keyword
+     */
+    Set<ExtensionDefinition> getExtensions();
+
+    /**
+     * Returns the module matching specified {@link QNameModule}, if present.
+     *
+     * @param qnameModule requested QNameModule
+     * @return Module, if present.
+     * @throws NullPointerException if qnameModule is null
+     */
+    Optional<Module> findModule(@NonNull QNameModule qnameModule);
+
+    /**
+     * Returns module instance (from the context) with specified namespace and no revision.
+     *
+     * @param namespace module namespace
+     * @return module instance which has name and revision the same as are the values specified in parameters
+     *         <code>namespace</code> and no revision.
+     */
+    default Optional<Module> findModule(final @NonNull URI namespace) {
+        return findModule(QNameModule.create(namespace));
+    }
+
+    /**
+     * Returns module instance (from the context) with specified namespace and revision.
+     *
+     * @param namespace module namespace
+     * @param revision module revision, may be null
+     * @return module instance which has name and revision the same as are the values specified in parameters
+     *         <code>namespace</code> and <code>revision</code>.
+     */
+    default Optional<Module> findModule(final @NonNull URI namespace, final @Nullable Revision revision) {
+        return findModule(QNameModule.create(namespace, revision));
+    }
+
+    /**
+     * Returns module instance (from the context) with specified namespace and revision.
+     *
+     * @param namespace module namespace
+     * @param revision module revision, may be null
+     * @return module instance which has name and revision the same as are the values specified in parameters
+     *         <code>namespace</code> and <code>revision</code>.
+     */
+    default Optional<Module> findModule(final @NonNull URI namespace, final @NonNull Optional<Revision> revision) {
+        return findModule(QNameModule.create(namespace, revision));
+    }
+
+    /**
+     * Returns module instance (from the context) with specified name and an optional revision.
+     *
+     * @param name
+     *            string with the module name
+     * @param revision
+     *            date of the module revision
+     * @return module instance which has name and revision the same as are the values specified in parameters
+     *                <code>name</code> and <code>revision</code>.
+     */
+    default Optional<Module> findModule(final String name, final Optional<Revision> revision) {
+        return findModules(name).stream().filter(module -> revision.equals(module.getRevision())).findAny();
+    }
+
+    /**
+     * Returns module instance (from the context) with specified name and revision.
+     *
+     * @param name
+     *            string with the module name
+     * @param revision
+     *            date of the module revision, may be null
+     * @return module instance which has name and revision the same as are the values specified in parameters
+     *         <code>name</code> and <code>revision</code>.
+     */
+    default Optional<Module> findModule(final String name, final @Nullable Revision revision) {
+        return findModule(name, Optional.ofNullable(revision));
+    }
+
+    /**
+     * Returns module instance (from the context) with specified name and no revision.
+     *
+     * @param name string with the module name
+     * @return module instance which has name and revision the same as are the values specified in <code>name</code>
+     *                and no revision.
+     * @throws NullPointerException if name is null
+     */
+    default Optional<Module> findModule(final String name) {
+        return findModule(name, Optional.empty());
+    }
+
+    /**
+     * Returns module instances (from the context) with a concrete name. Returned Set is required to have its iteration
+     * order guarantee that the latest revision is encountered first.
+     *
+     * @param name
+     *            string with the module name
+     * @return set of module instances with specified name.
+     */
+    default Set<Module> findModules(final String name) {
+        return Sets.filter(getModules(), m -> name.equals(m.getName()));
+    }
+
+    /**
+     * Returns module instance (from the context) with concrete namespace. Returned Set is required to have its
+     * iteration order guarantee that the latest revision is encountered first.
+     *
+     * @param namespace
+     *            URI instance with specified namespace
+     * @return module instance which has namespace equal to the
+     *         <code>namespace</code> or <code>null</code> in other cases
+     */
+    default Set<Module> findModules(final URI namespace) {
+        return Sets.filter(getModules(), m -> namespace.equals(m.getNamespace()));
+    }
+
+    @Override
+    default Set<ActionDefinition> getActions() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    default Optional<ActionDefinition> findAction(final QName qname) {
+        requireNonNull(qname);
+        return Optional.empty();
+    }
+
+    @Override
+    default Optional<NotificationDefinition> findNotification(final QName qname) {
+        final Optional<Set<NotificationDefinition>> defs = findModule(qname.getModule()).map(Module::getNotifications);
+        if (defs.isPresent()) {
+            for (NotificationDefinition def : defs.get()) {
+                if (qname.equals(def.getQName())) {
+                    return Optional.of(def);
+                }
+            }
+        }
+        return Optional.empty();
+    }
+
+    @Override
+    default Optional<String> getDescription() {
+        return Optional.empty();
+    }
+
+    @Override
+    default Optional<String> getReference() {
+        return Optional.empty();
+    }
+
+    @Override
+    default Collection<MustDefinition> getMustConstraints() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    default Optional<RevisionAwareXPath> getWhenCondition() {
+        return Optional.empty();
+    }
+
+    @Beta
+    @Override
+    default Optional<DataSchemaNode> findDataTreeChild(final QName name) {
+        return findModule(name.getModule()).flatMap(mod -> mod.findDataTreeChild(name));
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaContextListener.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaContextListener.java
new file mode 100644 (file)
index 0000000..89bbffa
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.api;
+
+import java.util.EventListener;
+
+/**
+ * Interface for listeners interested in updates of the global schema context.
+ * The global schema context reflects the global view of the model world, and
+ * all entities interacting at the global scale need to maintain a consistent
+ * view of that world.
+ */
+public interface SchemaContextListener extends EventListener {
+    /**
+     * The global schema context is being updated.
+     * @param context New global schema context
+     */
+    void onGlobalContextUpdated(SchemaContext context);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaContextProvider.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaContextProvider.java
new file mode 100644 (file)
index 0000000..3ee9bad
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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.model.api;
+
+import org.eclipse.jdt.annotation.NonNull;
+
+// FIXME: document this API contract
+public interface SchemaContextProvider {
+    /**
+     * Return the {@link SchemaContext} attached to this object.
+     *
+     * @return An SchemaContext instance.
+     * @throws IllegalStateException if the context is not available.
+     */
+    @NonNull SchemaContext getSchemaContext();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaNode.java
new file mode 100644 (file)
index 0000000..b308ec3
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.model.api;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * SchemaNode represents a node in schema tree.
+ */
+public interface SchemaNode extends DocumentedNode.WithStatus {
+    /**
+     * Returns QName of the instance of the type <code>SchemaNode</code>.
+     *
+     * @return QName with the name of the schema node
+     */
+    @NonNull QName getQName();
+
+    /**
+     * Returns the schema path of the instance of the type {@code SchemaNode}.
+     *
+     * @return schema path of the schema node
+     */
+    @NonNull SchemaPath getPath();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaPath.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaPath.java
new file mode 100644 (file)
index 0000000..f2ce302
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * 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.model.api;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.UnmodifiableIterator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Represents unique path to the every node inside the module.
+ */
+public abstract class SchemaPath implements Immutable {
+
+    /**
+     * An absolute SchemaPath.
+     */
+    private static final class AbsoluteSchemaPath extends SchemaPath {
+        private AbsoluteSchemaPath(final SchemaPath parent, final QName qname) {
+            super(parent, qname);
+        }
+
+        @Override
+        public boolean isAbsolute() {
+            return true;
+        }
+
+        @Override
+        public AbsoluteSchemaPath createChild(final QName element) {
+            return new AbsoluteSchemaPath(this, requireNonNull(element));
+        }
+    }
+
+    /**
+     * A relative SchemaPath.
+     */
+    private static final class RelativeSchemaPath extends SchemaPath {
+        private RelativeSchemaPath(final SchemaPath parent, final QName qname) {
+            super(parent, qname);
+        }
+
+        @Override
+        public boolean isAbsolute() {
+            return false;
+        }
+
+        @Override
+        public RelativeSchemaPath createChild(final QName element) {
+            return new RelativeSchemaPath(this, requireNonNull(element));
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<SchemaPath, ImmutableList> LEGACYPATH_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(SchemaPath.class, ImmutableList.class, "legacyPath");
+
+    /**
+     * Shared instance of the conceptual root schema node.
+     */
+    public static final @NonNull SchemaPath ROOT = new AbsoluteSchemaPath(null, null);
+
+    /**
+     * Shared instance of the "same" relative schema node.
+     */
+    public static final @NonNull SchemaPath SAME = new RelativeSchemaPath(null, null);
+
+    /**
+     * Parent path.
+     */
+    private final SchemaPath parent;
+
+    /**
+     * This component.
+     */
+    private final QName qname;
+
+    /**
+     * Cached hash code. We can use this since we are immutable.
+     */
+    private final int hash;
+
+    /**
+     * Cached legacy path, filled-in when {@link #getPath()} or {@link #getPathTowardsRoot()}
+     * is invoked.
+     */
+    private volatile ImmutableList<QName> legacyPath;
+
+    SchemaPath(final SchemaPath parent, final QName qname) {
+        this.parent = parent;
+        this.qname = qname;
+
+        int tmp = Objects.hashCode(parent);
+        if (qname != null) {
+            tmp = tmp * 31 + qname.hashCode();
+        }
+
+        hash = tmp;
+    }
+
+    private ImmutableList<QName> getLegacyPath() {
+        ImmutableList<QName> ret = legacyPath;
+        if (ret == null) {
+            final List<QName> tmp = new ArrayList<>();
+            for (QName item : getPathTowardsRoot()) {
+                tmp.add(item);
+            }
+            ret = ImmutableList.copyOf(Lists.reverse(tmp));
+            LEGACYPATH_UPDATER.lazySet(this, ret);
+        }
+
+        return ret;
+    }
+
+    /**
+     * Returns the complete path to schema node.
+     *
+     * @return list of <code>QName</code> instances which represents complete
+     *         path to schema node
+     *
+     * @deprecated Use {@link #getPathFromRoot()} instead.
+     */
+    @Deprecated
+    public List<QName> getPath() {
+        return getLegacyPath();
+    }
+
+    /**
+     * Constructs new instance of this class with the concrete path.
+     *
+     * @param path
+     *            list of QName instances which specifies exact path to the
+     *            module node
+     * @param absolute
+     *            boolean value which specifies if the path is absolute or
+     *            relative
+     *
+     * @return A SchemaPath instance.
+     */
+    public static @NonNull SchemaPath create(final Iterable<QName> path, final boolean absolute) {
+        return (absolute ? ROOT : SAME).createChild(path);
+    }
+
+    /**
+     * Constructs new instance of this class with the concrete path.
+     *
+     * @param absolute
+     *            boolean value which specifies if the path is absolute or
+     *            relative
+     * @param element
+     *            a single QName which specifies exact path to the
+     *            module node
+     *
+     * @return A SchemaPath instance.
+     */
+    public static @NonNull SchemaPath create(final boolean absolute, final QName element) {
+        return (absolute ? ROOT : SAME).createChild(element);
+    }
+
+    /**
+     * Constructs new instance of this class with the concrete path.
+     *
+     * @param absolute
+     *            boolean value which specifies if the path is absolute or
+     *            relative
+     * @param path
+     *            one or more QName instances which specifies exact path to the
+     *            module node
+     *
+     * @return A SchemaPath instance.
+     */
+    public static @NonNull SchemaPath create(final boolean absolute, final QName... path) {
+        return create(Arrays.asList(path), absolute);
+    }
+
+    /**
+     * Create a child path based on concatenation of this path and a relative path.
+     *
+     * @param relative Relative path
+     * @return A new child path
+     */
+    public @NonNull SchemaPath createChild(final Iterable<QName> relative) {
+        if (Iterables.isEmpty(relative)) {
+            return this;
+        }
+
+        SchemaPath parentPath = this;
+        for (QName item : relative) {
+            parentPath = parentPath.createChild(item);
+        }
+
+        return parentPath;
+    }
+
+    /**
+     * Create a child path based on concatenation of this path and a relative path.
+     *
+     * @param relative Relative SchemaPath
+     * @return A new child path
+     */
+    public @NonNull SchemaPath createChild(final SchemaPath relative) {
+        checkArgument(!relative.isAbsolute(), "Child creation requires relative path");
+        return createChild(relative.getPathFromRoot());
+    }
+
+    /**
+     * Create a child path based on concatenation of this path and an additional path element.
+     *
+     * @param element Relative SchemaPath elements
+     * @return A new child path
+     */
+    public abstract @NonNull SchemaPath createChild(QName element);
+
+    /**
+     * Create a child path based on concatenation of this path and additional
+     * path elements.
+     *
+     * @param elements Relative SchemaPath elements
+     * @return A new child path
+     */
+    public @NonNull SchemaPath createChild(final QName... elements) {
+        return createChild(Arrays.asList(elements));
+    }
+
+    /**
+     * Returns the list of nodes which need to be traversed to get from the
+     * starting point (root for absolute SchemaPaths) to the node represented
+     * by this object.
+     *
+     * @return list of <code>qname</code> instances which represents
+     *         path from the root to the schema node.
+     */
+    public Iterable<QName> getPathFromRoot() {
+        return getLegacyPath();
+    }
+
+    /**
+     * Returns the list of nodes which need to be traversed to get from this
+     * node to the starting point (root for absolute SchemaPaths).
+     *
+     * @return list of <code>qname</code> instances which represents
+     *         path from the schema node towards the root.
+     */
+    public Iterable<QName> getPathTowardsRoot() {
+        return () -> new UnmodifiableIterator<QName>() {
+            private SchemaPath current = SchemaPath.this;
+
+            @Override
+            public boolean hasNext() {
+                return current.parent != null;
+            }
+
+            @Override
+            public QName next() {
+                if (current.parent != null) {
+                    final QName ret = current.qname;
+                    current = current.parent;
+                    return ret;
+                }
+
+                throw new NoSuchElementException("No more elements available");
+            }
+        };
+    }
+
+    /**
+     * Returns the immediate parent SchemaPath.
+     *
+     * @return Parent path, null if this SchemaPath is already toplevel.
+     */
+    public SchemaPath getParent() {
+        return parent;
+    }
+
+    /**
+     * Get the last component of this path.
+     *
+     * @return The last component of this path.
+     */
+    public final QName getLastComponent() {
+        return qname;
+    }
+
+    /**
+     * Describes whether schema path is|isn't absolute.
+     *
+     * @return boolean value which is <code>true</code> if schema path is
+     *         absolute.
+     */
+    public abstract boolean isAbsolute();
+
+    @Override
+    public final int hashCode() {
+        return hash;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final SchemaPath other = (SchemaPath) obj;
+        return Objects.equals(qname, other.qname) && Objects.equals(parent, other.parent);
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("path", getPathFromRoot());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Status.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/Status.java
new file mode 100644 (file)
index 0000000..55f5ccd
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.model.api;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Enumeration describing YANG 'status' statement. If no status is specified, the
+ * default is CURRENT.
+ */
+@NonNullByDefault
+public enum Status {
+    /**
+     * CURRENT means that the definition is current and valid.
+     */
+    CURRENT,
+    /**
+     * DEPRECATED indicates an obsolete definition, but it permits new/
+     * continued implementation in order to foster interoperability with
+     * older/existing implementations.
+     */
+    DEPRECATED,
+    /**
+     * OBSOLETE means the definition is obsolete and SHOULD NOT be implemented
+     * and/or can be removed from implementations.
+     */
+    OBSOLETE
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/TypeAware.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/TypeAware.java
new file mode 100644 (file)
index 0000000..efd94bd
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api;
+
+/**
+ * Marker interface for SchemaNodes which store simple data, for which we have type information.
+ *
+ * @author Robert Varga
+ */
+public interface TypeAware {
+    /**
+     * Returns the type definition of stored data.
+     *
+     * @return type definition.
+     */
+    TypeDefinition<? extends TypeDefinition<?>> getType();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/TypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/TypeDefinition.java
new file mode 100644 (file)
index 0000000..341f78a
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.model.api;
+
+import java.util.Optional;
+
+/**
+ * YANG statement <code>typedef</code> contains also substatements
+ * <ul>
+ * <li><code>default</code> - default value which is compatible with
+ * <code>type</code>,</li>
+ * <li><code>type</code> - base type from which is <code>typedef</code> derived,
+ * </li>
+ * <li><code>units</code> - textual information about units associated with this
+ * type.</li>
+ * </ul>
+ * This interface contains the methods for getting the values of the arguments of substatements mentioned above.
+ * Furthermore {@link LeafSchemaNode} and {@link LeafListSchemaNode} interfaces contribute to their internal type
+ * definitions.
+ *
+ * @param <T>
+ *            type of the base type (YANG <code>type</code> substatement) which
+ *            is included in the instance of this type
+ */
+public interface TypeDefinition<T extends TypeDefinition<?>> extends SchemaNode {
+    /**
+     * Returns the base type from which this type is derived. If this is yang built-in type, returns null.
+     *
+     * @return value of <code>&lt;T&gt;</code> type which represents the base
+     *         type of instance of the <code>TypeDefinition</code> type or null,
+     *         if this is yang built-in type
+     */
+    T getBaseType();
+
+    /**
+     * Returns the unit which represents the value of the argument of the <code>units</code> substatement of the YANG
+     * <code>typedef</code>, <code>leaf</code> or <code>leaf-list</code> statements.
+     *
+     * @return string with units in which is type measured
+     */
+    Optional<String> getUnits();
+
+    /**
+     * Returns the default value which represents the value of the argument of the <code>default</code> substatement
+     * of the YANG <code>typedef</code> or <code>leaf</code> statement.
+     *
+     * @return instance of <code>Object</code> type which contains default value for <code>typedef</code>
+     */
+    Optional<? extends Object> getDefaultValue();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/TypedDataSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/TypedDataSchemaNode.java
new file mode 100644 (file)
index 0000000..f6ff17d
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016 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.api;
+
+/**
+ * A {@link DataSchemaNode} which holds values of the same type. This can be either a single value, like
+ * in a {@link LeafSchemaNode} or multiple values, like a {@link LeafListSchemaNode}.
+ *
+ * @author Robert Varga
+ */
+public interface TypedDataSchemaNode extends DataSchemaNode, TypeAware {
+    /**
+     * Returns type of the instance which implements <code>DataSchemaNode</code>.
+     *
+     * @return type definition of leaf or leaf-list schema node which represents the
+     *         value of the argument of the YANG <code>type</code> substatement
+     *         of the <code>leaf</code> or <code>leaf-list</code> statement
+     */
+    @Override
+    TypeDefinition<? extends TypeDefinition<?>> getType();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/UniqueConstraint.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/UniqueConstraint.java
new file mode 100644 (file)
index 0000000..92ba553
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016 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.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Relative;
+
+/**
+ * Interface describing YANG 'unique' constraint.
+ *
+ * <p>
+ * The 'unique' constraint specifies that the combined values of all the leaf instances specified in the argument
+ * string, including leafs with default values, MUST be unique within all list entry instances in which all referenced
+ * leafs exist (for more information see RFC-6020 section 7.8.3.).
+ */
+@Beta
+public interface UniqueConstraint {
+    @NonNull Set<Relative> getTag();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/UnknownSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/UnknownSchemaNode.java
new file mode 100644 (file)
index 0000000..274c465
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.model.api;
+
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Contains the methods for getting the details about the unknown node.
+ */
+public interface UnknownSchemaNode extends SchemaNode, AddedByUsesAware {
+    /**
+     * Returns QName instance with the name of the unknown node.
+     *
+     * @return QName with name the name of the unknown node.
+     */
+    QName getNodeType();
+
+    /**
+     * Returns name of the unknown node.
+     *
+     * @return string with the name of unknown node.
+     */
+    String getNodeParameter();
+
+    /**
+     * Describes whether the node was added through <code>augment</code> YANG
+     * statement.
+     *
+     * @return boolean value which is <code>true</code> if the node is added by
+     *         <code>augment</code> YANG statement
+     *
+     * @deprecated This method exposes mechanism of how this node was instantiated. This runs contrary to the idea
+     *             that a SchemaNode is part of the effective model of the world. Examining a node's DeclaredStatement
+     *             world should be sufficient to ascertain its origin.
+     */
+    @Deprecated
+    boolean isAddedByAugmentation();
+
+    /**
+     * Get extension definition which identifies this node.
+     *
+     * @return extension definition if exists, null otherwise
+     */
+    ExtensionDefinition getExtensionDefinition();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/UsesNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/UsesNode.java
new file mode 100644 (file)
index 0000000..f8e163c
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.model.api;
+
+import java.util.Map;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+
+/**
+ * Contains the methods for getting data and checking properties of the YANG <code>uses</code> substatement.
+ */
+public interface UsesNode extends WhenConditionAware, WithStatus, CopyableNode {
+    /**
+     * Returns the schema path to used grouping.
+     *
+     * @return schema path to 'grouping' on which this 'uses' statement points
+     */
+    @NonNull SchemaPath getGroupingPath();
+
+    /**
+     * Returns augmentations which were specified in this uses node.
+     *
+     * @return Set of augment statements defined under this uses node
+     */
+    @NonNull Set<AugmentationSchemaNode> getAugmentations();
+
+    /**
+     * Some of the properties of each node in the grouping can be refined with the "refine" statement.
+     *
+     * @return Map, where key is schema path of refined node and value is refined node
+     */
+    @NonNull Map<SchemaPath, SchemaNode> getRefines();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/WhenConditionAware.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/WhenConditionAware.java
new file mode 100644 (file)
index 0000000..1b07f48
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+
+/**
+ * Mix-in interface for nodes which can be conditional on a when statement.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface WhenConditionAware {
+    /**
+     * Returns when statement.
+     *
+     * <p>
+     * If when condition is present node defined by the parent data definition
+     * statement is only valid when the returned XPath
+     * expression conceptually evaluates to "true"
+     * for a particular instance, then the node defined by the parent data
+     * definition statement is valid; otherwise, it is not.
+     *
+     * @return XPath condition
+     */
+    Optional<RevisionAwareXPath> getWhenCondition();
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/YangStmtMapping.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/YangStmtMapping.java
new file mode 100644 (file)
index 0000000..0aafcc7
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2015 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.ActionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ActionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnydataEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnydataStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ArgumentEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ArgumentStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AugmentEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BaseEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BaseStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BitEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BitStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.CaseStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContactEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContactStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DefaultEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DefaultStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviateEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviateStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviationEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviationStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.EnumEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.EnumStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorAppTagEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorAppTagStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FractionDigitsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FractionDigitsStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.GroupingEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.GroupingStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ImportEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ImportStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IncludeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IncludeStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.InputStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.KeyEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.KeyStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafListEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafListStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LengthEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LengthStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MaxElementsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MaxElementsStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModifierEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModifierStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MustEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MustStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NotificationStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OrderedByEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OrderedByStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OrganizationEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OrganizationStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OutputStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PathEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PathStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PatternEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PatternStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PositionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PositionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PresenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PresenceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RangeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RangeStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RefineEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RequireInstanceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RequireInstanceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RpcStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UniqueEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UniqueStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnitsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnitsStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UsesEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YangVersionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YangVersionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YinElementEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YinElementStatement;
+
+/**
+ * Mapping for both RFC6020 and RFC7950 statements.
+ */
+@Beta
+@NonNullByDefault
+public enum YangStmtMapping implements StatementDefinition {
+    ACTION(ActionStatement.class, ActionEffectiveStatement.class, "action", "name"),
+    ANYDATA(AnydataStatement.class, AnydataEffectiveStatement.class, "anydata", "name"),
+    ANYXML(AnyxmlStatement.class, AnyxmlEffectiveStatement.class, "anyxml", "name"),
+    ARGUMENT(ArgumentStatement.class, ArgumentEffectiveStatement.class, "argument", "name"),
+    AUGMENT(AugmentStatement.class, AugmentEffectiveStatement.class, "augment", "target-node"),
+    BASE(BaseStatement.class, BaseEffectiveStatement.class, "base", "name"),
+    BELONGS_TO(BelongsToStatement.class, BelongsToEffectiveStatement.class, "belongs-to", "module"),
+    BIT(BitStatement.class, BitEffectiveStatement.class, "bit", "name"),
+    CASE(CaseStatement.class, CaseEffectiveStatement.class, "case", "name"),
+    CHOICE(ChoiceStatement.class, ChoiceEffectiveStatement.class, "choice", "name"),
+    CONFIG(ConfigStatement.class, ConfigEffectiveStatement.class, "config", "value"),
+    CONTACT(ContactStatement.class, ContactEffectiveStatement.class, "contact", "text", true),
+    CONTAINER(ContainerStatement.class, ContainerEffectiveStatement.class, "container", "name"),
+    DEFAULT(DefaultStatement.class, DefaultEffectiveStatement.class, "default", "value"),
+    DESCRIPTION(DescriptionStatement.class, DescriptionEffectiveStatement.class, "description", "text", true),
+    DEVIATE(DeviateStatement.class, DeviateEffectiveStatement.class, "deviate", "value"),
+    DEVIATION(DeviationStatement.class, DeviationEffectiveStatement.class, "deviation", "target-node"),
+    ENUM(EnumStatement.class, EnumEffectiveStatement.class, "enum", "name"),
+    ERROR_APP_TAG(ErrorAppTagStatement.class, ErrorAppTagEffectiveStatement.class, "error-app-tag", "value"),
+    ERROR_MESSAGE(ErrorMessageStatement.class, ErrorMessageEffectiveStatement.class, "error-message", "value", true),
+    EXTENSION(ExtensionStatement.class, ExtensionEffectiveStatement.class, "extension", "name"),
+    FEATURE(FeatureStatement.class, FeatureEffectiveStatement.class, "feature", "name"),
+    FRACTION_DIGITS(FractionDigitsStatement.class, FractionDigitsEffectiveStatement.class, "fraction-digits", "value"),
+    GROUPING(GroupingStatement.class, GroupingEffectiveStatement.class, "grouping", "name"),
+    IDENTITY(IdentityStatement.class, IdentityEffectiveStatement.class, "identity", "name"),
+    IF_FEATURE(IfFeatureStatement.class, IfFeatureEffectiveStatement.class, "if-feature", "name"),
+    IMPORT(ImportStatement.class, ImportEffectiveStatement.class, "import", "module"),
+    INCLUDE(IncludeStatement.class, IncludeEffectiveStatement.class, "include", "module"),
+    INPUT(InputStatement.class, InputEffectiveStatement.class, "input"),
+    KEY(KeyStatement.class, KeyEffectiveStatement.class, "key", "value"),
+    LEAF(LeafStatement.class, LeafEffectiveStatement.class, "leaf", "name"),
+    LEAF_LIST(LeafListStatement.class, LeafListEffectiveStatement.class, "leaf-list", "name"),
+    LENGTH(LengthStatement.class, LengthEffectiveStatement.class, "length", "value"),
+    LIST(ListStatement.class, ListEffectiveStatement.class, "list", "name"),
+    MANDATORY(MandatoryStatement.class, MandatoryEffectiveStatement.class, "mandatory", "value"),
+    MAX_ELEMENTS(MaxElementsStatement.class, MaxElementsEffectiveStatement.class, "max-elements", "value"),
+    MIN_ELEMENTS(MinElementsStatement.class, MinElementsEffectiveStatement.class, "min-elements", "value"),
+    MODIFIER(ModifierStatement.class, ModifierEffectiveStatement.class, "modifier", "value"),
+    MODULE(ModuleStatement.class, ModuleEffectiveStatement.class, "module", "name"),
+    MUST(MustStatement.class, MustEffectiveStatement.class, "must", "condition"),
+    NAMESPACE(NamespaceStatement.class, NamespaceEffectiveStatement.class, "namespace", "uri"),
+    NOTIFICATION(NotificationStatement.class, NotificationEffectiveStatement.class, "notification", "name"),
+    ORDERED_BY(OrderedByStatement.class, OrderedByEffectiveStatement.class, "ordered-by", "value"),
+    ORGANIZATION(OrganizationStatement.class, OrganizationEffectiveStatement.class, "organization", "text", true),
+    OUTPUT(OutputStatement.class, OutputEffectiveStatement.class, "output"),
+    PATH(PathStatement.class, PathEffectiveStatement.class, "path", "value"),
+    PATTERN(PatternStatement.class, PatternEffectiveStatement.class, "pattern", "value"),
+    POSITION(PositionStatement.class, PositionEffectiveStatement.class, "position", "value"),
+    PREFIX(PrefixStatement.class, PrefixEffectiveStatement.class, "prefix", "value"),
+    PRESENCE(PresenceStatement.class, PresenceEffectiveStatement.class, "presence", "value"),
+    RANGE(RangeStatement.class, RangeEffectiveStatement.class, "range", "value"),
+    REFERENCE(ReferenceStatement.class, ReferenceEffectiveStatement.class, "reference", "text", true),
+    REFINE(RefineStatement.class, RefineEffectiveStatement.class, "refine", "target-node"),
+    REQUIRE_INSTANCE(RequireInstanceStatement.class, RequireInstanceEffectiveStatement.class, "require-instance",
+        "value"),
+    REVISION(RevisionStatement.class, RevisionEffectiveStatement.class, "revision", "date"),
+    REVISION_DATE(RevisionDateStatement.class, RevisionDateEffectiveStatement.class, "revision-date", "date"),
+    RPC(RpcStatement.class, RpcEffectiveStatement.class, "rpc", "name"),
+    STATUS(StatusStatement.class, StatusEffectiveStatement.class, "status", "value"),
+    SUBMODULE(SubmoduleStatement.class, SubmoduleEffectiveStatement.class, "submodule", "name"),
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    TYPE(TypeStatement.class, (Class) TypeEffectiveStatement.class, "type", "name"),
+    TYPEDEF(TypedefStatement.class, TypedefEffectiveStatement.class, "typedef", "name"),
+    UNIQUE(UniqueStatement.class, UniqueEffectiveStatement.class, "unique", "tag"),
+    UNITS(UnitsStatement.class, UnitsEffectiveStatement.class, "units", "name"),
+    USES(UsesStatement.class, UsesEffectiveStatement.class, "uses", "name"),
+    VALUE(ValueStatement.class, ValueEffectiveStatement.class, "value", "value"),
+    WHEN(WhenStatement.class, WhenEffectiveStatement.class, "when", "condition"),
+    YANG_VERSION(YangVersionStatement.class, YangVersionEffectiveStatement.class, "yang-version", "value"),
+    YIN_ELEMENT(YinElementStatement.class, YinElementEffectiveStatement.class, "yin-element", "value");
+
+    private final Class<? extends DeclaredStatement<?>> declaredType;
+    private final Class<? extends EffectiveStatement<?, ?>> effectiveType;
+    private final QName name;
+    private final @Nullable QName argument;
+    private final boolean yinElement;
+
+    @SuppressFBWarnings("NP_STORE_INTO_NONNULL_FIELD")
+    YangStmtMapping(final Class<? extends DeclaredStatement<?>> declared,
+            final Class<? extends EffectiveStatement<?, ?>> effective, final String nameStr) {
+        declaredType = requireNonNull(declared);
+        effectiveType = requireNonNull(effective);
+        name = yinQName(nameStr);
+        argument = null;
+        yinElement = false;
+    }
+
+    YangStmtMapping(final Class<? extends DeclaredStatement<?>> declared,
+            final Class<? extends EffectiveStatement<?, ?>> effective, final String nameStr, final String argumentStr) {
+        this(declared, effective, nameStr, argumentStr, false);
+    }
+
+    YangStmtMapping(final Class<? extends DeclaredStatement<?>> declared,
+            final Class<? extends EffectiveStatement<?, ?>> effective, final String nameStr, final String argumentStr,
+            final boolean yinElement) {
+        declaredType = requireNonNull(declared);
+        effectiveType = requireNonNull(effective);
+        name = yinQName(nameStr);
+        argument = yinQName(argumentStr);
+        this.yinElement = yinElement;
+    }
+
+    private static QName yinQName(final String nameStr) {
+        return QName.create(YangConstants.RFC6020_YIN_MODULE, nameStr).intern();
+    }
+
+    @Override
+    public QName getStatementName() {
+        return name;
+    }
+
+    @Override
+    public Optional<ArgumentDefinition> getArgumentDefinition() {
+        return ArgumentDefinition.ofNullable(argument, yinElement);
+    }
+
+    @Override
+    public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+        return declaredType;
+    }
+
+    @Override
+    public Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass() {
+        return effectiveType;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/AbstractStatementDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/AbstractStatementDefinition.java
new file mode 100644 (file)
index 0000000..150bff2
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.api.meta;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Abstract utility class to handle StatementArgumentDefinition aspect of {@link StatementDefinition}. Most users should
+ * use this class or {@link DefaultStatementDefinition}.
+ */
+@Beta
+@NonNullByDefault
+public abstract class AbstractStatementDefinition implements StatementDefinition {
+    private final QName statementName;
+    private final @Nullable QName argumentName;
+    private final boolean yinElement;
+
+    protected AbstractStatementDefinition(final QName statementName) {
+        this(statementName, false, null);
+    }
+
+    protected AbstractStatementDefinition(final QName statementName, final boolean yinElement,
+        final @Nullable QName argumentName) {
+        this.statementName = requireNonNull(statementName);
+        this.yinElement = yinElement;
+        this.argumentName = argumentName;
+    }
+
+    @Override
+    public final QName getStatementName() {
+        return statementName;
+    }
+
+    @Override
+    public final Optional<ArgumentDefinition> getArgumentDefinition() {
+        return ArgumentDefinition.ofNullable(argumentName, yinElement);
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        helper.add("name", statementName);
+        if (argumentName != null) {
+            helper.add("argument", argumentName).add("yin-element", yinElement);
+        }
+        return helper;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/ArgumentDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/ArgumentDefinition.java
new file mode 100644 (file)
index 0000000..194426c
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.api.meta;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+@Beta
+public abstract class ArgumentDefinition implements Immutable {
+    private static final class YinAttribute extends ArgumentDefinition {
+        YinAttribute(final QName argumentName) {
+            super(argumentName);
+        }
+
+        @Override
+        public boolean isYinElement() {
+            return false;
+        }
+    }
+
+    private static final class YinElement extends ArgumentDefinition {
+        YinElement(final QName argumentName) {
+            super(argumentName);
+        }
+
+        @Override
+        public boolean isYinElement() {
+            return true;
+        }
+    }
+
+    private final @NonNull QName argumentName;
+
+    ArgumentDefinition(final QName argumentName) {
+        this.argumentName = requireNonNull(argumentName);
+    }
+
+    public static @NonNull ArgumentDefinition of(final QName argumentName, final boolean yinElement) {
+        return yinElement ? new YinElement(argumentName) : new YinAttribute(argumentName);
+    }
+
+    public static @NonNull Optional<ArgumentDefinition> ofNullable(final @Nullable QName argumentName,
+            final boolean yinElement) {
+        return argumentName == null ? Optional.empty() : Optional.of(of(argumentName, yinElement));
+    }
+
+
+    public final @NonNull QName getArgumentName() {
+        return argumentName;
+    }
+
+    /**
+     * Returns true, if argument of statement is represented as value of YIN element. If argument of statement is
+     * represented as argument of YIN element, returns false.
+     *
+     * @return returns true, if statement argument is represented as value of YIN element, otherwise returns false.
+     */
+    public abstract boolean isYinElement();
+
+    @Override
+    public final int hashCode() {
+        return Boolean.hashCode(isYinElement()) * 31 + argumentName.hashCode();
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof ArgumentDefinition)) {
+            return false;
+        }
+        final ArgumentDefinition other = (ArgumentDefinition) obj;
+        return isYinElement() == other.isYinElement() && argumentName.equals(other.argumentName);
+    }
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(ArgumentDefinition.class).add("argumentName", argumentName)
+                .add("yinElement", isYinElement()).toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatement.java
new file mode 100644 (file)
index 0000000..ca2132e
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015 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.api.meta;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Collections2;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.stream.Stream;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Represents declared statement.
+ *
+ * @param <A> Argument type ({@link Void} if statement does not have argument.)
+ */
+public interface DeclaredStatement<A> extends ModelStatement<A> {
+    /**
+     * Returns statement argument as was present in original source.
+     *
+     * @return statement argument as was present in original source or null, if statement does not take argument.
+     */
+    @Nullable String rawArgument();
+
+    /**
+     * Returns collection of explicitly declared child statements, while preserving its original ordering from original
+     * source.
+     *
+     * @return Collection of statements, which were explicitly declared in source of model.
+     */
+    @NonNull Collection<? extends DeclaredStatement<?>> declaredSubstatements();
+
+    /**
+     * Returns collection of explicitly declared child statements, while preserving its original ordering from original
+     * source.
+     *
+     * @param type {@link DeclaredStatement} type
+     * @return Collection of statements, which were explicitly declared in source of model.
+     * @throws NullPointerException if {@code type} is null
+     */
+    default <S extends DeclaredStatement<?>> @NonNull Collection<? extends S> declaredSubstatements(
+            final Class<S> type) {
+        requireNonNull(type);
+        return Collections2.transform(Collections2.filter(declaredSubstatements(), type::isInstance), type::cast);
+    }
+
+    /**
+     * Find the first effective substatement of specified type.
+     *
+     * @param type {@link DeclaredStatement} type
+     * @return First declared substatement, or empty if no match is found.
+     * @throws NullPointerException if {@code type} is null
+     */
+    @Beta
+    default <T extends DeclaredStatement<?>> @NonNull Optional<T> findFirstDeclaredSubstatement(
+            @NonNull final Class<T> type) {
+        requireNonNull(type);
+        return streamDeclaredSubstatements(type).filter(type::isInstance).findFirst().map(type::cast);
+    }
+
+    /**
+     * Find the first declared substatement of specified type and return its value.
+     *
+     * @return First declared substatement's argument, or empty if no match is found.
+     * @throws NullPointerException if {@code type} is null
+     */
+    @Beta
+    default <V, T extends DeclaredStatement<V>> @NonNull Optional<V> findFirstDeclaredSubstatementArgument(
+            @NonNull final Class<T> type) {
+        return findFirstDeclaredSubstatement(type).map(DeclaredStatement::argument);
+    }
+
+    /**
+     * Find all declared substatements of specified type and return them as a stream.
+     *
+     * @return A stream of all declared substatements of specified type.
+     * @throws NullPointerException if {@code type} is null
+     */
+    @Beta
+    default <T extends DeclaredStatement<?>> @NonNull Stream<T> streamDeclaredSubstatements(
+            @NonNull final Class<T> type) {
+        requireNonNull(type);
+        return declaredSubstatements().stream().filter(type::isInstance).map(type::cast);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DefaultStatementDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DefaultStatementDefinition.java
new file mode 100644 (file)
index 0000000..3310060
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.meta;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Default implementation of the {@link StatementDefinition} contract. Instances of this class should be used as
+ * well-known singletons.
+ *
+ * @author Robert Varga
+ *
+ * @param <A> Argument type
+ * @param <D> Declared statement representation
+ * @param <E> Effective statement representation
+ */
+@Beta
+@NonNullByDefault
+public final class DefaultStatementDefinition<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+        extends AbstractStatementDefinition {
+    private final Class<E> effectiveRepresentation;
+    private final Class<D> declaredRepresentation;
+
+    DefaultStatementDefinition(final QName statementName, final Class<D> declaredRepresentation,
+            final Class<E> effectiveRepresentation, final boolean argumentYinElement,
+            final @Nullable QName argumentName) {
+        super(statementName, argumentYinElement, argumentName);
+        this.declaredRepresentation = requireNonNull(declaredRepresentation);
+        this.effectiveRepresentation = requireNonNull(effectiveRepresentation);
+
+        checkArgument(declaredRepresentation.isInterface(), "Declared representation %s is not an interface",
+            declaredRepresentation);
+        checkArgument(effectiveRepresentation.isInterface(), "Effective representation %s is not an interface",
+            effectiveRepresentation);
+    }
+
+    public static <A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+            DefaultStatementDefinition<A, D, E> of(final QName statementName, final Class<D> declaredRepresentation,
+                    final Class<E> effectiveRepresentation) {
+        return new DefaultStatementDefinition<>(statementName, declaredRepresentation, effectiveRepresentation, false,
+                null);
+    }
+
+    public static <A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+            DefaultStatementDefinition<A, D, E> of(final QName statementName, final Class<D> declaredRepresentation,
+                    final Class<E> effectiveRepresentation, final QName argumentName) {
+        return of(statementName, declaredRepresentation, effectiveRepresentation, argumentName, false);
+    }
+
+    public static <A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+            DefaultStatementDefinition<A, D, E> of(final QName statementName, final Class<D> declaredRepresentation,
+                    final Class<E> effectiveRepresentation, final QName argumentName,
+                    final boolean argumentYinElement) {
+        return new DefaultStatementDefinition<>(statementName, declaredRepresentation, effectiveRepresentation,
+                argumentYinElement, requireNonNull(argumentName));
+    }
+
+    @Override
+    public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+        return declaredRepresentation;
+    }
+
+    @Override
+    public Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass() {
+        return effectiveRepresentation;
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper)
+                .add("declared", declaredRepresentation)
+                .add("effective", effectiveRepresentation);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/EffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/EffectiveStatement.java
new file mode 100644 (file)
index 0000000..415a296
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015 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.api.meta;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Effective model statement which should be used to derive application behaviour.
+ *
+ * @param <A> Argument type ({@link Void} if statement does not have argument.)
+ * @param <D> Class representing declared version of this statement.
+ */
+public interface EffectiveStatement<A, D extends DeclaredStatement<A>> extends ModelStatement<A> {
+    /**
+     * Returns statement, which was explicit declaration of this effective
+     * statement.
+     *
+     * @return statement, which was explicit declaration of this effective
+     *         statement or null if statement was inferred from context.
+     */
+    @Nullable D getDeclared();
+
+    /**
+     * Returns value associated with supplied identifier.
+     *
+     * @param <K> Identifier type
+     * @param <V> Value type
+     * @param <N> Namespace identifier type
+     * @param namespace Namespace type
+     * @param identifier Identifier of element.
+     * @return Value if present
+     */
+    //<K, V, N extends IdentifierNamespace<? super K, ? extends V>> V
+    <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends V> get(@NonNull Class<N> namespace,
+            @NonNull K identifier);
+
+    /**
+     * Returns all local values from supplied namespace.
+     *
+     * @param <K> Identifier type
+     * @param <V> Value type
+     * @param <N> Namespace identifier type
+     * @param namespace Namespace type
+     * @return Key-value mappings, empty if the namespace does not exist.
+     * @throws NullPointerException if namespace is null
+     */
+    <K, V, N extends IdentifierNamespace<K, V>> @NonNull Map<K, V> getAll(@NonNull Class<N> namespace);
+
+    /**
+     * Returns all local values from supplied namespace.
+     *
+     * @param <K> Identifier type
+     * @param <V> Value type
+     * @param <N> Namespace identifier type
+     * @param namespace Namespace type
+     * @return Key-value mappings, empty if the namespace does not exist.
+     * @throws NullPointerException if namespace is null
+     * @deprecated Use {@link #getAll(Class)} instead
+     */
+    @Deprecated(forRemoval = true)
+    default <K, V, N extends IdentifierNamespace<K, V>> @NonNull Map<K, V> findAll(final @NonNull Class<N> namespace) {
+        return getAll(requireNonNull(namespace));
+    }
+
+    /**
+     * Returns a collection of all effective substatements.
+     *
+     * @return collection of all effective substatements.
+     */
+    @NonNull Collection<? extends EffectiveStatement<?, ?>> effectiveSubstatements();
+
+    /**
+     * Find the first effective substatement of specified type.
+     *
+     * @return First effective substatement, or empty if no match is found.
+     */
+    @Beta
+    default <T extends EffectiveStatement<?, ?>> Optional<T> findFirstEffectiveSubstatement(
+            final @NonNull Class<T> type) {
+        return effectiveSubstatements().stream().filter(type::isInstance).findFirst().map(type::cast);
+    }
+
+    /**
+     * Find the first effective substatement of specified type and return its value.
+     *
+     * @return First effective substatement's argument, or empty if no match is found.
+     */
+    @Beta
+    default <V, T extends EffectiveStatement<V, ?>> Optional<V> findFirstEffectiveSubstatementArgument(
+            final @NonNull Class<T> type) {
+        return findFirstEffectiveSubstatement(type).map(EffectiveStatement::argument);
+    }
+
+    /**
+     * Find all effective substatements of specified type and return them as a stream.
+     *
+     * @return A stream of all effective substatements of specified type.
+     */
+    @Beta
+    default <T extends EffectiveStatement<?, ?>> Stream<T> streamEffectiveSubstatements(final @NonNull Class<T> type) {
+        return effectiveSubstatements().stream().filter(type::isInstance).map(type::cast);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/ForwardingEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/ForwardingEffectiveStatement.java
new file mode 100644 (file)
index 0000000..e4ce07f
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.meta;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ForwardingObject;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Utility forwarding implementation of {@link EffectiveStatement} contract. This class is useful for implementing
+ * wrapped statements.
+ *
+ * @author Robert Varga
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ * @param <E> Effective Statement representation
+ */
+@Beta
+public abstract class ForwardingEffectiveStatement<A, D extends DeclaredStatement<A>,
+        E extends EffectiveStatement<A, D>> extends ForwardingObject implements EffectiveStatement<A, D> {
+
+    @Override
+    protected abstract E delegate();
+
+
+    @Override
+    public D getDeclared() {
+        return delegate().getDeclared();
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends V> get(final Class<N> namespace,
+            final K identifier) {
+        return delegate().get(namespace, identifier);
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAll(final Class<N> namespace) {
+        return delegate().getAll(namespace);
+    }
+
+    @Override
+    public Collection<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
+        return delegate().effectiveSubstatements();
+    }
+
+    @Override
+    public StatementDefinition statementDefinition() {
+        return delegate().statementDefinition();
+    }
+
+    @Override
+    public A argument() {
+        return delegate().argument();
+    }
+
+    @Override
+    public StatementSource getStatementSource() {
+        return delegate().getStatementSource();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/IdentifierNamespace.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/IdentifierNamespace.java
new file mode 100644 (file)
index 0000000..d6d0f78
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 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.api.meta;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Model specific namespace which allows access to specific
+ *
+ * {@link IdentifierNamespace} serves as common superclass for YANG model namespaces, which are type-captured
+ * subclasses. This type capture of namespace allows for handy type-safe reading methods such as
+ * {@link EffectiveStatement#get(Class, Object)} and still allows introduction of new namespaces without need to change
+ * model APIs.
+ *
+ * @param <K> Identifier type
+ * @param <V> Value type
+ */
+public interface IdentifierNamespace<K, V> {
+    /**
+     * Returns value associated with supplied identifier.
+     *
+     * @param identifier Identifier of value
+     * @return value or null, if identifier is not present in namespace.
+     */
+    @Nullable V get(@NonNull K identifier);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/ModelStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/ModelStatement.java
new file mode 100644 (file)
index 0000000..74b473c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015 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.api.meta;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Model statement. There are two base types of model statements:
+ * <ul>
+ * <li>{@link DeclaredStatement} - Statement representation as was defined in original
+ * source. This representation could be used during computation of effective model
+ * or during transforming YANG model from one serialization format to other.
+ * </li>
+ * <li>
+ * {@link EffectiveStatement} - Representation of effective statement - this
+ * statement may be different from declared, in such way, that it contains additional
+ * substatements, provides access to model namespaces. Some effective statements may be not
+ * directly declared in YANG source, but could be inferred by semantic processing of
+ * other statements (eg. uses, augment).
+ * </li>
+ * </ul>
+ *
+ * @param <A> Argument type ({@link Void} if statement does not have argument.)
+ */
+public interface ModelStatement<A> {
+    /**
+     * Statement Definition of this statement.
+     *
+     * @return definition of this statement.
+     */
+    @NonNull StatementDefinition statementDefinition();
+
+    /**
+     * Returns statement argument.
+     *
+     * @return statement argument or null if statement does not have argument.
+     */
+    // FIXME: figure out @NonNull argument override in subclasses and switch to JDT @Nullable
+    @Nullable A argument();
+
+    /**
+     * Returns statement source, which denotes if statement was
+     * explicitly declared in original model or inferred during
+     * semantic processing of model.
+     *
+     * @return statement source.
+     */
+    @NonNull StatementSource getStatementSource();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/StatementDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/StatementDefinition.java
new file mode 100644 (file)
index 0000000..df3247e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015 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.api.meta;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Definition / model of YANG {@link DeclaredStatement} and {@link EffectiveStatement}.
+ *
+ * <p>
+ * Statement concept is defined in RFC6020 section 6.3: <blockquote> A YANG
+ * module contains a sequence of statements. Each statement starts with a
+ * keyword, followed by zero or one argument </blockquote>
+ *
+ * <p>
+ * Source: <a href="https://tools.ietf.org/html/rfc6020#section-6.3"> </a>
+ */
+public interface StatementDefinition extends Immutable {
+    /**
+     * Returns name of the statement.
+     *
+     * @return Name of the statement
+     */
+    @NonNull QName getStatementName();
+
+    /**
+     * Returns name of statement argument or null, if statement does not have argument.
+     *
+     * @return argument name or null, if statement does not take argument.
+     */
+    @NonNull Optional<ArgumentDefinition> getArgumentDefinition();
+
+    /**
+     * Returns class which represents declared version of statement associated with this definition. This class should
+     * be an interface which provides convenience access to declared substatements.
+     *
+     * @return class which represents declared version of statement associated with this definition.
+     */
+    @NonNull Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass();
+
+    /**
+     * Returns class which represents derived behaviour from supplied statement. This class should be an interface which
+     * defines convenience access to statement properties, namespace items and substatements.
+     *
+     * @return class which represents effective version of statement associated with this definition
+     */
+    @NonNull Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/StatementSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/StatementSource.java
new file mode 100644 (file)
index 0000000..2752b3c
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 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.api.meta;
+
+/**
+ * Origin of statement.
+ * Represents origin of statement - if it was explicitly present
+ * in model representation or if it was inferred from context.
+ */
+public enum StatementSource {
+
+    /**
+     * Statement was explicitly declared by author of the supplied model.
+     */
+    DECLARATION,
+
+    /**
+     * Statement was derived from context of YANG model / statement
+     * and represents effective model.
+     *
+     * <p>
+     * Effective context nodes are derived from applicable {@link #DECLARATION}
+     * statements by interpreting their semantic meaning in context
+     * of current statement.
+     */
+    CONTEXT
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/package-info.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/package-info.java
new file mode 100644 (file)
index 0000000..9f2b900
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 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
+ */
+
+/**
+ * Meta model of YANG model as was defined in RFC6020 and extracted by analysis
+ * of YANG text.
+ *
+ * <p>
+ * Existence of meta-model allows for better evolution of YANG language as it evolves
+ * and allows for better support of different serializations of YANG model.
+ *
+ * <h2>Statements</h2>
+ * YANG source is defined as sequence of statement in
+ * <a href="https://tools.ietf.org/html/rfc6020#section-6.3">RFC6020, Section 6.3</a>.
+ * this model is also correct for YIN, which is XML serialisation of YANG source.
+ *
+ * <p>
+ * Statements are represented as instances / subclasses of
+ * {@link org.opendaylight.yangtools.yang.model.api.meta.ModelStatement} concept and its two subconcepts which are:
+ * <ul>
+ * <li>
+ * {@link org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement} - this contains navigable
+ * set of statements model as they was defined / present in original processed
+ * sources.
+ * </li>
+ * <li>{@link org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement} - navigable set of statements
+ * which represents effective model of parsed YANG sources, which is derived by rules
+ * present in YANG specification and/or was introduced in form of extensions.
+ * </li>
+ * </ul>
+ *
+ * <p>
+ * Clear separation of declared / effective model is needed, since statement definition also
+ * contains information how effective model is computed and there is no one to one mapping
+ * between declared and effective model thanks to statements such as {@code uses},
+ * {@code augment},{@code deviate},{@code refine}.
+ *
+ * <h2>Identifiers and Namespaces</h2>
+ * Effective model of YANG has several identifier types and namespaces, which behaves differently
+ * and are mostly used during processing data and/or during computing effective (semantic) model.
+ *
+ * <p>
+ * Common abstraction for various types of namespaces is
+ * {@link org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace}
+ * from which concrete effective model namespaces are derived.
+ */
+package org.opendaylight.yangtools.yang.model.api.meta;
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/package-info.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/package-info.java
new file mode 100644 (file)
index 0000000..da81108
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * 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
+ */
+
+/**
+ * Definition of structures and DOM like API of effected YANG schema.
+ *
+ * <p>
+ * This package is structured into following logical units:
+ * <dl>
+ * <dt>YANG Meta model</dt>
+ * <dd>Meta model of YANG, which defines basic concepts and building blocks of YANG models
+ * such as {@link org.opendaylight.yangtools.yang.model.api.meta.ModelStatement}.</dd>
+ * <dt>YANG Statement model</dt>
+ * <dd>Concrete java model of YANG statements, which defines basic relationship between statements
+ * and represents these statements.</dd>
+ *
+ * <dt>YANG Effective model</dt>
+ * <dd>Effective model of processed YANG models, which represents semantic interpretation
+ * of YANG models and provides convenience views for interpreting models.
+ * </dd>
+ * </dl>
+ *
+ *
+ * <h2>YANG Effective model</h2>
+ * <h3>Effective model statement mapping</h3>
+ *
+ * <dl>
+ * <dt>anyxml
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode}
+ *
+ * <dt>argument
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ExtensionDefinition#getArgument()}
+ *
+ * <dt>augment
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode}
+ *
+ * <dt>base
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition#getIdentities()}
+ *
+ * <dt>belongs-to
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement#getBelongsTo()}
+ *
+ * <dt>bit
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit}
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition#getBits()}
+ *
+ * <dt>case
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.CaseSchemaNode}
+ *
+ * <dt>choice
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode}
+ *
+ * <dt>config
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.DataSchemaNode#isConfiguration()}
+ *
+ * <dt>contact
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.Module#getContact()}
+ *
+ * <dt>container
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode}
+ *
+ * <dt>default
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.TypeDefinition#getDefaultValue()}
+ *
+ * <dt>description
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.SchemaNode#getDescription()}
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition#getDescription()}
+ *
+ * <dt>enum
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair}
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition#getValues()}
+ *
+ * <dt>error-app-tag
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition#getErrorAppTag()}
+ *
+ * <dt>error-message
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition#getErrorMessage()}
+ *
+ * <dt>extension
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ExtensionDefinition}
+ *
+ * <dt>deviation
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.Deviation}
+ *
+ * <dt>deviate
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.DeviateKind}
+ *
+ * <dt>feature
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.FeatureDefinition}
+ *
+ * <dt>fraction-digits
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition#getFractionDigits()}
+ *
+ * <dt>grouping
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.GroupingDefinition}
+ *
+ * <dt>identity
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode}
+ *
+ * <dt>if-feature
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureStatement}
+ *
+ * <dt>import
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ModuleImport}
+ *
+ * <dt>include
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.stmt.IncludeStatement}
+ *
+ * <dt>input
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.RpcDefinition#getInput()}
+ *
+ * <dt>key
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ListSchemaNode#getKeyDefinition()}
+ *
+ * <dt>leaf
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.LeafSchemaNode}
+ *
+ * <dt>leaf-list
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode}
+ *
+ * <dt>length
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.LengthConstraint}
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition#getLengthConstraint()}
+ *
+ * <dt>list
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ListSchemaNode}
+ *
+ * <dt>mandatory
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.MandatoryAware#isMandatory()}
+ *
+ * <dt>max-elements
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ElementCountConstraint#getMinElements()}
+ *
+ * <dt>min-elements
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ElementCountConstraint#getMaxElements()}
+ *
+ * <dt>module
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.Module}
+ *
+ * <dt>must
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.MustConstraintAware#getMustConstraints()}
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.MustDefinition}
+ *
+ * <dt>namespace
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.Module#getNamespace()}
+ *
+ * <dt>notification
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.NotificationDefinition}
+ *
+ * <dt>ordered-by
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ListSchemaNode#isUserOrdered()}
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode#isUserOrdered()}
+ *
+ * <dt>organization
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.Module#getOrganization()}
+ *
+ * <dt>output
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.RpcDefinition#getOutput()}
+ *
+ * <dt>path
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition#getPathStatement()}
+ *
+ * <dt>pattern
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.PatternConstraint}
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition}
+ *
+ * <dt>position
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit#getPosition()}
+ *
+ * <dt>prefix
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.Module#getPrefix()}
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ModuleImport#getPrefix()}
+ *
+ * <dt>presence
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode#isPresenceContainer()}
+ *
+ * <dt>range
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.RangeConstraint}
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition#getRangeConstraint()}
+ *
+ * <dt>reference
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.SchemaNode#getReference()}
+ *
+ * <dt>refine
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement}
+ *
+ * <dt>require-instance
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition#requireInstance()}
+ *
+ * <dt>revision
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.Module#getRevision()}
+ *
+ * <dt>revision-date
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ModuleImport#getRevision()}
+ *
+ * <dt>rpc
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.RpcDefinition}
+ *
+ * <dt>status
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.SchemaNode#getStatus()}
+ *
+ * <dt>submodule
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement}
+ *
+ * <dt>type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.TypeDefinition}
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.LeafSchemaNode#getType()}
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode#getType()}
+ *
+ * <dt>typedef
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.TypeDefinition}
+ *
+ * <dt>unique
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.UniqueConstraint}
+ *
+ * <dt>units
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.TypeDefinition#getUnits()}
+ *
+ * <dt>uses
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.UsesNode}
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.DataNodeContainer#getUses()}
+ *
+ * <dt>value
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair#getValue()}
+ *
+ * <dt>when
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.WhenConditionAware#getWhenCondition()}
+ *
+ * <dt>yang-version
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.Module#getYangVersion()}
+ *
+ * <dt>yin-element
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ExtensionDefinition#isYinElement()}
+ *
+ * <dt>add
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.DeviateKind#ADD}
+ *
+ * <dt>current
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.Status#CURRENT}
+ *
+ * <dt>delete
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.DeviateKind#DELETE}
+ *
+ * <dt>deprecated
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.Status#DEPRECATED}
+ *
+ * <dt>false
+ *   <dd>{@link java.lang.Boolean#FALSE}
+ *
+ * <dt>max
+ *   <dd>Not exposed
+ *
+ * <dt>min
+ *   <dd>Not exposed
+ *
+ * <dt>not-supported
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.DeviateKind#NOT_SUPPORTED}
+ *
+ * <dt>obsolete
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.Status#OBSOLETE}
+ *
+ * <dt>replace
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.DeviateKind#REPLACE}
+ *
+ * <dt>system
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ListSchemaNode#isUserOrdered()}
+ *
+ * <dt>true
+ *   <dd>{@link java.lang.Boolean#TRUE}
+ *
+ * <dt>unbounded
+ *   <dd>Not exposed
+ *
+ * <dt>user
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.ListSchemaNode#isUserOrdered()}
+ * </dl>
+ *
+ *
+ * <h3>YANG Base Type Mapping</h3>
+ *
+ *
+ * <dl>
+ * <dt>Int8 built-in type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition}
+ *
+ * <dt>Int16 built-in type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition}
+ *
+ * <dt>Int32 built-in type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition}
+ *
+ * <dt>Int64 built-in type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition}
+ *
+ * <dt>Uint8 built-in type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition}
+ *
+ * <dt>Uint16 built-in type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition}
+ *
+ * <dt>Uint32 built-in type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition}
+ *
+ * <dt>Uint64 built-in type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition}
+ *
+ * <dt>Decimal64 built-in type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition}
+ *
+ * <dt>Boolean built-in type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition}
+ *
+ * <dt>Enumeration built-in type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition}
+ *
+ * <dt>Bits Built-In Type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition}
+ *
+ * <dt>The binary Built-In Type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition}
+ *
+ * <dt>The leafref Built-In Type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition}
+ *
+ * <dt>The identityref Built-In Type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition}
+ *
+ * <dt>The empty Built-In Type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition}
+ *
+ * <dt>The union Built-In Type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition}
+ * <dt>The instance-identifier Built-In Type
+ *   <dd>{@link org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition}
+ *
+ * </dl>
+ */
+package org.opendaylight.yangtools.yang.model.api;
+
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ActionEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ActionEffectiveStatement.java
new file mode 100644 (file)
index 0000000..4dbbbe2
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+
+@Beta
+public interface ActionEffectiveStatement extends SchemaTreeEffectiveStatement<ActionStatement>,
+    DataTreeAwareEffectiveStatement<QName, ActionStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ActionStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ActionStatement.java
new file mode 100644 (file)
index 0000000..e24563b
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Represents YANG action statement.
+ *
+ * <p>
+ * The "action" statement is used to define an operation connected to a
+ * specific container or list data node.  It takes one argument, which
+ * is an identifier, followed by a block of substatements that holds
+ * detailed action information.  The argument is the name of the action.
+ */
+@Beta
+public interface ActionStatement extends OperationDeclaredStatement {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ActionStatementAwareDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ActionStatementAwareDeclaredStatement.java
new file mode 100644 (file)
index 0000000..3d29b4e
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+/**
+ * Statement which can contain action statements.
+ */
+@Beta
+public interface ActionStatementAwareDeclaredStatement<A> extends DeclaredStatement<A> {
+    /**
+     * Return actions in this container. Since actions were introduced in RFC7950, hence RFC6020 semantics always
+     * returns an empty collection.
+     *
+     * @return collection of action statements
+     */
+    default @NonNull Collection<? extends ActionStatement> getActions() {
+        return declaredSubstatements(ActionStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AnydataEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AnydataEffectiveStatement.java
new file mode 100644 (file)
index 0000000..d88a2c9
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public interface AnydataEffectiveStatement extends DataTreeEffectiveStatement<AnydataStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AnydataStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AnydataStatement.java
new file mode 100644 (file)
index 0000000..0b0325b
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * The "anydata" statement defines an interior node in the schema tree. It takes one argument, which is an identifier,
+ * followed by a block of substatements that holds detailed anydata information.
+ *
+ * <p>
+ * The "anydata" statement is used to represent an unknown set of nodes that can be modeled with YANG, except anyxml,
+ * but for which the data model is not known at module design time.  It is possible, though not required, for the data
+ * model for anydata content to become known through protocol signaling or other means that are outside the scope
+ * of this document.
+ */
+@Beta
+public interface AnydataStatement extends DataDefinitionStatement, ConfigStatementAwareDeclaredStatement<QName>,
+    MandatoryStatementAwareDeclaredStatement<QName>, MustStatementAwareDeclaredStatement<QName> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AnyxmlEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AnyxmlEffectiveStatement.java
new file mode 100644 (file)
index 0000000..9e8fdaa
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public interface AnyxmlEffectiveStatement extends DataTreeEffectiveStatement<AnyxmlStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AnyxmlStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AnyxmlStatement.java
new file mode 100644 (file)
index 0000000..c10b472
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface AnyxmlStatement extends DataDefinitionStatement, ConfigStatementAwareDeclaredStatement<QName>,
+    MandatoryStatementAwareDeclaredStatement<QName>, MustStatementAwareDeclaredStatement<QName> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ArgumentEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ArgumentEffectiveStatement.java
new file mode 100644 (file)
index 0000000..100ec08
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface ArgumentEffectiveStatement extends EffectiveStatement<QName, ArgumentStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ArgumentStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ArgumentStatement.java
new file mode 100644 (file)
index 0000000..4a9c5ea
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+@Rfc6020AbnfRule("argument-stmt")
+public interface ArgumentStatement extends DeclaredStatement<QName> {
+    default @NonNull QName getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+
+    default @Nullable YinElementStatement getYinElement() {
+        final Optional<YinElementStatement> opt = findFirstDeclaredSubstatement(YinElementStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+}
+
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AugmentEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AugmentEffectiveStatement.java
new file mode 100644 (file)
index 0000000..e35d26d
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface AugmentEffectiveStatement extends EffectiveStatement<SchemaNodeIdentifier, AugmentStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AugmentStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/AugmentStatement.java
new file mode 100644 (file)
index 0000000..ac8dc25
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.stmt.DocumentedDeclaredStatement.WithStatus;
+
+public interface AugmentStatement extends WithStatus<SchemaNodeIdentifier>,
+        DataDefinitionAwareDeclaredStatement<SchemaNodeIdentifier>,
+        NotificationStatementAwareDeclaredStatement<SchemaNodeIdentifier>,
+        ActionStatementAwareDeclaredStatement<SchemaNodeIdentifier>,
+        WhenStatementAwareDeclaredStatement<SchemaNodeIdentifier> {
+
+    default @NonNull SchemaNodeIdentifier getTargetNode() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+
+    default @NonNull Collection<? extends CaseStatement> getCases() {
+        return declaredSubstatements(CaseStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BaseEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BaseEffectiveStatement.java
new file mode 100644 (file)
index 0000000..03d2d0e
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface BaseEffectiveStatement extends EffectiveStatement<QName, BaseStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BaseStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BaseStatement.java
new file mode 100644 (file)
index 0000000..703166e
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface BaseStatement extends DeclaredStatement<QName> {
+    default @NonNull QName getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BelongsToEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BelongsToEffectiveStatement.java
new file mode 100644 (file)
index 0000000..0a8c192
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface BelongsToEffectiveStatement extends EffectiveStatement<String, BelongsToStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BelongsToStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BelongsToStatement.java
new file mode 100644 (file)
index 0000000..67f53ee
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface BelongsToStatement extends DeclaredStatement<String> {
+    default @NonNull String getModule() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+
+    default @NonNull PrefixStatement getPrefix() {
+        return findFirstDeclaredSubstatement(PrefixStatement.class).get();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BitEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BitEffectiveStatement.java
new file mode 100644 (file)
index 0000000..38e00a9
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface BitEffectiveStatement extends EffectiveStatement<QName, BitStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BitStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BitStatement.java
new file mode 100644 (file)
index 0000000..4c7ae1a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface BitStatement extends DocumentedDeclaredStatement.WithStatus<QName>,
+        IfFeatureAwareDeclaredStatement<QName> {
+    default @NonNull String getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument()).getLocalName();
+    }
+
+    default @Nullable PositionStatement getPosition() {
+        final Optional<PositionStatement> opt = findFirstDeclaredSubstatement(PositionStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BodyDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/BodyDeclaredStatement.java
new file mode 100644 (file)
index 0000000..0c16655
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+
+@Beta
+public interface BodyDeclaredStatement extends NotificationStatementAwareDeclaredStatement<String>,
+        DataDefinitionAwareDeclaredStatement.WithReusableDefinitions<String> {
+    default @NonNull Collection<? extends ExtensionStatement> getExtensions() {
+        return declaredSubstatements(ExtensionStatement.class);
+    }
+
+    default @NonNull Collection<? extends FeatureStatement> getFeatures() {
+        return declaredSubstatements(FeatureStatement.class);
+    }
+
+    default @NonNull Collection<? extends IdentityStatement> getIdentities() {
+        return declaredSubstatements(IdentityStatement.class);
+    }
+
+    default @NonNull Collection<? extends AugmentStatement> getAugments() {
+        return declaredSubstatements(AugmentStatement.class);
+    }
+
+    default @NonNull Collection<? extends RpcStatement> getRpcs() {
+        return declaredSubstatements(RpcStatement.class);
+    }
+
+    default @NonNull Collection<? extends DeviationStatement> getDeviations() {
+        return declaredSubstatements(DeviationStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/CaseEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/CaseEffectiveStatement.java
new file mode 100644 (file)
index 0000000..1f9a675
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+
+@Beta
+public interface CaseEffectiveStatement extends SchemaTreeEffectiveStatement<CaseStatement>,
+    SchemaTreeAwareEffectiveStatement<QName, CaseStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/CaseEffectiveStatementNamespace.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/CaseEffectiveStatementNamespace.java
new file mode 100644 (file)
index 0000000..30b7a1d
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Namespace of available cases in a choice node. According to RFC7950 section 6.2.1:
+ * <pre>
+ *     All cases within a choice share the same case identifier
+ *     namespace.  This namespace is scoped to the parent choice node.
+ * </pre>
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class CaseEffectiveStatementNamespace extends EffectiveStatementNamespace<CaseEffectiveStatement> {
+    private CaseEffectiveStatementNamespace() {
+        // Should never be instantiated
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/CaseStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/CaseStatement.java
new file mode 100644 (file)
index 0000000..a520928
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface CaseStatement extends DocumentedDeclaredStatement.WithStatus<QName>,
+        DataDefinitionAwareDeclaredStatement<QName>, WhenStatementAwareDeclaredStatement<QName> {
+    default @NonNull QName getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ChoiceEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ChoiceEffectiveStatement.java
new file mode 100644 (file)
index 0000000..6c55562
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+
+@Beta
+public interface ChoiceEffectiveStatement extends SchemaTreeEffectiveStatement<ChoiceStatement>,
+    SchemaTreeAwareEffectiveStatement<QName, ChoiceStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ChoiceStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ChoiceStatement.java
new file mode 100644 (file)
index 0000000..c1e9f7a
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface ChoiceStatement extends DataDefinitionStatement, ConfigStatementAwareDeclaredStatement<QName>,
+        DefaultStatementAwareDeclaredStatement, MandatoryStatementAwareDeclaredStatement<QName> {
+
+    default @NonNull Collection<? extends CaseStatement> getCases() {
+        return declaredSubstatements(CaseStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ConfigEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ConfigEffectiveStatement.java
new file mode 100644 (file)
index 0000000..cc372e8
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2016 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+public interface ConfigEffectiveStatement extends EffectiveStatement<Boolean, ConfigStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ConfigStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ConfigStatement.java
new file mode 100644 (file)
index 0000000..2590cb5
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface ConfigStatement extends DeclaredStatement<Boolean> {
+    default boolean getValue() {
+        return argument().booleanValue();
+    }
+}
+
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ConfigStatementAwareDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ConfigStatementAwareDeclaredStatement.java
new file mode 100644 (file)
index 0000000..38a4745
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+@Beta
+public interface ConfigStatementAwareDeclaredStatement<A> extends DeclaredStatement<A> {
+    /**
+     * Return a {@link ConfigStatement} child, if present.
+     *
+     * @return A {@link ConfigStatement}, or empty if none is present.
+     */
+    default @NonNull Optional<ConfigStatement> getConfig() {
+        return findFirstDeclaredSubstatement(ConfigStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ConstrainedDocumentedDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ConstrainedDocumentedDeclaredStatement.java
new file mode 100644 (file)
index 0000000..9eea266
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 Pantheon Technoglogies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Common interface for statements which contain either a description/reference or a description/reference/status combo.
+ */
+@Beta
+public interface ConstrainedDocumentedDeclaredStatement<T> extends DocumentedDeclaredStatement<T> {
+
+    default @NonNull Optional<ErrorAppTagStatement> getErrorAppTagStatement() {
+        return findFirstDeclaredSubstatement(ErrorAppTagStatement.class);
+    }
+
+    default @NonNull Optional<ErrorMessageStatement> getErrorMessageStatement() {
+        return findFirstDeclaredSubstatement(ErrorMessageStatement.class);
+    }
+
+    interface WithStatus<T> extends ConstrainedDocumentedDeclaredStatement<T>,
+            DocumentedDeclaredStatement.WithStatus<T> {
+
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ContactEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ContactEffectiveStatement.java
new file mode 100644 (file)
index 0000000..193110c
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface ContactEffectiveStatement extends EffectiveStatement<String, ContactStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ContactStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ContactStatement.java
new file mode 100644 (file)
index 0000000..e511fce
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface ContactStatement extends DeclaredStatement<String> {
+    default @NonNull String getText() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(rawArgument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ContainerEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ContainerEffectiveStatement.java
new file mode 100644 (file)
index 0000000..75800b7
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+
+@Beta
+public interface ContainerEffectiveStatement extends DataTreeEffectiveStatement<ContainerStatement>,
+     DataTreeAwareEffectiveStatement<QName, ContainerStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ContainerStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ContainerStatement.java
new file mode 100644 (file)
index 0000000..45ab6ac
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface ContainerStatement extends DataDefinitionStatement,
+        DataDefinitionAwareDeclaredStatement.WithReusableDefinitions<QName>,
+        ActionStatementAwareDeclaredStatement<QName>, ConfigStatementAwareDeclaredStatement<QName>,
+        NotificationStatementAwareDeclaredStatement<QName>, MustStatementAwareDeclaredStatement<QName> {
+    default @Nullable PresenceStatement getPresence() {
+        final Optional<PresenceStatement> opt = findFirstDeclaredSubstatement(PresenceStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataDefinitionAwareDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataDefinitionAwareDeclaredStatement.java
new file mode 100644 (file)
index 0000000..cfedd4c
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+@Beta
+public interface DataDefinitionAwareDeclaredStatement<A> extends DeclaredStatement<A> {
+    default @NonNull Collection<? extends DataDefinitionStatement> getDataDefinitions() {
+        return declaredSubstatements(DataDefinitionStatement.class);
+    }
+
+    interface WithReusableDefinitions<A> extends DataDefinitionAwareDeclaredStatement<A> {
+        default @NonNull Collection<? extends TypedefStatement> getTypedefs() {
+            return declaredSubstatements(TypedefStatement.class);
+        }
+
+        default @NonNull Collection<? extends GroupingStatement> getGroupings() {
+            return declaredSubstatements(GroupingStatement.class);
+        }
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataDefinitionStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataDefinitionStatement.java
new file mode 100644 (file)
index 0000000..e66018d
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Statement that defines new data nodes.
+ * One of container, leaf, leaf-list, list, choice, case,
+ * augment, uses, anyxml and anydata.
+ *
+ * <p>
+ * Defined in: <a href="https://tools.ietf.org/html/rfc6020#section-3">RFC6020, Section 3</a>
+ */
+@Rfc6020AbnfRule("data-def-stmt")
+public interface DataDefinitionStatement extends DocumentedDeclaredStatement.WithStatus<QName>,
+        WhenStatementAwareDeclaredStatement<QName> {
+    default @NonNull QName getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataTreeAwareEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataTreeAwareEffectiveStatement.java
new file mode 100644 (file)
index 0000000..782db09
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+/**
+ * Interface implemented by all {@link SchemaTreeAwareEffectiveStatement}s which can contain a {@code data tree} child.
+ *
+ * @param <A> Argument type
+ * @param <D> Class representing declared version of this statement.
+ * @author Robert Varga
+ */
+@Beta
+public interface DataTreeAwareEffectiveStatement<A, D extends DeclaredStatement<A>>
+        extends SchemaTreeAwareEffectiveStatement<A, D> {
+
+    /**
+     * Namespace of {@code data node}s. This is a subtree of {@link SchemaTreeAwareEffectiveStatement.Namespace} in that
+     * all data nodes are also schema nodes. The structure of the tree is different, though, as {@code choice}
+     * and {@code case} statements are glossed over and they do not contribute to the tree hierarchy -- only their
+     * children do.
+     *
+     * <p>
+     * This corresponds to the {@code data tree} view of a YANG-defined data.
+     *
+     * @param <T> Child statement type
+     */
+    @NonNullByDefault
+    abstract class Namespace<T extends DataTreeEffectiveStatement<?>> extends EffectiveStatementNamespace<T> {
+        private Namespace() {
+            // Should never be instantiated
+        }
+    }
+
+    /**
+     * Find a {@code data tree} child {@link DataTreeEffectiveStatement}, as identified by its QName argument.
+     *
+     * @param qname Child identifier
+     * @return Data tree child, or empty
+     * @throws NullPointerException if {@code qname} is null
+     */
+    default <E extends DataTreeEffectiveStatement<?>> @NonNull Optional<E> findDataTreeNode(
+            final @NonNull QName qname) {
+        return get(Namespace.class, requireNonNull(qname));
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataTreeEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataTreeEffectiveStatement.java
new file mode 100644 (file)
index 0000000..a55fa80
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Common interface grouping all {@link EffectiveStatement}s which are accessible via
+ * {@link DataTreeAwareEffectiveStatement.Namespace}. This such statement corresponds to a {@code data node}.
+ *
+ * <p>
+ * This interface could be named {@code SchemaNodeEffectiveStatement}, but that could induce a notion that it has
+ * something to do with {@link DataSchemaNode} -- which it has not. DataSchemaNode semantics are wrong in may aspects
+ * and while implementations of this interface may also implement DataSchemaNode, the semantics of this interface should
+ * always be preferred and DataSchemaNode is to be treated as deprecated whenever possible.
+ *
+ * @param <D> Declared statement type
+ * @author Robert Varga
+ */
+@Beta
+public interface DataTreeEffectiveStatement<D extends DeclaredStatement<QName>>
+    extends SchemaTreeEffectiveStatement<D> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DefaultEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DefaultEffectiveStatement.java
new file mode 100644 (file)
index 0000000..b605858
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface DefaultEffectiveStatement extends EffectiveStatement<String, DefaultStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DefaultStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DefaultStatement.java
new file mode 100644 (file)
index 0000000..6b325b1
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface DefaultStatement extends DeclaredStatement<String> {
+    default @NonNull String getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DefaultStatementAwareDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DefaultStatementAwareDeclaredStatement.java
new file mode 100644 (file)
index 0000000..aa91807
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+/**
+ * Marker interface for statements which may contain a 'default' statement, as defined in RFC7950.
+ */
+@Beta
+public interface DefaultStatementAwareDeclaredStatement extends DeclaredStatement<QName> {
+    /**
+     * Return a {@link DefaultStatement} child, if present.
+     *
+     * @return A {@link DefaultStatement}, or empty if none is present.
+     */
+    default @NonNull Optional<DefaultStatement> getDefault() {
+        return findFirstDeclaredSubstatement(DefaultStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DescriptionEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DescriptionEffectiveStatement.java
new file mode 100644 (file)
index 0000000..9ab3975
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface DescriptionEffectiveStatement extends EffectiveStatement<String, DescriptionStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DescriptionStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DescriptionStatement.java
new file mode 100644 (file)
index 0000000..80f9d07
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface DescriptionStatement extends DeclaredStatement<String> {
+    default @NonNull String getText() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
+
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DeviateEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DeviateEffectiveStatement.java
new file mode 100644 (file)
index 0000000..97d2352
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.DeviateKind;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface DeviateEffectiveStatement extends EffectiveStatement<DeviateKind, DeviateStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DeviateStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DeviateStatement.java
new file mode 100644 (file)
index 0000000..1379fd6
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.DeviateKind;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface DeviateStatement extends DeclaredStatement<DeviateKind> {
+    default @NonNull DeviateKind getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DeviationEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DeviationEffectiveStatement.java
new file mode 100644 (file)
index 0000000..524d90f
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface DeviationEffectiveStatement extends EffectiveStatement<SchemaNodeIdentifier, DeviationStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DeviationStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DeviationStatement.java
new file mode 100644 (file)
index 0000000..adec391
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+
+public interface DeviationStatement extends DocumentedDeclaredStatement<SchemaNodeIdentifier> {
+    default @NonNull SchemaNodeIdentifier getTargetNode() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+
+    default @NonNull Collection<? extends DeviateStatement> getDeviateStatements() {
+        return declaredSubstatements(DeviateStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DocumentedDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DocumentedDeclaredStatement.java
new file mode 100644 (file)
index 0000000..fddffb9
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018 Pantheon Technoglogies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+/**
+ * Common interface for statements which contain either a description/reference or a description/reference/status combo.
+ */
+@Beta
+public interface DocumentedDeclaredStatement<T> extends DeclaredStatement<T> {
+    /**
+     * Return description statement, if available.
+     *
+     * @return description statement
+     */
+    default @NonNull Optional<DescriptionStatement> getDescription() {
+        return findFirstDeclaredSubstatement(DescriptionStatement.class);
+    }
+
+    /**
+     * Return description statement, if available.
+     *
+     * @return description statement
+     */
+    default @NonNull Optional<ReferenceStatement> getReference() {
+        return findFirstDeclaredSubstatement(ReferenceStatement.class);
+    }
+
+    interface WithStatus<T> extends DocumentedDeclaredStatement<T> {
+        default @NonNull Optional<StatusStatement> getStatus() {
+            return findFirstDeclaredSubstatement(StatusStatement.class);
+        }
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/EffectiveStatementNamespace.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/EffectiveStatementNamespace.java
new file mode 100644 (file)
index 0000000..188034b
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+
+/**
+ * Common super-interface for {@link IdentifierNamespace}s which hold {@link EffectiveStatement}s.
+ *
+ * @author Robert Varga
+ *
+ * @param <E> Effective statement type
+ */
+@Beta
+public abstract class EffectiveStatementNamespace<E extends NamespacedEffectiveStatement<?>>
+    implements IdentifierNamespace<QName, E> {
+
+    protected EffectiveStatementNamespace() {
+        // Subclasses should guard against instantiation
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/EnumEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/EnumEffectiveStatement.java
new file mode 100644 (file)
index 0000000..3d172b5
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface EnumEffectiveStatement extends EffectiveStatement<String, EnumStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/EnumStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/EnumStatement.java
new file mode 100644 (file)
index 0000000..1d21bdb
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+public interface EnumStatement extends DocumentedDeclaredStatement.WithStatus<String>,
+        IfFeatureAwareDeclaredStatement<String> {
+    default @NonNull String getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+
+    default @Nullable ValueStatement getValue() {
+        final Optional<ValueStatement> opt = findFirstDeclaredSubstatement(ValueStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ErrorAppTagEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ErrorAppTagEffectiveStatement.java
new file mode 100644 (file)
index 0000000..162bd91
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface ErrorAppTagEffectiveStatement extends EffectiveStatement<String, ErrorAppTagStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ErrorAppTagStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ErrorAppTagStatement.java
new file mode 100644 (file)
index 0000000..0d33c9e
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface ErrorAppTagStatement extends DeclaredStatement<String> {
+    default @NonNull String getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
+
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ErrorMessageEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ErrorMessageEffectiveStatement.java
new file mode 100644 (file)
index 0000000..6c0d99b
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface ErrorMessageEffectiveStatement extends EffectiveStatement<String, ErrorMessageStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ErrorMessageStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ErrorMessageStatement.java
new file mode 100644 (file)
index 0000000..d07b41e
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface ErrorMessageStatement extends DeclaredStatement<String> {
+    default @NonNull String getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
+
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ExtensionEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ExtensionEffectiveStatement.java
new file mode 100644 (file)
index 0000000..d8a8586
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public interface ExtensionEffectiveStatement extends NamespacedEffectiveStatement<ExtensionStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ExtensionEffectiveStatementNamespace.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ExtensionEffectiveStatementNamespace.java
new file mode 100644 (file)
index 0000000..493bcb8
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Namespace of available extensions. According to RFC7950 section 6.2.1:
+ * <pre>
+ *     All extension names defined in a module and its submodules share
+ *     the same extension identifier namespace.
+ * </pre>
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class ExtensionEffectiveStatementNamespace
+        extends EffectiveStatementNamespace<ExtensionEffectiveStatement> {
+    private ExtensionEffectiveStatementNamespace() {
+        // Should never be instantiated
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ExtensionStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ExtensionStatement.java
new file mode 100644 (file)
index 0000000..01f7120
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface ExtensionStatement extends DocumentedDeclaredStatement.WithStatus<QName> {
+    default @Nullable ArgumentStatement getArgument() {
+        final Optional<ArgumentStatement> opt = findFirstDeclaredSubstatement(ArgumentStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FeatureEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FeatureEffectiveStatement.java
new file mode 100644 (file)
index 0000000..bb9aa8a
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public interface FeatureEffectiveStatement extends NamespacedEffectiveStatement<FeatureStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FeatureEffectiveStatementNamespace.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FeatureEffectiveStatementNamespace.java
new file mode 100644 (file)
index 0000000..422dd39
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Namespace of available features. According to RFC7950 section 6.2.1:
+ * <pre>
+ *     All feature names defined in a module and its submodules share the
+ *     same feature identifier namespace.
+ * </pre>
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class FeatureEffectiveStatementNamespace
+        extends EffectiveStatementNamespace<FeatureEffectiveStatement> {
+    private FeatureEffectiveStatementNamespace() {
+        // Should never be instantiated
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FeatureStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FeatureStatement.java
new file mode 100644 (file)
index 0000000..6f4b13b
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface FeatureStatement extends DocumentedDeclaredStatement.WithStatus<QName>,
+        IfFeatureAwareDeclaredStatement<QName> {
+    default @NonNull QName getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FractionDigitsEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FractionDigitsEffectiveStatement.java
new file mode 100644 (file)
index 0000000..fd5a405
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface FractionDigitsEffectiveStatement extends EffectiveStatement<Integer, FractionDigitsStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FractionDigitsStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/FractionDigitsStatement.java
new file mode 100644 (file)
index 0000000..c76e2b8
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface FractionDigitsStatement extends DeclaredStatement<Integer> {
+    default int getValue() {
+        return argument().intValue();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/GroupingEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/GroupingEffectiveStatement.java
new file mode 100644 (file)
index 0000000..d9b0c52
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface GroupingEffectiveStatement extends EffectiveStatement<QName, GroupingStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/GroupingStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/GroupingStatement.java
new file mode 100644 (file)
index 0000000..3232874
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface GroupingStatement extends DocumentedDeclaredStatement.WithStatus<QName>,
+        DataDefinitionAwareDeclaredStatement.WithReusableDefinitions<QName>,
+        NotificationStatementAwareDeclaredStatement<QName>, ActionStatementAwareDeclaredStatement<QName> {
+    default @NonNull QName getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IdentityEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IdentityEffectiveStatement.java
new file mode 100644 (file)
index 0000000..f0e94be
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public interface IdentityEffectiveStatement extends NamespacedEffectiveStatement<IdentityStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IdentityEffectiveStatementNamespace.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IdentityEffectiveStatementNamespace.java
new file mode 100644 (file)
index 0000000..7c59a91
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Namespace of available identities. According to RFC7950 section 6.2.1:
+ * <pre>
+ *     All identity names defined in a module and its submodules share
+ *     the same identity identifier namespace.
+ * </pre>
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class IdentityEffectiveStatementNamespace
+        extends EffectiveStatementNamespace<IdentityEffectiveStatement> {
+    private IdentityEffectiveStatementNamespace() {
+        // Should never be instantiated
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IdentityStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IdentityStatement.java
new file mode 100644 (file)
index 0000000..0ebbe18
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface IdentityStatement extends DocumentedDeclaredStatement.WithStatus<QName>,
+        IfFeatureAwareDeclaredStatement<QName> {
+    default @NonNull QName getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+
+    /**
+     * Returns the base identities of this identity.
+     *
+     * @return base identities, empty if there are none.
+     */
+    default @NonNull Collection<? extends BaseStatement> getBases() {
+        return declaredSubstatements(BaseStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IfFeatureAwareDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IfFeatureAwareDeclaredStatement.java
new file mode 100644 (file)
index 0000000..8199015
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018 Pantheon Technoglogies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+@Beta
+public interface IfFeatureAwareDeclaredStatement<A> extends DeclaredStatement<A> {
+    /**
+     * Return attached if-feature statements. Metamodel differs here between RFC6020 and RFC7950: some nodes will be
+     * returning an empty collection in YANG 1.0 mode.
+     *
+     * @return collection of if-feature statements
+     */
+    default @NonNull Collection<? extends IfFeatureStatement> getIfFeatures() {
+        return declaredSubstatements(IfFeatureStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IfFeatureEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IfFeatureEffectiveStatement.java
new file mode 100644 (file)
index 0000000..44d0b37
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface IfFeatureEffectiveStatement extends EffectiveStatement<IfFeatureExpr, IfFeatureStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IfFeatureExpr.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IfFeatureExpr.java
new file mode 100644 (file)
index 0000000..37c1d73
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.api.stmt;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Predicate;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * A resolved {@code if-feature} expression, implementing a {@link Predicate}. Internal representation is that of
+ * a tree of expressions, optimized for memory usage. {@link #negate()} performs an efficient logical negation without
+ * relying on default predicate methods. Other Predicate methods, like {@link #and(Predicate)} are not optimized in
+ * this implementation.
+ *
+ * <p>
+ * The set of features referenced in this expression is available through {@link #getReferencedFeatures()}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class IfFeatureExpr implements Immutable, Predicate<Set<QName>> {
+    private abstract static class Single extends IfFeatureExpr {
+        final QName qname;
+
+        Single(final QName qname) {
+            this.qname = requireNonNull(qname);
+        }
+
+        @Override
+        public final ImmutableSet<QName> getReferencedFeatures() {
+            return ImmutableSet.of(qname);
+        }
+
+        @Override
+        public final int hashCode() {
+            return qname.hashCode();
+        }
+
+        @Override
+        final void addQNames(final Set<QName> set) {
+            set.add(qname);
+        }
+
+        @Override
+        public final boolean equals(final Object obj) {
+            return this == obj || getClass().isInstance(obj) && qname.equals(((Single) obj).qname);
+        }
+    }
+
+    // We are using arrays to hold our components to save a wee bit of space. The arrays originate from Sets retaining
+    // insertion order of Lists, each component is guaranteed to be unique, in definition order, not appearing multiple
+    // times
+    private abstract static class AbstractArray<T> extends IfFeatureExpr {
+        final T[] array;
+
+        AbstractArray(final T[] array) {
+            this.array = requireNonNull(array);
+            verify(array.length > 1);
+        }
+
+        @Override
+        public final int hashCode() {
+            return Arrays.hashCode(array);
+        }
+
+        @Override
+        public final boolean equals(final Object obj) {
+            return this == obj || getClass().isInstance(obj)
+                    && Arrays.deepEquals(array, ((AbstractArray<?>) obj).array);
+        }
+
+        abstract String infix();
+    }
+
+    private abstract static class Complex extends AbstractArray<IfFeatureExpr> {
+        Complex(final IfFeatureExpr[] array) {
+            super(array);
+        }
+
+        @Override
+        public final Set<QName> getReferencedFeatures() {
+            final Set<QName> ret = new HashSet<>();
+            addQNames(ret);
+            return ret;
+        }
+
+        @Override
+        final void addQNames(final Set<QName> set) {
+            for (IfFeatureExpr expr : array) {
+                expr.addQNames(set);
+            }
+        }
+
+        final IfFeatureExpr[] negateExprs() {
+            final IfFeatureExpr[] ret = new IfFeatureExpr[array.length];
+            for (int i = 0; i < array.length; i++) {
+                ret[i] = verifyNotNull(array[i].negate());
+            }
+            return ret;
+        }
+
+        @Override
+        public final String toString() {
+            final StringBuilder sb = new StringBuilder("(");
+            sb.append(array[0]);
+            final String sep = infix();
+            for (int i = 1; i < array.length; ++i) {
+                sb.append(sep).append(array[i]);
+            }
+            return sb.append(')').toString();
+        }
+    }
+
+    private abstract static class Compound extends AbstractArray<QName> {
+        Compound(final QName[] qnames) {
+            super(qnames);
+        }
+
+        @Override
+        public final ImmutableSet<QName> getReferencedFeatures() {
+            return ImmutableSet.copyOf(array);
+        }
+
+        @Override
+        final void addQNames(final Set<QName> set) {
+            set.addAll(Arrays.asList(array));
+        }
+
+        @Override
+        public final String toString() {
+            final StringBuilder sb = new StringBuilder();
+            if (negated()) {
+                sb.append("not ");
+            }
+
+            sb.append("(\"").append(array[0]).append('"');
+            final String sep = infix();
+            for (int i = 1; i < array.length; ++i) {
+                sb.append(sep).append('"').append(array[i]).append('"');
+            }
+            return sb.append(')').toString();
+        }
+
+        abstract boolean negated();
+    }
+
+    private static final class Absent extends Single {
+        Absent(final QName qname) {
+            super(qname);
+        }
+
+        @Override
+        public IfFeatureExpr negate() {
+            return isPresent(qname);
+        }
+
+        @Override
+        public boolean test(final Set<QName> supportedFeatures) {
+            return !supportedFeatures.contains(qname);
+        }
+
+        @Override
+        public String toString() {
+            return "not \"" + qname + '"';
+        }
+    }
+
+    private static final class Present extends Single {
+        Present(final QName qname) {
+            super(qname);
+        }
+
+        @Override
+        public IfFeatureExpr negate() {
+            return new Absent(qname);
+        }
+
+        @Override
+        public boolean test(final Set<QName> supportedFeatures) {
+            return supportedFeatures.contains(qname);
+        }
+
+        @Override
+        public String toString() {
+            return "\"" + qname + '"';
+        }
+    }
+
+    private static final class AllExprs extends Complex {
+        AllExprs(final IfFeatureExpr[] exprs) {
+            super(exprs);
+        }
+
+        @Override
+        public IfFeatureExpr negate() {
+            return new AnyExpr(negateExprs());
+        }
+
+        @Override
+        public boolean test(final Set<QName> supportedFeatures) {
+            for (IfFeatureExpr expr : array) {
+                if (!expr.test(supportedFeatures)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        String infix() {
+            return " and ";
+        }
+    }
+
+    private static final class AnyExpr extends Complex {
+        AnyExpr(final IfFeatureExpr[] exprs) {
+            super(exprs);
+        }
+
+        @Override
+        public IfFeatureExpr negate() {
+            return new AllExprs(negateExprs());
+        }
+
+        @Override
+        public boolean test(final Set<QName> supportedFeatures) {
+            for (IfFeatureExpr expr : array) {
+                if (expr.test(supportedFeatures)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        String infix() {
+            return " or ";
+        }
+    }
+
+    private abstract static class AbstractAll extends Compound {
+        AbstractAll(final QName[] qnames) {
+            super(qnames);
+        }
+
+        @Override
+        public final boolean test(final Set<QName> supportedFeatures) {
+            final boolean neg = negated();
+            for (QName qname : array) {
+                if (supportedFeatures.contains(qname) == neg) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        final String infix() {
+            return " and ";
+        }
+    }
+
+    private static final class All extends AbstractAll {
+        All(final QName[] qnames) {
+            super(qnames);
+        }
+
+        @Override
+        public IfFeatureExpr negate() {
+            return new NotAll(array);
+        }
+
+        @Override
+        boolean negated() {
+            return false;
+        }
+    }
+
+    private static final class NotAll extends AbstractAll {
+        NotAll(final QName[] qnames) {
+            super(qnames);
+        }
+
+        @Override
+        public IfFeatureExpr negate() {
+            return new All(array);
+        }
+
+        @Override
+        boolean negated() {
+            return true;
+        }
+    }
+
+    private abstract static class AbstractAny extends Compound {
+        AbstractAny(final QName[] qnames) {
+            super(qnames);
+        }
+
+        @Override
+        public final boolean test(final Set<QName> supportedFeatures) {
+            for (QName qname : array) {
+                if (supportedFeatures.contains(qname)) {
+                    return !negated();
+                }
+            }
+            return negated();
+        }
+
+        @Override
+        final String infix() {
+            return " or ";
+        }
+    }
+
+    private static final class Any extends AbstractAny {
+        Any(final QName[] array) {
+            super(array);
+        }
+
+        @Override
+        public IfFeatureExpr negate() {
+            return new NotAny(array);
+        }
+
+        @Override
+        boolean negated() {
+            return false;
+        }
+    }
+
+    private static final class NotAny extends AbstractAny {
+        NotAny(final QName[] qnames) {
+            super(qnames);
+        }
+
+        @Override
+        public IfFeatureExpr negate() {
+            return new Any(array);
+        }
+
+        @Override
+        boolean negated() {
+            return true;
+        }
+    }
+
+    /**
+     * Construct an assertion that a feature is present in the set passed to {@link #test(Set)}.
+     *
+     * @param qname Feature QName
+     * @return An expression
+     * @throws NullPointerException if {@code qname} is null
+     */
+    public static final @NonNull IfFeatureExpr isPresent(final QName qname) {
+        return new Present(qname);
+    }
+
+    /**
+     * Construct a intersection (logical {@code AND}) expression of specified expressions.
+     *
+     * @param exprs Constituent expressions
+     * @return An expression
+     * @throws NullPointerException if {@code exprs} or any of its members is null
+     * @throws IllegalArgumentException if {@code exprs} is empty
+     */
+    public static final @NonNull IfFeatureExpr and(final Set<IfFeatureExpr> exprs) {
+        checkArgument(!exprs.isEmpty(), "Expressions may not be empty");
+        if (exprs.size() == 1) {
+            return exprs.iterator().next();
+        }
+        final Boolean composition = composition(exprs);
+        if (composition == null) {
+            return new AllExprs(exprs.toArray(new IfFeatureExpr[0]));
+        }
+
+        final QName[] qnames = extractQNames(exprs);
+        return composition ? new All(qnames) : new NotAny(qnames);
+    }
+
+    /**
+     * Construct a union (logical {@code OR}) expression of specified expressions.
+     *
+     * @param exprs Constituent expressions
+     * @return An expression
+     * @throws NullPointerException if {@code exprs} or any of its members is null
+     * @throws IllegalArgumentException if {@code exprs} is empty
+     */
+    public static final @NonNull IfFeatureExpr or(final Set<IfFeatureExpr> exprs) {
+        checkArgument(!exprs.isEmpty(), "Expressions may not be empty");
+        if (exprs.size() == 1) {
+            return exprs.iterator().next();
+        }
+        final Boolean composition = composition(exprs);
+        if (composition == null) {
+            return new AnyExpr(exprs.toArray(new IfFeatureExpr[0]));
+        }
+
+        final QName[] qnames = extractQNames(exprs);
+        return composition ? new Any(qnames) : new NotAll(qnames);
+    }
+
+    /**
+     * Returns the set of all {@code feature}s referenced by this expression. Each feature is identified by its QName.
+     *
+     * @return The set of referenced features. Mutability of the returned Set and order of features is undefined.
+     */
+    public abstract @NonNull Set<QName> getReferencedFeatures();
+
+    @Override
+    public abstract @NonNull IfFeatureExpr negate();
+
+    @Override
+    public abstract boolean test(Set<QName> supportedFeatures);
+
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals(Object obj);
+
+    @Override
+    public abstract String toString();
+
+    /**
+     * Add QNames referenced by this expression into a target set.
+     *
+     * @param set The set to fill
+     * @throws NullPointerException if {@code set} is null
+     */
+    abstract void addQNames(@NonNull Set<QName> set);
+
+    @SuppressFBWarnings(value = "NP_BOOLEAN_RETURN_NULL",
+            justification = "We need a tri-state value, this is the simplest")
+    private static @Nullable Boolean composition(final Set<IfFeatureExpr> exprs) {
+        boolean negative = false;
+        boolean positive = false;
+        for (IfFeatureExpr expr : exprs) {
+            if (expr instanceof Present) {
+                positive = true;
+            } else if (expr instanceof Absent) {
+                negative = true;
+            } else {
+                return null;
+            }
+        }
+
+        verify(negative || positive, "Unresolved expressions %s", exprs);
+        return positive == negative ? null : positive;
+    }
+
+    private static QName[] extractQNames(final Set<IfFeatureExpr> exprs) {
+        return exprs.stream().map(expr -> {
+            verify(expr instanceof Single, "Unexpected expression %s", expr);
+            return ((Single) expr).qname;
+        }).collect(ImmutableSet.toImmutableSet()).toArray(new QName[0]);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IfFeatureStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IfFeatureStatement.java
new file mode 100644 (file)
index 0000000..7fff6d9
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+/**
+ * Represents YANG if-feature statement. The "if-feature" statement makes its parent statement conditional.
+ */
+public interface IfFeatureStatement extends DeclaredStatement<IfFeatureExpr> {
+    /**
+     * In YANG 1.1 (RFC7950) implementation of IfFeatureStatement, the argument is a boolean expression over feature
+     * names defined by "feature" statements. Hence, add implementation to return a predicate on a collection
+     * of features against which to evaluate.
+     *
+     * @return Predicate on a collection of QNames against which to evaluate
+     */
+    @Beta
+    default @NonNull IfFeatureExpr getIfFeaturePredicate() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ImportEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ImportEffectiveStatement.java
new file mode 100644 (file)
index 0000000..ab7155a
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface ImportEffectiveStatement extends EffectiveStatement<String, ImportStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ImportStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ImportStatement.java
new file mode 100644 (file)
index 0000000..af00ae9
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+public interface ImportStatement extends DocumentedDeclaredStatement<String> {
+    default @NonNull String getModule() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(rawArgument());
+    }
+
+    default @NonNull PrefixStatement getPrefix() {
+        return findFirstDeclaredSubstatement(PrefixStatement.class).get();
+    }
+
+    default @Nullable RevisionDateStatement getRevisionDate() {
+        final Optional<RevisionDateStatement> opt = findFirstDeclaredSubstatement(RevisionDateStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IncludeEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IncludeEffectiveStatement.java
new file mode 100644 (file)
index 0000000..a0834af
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface IncludeEffectiveStatement extends EffectiveStatement<String, IncludeStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IncludeStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/IncludeStatement.java
new file mode 100644 (file)
index 0000000..28ff9c0
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+public interface IncludeStatement extends DocumentedDeclaredStatement<String> {
+    default @NonNull String getModule() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+
+    default @Nullable RevisionDateStatement getRevisionDate() {
+        final Optional<RevisionDateStatement> opt = findFirstDeclaredSubstatement(RevisionDateStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/InputEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/InputEffectiveStatement.java
new file mode 100644 (file)
index 0000000..71f90cd
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+
+@Beta
+public interface InputEffectiveStatement extends SchemaTreeEffectiveStatement<InputStatement>,
+    DataTreeAwareEffectiveStatement<QName, InputStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/InputStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/InputStatement.java
new file mode 100644 (file)
index 0000000..1f39845
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface InputStatement extends DeclaredStatement<QName>,
+        DataDefinitionAwareDeclaredStatement.WithReusableDefinitions<QName>,
+        MustStatementAwareDeclaredStatement<QName> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/KeyEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/KeyEffectiveStatement.java
new file mode 100644 (file)
index 0000000..d14293e
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface KeyEffectiveStatement extends EffectiveStatement<Collection<SchemaNodeIdentifier>, KeyStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/KeyStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/KeyStatement.java
new file mode 100644 (file)
index 0000000..e015604
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface KeyStatement extends DeclaredStatement<Collection<SchemaNodeIdentifier>> {
+
+}
+
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LeafEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LeafEffectiveStatement.java
new file mode 100644 (file)
index 0000000..5bd3b93
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public interface LeafEffectiveStatement extends DataTreeEffectiveStatement<LeafStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LeafListEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LeafListEffectiveStatement.java
new file mode 100644 (file)
index 0000000..2ad9622
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public interface LeafListEffectiveStatement extends DataTreeEffectiveStatement<LeafListStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LeafListStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LeafListStatement.java
new file mode 100644 (file)
index 0000000..dabe9ae
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface LeafListStatement extends MultipleElementsDeclaredStatement, TypeAwareDeclaredStatement,
+        ConfigStatementAwareDeclaredStatement<QName>, MustStatementAwareDeclaredStatement<QName> {
+    /**
+     * Return default statements defined in this leaf-list. For RFC6020 semantics, this method returns an empty
+     * collection.
+     *
+     * @return collection of default statements
+     */
+    default @NonNull Collection<? extends DefaultStatement> getDefaults() {
+        return declaredSubstatements(DefaultStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LeafStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LeafStatement.java
new file mode 100644 (file)
index 0000000..ee20d07
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface LeafStatement extends DataDefinitionStatement, TypeAwareDeclaredStatement,
+        ConfigStatementAwareDeclaredStatement<QName>, DefaultStatementAwareDeclaredStatement,
+        MandatoryStatementAwareDeclaredStatement<QName>, MustStatementAwareDeclaredStatement<QName> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LengthEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LengthEffectiveStatement.java
new file mode 100644 (file)
index 0000000..82c3000
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface LengthEffectiveStatement extends EffectiveStatement<List<ValueRange>, LengthStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LengthStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LengthStatement.java
new file mode 100644 (file)
index 0000000..728cdd0
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+
+public interface LengthStatement extends ConstrainedDocumentedDeclaredStatement<List<ValueRange>> {
+    @Override
+    @NonNull List<ValueRange> argument();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LinkageDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/LinkageDeclaredStatement.java
new file mode 100644 (file)
index 0000000..2c66bfe
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+@Beta
+public interface LinkageDeclaredStatement extends DeclaredStatement<String> {
+    default @NonNull Collection<? extends ImportStatement> getImports() {
+        return declaredSubstatements(ImportStatement.class);
+    }
+
+    default @NonNull Collection<? extends IncludeStatement> getIncludes() {
+        return declaredSubstatements(IncludeStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ListEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ListEffectiveStatement.java
new file mode 100644 (file)
index 0000000..5dbf4ba
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+
+@Beta
+public interface ListEffectiveStatement extends DataTreeEffectiveStatement<ListStatement>,
+    DataTreeAwareEffectiveStatement<QName, ListStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ListStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ListStatement.java
new file mode 100644 (file)
index 0000000..dfbe8dc
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface ListStatement extends MultipleElementsDeclaredStatement,
+        DataDefinitionAwareDeclaredStatement.WithReusableDefinitions<QName>,
+        ConfigStatementAwareDeclaredStatement<QName>, ActionStatementAwareDeclaredStatement<QName>,
+        MustStatementAwareDeclaredStatement<QName>, NotificationStatementAwareDeclaredStatement<QName> {
+    default @Nullable KeyStatement getKey() {
+        final Optional<KeyStatement> opt = findFirstDeclaredSubstatement(KeyStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+
+    default @NonNull Collection<? extends UniqueStatement> getUnique() {
+        return declaredSubstatements(UniqueStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MandatoryEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MandatoryEffectiveStatement.java
new file mode 100644 (file)
index 0000000..7578295
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+public interface MandatoryEffectiveStatement extends EffectiveStatement<Boolean, MandatoryStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MandatoryStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MandatoryStatement.java
new file mode 100644 (file)
index 0000000..fca875d
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface MandatoryStatement extends DeclaredStatement<Boolean> {
+    default @NonNull Boolean getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MandatoryStatementAwareDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MandatoryStatementAwareDeclaredStatement.java
new file mode 100644 (file)
index 0000000..a7a1c6a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+/**
+ * Marker interface for statements which may contain a 'mandatory' statement, as defined in RFC7950.
+ */
+@Beta
+public interface MandatoryStatementAwareDeclaredStatement<T> extends DeclaredStatement<T> {
+    /**
+     * Return a {@link MandatoryStatement} child, if present.
+     *
+     * @return A {@link MandatoryStatement}, or empty if none is present.
+     */
+    default @NonNull Optional<MandatoryStatement> getMandatory() {
+        return findFirstDeclaredSubstatement(MandatoryStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MaxElementsEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MaxElementsEffectiveStatement.java
new file mode 100644 (file)
index 0000000..d4cd28d
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface MaxElementsEffectiveStatement extends EffectiveStatement<String, MaxElementsStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MaxElementsStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MaxElementsStatement.java
new file mode 100644 (file)
index 0000000..cf669e2
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface MaxElementsStatement extends DeclaredStatement<String> {
+    default @NonNull String getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MetaDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MetaDeclaredStatement.java
new file mode 100644 (file)
index 0000000..6ed4e38
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018 Pantheon Technoglogies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+
+/**
+ * Common interface for statements which contain either a description/reference or a description/reference/status combo.
+ */
+@Beta
+public interface MetaDeclaredStatement<T> extends DocumentedDeclaredStatement<T> {
+    default Optional<OrganizationStatement> getOrganization() {
+        return findFirstDeclaredSubstatement(OrganizationStatement.class);
+    }
+
+    default Optional<ContactStatement> getContact() {
+        return findFirstDeclaredSubstatement(ContactStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MinElementsEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MinElementsEffectiveStatement.java
new file mode 100644 (file)
index 0000000..e623fa2
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface MinElementsEffectiveStatement extends EffectiveStatement<Integer, MinElementsStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MinElementsStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MinElementsStatement.java
new file mode 100644 (file)
index 0000000..5105e33
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface MinElementsStatement extends DeclaredStatement<Integer> {
+    default @NonNull Integer getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModifierEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModifierEffectiveStatement.java
new file mode 100644 (file)
index 0000000..6cc3418
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+
+@Beta
+public interface ModifierEffectiveStatement extends EffectiveStatement<ModifierKind, ModifierStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModifierStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModifierStatement.java
new file mode 100644 (file)
index 0000000..0c9cda2
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+
+/**
+ * Represents YANG modifier statement.
+ *
+ * <p>
+ * The "modifier" statement, which is an optional substatement to the "pattern" statement, takes as an argument
+ * the string "invert-match". If a pattern has the "invert-match" modifier present, the type is restricted to values
+ * that do not match the pattern.
+ */
+@Beta
+public interface ModifierStatement extends DeclaredStatement<ModifierKind> {
+    default @NonNull ModifierKind getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModuleEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModuleEffectiveStatement.java
new file mode 100644 (file)
index 0000000..2145526
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+
+/**
+ * Effective view of a {@link ModuleStatement}.
+ */
+@Beta
+// FIXME: 5.0.0: we should reshuffle the String here, as module name is in reality a YANG identifier, e.g. not just
+//               an ordinary String. We really want this to be a QName, so that we do not need the localQNameModule
+//               bit, but that may be problematic with ModuleStatement, which is getting created before we even know
+//               the namespace :( A type capture of the string may just be sufficient.
+public interface ModuleEffectiveStatement extends DataTreeAwareEffectiveStatement<String, ModuleStatement> {
+    /**
+     * Namespace mapping all known prefixes in a module to their modules. Note this namespace includes the module
+     * in which it is instantiated.
+     */
+    abstract class PrefixToEffectiveModuleNamespace
+            implements IdentifierNamespace<String, @NonNull ModuleEffectiveStatement> {
+        private PrefixToEffectiveModuleNamespace() {
+            // This class should never be subclassed
+        }
+    }
+
+    /**
+     * Namespace mapping all known {@link QNameModule}s to their encoding prefixes. This includes the declaration
+     * from prefix/namespace/revision and all imports as they were resolved.
+     */
+    abstract class QNameModuleToPrefixNamespace implements IdentifierNamespace<QNameModule, @NonNull String> {
+        private QNameModuleToPrefixNamespace() {
+            // This class should never be subclassed
+        }
+    }
+
+    /**
+     * Namespace mapping all included submodules. The namespaces is keyed by submodule name.
+     */
+    abstract class NameToEffectiveSubmoduleNamespace
+            implements IdentifierNamespace<String, @NonNull SubmoduleEffectiveStatement> {
+        private NameToEffectiveSubmoduleNamespace() {
+            // This class should never be subclassed
+        }
+    }
+
+    /**
+     * Get the local QNameModule of this module. All implementations need to override this default method.
+     *
+     * @return Local QNameModule
+     */
+    @NonNull QNameModule localQNameModule();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModuleHeaderGroup.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModuleHeaderGroup.java
new file mode 100644 (file)
index 0000000..681dea4
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+@Rfc6020AbnfRule("module-header-stmts")
+public interface ModuleHeaderGroup {
+
+    @Nullable YangVersionStatement getYangVersion();
+
+    @NonNull NamespaceStatement getNamespace();
+
+    @NonNull PrefixStatement getPrefix();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModuleStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ModuleStatement.java
new file mode 100644 (file)
index 0000000..693926f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+
+public interface ModuleStatement extends MetaDeclaredStatement<String>, ModuleHeaderGroup, LinkageDeclaredStatement,
+        RevisionAwareDeclaredStatement, BodyDeclaredStatement {
+    default @NonNull String getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(rawArgument());
+    }
+
+    @Override
+    default YangVersionStatement getYangVersion() {
+        final Optional<YangVersionStatement> opt = findFirstDeclaredSubstatement(YangVersionStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+
+    @Override
+    default NamespaceStatement getNamespace() {
+        return findFirstDeclaredSubstatement(NamespaceStatement.class).get();
+    }
+
+    @Override
+    default PrefixStatement getPrefix() {
+        return findFirstDeclaredSubstatement(PrefixStatement.class).get();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MultipleElementsDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MultipleElementsDeclaredStatement.java
new file mode 100644 (file)
index 0000000..60f4c1c
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018 Pantheon Technoglogies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+
+@Beta
+public interface MultipleElementsDeclaredStatement extends DataDefinitionStatement {
+    default @NonNull Optional<MinElementsStatement> getMinElements() {
+        return findFirstDeclaredSubstatement(MinElementsStatement.class);
+    }
+
+    default @NonNull Optional<MaxElementsStatement> getMaxElements() {
+        return findFirstDeclaredSubstatement(MaxElementsStatement.class);
+    }
+
+    default @NonNull Optional<OrderedByStatement> getOrderedBy() {
+        return findFirstDeclaredSubstatement(OrderedByStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MustEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MustEffectiveStatement.java
new file mode 100644 (file)
index 0000000..2f1d281
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface MustEffectiveStatement extends EffectiveStatement<RevisionAwareXPath, MustStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MustStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MustStatement.java
new file mode 100644 (file)
index 0000000..50d91ed
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+
+public interface MustStatement extends ConstrainedDocumentedDeclaredStatement<RevisionAwareXPath> {
+    default @NonNull RevisionAwareXPath getCondition() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MustStatementAwareDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/MustStatementAwareDeclaredStatement.java
new file mode 100644 (file)
index 0000000..ce1a05b
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+@Beta
+public interface MustStatementAwareDeclaredStatement<A> extends DeclaredStatement<A> {
+    /**
+     * Return collection of {@link MustStatement}. For RFC6020, this method returns an empty collection for statements
+     * which do not allow for must statement children.
+     *
+     * @return collection of must statements
+     */
+    default @NonNull Collection<? extends MustStatement> getMustStatements() {
+        return declaredSubstatements(MustStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NamespaceEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NamespaceEffectiveStatement.java
new file mode 100644 (file)
index 0000000..327b569
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.net.URI;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface NamespaceEffectiveStatement extends EffectiveStatement<URI, NamespaceStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NamespaceStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NamespaceStatement.java
new file mode 100644 (file)
index 0000000..c7e8d72
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.net.URI;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface NamespaceStatement extends DeclaredStatement<URI> {
+    default @NonNull URI getUri() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NamespacedEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NamespacedEffectiveStatement.java
new file mode 100644 (file)
index 0000000..3fb7ee2
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Common super-interface for all statements which can be held in one of the {@link EffectiveStatementNamespace}s.
+ *
+ * @param <D> Declared statement type
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface NamespacedEffectiveStatement<D extends DeclaredStatement<QName>>
+    extends EffectiveStatement<QName, D>, Identifiable<QName> {
+
+    @Override
+    default QName getIdentifier() {
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NotificationEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NotificationEffectiveStatement.java
new file mode 100644 (file)
index 0000000..0d5add2
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+
+@Beta
+public interface NotificationEffectiveStatement extends SchemaTreeEffectiveStatement<NotificationStatement>,
+    DataTreeAwareEffectiveStatement<QName, NotificationStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NotificationStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NotificationStatement.java
new file mode 100644 (file)
index 0000000..aed5b0e
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface NotificationStatement extends DocumentedDeclaredStatement.WithStatus<QName>,
+        DataDefinitionAwareDeclaredStatement.WithReusableDefinitions<QName>, IfFeatureAwareDeclaredStatement<QName>,
+        MustStatementAwareDeclaredStatement<QName> {
+    default @NonNull QName getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NotificationStatementAwareDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/NotificationStatementAwareDeclaredStatement.java
new file mode 100644 (file)
index 0000000..be81668
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+@Beta
+public interface NotificationStatementAwareDeclaredStatement<A> extends DeclaredStatement<A> {
+    /**
+     * Return collection of {@link NotificationStatement}. For RFC6020, this method returns an empty collection for
+     * statements which do not allow for must statement children.
+     *
+     * @return collection of notification statements
+     */
+    default @NonNull Collection<? extends NotificationStatement> getNotifications() {
+        return declaredSubstatements(NotificationStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OperationDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OperationDeclaredStatement.java
new file mode 100644 (file)
index 0000000..c847992
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Common interface for action and rpc statements.
+ */
+@Beta
+public interface OperationDeclaredStatement extends DocumentedDeclaredStatement.WithStatus<QName>,
+        IfFeatureAwareDeclaredStatement<QName> {
+    default @NonNull QName getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+
+    default @NonNull Optional<InputStatement> getInput() {
+        return findFirstDeclaredSubstatement(InputStatement.class);
+    }
+
+    default @NonNull Optional<OutputStatement> getOutput() {
+        return findFirstDeclaredSubstatement(OutputStatement.class);
+    }
+
+    default @NonNull Collection<? extends TypedefStatement> getTypedefs() {
+        return declaredSubstatements(TypedefStatement.class);
+    }
+
+    default @NonNull Collection<? extends GroupingStatement> getGroupings() {
+        return declaredSubstatements(GroupingStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OrderedByEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OrderedByEffectiveStatement.java
new file mode 100644 (file)
index 0000000..724e272
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface OrderedByEffectiveStatement extends EffectiveStatement<String, OrderedByStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OrderedByStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OrderedByStatement.java
new file mode 100644 (file)
index 0000000..554f215
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface OrderedByStatement extends DeclaredStatement<String> {
+    default @NonNull String getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OrganizationEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OrganizationEffectiveStatement.java
new file mode 100644 (file)
index 0000000..2c1f941
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface OrganizationEffectiveStatement extends EffectiveStatement<String, OrganizationStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OrganizationStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OrganizationStatement.java
new file mode 100644 (file)
index 0000000..456c8e7
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface OrganizationStatement extends DeclaredStatement<String> {
+    default @NonNull String getText() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OutputEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OutputEffectiveStatement.java
new file mode 100644 (file)
index 0000000..3172152
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+
+@Beta
+public interface OutputEffectiveStatement extends SchemaTreeEffectiveStatement<OutputStatement>,
+    DataTreeAwareEffectiveStatement<QName, OutputStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OutputStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/OutputStatement.java
new file mode 100644 (file)
index 0000000..f15d820
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface OutputStatement extends DeclaredStatement<QName>,
+        DataDefinitionAwareDeclaredStatement.WithReusableDefinitions<QName>,
+        MustStatementAwareDeclaredStatement<QName> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PathEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PathEffectiveStatement.java
new file mode 100644 (file)
index 0000000..219c5a2
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface PathEffectiveStatement extends EffectiveStatement<PathExpression, PathStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PathStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PathStatement.java
new file mode 100644 (file)
index 0000000..272f7a3
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface PathStatement extends DeclaredStatement<PathExpression> {
+    default @NonNull PathExpression getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PatternEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PatternEffectiveStatement.java
new file mode 100644 (file)
index 0000000..9f1c904
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+
+@Beta
+public interface PatternEffectiveStatement extends EffectiveStatement<PatternConstraint, PatternStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PatternStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PatternStatement.java
new file mode 100644 (file)
index 0000000..b147685
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+
+public interface PatternStatement extends ConstrainedDocumentedDeclaredStatement<PatternConstraint> {
+    default @NonNull PatternConstraint getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+
+    /**
+     * Return a modifier statement, if present. In RFC6020 semantics, there are no modifiers and this methods always
+     * returns null.
+     *
+     * @return modifier statement, nul if not present.
+     */
+    default @Nullable ModifierStatement getModifierStatement() {
+        final Optional<ModifierStatement> opt = findFirstDeclaredSubstatement(ModifierStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PositionEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PositionEffectiveStatement.java
new file mode 100644 (file)
index 0000000..56e4146
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface PositionEffectiveStatement extends EffectiveStatement<Long, PositionStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PositionStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PositionStatement.java
new file mode 100644 (file)
index 0000000..e5a732c
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface PositionStatement extends DeclaredStatement<Long> {
+    default long getValue() {
+        return argument().longValue();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PrefixEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PrefixEffectiveStatement.java
new file mode 100644 (file)
index 0000000..a846f6e
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface PrefixEffectiveStatement extends EffectiveStatement<String, PrefixStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PrefixStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PrefixStatement.java
new file mode 100644 (file)
index 0000000..44354e0
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface PrefixStatement extends DeclaredStatement<String> {
+    default @NonNull String getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(rawArgument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PresenceEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PresenceEffectiveStatement.java
new file mode 100644 (file)
index 0000000..f23a947
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface PresenceEffectiveStatement extends EffectiveStatement<String, PresenceStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PresenceStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/PresenceStatement.java
new file mode 100644 (file)
index 0000000..c570225
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface PresenceStatement extends DeclaredStatement<String> {
+    default @NonNull String getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RangeEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RangeEffectiveStatement.java
new file mode 100644 (file)
index 0000000..8fcb580
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface RangeEffectiveStatement extends EffectiveStatement<List<ValueRange>, RangeStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RangeStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RangeStatement.java
new file mode 100644 (file)
index 0000000..4071333
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+
+public interface RangeStatement extends ConstrainedDocumentedDeclaredStatement<List<ValueRange>> {
+    @Override
+    @NonNull List<ValueRange> argument();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ReferenceEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ReferenceEffectiveStatement.java
new file mode 100644 (file)
index 0000000..77bbd09
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface ReferenceEffectiveStatement extends EffectiveStatement<String, ReferenceStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ReferenceStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ReferenceStatement.java
new file mode 100644 (file)
index 0000000..e5341c8
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface ReferenceStatement extends DeclaredStatement<String> {
+    default @NonNull String getText() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(rawArgument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RefineEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RefineEffectiveStatement.java
new file mode 100644 (file)
index 0000000..27c72a5
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface RefineEffectiveStatement extends EffectiveStatement<SchemaNodeIdentifier, RefineStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RefineStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RefineStatement.java
new file mode 100644 (file)
index 0000000..fb9afef
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+public interface RefineStatement extends ConfigStatementAwareDeclaredStatement<SchemaNodeIdentifier>,
+        DocumentedDeclaredStatement<SchemaNodeIdentifier>, IfFeatureAwareDeclaredStatement<SchemaNodeIdentifier>,
+        MandatoryStatementAwareDeclaredStatement<SchemaNodeIdentifier>,
+        MustStatementAwareDeclaredStatement<SchemaNodeIdentifier> {
+    default @NonNull String getTargetNode() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(rawArgument());
+    }
+
+    default @NonNull Collection<? extends DefaultStatement> getDefaults() {
+        return declaredSubstatements(DefaultStatement.class);
+    }
+
+    default @Nullable PresenceStatement getPresence() {
+        final Optional<PresenceStatement> opt = findFirstDeclaredSubstatement(PresenceStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+
+    default @Nullable MinElementsStatement getMinElements() {
+        final Optional<MinElementsStatement> opt = findFirstDeclaredSubstatement(MinElementsStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+
+    default @Nullable MaxElementsStatement getMaxElements() {
+        final Optional<MaxElementsStatement> opt = findFirstDeclaredSubstatement(MaxElementsStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RequireInstanceEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RequireInstanceEffectiveStatement.java
new file mode 100644 (file)
index 0000000..3702bd1
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface RequireInstanceEffectiveStatement extends EffectiveStatement<Boolean, RequireInstanceStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RequireInstanceStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RequireInstanceStatement.java
new file mode 100644 (file)
index 0000000..3614b32
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface RequireInstanceStatement extends DeclaredStatement<Boolean> {
+    default boolean getValue() {
+        return argument().booleanValue();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionAwareDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionAwareDeclaredStatement.java
new file mode 100644 (file)
index 0000000..4679908
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018 Pantheon Technoglogies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+@Beta
+public interface RevisionAwareDeclaredStatement extends DeclaredStatement<String> {
+    default @NonNull Collection<? extends RevisionStatement> getRevisions() {
+        return declaredSubstatements(RevisionStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionDateEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionDateEffectiveStatement.java
new file mode 100644 (file)
index 0000000..78dfb56
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface RevisionDateEffectiveStatement extends EffectiveStatement<Revision, RevisionDateStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionDateStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionDateStatement.java
new file mode 100644 (file)
index 0000000..dd2c652
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface RevisionDateStatement extends DeclaredStatement<Revision> {
+    default Revision getDate() {
+        return argument();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionEffectiveStatement.java
new file mode 100644 (file)
index 0000000..073c1cc
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface RevisionEffectiveStatement extends EffectiveStatement<Revision, RevisionStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RevisionStatement.java
new file mode 100644 (file)
index 0000000..a3838d8
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Revision;
+
+public interface RevisionStatement extends DocumentedDeclaredStatement<Revision> {
+    default @NonNull Revision getDate() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/Rfc6020AbnfRule.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/Rfc6020AbnfRule.java
new file mode 100644 (file)
index 0000000..385d92b
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * References ABNF rule defined in RFC6020 - YANG Specification.
+ *
+ * <p>
+ * An interface / class annotated with this annotation
+ * is Java representation of data represented by ABNF rule
+ * provided as {@link #value()}. Java representation
+ * does not need to be direct,
+ * but must retain all information in some, publicly
+ * accessible form for consumers.
+ * </p>
+ * <p>
+ * Note that this annotation is used currently only for documentation
+ * and does not affect any runtime behaviour.
+ * </p>
+ */
+@Documented
+@Retention(RetentionPolicy.SOURCE)
+@interface Rfc6020AbnfRule {
+
+    String[] value();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RpcEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RpcEffectiveStatement.java
new file mode 100644 (file)
index 0000000..87c4b0e
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+
+@Beta
+public interface RpcEffectiveStatement extends SchemaTreeEffectiveStatement<RpcStatement>,
+    DataTreeAwareEffectiveStatement<QName, RpcStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RpcStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/RpcStatement.java
new file mode 100644 (file)
index 0000000..20d928e
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+public interface RpcStatement extends OperationDeclaredStatement {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SchemaNodeIdentifier.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SchemaNodeIdentifier.java
new file mode 100644 (file)
index 0000000..86be319
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.UnmodifiableIterator;
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Represents unique path to the every schema node inside the schema node identifier namespace.
+ */
+public abstract class SchemaNodeIdentifier implements Immutable {
+
+    /**
+     * An absolute schema node identifier.
+     */
+    public static final class Absolute extends SchemaNodeIdentifier {
+        private Absolute(final SchemaNodeIdentifier parent, final QName qname) {
+            super(parent, qname);
+        }
+
+        @Override
+        public boolean isAbsolute() {
+            return true;
+        }
+
+        @Override
+        public Absolute createChild(final QName element) {
+            return new Absolute(this, requireNonNull(element));
+        }
+    }
+
+    /**
+     * A relative schema node identifier.
+     */
+    public static final class Relative extends SchemaNodeIdentifier {
+        private Relative(final SchemaNodeIdentifier parent, final QName qname) {
+            super(parent, qname);
+        }
+
+        @Override
+        public boolean isAbsolute() {
+            return false;
+        }
+
+        @Override
+        public Relative createChild(final QName element) {
+            return new Relative(this, requireNonNull(element));
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<SchemaNodeIdentifier, ImmutableList> LEGACYPATH_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(SchemaNodeIdentifier.class, ImmutableList.class, "legacyPath");
+    private static final AtomicReferenceFieldUpdater<SchemaNodeIdentifier, SchemaPath> SCHEMAPATH_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(SchemaNodeIdentifier.class, SchemaPath.class, "schemaPath");
+    /**
+     * Shared instance of the conceptual root schema node.
+     */
+    public static final Absolute ROOT = new Absolute(null, null);
+
+    /**
+     * Shared instance of the "same" relative schema node.
+     */
+    public static final Relative SAME = new Relative(null, null);
+
+    /**
+     * Parent path.
+     */
+    private final SchemaNodeIdentifier parent;
+
+    /**
+     * This component.
+     */
+    private final QName qname;
+
+    /**
+     * Cached hash code. We can use this since we are immutable.
+     */
+    private final int hash;
+
+    /**
+     * Cached legacy path, filled-in when {@link #getPath()} or {@link #getPathTowardsRoot()}
+     * is invoked.
+     */
+    private volatile ImmutableList<QName> legacyPath;
+
+    /**
+     * Cached SchemaPath.
+     */
+    private volatile SchemaPath schemaPath;
+
+    SchemaNodeIdentifier(final SchemaNodeIdentifier parent, final QName qname) {
+        this.parent = parent;
+        this.qname = qname;
+
+        int tmp = Objects.hashCode(parent);
+        if (qname != null) {
+            tmp = tmp * 31 + qname.hashCode();
+        }
+
+        hash = tmp;
+    }
+
+    private ImmutableList<QName> getLegacyPath() {
+        ImmutableList<QName> ret = legacyPath;
+        if (ret == null) {
+            ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse();
+            LEGACYPATH_UPDATER.lazySet(this, ret);
+        }
+
+        return ret;
+    }
+
+    /**
+     * Constructs new instance of this class with the concrete path.
+     *
+     * @param path
+     *            list of QName instances which specifies exact path to the
+     *            module node
+     * @param absolute
+     *            boolean value which specifies if the path is absolute or
+     *            relative
+     *
+     * @return A SchemaNodeIdentifier instance.
+     */
+    public static SchemaNodeIdentifier create(final Iterable<QName> path, final boolean absolute) {
+        final SchemaNodeIdentifier parent = absolute ? ROOT : SAME;
+        return parent.createChild(path);
+    }
+
+    /**
+     * Constructs new instance of this class with the concrete path.
+     *
+     * @param absolute
+     *            boolean value which specifies if the path is absolute or
+     *            relative
+     * @param path
+     *            one or more QName instances which specifies exact path to the
+     *            module node
+     *
+     * @return A SchemaPath instance.
+     */
+    public static SchemaNodeIdentifier create(final boolean absolute, final QName... path) {
+        return create(Arrays.asList(path), absolute);
+    }
+
+    /**
+     * Create a child path based on concatenation of this path and a relative path.
+     *
+     * @param relative Relative path
+     * @return A new child path
+     */
+    public SchemaNodeIdentifier createChild(final Iterable<QName> relative) {
+        if (Iterables.isEmpty(relative)) {
+            return this;
+        }
+
+        SchemaNodeIdentifier parentNode = this;
+        for (QName item : relative) {
+            parentNode = parentNode.createChild(item);
+        }
+
+        return parentNode;
+    }
+
+    /**
+     * Create a child path based on concatenation of this path and a relative path.
+     *
+     * @param relative Relative SchemaPath
+     * @return A new child path
+     */
+    public SchemaNodeIdentifier createChild(final SchemaNodeIdentifier relative) {
+        checkArgument(!relative.isAbsolute(), "Child creation requires relative path");
+        return createChild(relative.getPathFromRoot());
+    }
+
+    /**
+     * Create a child path based on concatenation of this path and an additional path element.
+     *
+     * @param element Next SchemaPath element
+     * @return A new child path
+     */
+    public abstract SchemaNodeIdentifier createChild(QName element);
+
+    /**
+     * Create a child path based on concatenation of this path and additional
+     * path elements.
+     *
+     * @param elements Relative SchemaPath elements
+     * @return A new child path
+     */
+    public SchemaNodeIdentifier createChild(final QName... elements) {
+        return createChild(Arrays.asList(elements));
+    }
+
+    /**
+     * Returns the list of nodes which need to be traversed to get from the
+     * starting point (root for absolute SchemaPaths) to the node represented
+     * by this object.
+     *
+     * @return list of <code>qname</code> instances which represents
+     *         path from the root to the schema node.
+     */
+    public Iterable<QName> getPathFromRoot() {
+        return getLegacyPath();
+    }
+
+    /**
+     * Returns the list of nodes which need to be traversed to get from this
+     * node to the starting point (root for absolute SchemaPaths).
+     *
+     * @return list of <code>qname</code> instances which represents
+     *         path from the schema node towards the root.
+     */
+    public Iterable<QName> getPathTowardsRoot() {
+        return () -> new UnmodifiableIterator<QName>() {
+            private SchemaNodeIdentifier current = SchemaNodeIdentifier.this;
+
+            @Override
+            public boolean hasNext() {
+                return current.parent != null;
+            }
+
+            @Override
+            public QName next() {
+                if (current.parent != null) {
+                    final QName ret = current.qname;
+                    current = current.parent;
+                    return ret;
+                } else {
+                    throw new NoSuchElementException("No more elements available");
+                }
+            }
+        };
+    }
+
+    /**
+     * Returns the immediate parent SchemaPath.
+     *
+     * @return Parent path, null if this SchemaPath is already toplevel.
+     */
+    public SchemaNodeIdentifier getParent() {
+        return parent;
+    }
+
+    /**
+     * Get the last component of this path.
+     *
+     * @return The last component of this path.
+     */
+    public final QName getLastComponent() {
+        return qname;
+    }
+
+    private SchemaPath createSchemaPath() {
+        final SchemaPath newPath;
+        if (parent == null) {
+            final SchemaPath parentPath = isAbsolute() ? SchemaPath.ROOT : SchemaPath.SAME;
+            newPath = qname == null ? parentPath : parentPath.createChild(qname);
+        } else {
+            newPath = parent.asSchemaPath().createChild(qname);
+        }
+
+        return SCHEMAPATH_UPDATER.compareAndSet(this, null, newPath) ? newPath : schemaPath;
+    }
+
+    /**
+     * Create the {@link SchemaPath} equivalent of this identifier.
+     *
+     * @return SchemaPath equivalent.
+     */
+    public final SchemaPath asSchemaPath() {
+        final SchemaPath ret = schemaPath;
+        return ret != null ? ret : createSchemaPath();
+    }
+
+    /**
+     * Describes whether schema node identifier is|isn't absolute.
+     *
+     * @return boolean value which is <code>true</code> if schema path is
+     *         absolute.
+     */
+    public abstract boolean isAbsolute();
+
+    @Override
+    public final int hashCode() {
+        return hash;
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final SchemaNodeIdentifier other = (SchemaNodeIdentifier) obj;
+        return Objects.equals(qname, other.qname) && Objects.equals(parent, other.parent);
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("path", getPathFromRoot());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SchemaTreeAwareEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SchemaTreeAwareEffectiveStatement.java
new file mode 100644 (file)
index 0000000..58a09a5
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Interface implemented by all {@link EffectiveStatement}s which can contain a {@code schema tree} child. This tree
+ * can be walked using {@link SchemaNodeIdentifier}, looking up each component of
+ * {@link SchemaNodeIdentifier#getPathFromRoot()} using {@link #findSchemaTreeNode(QName)}.
+ *
+ * @param <A> Argument type
+ * @param <D> Class representing declared version of this statement.
+ * @author Robert Varga
+ */
+@Beta
+public interface SchemaTreeAwareEffectiveStatement<A, D extends DeclaredStatement<A>> extends EffectiveStatement<A, D> {
+    /**
+     * Namespace of {@code schema node}s defined within this node.
+     *
+     * @param <T> Child statement type
+     * @author Robert Varga
+     */
+    @NonNullByDefault
+    abstract class Namespace<T extends SchemaTreeEffectiveStatement<?>> extends EffectiveStatementNamespace<T> {
+        private Namespace() {
+            // Should never be instantiated
+        }
+    }
+
+    /**
+     * Find a {@code schema tree} child {@link SchemaTreeEffectiveStatement}, as identified by its QName argument.
+     *
+     * @param qname Child identifier
+     * @return Schema tree child, or empty
+     * @throws NullPointerException if {@code qname} is null
+     */
+    default <E extends SchemaTreeEffectiveStatement<?>> @NonNull Optional<E> findSchemaTreeNode(
+            final @NonNull QName qname) {
+        return get(Namespace.class, requireNonNull(qname));
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SchemaTreeEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SchemaTreeEffectiveStatement.java
new file mode 100644 (file)
index 0000000..9468c2b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Common interface grouping all {@link EffectiveStatement}s which are accessible via
+ * {@link SchemaTreeAwareEffectiveStatement.Namespace}. This such statement corresponds to a {@code schema node}.
+ *
+ * <p>
+ * This interface could be named {@code SchemaNodeEffectiveStatement}, but that could induce a notion that it has
+ * something to do with {@link SchemaNode} -- which it has not. SchemaNode semantics are wrong in may aspects
+ * and while implementations of this interface may also implement SchemaNode, the semantics of this interface should
+ * always be preferred and SchemaNode is to be treated as deprecated whenever possible.
+ *
+ * @param <D> Declared statement type
+ * @author Robert Varga
+ */
+@Beta
+public interface SchemaTreeEffectiveStatement<D extends DeclaredStatement<QName>>
+    extends NamespacedEffectiveStatement<D> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/StatusEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/StatusEffectiveStatement.java
new file mode 100644 (file)
index 0000000..c139fb7
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface StatusEffectiveStatement extends EffectiveStatement<Status, StatusStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/StatusStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/StatusStatement.java
new file mode 100644 (file)
index 0000000..ee1dfbf
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface StatusStatement extends DeclaredStatement<Status> {
+    default @NonNull Status getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SubmoduleEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SubmoduleEffectiveStatement.java
new file mode 100644 (file)
index 0000000..79c95a8
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface SubmoduleEffectiveStatement extends EffectiveStatement<String, SubmoduleStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SubmoduleStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SubmoduleStatement.java
new file mode 100644 (file)
index 0000000..2e003db
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+public interface SubmoduleStatement extends MetaDeclaredStatement<String>, LinkageDeclaredStatement,
+        RevisionAwareDeclaredStatement, BodyDeclaredStatement {
+    default @NonNull String getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(rawArgument());
+    }
+
+    default @Nullable YangVersionStatement getYangVersion() {
+        final Optional<YangVersionStatement> opt = findFirstDeclaredSubstatement(YangVersionStatement.class);
+        return opt.isPresent() ? opt.get() : null;
+    }
+
+    default @NonNull BelongsToStatement getBelongsTo() {
+        return findFirstDeclaredSubstatement(BelongsToStatement.class).get();
+    }
+}
+
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypeAwareDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypeAwareDeclaredStatement.java
new file mode 100644 (file)
index 0000000..14b507f
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018 Pantheon Technoglogies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+@Beta
+public interface TypeAwareDeclaredStatement extends DeclaredStatement<QName> {
+    default @NonNull TypeStatement getType() {
+        return findFirstDeclaredSubstatement(TypeStatement.class).get();
+    }
+
+    default @NonNull Optional<UnitsStatement> getUnits() {
+        return findFirstDeclaredSubstatement(UnitsStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypeDefinitionAware.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypeDefinitionAware.java
new file mode 100644 (file)
index 0000000..7558ab2
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Interface indicating an entity which acts as a holder of a {@link TypeDefinition}.
+ */
+@Beta
+public interface TypeDefinitionAware {
+    /**
+     * Return this statement's effective type definition.
+     *
+     * @return Effective {@link TypeDefinition} as defined by this statement.
+     */
+    @NonNull TypeDefinition<?> getTypeDefinition();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypeEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypeEffectiveStatement.java
new file mode 100644 (file)
index 0000000..3b52885
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+public interface TypeEffectiveStatement<T extends TypeStatement> extends EffectiveStatement<String, T>,
+       TypeDefinitionAware {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypeStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypeStatement.java
new file mode 100644 (file)
index 0000000..41887f6
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+@Rfc6020AbnfRule("type-stmt")
+public interface TypeStatement extends DeclaredStatement<String> {
+    default @NonNull String getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+
+    @Rfc6020AbnfRule("numerical-restrictions")
+    interface NumericalRestrictions extends TypeStatement {
+
+        default @NonNull RangeStatement getRange() {
+            return findFirstDeclaredSubstatement(RangeStatement.class).get();
+        }
+    }
+
+    @Rfc6020AbnfRule("decimal64-specification")
+    interface Decimal64Specification extends TypeStatement {
+        default @NonNull FractionDigitsStatement getFractionDigits() {
+            return findFirstDeclaredSubstatement(FractionDigitsStatement.class).get();
+        }
+
+        default @Nullable RangeStatement getRange() {
+            final Optional<RangeStatement> opt = findFirstDeclaredSubstatement(RangeStatement.class);
+            return opt.isPresent() ? opt.get() : null;
+        }
+    }
+
+    @Rfc6020AbnfRule("string-restrictions")
+    interface StringRestrictions extends TypeStatement {
+        default @Nullable LengthStatement getLength() {
+            final Optional<LengthStatement> opt = findFirstDeclaredSubstatement(LengthStatement.class);
+            return opt.isPresent() ? opt.get() : null;
+        }
+
+        default @NonNull Collection<? extends PatternStatement> getPatterns() {
+            return declaredSubstatements(PatternStatement.class);
+        }
+    }
+
+    @Rfc6020AbnfRule("enum-specification")
+    interface EnumSpecification extends TypeStatement {
+
+        default @NonNull Collection<? extends EnumStatement> getEnums() {
+            return declaredSubstatements(EnumStatement.class);
+        }
+    }
+
+    @Rfc6020AbnfRule("leafref-specification")
+    interface LeafrefSpecification extends TypeStatement {
+        default @NonNull PathStatement getPath() {
+            return findFirstDeclaredSubstatement(PathStatement.class).get();
+        }
+
+        /**
+         * Return require-instance statement child, if present. For RFC6020 semantics, this method always returns
+         * null.
+         *
+         * @return require-instance statement, if present.
+         */
+        default @Nullable RequireInstanceStatement getRequireInstance() {
+            final Optional<RequireInstanceStatement> opt =
+                    findFirstDeclaredSubstatement(RequireInstanceStatement.class);
+            return opt.isPresent() ? opt.get() : null;
+        }
+    }
+
+    @Rfc6020AbnfRule("instanceidentifier-specification")
+    interface InstanceIdentifierSpecification extends TypeStatement {
+        /**
+         * Return require-instance statement child, if present. For RFC6020 semantics, this method always returns
+         * null.
+         *
+         * @return require-instance statement, if present.
+         */
+        default @Nullable RequireInstanceStatement getRequireInstance() {
+            final Optional<RequireInstanceStatement> opt =
+                    findFirstDeclaredSubstatement(RequireInstanceStatement.class);
+            return opt.isPresent() ? opt.get() : null;
+        }
+    }
+
+    @Rfc6020AbnfRule("identityref-specification")
+    interface IdentityRefSpecification extends TypeStatement {
+        /**
+         * Returns the base statements.
+         *
+         * @return collection of base statements (in YANG 1.1 models) or a collection containing just one base
+         *         statement (in YANG 1.0 models)
+         */
+        default @NonNull Collection<? extends BaseStatement> getBases() {
+            return declaredSubstatements(BaseStatement.class);
+        }
+    }
+
+    @Rfc6020AbnfRule("bits-specification")
+    interface BitsSpecification extends TypeStatement {
+        default @NonNull Collection<? extends BitStatement> getBits() {
+            return declaredSubstatements(BitStatement.class);
+        }
+    }
+
+    @Rfc6020AbnfRule("union-specification")
+    interface UnionSpecification extends TypeStatement {
+        default @NonNull Collection<? extends TypeStatement> getTypes() {
+            return declaredSubstatements(TypeStatement.class);
+        }
+    }
+
+    @Rfc6020AbnfRule("binary-specification")
+    interface BinarySpecification extends TypeStatement {
+        default @NonNull Collection<? extends LengthStatement> getLength() {
+            return declaredSubstatements(LengthStatement.class);
+        }
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypedefEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypedefEffectiveStatement.java
new file mode 100644 (file)
index 0000000..da3b3e9
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Effective model statement which should be used to derive application behaviour related to typedefs.
+ */
+public interface TypedefEffectiveStatement extends EffectiveStatement<QName, TypedefStatement>, TypeDefinitionAware {
+
+    /**
+     * Return this type definition as an effective type statement.
+     *
+     * @return Effective type statement.
+     */
+    @Beta
+    @NonNull TypeEffectiveStatement<TypeStatement> asTypeEffectiveStatement();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypedefStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/TypedefStatement.java
new file mode 100644 (file)
index 0000000..2329c11
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface TypedefStatement extends DocumentedDeclaredStatement.WithStatus<QName>, TypeAwareDeclaredStatement,
+        DefaultStatementAwareDeclaredStatement {
+    default @NonNull QName getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UniqueEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UniqueEffectiveStatement.java
new file mode 100644 (file)
index 0000000..f487414
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Relative;
+
+@Beta
+public interface UniqueEffectiveStatement extends EffectiveStatement<Set<Relative>, UniqueStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UniqueStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UniqueStatement.java
new file mode 100644 (file)
index 0000000..7744180
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Relative;
+
+public interface UniqueStatement extends DeclaredStatement<Set<SchemaNodeIdentifier.Relative>> {
+
+    default Set<Relative> getTag() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
+
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnitsEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnitsEffectiveStatement.java
new file mode 100644 (file)
index 0000000..358f678
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface UnitsEffectiveStatement extends EffectiveStatement<String, UnitsStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnitsStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnitsStatement.java
new file mode 100644 (file)
index 0000000..127481a
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface UnitsStatement extends DeclaredStatement<String> {
+    default @NonNull String getName() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnknownEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnknownEffectiveStatement.java
new file mode 100644 (file)
index 0000000..3b68992
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface UnknownEffectiveStatement<A> extends EffectiveStatement<A, UnknownStatement<A>> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnknownStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnknownStatement.java
new file mode 100644 (file)
index 0000000..d4d326d
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+/**
+ * A statement not covered by the base metamodel, e.g. it is not expressed directly in terms of concrete statement
+ * interfaces in this package. This interface is meant to be specialized by external semantic plugins, such that they
+ * are properly anchored in the metamodel.
+ *
+ * @param <A> Argument type ({@link Void} if statement does not have argument.)
+ */
+public interface UnknownStatement<A> extends DeclaredStatement<A> {
+    default @Nullable A getArgument() {
+        return argument();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnrecognizedEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnrecognizedEffectiveStatement.java
new file mode 100644 (file)
index 0000000..0a0af9f
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface UnrecognizedEffectiveStatement extends EffectiveStatement<String, UnrecognizedStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnrecognizedStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnrecognizedStatement.java
new file mode 100644 (file)
index 0000000..f14c0ec
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2017 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Common interface for unrecognized unknown statements. UnrecognizedStatement
+ * is an instance of a statement defined via an 'extension' statement, for which
+ * the parser did not have semantic support (in which case the statement would
+ * result in a corresponding semantic subclass of {@link UnknownStatement}).
+ */
+@Beta
+public interface UnrecognizedStatement extends UnknownStatement<String> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnresolvedNumber.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UnresolvedNumber.java
new file mode 100644 (file)
index 0000000..c90106c
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.api.stmt;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Range;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+@Beta
+public abstract class UnresolvedNumber extends Number implements Immutable {
+    private static final long serialVersionUID = 1L;
+    private static final UnresolvedNumber MAX = new UnresolvedNumber() {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public <T extends Number & Comparable<T>> T resolveLength(final Range<? extends T> span) {
+            return resolve(span.upperEndpoint());
+        }
+
+        @Override
+        public <T extends Number & Comparable<T>> T resolveRange(final Range<? extends T> span) {
+            return resolve(span.upperEndpoint());
+        }
+
+        @Override
+        public String toString() {
+            return "max";
+        }
+
+        private Object readResolve() {
+            return MAX;
+        }
+    };
+
+    private static final UnresolvedNumber MIN = new UnresolvedNumber() {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public <T extends Number & Comparable<T>> T resolveLength(final Range<? extends T> span) {
+            return resolve(span.lowerEndpoint());
+        }
+
+        @Override
+        public <T extends Number & Comparable<T>> T resolveRange(final Range<? extends T> span) {
+            return resolve(span.lowerEndpoint());
+        }
+
+        @Override
+        public String toString() {
+            return "min";
+        }
+
+        private Object readResolve() {
+            return MIN;
+        }
+    };
+
+    public static UnresolvedNumber min() {
+        return MIN;
+    }
+
+    public static UnresolvedNumber max() {
+        return MAX;
+    }
+
+    @Override
+    public final int intValue() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final long longValue() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final float floatValue() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final double doubleValue() {
+        throw new UnsupportedOperationException();
+    }
+
+    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+            justification = "https://github.com/spotbugs/spotbugs/issues/811")
+    private static <T extends Number> T resolve(final T number) {
+        checkArgument(!(number instanceof UnresolvedNumber));
+        return number;
+    }
+
+    public abstract <T extends Number & Comparable<T>> @NonNull T resolveLength(Range<? extends T> span);
+
+    public abstract <T extends Number & Comparable<T>> @NonNull T resolveRange(Range<? extends T> span);
+
+    @Override
+    public abstract String toString();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UsesEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UsesEffectiveStatement.java
new file mode 100644 (file)
index 0000000..0f31ace
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface UsesEffectiveStatement extends EffectiveStatement<QName, UsesStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UsesStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/UsesStatement.java
new file mode 100644 (file)
index 0000000..4f6bd7b
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+
+public interface UsesStatement extends DataDefinitionStatement {
+    default @NonNull Collection<? extends RefineStatement> getRefines() {
+        return declaredSubstatements(RefineStatement.class);
+    }
+
+    default @NonNull Collection<? extends AugmentStatement> getAugments() {
+        return declaredSubstatements(AugmentStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ValueEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ValueEffectiveStatement.java
new file mode 100644 (file)
index 0000000..dc4df6f
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface ValueEffectiveStatement extends EffectiveStatement<Integer, ValueStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ValueRange.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ValueRange.java
new file mode 100644 (file)
index 0000000..e124a5c
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * YANG specification of a numeric value range. This object is used for {@link LengthStatement} and
+ * {@link RangeStatement}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class ValueRange {
+    private static final class Singleton extends ValueRange {
+        private final @NonNull Number value;
+
+        Singleton(final @NonNull Number value) {
+            this.value = requireNonNull(value);
+        }
+
+        @Override
+        public @NonNull Number lowerBound() {
+            return value;
+        }
+
+        @Override
+        public @NonNull Number upperBound() {
+            return value;
+        }
+
+        @Override
+        public String toString() {
+            return value.toString();
+        }
+    }
+
+    private static final class Range extends ValueRange {
+        private final @NonNull Number lower;
+        private final @NonNull Number upper;
+
+        Range(final Number lower, final Number upper) {
+            this.lower = requireNonNull(lower);
+            this.upper = requireNonNull(upper);
+        }
+
+        @Override
+        public @NonNull Number lowerBound() {
+            return lower;
+        }
+
+        @Override
+        public @NonNull Number upperBound() {
+            return upper;
+        }
+
+        @Override
+        public String toString() {
+            return lower + ".." + upper;
+        }
+    }
+
+    public static ValueRange of(final @NonNull Number value) {
+        return new Singleton(value);
+    }
+
+    public static ValueRange of(final @NonNull Number lower, final @NonNull Number upper) {
+        return lower.equals(upper) ? of(lower) : new Range(lower, upper);
+    }
+
+    public abstract @NonNull Number lowerBound();
+
+    public abstract @NonNull Number upperBound();
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(lowerBound(), upperBound());
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof ValueRange)) {
+            return false;
+        }
+        final ValueRange other = (ValueRange) obj;
+        return lowerBound().equals(other.lowerBound()) && upperBound().equals(other.upperBound());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ValueStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/ValueStatement.java
new file mode 100644 (file)
index 0000000..a3037bb
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface ValueStatement extends DeclaredStatement<Integer> {
+    default int getValue() {
+        return argument().intValue();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/WhenEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/WhenEffectiveStatement.java
new file mode 100644 (file)
index 0000000..c37fb34
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface WhenEffectiveStatement extends EffectiveStatement<RevisionAwareXPath, WhenStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/WhenStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/WhenStatement.java
new file mode 100644 (file)
index 0000000..ac8d6ff
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+
+public interface WhenStatement extends DocumentedDeclaredStatement<RevisionAwareXPath> {
+    default @NonNull RevisionAwareXPath getCondition() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/WhenStatementAwareDeclaredStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/WhenStatementAwareDeclaredStatement.java
new file mode 100644 (file)
index 0000000..26ab491
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018 Pantheon Technoglogies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+
+@Beta
+public interface WhenStatementAwareDeclaredStatement<A> extends IfFeatureAwareDeclaredStatement<A> {
+    default @NonNull Optional<WhenStatement> getWhenStatement() {
+        return findFirstDeclaredSubstatement(WhenStatement.class);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/YangVersionEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/YangVersionEffectiveStatement.java
new file mode 100644 (file)
index 0000000..17790b8
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface YangVersionEffectiveStatement extends EffectiveStatement<YangVersion, YangVersionStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/YangVersionStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/YangVersionStatement.java
new file mode 100644 (file)
index 0000000..74df26c
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface YangVersionStatement extends DeclaredStatement<YangVersion> {
+    default @NonNull YangVersion getValue() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
+
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/YinElementEffectiveStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/YinElementEffectiveStatement.java
new file mode 100644 (file)
index 0000000..20633d0
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+@Beta
+public interface YinElementEffectiveStatement extends EffectiveStatement<Boolean, YinElementStatement> {
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/YinElementStatement.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/YinElementStatement.java
new file mode 100644 (file)
index 0000000..9895e94
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.api.stmt;
+
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public interface YinElementStatement extends DeclaredStatement<Boolean> {
+    default boolean getValue() {
+        return argument().booleanValue();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/compat/ActionNodeContainerCompat.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/compat/ActionNodeContainerCompat.java
new file mode 100644 (file)
index 0000000..1f0ced6
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.api.stmt.compat;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
+
+/**
+ * Compatibility bridge between {@link ActionNodeContainer#findAction(QName)} and
+ * {@link SchemaTreeAwareEffectiveStatement}.
+ */
+@Beta
+public interface ActionNodeContainerCompat<A, D extends DeclaredStatement<A>>
+        extends SchemaTreeAwareEffectiveStatement<A, D>, ActionNodeContainer {
+
+    @Override
+    default Optional<ActionDefinition> findAction(final QName qname) {
+        // 'action' identifier must never collide with another element, hence if we look it up and it ends up being
+        // an ActionDefinition, we have found a match.
+        return get(Namespace.class, qname)
+                .flatMap(child -> child instanceof ActionDefinition ? Optional.of(child) : Optional.empty());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/compat/NotificationNodeContainerCompat.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/compat/NotificationNodeContainerCompat.java
new file mode 100644 (file)
index 0000000..37a64bf
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.api.stmt.compat;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
+
+/**
+ * Compatibility bridge between {@link NotificationNodeContainer#findNotification(QName)} and
+ * {@link SchemaTreeAwareEffectiveStatement}.
+ */
+@Beta
+public interface NotificationNodeContainerCompat<A, D extends DeclaredStatement<A>>
+        extends SchemaTreeAwareEffectiveStatement<A, D>, NotificationNodeContainer {
+
+    @Override
+    default Optional<NotificationDefinition> findNotification(final QName qname) {
+        // 'notification' identifier must never collide with another element, hence if we look it up and it ends up
+        // being an NotificationDefinition, we have found a match.
+        return get(Namespace.class, qname)
+                .flatMap(child -> child instanceof NotificationDefinition ? Optional.of(child) : Optional.empty());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/compat/package-info.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/compat/package-info.java
new file mode 100644 (file)
index 0000000..abc791c
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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
+ */
+/**
+ * Compatibility interfaces for implementations which implement both
+ * {@link org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement} and
+ * {@link org.opendaylight.yangtools.yang.model.api.SchemaNode}.
+ */
+package org.opendaylight.yangtools.yang.model.api.stmt.compat;
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/BinaryTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/BinaryTypeDefinition.java
new file mode 100644 (file)
index 0000000..da7f4ad
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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.model.api.type;
+
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The binary built-in type represents any binary data, i.e., a sequence of
+ * octets.
+ * <br>
+ * Binary values are encoded with the base64 encoding scheme (see <a
+ * href="https://tools.ietf.org/html/rfc4648#section-4">[RFC4648], Section
+ * 4</a>). <br>
+ * The canonical form of a binary value follows the rules in <a
+ * href="https://tools.ietf.org/html/rfc4648">[RFC4648]</a>.
+ *
+ * <br>
+ * This interface was modeled according to definition in <a
+ * href="https://tools.ietf.org/html/rfc6020#section-9.8">[RFC-6020] The binary
+ * Built-In Type</a>
+ */
+public interface BinaryTypeDefinition extends LengthRestrictedTypeDefinition<BinaryTypeDefinition> {
+
+    static String toString(final @NonNull BinaryTypeDefinition type) {
+        return TypeDefinitions.toStringHelper(type).add("length", type.getLengthConstraint().orElse(null)).toString();
+    }
+
+    static int hashCode(final @NonNull BinaryTypeDefinition type) {
+        return Objects.hash(type.getPath(), type.getUnknownSchemaNodes(), type.getBaseType(),
+            type.getUnits().orElse(null), type.getDefaultValue().orElse(null), type.getLengthConstraint().orElse(null));
+    }
+
+    static boolean equals(final @NonNull BinaryTypeDefinition type, final @Nullable Object obj) {
+        if (type == obj) {
+            return true;
+        }
+
+        final BinaryTypeDefinition other = TypeDefinitions.castIfEquals(BinaryTypeDefinition.class, type, obj);
+        return other != null && type.getLengthConstraint().equals(other.getLengthConstraint());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/BitsTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/BitsTypeDefinition.java
new file mode 100644 (file)
index 0000000..89e8eb9
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.model.api.type;
+
+import java.util.List;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Makes is possible to access to the individual bits values of this type.
+ */
+public interface BitsTypeDefinition extends TypeDefinition<BitsTypeDefinition> {
+    /**
+     * Returns all bit values.
+     *
+     * @return list of <code>Bit</code> type instastances with data about all
+     *         individual bits of <code>bits</code> YANG built-in type
+     */
+    @NonNull List<Bit> getBits();
+
+    static int hashCode(final @NonNull BitsTypeDefinition type) {
+        return Objects.hash(type.getPath(), type.getUnknownSchemaNodes(), type.getBaseType(),
+            type.getUnits().orElse(null), type.getDefaultValue().orElse(null), type.getBits());
+    }
+
+    static boolean equals(final @NonNull BitsTypeDefinition type, final @Nullable Object obj) {
+        if (type == obj) {
+            return true;
+        }
+
+        final BitsTypeDefinition other = TypeDefinitions.castIfEquals(BitsTypeDefinition.class, type, obj);
+        return other != null && type.getBits().equals(other.getBits());
+    }
+
+    static String toString(final @NonNull BitsTypeDefinition type) {
+        return TypeDefinitions.toStringHelper(type).add("bits", type.getBits()).toString();
+    }
+
+    /**
+     * Contains the methods for accessing the data about the individual bit of
+     * <code>bits</code> YANG type.
+     */
+    interface Bit extends SchemaNode {
+        /**
+         * Returns the name of the concrete bit.
+         *
+         * @return string with the name of the concrete bit
+         */
+        @NonNull String getName();
+
+        /**
+         * The position value MUST be in the range 0 to 4294967295, and it MUST
+         * be unique within the bits type.
+         *
+         * @return The position value of bit in range from 0 to 4294967295.
+         */
+        long getPosition();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/BooleanTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/BooleanTypeDefinition.java
new file mode 100644 (file)
index 0000000..7b18689
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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.model.api.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Marker interface which marks that type definition represents the built-in
+ * YANG <code>boolean</code> type.
+ */
+public interface BooleanTypeDefinition extends TypeDefinition<BooleanTypeDefinition> {
+
+    static int hashCode(final @NonNull BooleanTypeDefinition type) {
+        return TypeDefinitions.basicHashCode(type);
+    }
+
+    static boolean equals(final @NonNull BooleanTypeDefinition type, final @Nullable Object obj) {
+        if (type == obj) {
+            return true;
+        }
+
+        return TypeDefinitions.castIfEquals(BooleanTypeDefinition.class, type, obj) != null;
+    }
+
+    static String toString(final @NonNull BooleanTypeDefinition type) {
+        return TypeDefinitions.toStringHelper(type).toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/DecimalTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/DecimalTypeDefinition.java
new file mode 100644 (file)
index 0000000..75b0df6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.model.api.type;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+
+/**
+ * Contains methods for getting data from the YANG <code>type</code> substatement for <code>decimal64</code> built-in
+ * type.
+ */
+public interface DecimalTypeDefinition extends RangeRestrictedTypeDefinition<DecimalTypeDefinition, BigDecimal> {
+    /**
+     * Returns integer between 1 and 18 inclusively.
+     *
+     * <p>
+     * The "fraction-digits" statement controls the size of the minimum
+     * difference between values of a decimal64 type, by restricting the value
+     * space to numbers that are expressible as "i x 10^-n" where n is the
+     * fraction-digits argument.
+     *
+     * @return number of fraction digits
+     */
+    int getFractionDigits();
+
+    static int hashCode(final DecimalTypeDefinition type) {
+        return Objects.hash(type.getPath(), type.getUnknownSchemaNodes(), type.getBaseType(),
+            type.getUnits().orElse(null), type.getDefaultValue().orElse(null), type.getFractionDigits(),
+            type.getRangeConstraint().orElse(null));
+    }
+
+    static boolean equals(final DecimalTypeDefinition type, final Object obj) {
+        if (type == obj) {
+            return true;
+        }
+
+        final DecimalTypeDefinition other = TypeDefinitions.castIfEquals(DecimalTypeDefinition.class, type, obj);
+        return other != null && type.getFractionDigits() == other.getFractionDigits()
+                && type.getRangeConstraint().equals(other.getRangeConstraint());
+    }
+
+    static String toString(final DecimalTypeDefinition type) {
+        return TypeDefinitions.toStringHelper(type).add("fractionDigits", type.getFractionDigits()).toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/EmptyTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/EmptyTypeDefinition.java
new file mode 100644 (file)
index 0000000..7595744
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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.model.api.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+public interface EmptyTypeDefinition extends TypeDefinition<EmptyTypeDefinition> {
+
+    static int hashCode(final @NonNull EmptyTypeDefinition type) {
+        return TypeDefinitions.basicHashCode(type);
+    }
+
+    static boolean equals(final @NonNull EmptyTypeDefinition type, final @Nullable Object obj) {
+        if (type == obj) {
+            return true;
+        }
+
+        return TypeDefinitions.castIfEquals(EmptyTypeDefinition.class, type, obj) != null;
+    }
+
+    static String toString(final @NonNull EmptyTypeDefinition type) {
+        return TypeDefinitions.toStringHelper(type).toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/EnumTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/EnumTypeDefinition.java
new file mode 100644 (file)
index 0000000..d07c030
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.model.api.type;
+
+import java.util.List;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Makes is possible to access to the individual enumeration values of this type.
+ */
+public interface EnumTypeDefinition extends TypeDefinition<EnumTypeDefinition> {
+    /**
+     * Contains the methods for accessing the data about the concrete enumeration item which represents {@code enum}
+     * YANG type.
+     */
+    interface EnumPair extends DocumentedNode.WithStatus {
+        /**
+         * The name to specify each assigned name of an enumeration type.
+         *
+         * @return name of each assigned name of an enumeration type.
+         */
+        String getName();
+
+        /**
+         * The "value" statement, which is optional, is used to associate an integer value with the assigned name
+         * for the enum. This integer value MUST be unique within the enumeration type.
+         *
+         * @return integer value assigned to enumeration
+         */
+        int getValue();
+    }
+
+    /**
+     * Returns all enumeration values.
+     *
+     * @return list of {@code EnumPair} type instances which contain the data about all individual enumeration pairs
+     *         of {@code enumeration} YANG built-in type
+     */
+    @NonNull List<EnumPair> getValues();
+
+    static boolean equals(final @NonNull EnumTypeDefinition type, final @Nullable Object obj) {
+        if (type == obj) {
+            return true;
+        }
+
+        final EnumTypeDefinition other = TypeDefinitions.castIfEquals(EnumTypeDefinition.class, type, obj);
+        return other != null && type.getValues().equals(other.getValues());
+    }
+
+    static int hashCode(final @NonNull EnumTypeDefinition type) {
+        return Objects.hash(type.getPath(), type.getUnknownSchemaNodes(), type.getBaseType(),
+            type.getUnits().orElse(null),
+            type.getDefaultValue(), type.getValues());
+    }
+
+    static String toString(final @NonNull EnumTypeDefinition type) {
+        return TypeDefinitions.toStringHelper(type).add("values", type.getValues()).toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/IdentityTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/IdentityTypeDefinition.java
new file mode 100644 (file)
index 0000000..e5696bb
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.model.api.type;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Contains the method for getting the details about YANG <code>identity</code>.
+ */
+public interface IdentityTypeDefinition extends TypeDefinition<IdentityTypeDefinition> {
+    /**
+     * Returns the name of the YANG identity.
+     *
+     * @return QName of the YANG identity
+     */
+    QName getIdentityName();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/IdentityrefTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/IdentityrefTypeDefinition.java
new file mode 100644 (file)
index 0000000..643030f
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.model.api.type;
+
+import java.util.Objects;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Contains method for getting data from <code>identityref</code> built-in YANG type.
+ */
+public interface IdentityrefTypeDefinition extends TypeDefinition<IdentityrefTypeDefinition> {
+    /**
+     * Returns the set of identities this reference points to.
+     *
+     * @return set of identities to which the instance of this type refers (in YANG 1.1 models) or a set containing
+     *         just one identity (in YANG 1.0 models)
+     */
+    @NonNull Set<IdentitySchemaNode> getIdentities();
+
+    static int hashCode(final @NonNull IdentityrefTypeDefinition type) {
+        return Objects.hash(type.getPath(), type.getUnknownSchemaNodes(), type.getBaseType(),
+            type.getUnits().orElse(null), type.getDefaultValue().orElse(null), type.getIdentities());
+    }
+
+    static boolean equals(final @NonNull IdentityrefTypeDefinition type, final @Nullable Object obj) {
+        if (type == obj) {
+            return true;
+        }
+
+        final IdentityrefTypeDefinition other = TypeDefinitions.castIfEquals(IdentityrefTypeDefinition.class, type,
+            obj);
+        return other != null && type.getIdentities().equals(other.getIdentities());
+    }
+
+    static String toString(final @NonNull IdentityrefTypeDefinition type) {
+        return TypeDefinitions.toStringHelper(type).add("identities", type.getIdentities()).toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/InstanceIdentifierTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/InstanceIdentifierTypeDefinition.java
new file mode 100644 (file)
index 0000000..34fd30b
--- /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.model.api.type;
+
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Contains methods for getting data from the <code>instance-identifier</code> YANG built-in type.
+ */
+public interface InstanceIdentifierTypeDefinition
+        extends RequireInstanceRestrictedTypeDefinition<InstanceIdentifierTypeDefinition> {
+    static int hashCode(final @NonNull InstanceIdentifierTypeDefinition type) {
+        return Objects.hash(type.getPath(), type.getUnknownSchemaNodes(), type.getBaseType(),
+            type.getUnits().orElse(null), type.getDefaultValue().orElse(null), type.requireInstance());
+    }
+
+    static boolean equals(final @NonNull InstanceIdentifierTypeDefinition type, final @Nullable Object obj) {
+        if (type == obj) {
+            return true;
+        }
+
+        final InstanceIdentifierTypeDefinition other = TypeDefinitions.castIfEquals(
+            InstanceIdentifierTypeDefinition.class, type, obj);
+        return other != null && type.requireInstance() == other.requireInstance();
+    }
+
+    static String toString(final @NonNull InstanceIdentifierTypeDefinition type) {
+        return TypeDefinitions.toStringHelper(type).add("requireInstance", type.requireInstance()).toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Int16TypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Int16TypeDefinition.java
new file mode 100644 (file)
index 0000000..d8856e3
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Type definition derived from int16 type.
+ *
+ * @author Robert Varga
+ */
+public interface Int16TypeDefinition extends RangeRestrictedTypeDefinition<Int16TypeDefinition, Short> {
+
+    static int hashCode(final @NonNull Int16TypeDefinition type) {
+        return TypeDefinitions.hashCode(type);
+    }
+
+    static boolean equals(final @NonNull Int16TypeDefinition type, final @Nullable Object obj) {
+        return TypeDefinitions.equals(Int16TypeDefinition.class, type, obj);
+    }
+
+    static String toString(final @NonNull Int16TypeDefinition type) {
+        return TypeDefinitions.toString(type);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Int32TypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Int32TypeDefinition.java
new file mode 100644 (file)
index 0000000..e487443
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Type definition derived from int32 type.
+ *
+ * @author Robert Varga
+ */
+public interface Int32TypeDefinition extends RangeRestrictedTypeDefinition<Int32TypeDefinition, Integer> {
+
+    static int hashCode(final @NonNull Int32TypeDefinition type) {
+        return TypeDefinitions.hashCode(type);
+    }
+
+    static boolean equals(final @NonNull Int32TypeDefinition type, final @Nullable Object obj) {
+        return TypeDefinitions.equals(Int32TypeDefinition.class, type, obj);
+    }
+
+    static String toString(final @NonNull Int32TypeDefinition type) {
+        return TypeDefinitions.toString(type);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Int64TypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Int64TypeDefinition.java
new file mode 100644 (file)
index 0000000..d27267f
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Type definition derived from int64 type.
+ *
+ * @author Robert Varga
+ */
+public interface Int64TypeDefinition extends RangeRestrictedTypeDefinition<Int64TypeDefinition, Long> {
+
+    static int hashCode(final @NonNull Int64TypeDefinition type) {
+        return TypeDefinitions.hashCode(type);
+    }
+
+    static boolean equals(final @NonNull Int64TypeDefinition type, final @Nullable Object obj) {
+        return TypeDefinitions.equals(Int64TypeDefinition.class, type, obj);
+    }
+
+    static String toString(final @NonNull Int64TypeDefinition type) {
+        return TypeDefinitions.toString(type);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Int8TypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Int8TypeDefinition.java
new file mode 100644 (file)
index 0000000..d5f94d7
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Type definition derived from int8 type.
+ *
+ * @author Robert Varga
+ */
+public interface Int8TypeDefinition extends RangeRestrictedTypeDefinition<Int8TypeDefinition, Byte> {
+
+    static int hashCode(final @NonNull Int8TypeDefinition type) {
+        return TypeDefinitions.hashCode(type);
+    }
+
+    static boolean equals(final @NonNull Int8TypeDefinition type, final @Nullable Object obj) {
+        return TypeDefinitions.equals(Int8TypeDefinition.class, type, obj);
+    }
+
+    static String toString(final @NonNull Int8TypeDefinition type) {
+        return TypeDefinitions.toString(type);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/LeafrefTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/LeafrefTypeDefinition.java
new file mode 100644 (file)
index 0000000..d692091
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.model.api.type;
+
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+
+public interface LeafrefTypeDefinition extends RequireInstanceRestrictedTypeDefinition<LeafrefTypeDefinition> {
+
+    PathExpression getPathStatement();
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>
+     * For YANG version 1 (RFC6020), this should always return true.
+     */
+    @Override
+    boolean requireInstance();
+
+    static int hashCode(final @NonNull LeafrefTypeDefinition type) {
+        return Objects.hash(type.getPath(), type.getUnknownSchemaNodes(), type.getBaseType(),
+            type.getUnits().orElse(null), type.getDefaultValue().orElse(null), type.getPathStatement());
+    }
+
+    static boolean equals(final @NonNull LeafrefTypeDefinition type, final @Nullable Object obj) {
+        if (type == obj) {
+            return true;
+        }
+
+        final LeafrefTypeDefinition other = TypeDefinitions.castIfEquals(LeafrefTypeDefinition.class, type, obj);
+        return other != null && type.getPathStatement().equals(other.getPathStatement());
+    }
+
+    static String toString(final @NonNull LeafrefTypeDefinition type) {
+        return TypeDefinitions.toStringHelper(type).add("pathStatement", type.getPathStatement()).toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/LengthConstraint.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/LengthConstraint.java
new file mode 100644 (file)
index 0000000..e3f59ab
--- /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.model.api.type;
+
+import com.google.common.collect.RangeSet;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+
+/**
+ * A single value length restriction, as expressed by a length statement, as specified by
+ * <a href="https://tools.ietf.org/html/rfc6020#section-9.4.4">[RFC-6020] The length Statement</a>.
+ */
+public interface LengthConstraint extends ConstraintMetaDefinition {
+    /**
+     * Return allowed length ranges. Returned RangeSet must not be empty.
+     *
+     * @return Set of allowed lengths.
+     */
+    RangeSet<Integer> getAllowedRanges();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/LengthRestrictedTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/LengthRestrictedTypeDefinition.java
new file mode 100644 (file)
index 0000000..559d594
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.type;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Interface for {@link TypeDefinition}s which can have their values restricted to a set of allowed lengths.
+ *
+ * @param <T> Concrete {@link TypeDefinition} subinterface
+ */
+public interface LengthRestrictedTypeDefinition<T extends TypeDefinition<T>> extends TypeDefinition<T> {
+    /**
+     * Returns length constraint of this type, if applicable. This is the effective constraint, e.g. it includes any
+     * length constraints implied by base type hierarchy.
+     *
+     * @return length constraint which are specified in the <code>length</code> substatement of the <code>type</code>
+     *         statement.
+     */
+    Optional<LengthConstraint> getLengthConstraint();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/ModifierKind.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/ModifierKind.java
new file mode 100644 (file)
index 0000000..9112673
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016 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.api.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.Maps;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Enum describing the effect of a YANG modifier statement.
+ *
+ * <p>
+ * As of YANG 1.1 (RFC7950) there is only one modifier value available and that is "invert-match". If there are more
+ * possible values added in the future, this enum can be extended with more enum constants.
+ */
+public enum ModifierKind {
+    INVERT_MATCH("invert-match");
+
+    private static final Map<String, ModifierKind> MODIFIER_KIND_MAP = Maps.uniqueIndex(
+        Arrays.asList(ModifierKind.values()), ModifierKind::getKeyword);
+
+    private final @NonNull String keyword;
+
+    ModifierKind(final @NonNull String keyword) {
+        this.keyword = requireNonNull(keyword);
+    }
+
+    /**
+     * YANG keyword of this modifier.
+     *
+     * @return String that corresponds to the YANG keyword.
+     */
+    public @NonNull String getKeyword() {
+        return keyword;
+    }
+
+    /**
+     * Returns ModifierKind based on supplied YANG keyword.
+     *
+     * @param keyword YANG keyword in string form
+     * @return ModifierKind based on supplied YANG keyword
+     * @throws NullPointerException if keyword is null
+     */
+    public static Optional<ModifierKind> parse(final @NonNull String keyword) {
+        return Optional.ofNullable(MODIFIER_KIND_MAP.get(requireNonNull(keyword)));
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/PatternConstraint.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/PatternConstraint.java
new file mode 100644 (file)
index 0000000..5bf8762
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.model.api.type;
+
+import java.util.Optional;
+import java.util.regex.Pattern;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+
+/**
+ * Contains the method for getting the data from the YANG <code>pattern</code> which is substatement
+ * of <code>type</code> statement.
+ */
+public interface PatternConstraint extends ConstraintMetaDefinition {
+
+    /**
+     * Returns a Java {@link Pattern}-compatible regular expression (pattern). Returned string performs equivalent
+     * matching in terms of enforcement, but it may have a structure completely different from the one in YANG model.
+     * This string DOES NOT include the effects of the modifier (if present, as indicated by {@link #getModifier()}.
+     *
+     * @return string Java Pattern regular expression
+     */
+    // FIXME: should we be providing a Pattern instance? This, along with the other method is treading the fine
+    //        balance between usability of the effective model, the purity of effective view model and memory
+    //        overhead. We pick usability and memory footprint and expose both methods from effective model.
+    String getJavaPatternString();
+
+    /**
+     * Returns a raw regular expression as it was declared in a source. This string conforms to XSD regular expression
+     * syntax, which is notably different from Java's Pattern string.
+     *
+     * @return argument of pattern statement as it was declared in YANG model.
+     */
+    String getRegularExpressionString();
+
+    /**
+     * RFC7950 allows a pattern constraint to be inverted. For this purpose a general modifier concept has been
+     * introduced. A pattern can have at most one such modifier.
+     *
+     * @return modifier, if present
+     */
+    Optional<ModifierKind> getModifier();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/RangeConstraint.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/RangeConstraint.java
new file mode 100644 (file)
index 0000000..9a6595e
--- /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.model.api.type;
+
+import com.google.common.collect.RangeSet;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+
+/**
+ * A single value range restriction, as expressed by a range statement, as specified by
+ * <a href="https://tools.ietf.org/html/rfc6020#section-9.2.4">[RFC-6020] The range Statement</a>.
+ */
+public interface RangeConstraint<T extends Number & Comparable<T>> extends ConstraintMetaDefinition {
+    /**
+     * Return allowed length ranges. Returned RangeSet must not be empty.
+     *
+     * @return Set of allowed lengths.
+     */
+    RangeSet<T> getAllowedRanges();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/RangeRestrictedTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/RangeRestrictedTypeDefinition.java
new file mode 100644 (file)
index 0000000..55c15ec
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.type;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Interface for {@link TypeDefinition}s which can have their values restricted to a set of allowed values.
+ *
+ * @param <T> Concrete {@link TypeDefinition} subinterface
+ * @param <N> Native data type
+ */
+public interface RangeRestrictedTypeDefinition<T extends TypeDefinition<T>, N extends Number & Comparable<N>>
+        extends TypeDefinition<T> {
+    /**
+     * Returns range constraint of this type, if applicable. This is the effective constraint, e.g. it includes any
+     * range constraints implied by base type hierarchy.
+     *
+     * @return range constraint which are specified in the <code>range</code> substatement of the <code>type</code>
+     *         statement.
+     */
+    Optional<RangeConstraint<N>> getRangeConstraint();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/RequireInstanceRestrictedTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/RequireInstanceRestrictedTypeDefinition.java
new file mode 100644 (file)
index 0000000..b04fd8e
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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.model.api.type;
+
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Interface for {@link TypeDefinition}s which can be restricted through a require-instance statement.
+ *
+ * @param <T> Concrete {@link TypeDefinition} subinterface
+ */
+public interface RequireInstanceRestrictedTypeDefinition<T extends TypeDefinition<T>> extends TypeDefinition<T> {
+    /**
+     * Returns true or false which represents argument of <code>require-instance</code> statement. This statement is
+     * the substatement of the <code>type</code> statement.
+     *
+     * @return boolean value which is true if the <code>require-instance</code> statement is true and vice versa
+     */
+    boolean requireInstance();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/StringTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/StringTypeDefinition.java
new file mode 100644 (file)
index 0000000..f4bbffa
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.model.api.type;
+
+import java.util.List;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Contains method for getting data from the <code>string</code> YANG built-in type.
+ */
+public interface StringTypeDefinition extends LengthRestrictedTypeDefinition<StringTypeDefinition> {
+    /**
+     * Returns patterns specified in the string.
+     *
+     * @return list of pattern constraints which are specified in the {@code pattern} substatement of the {@code type}
+     *         statement
+     */
+    @NonNull List<PatternConstraint> getPatternConstraints();
+
+    static int hashCode(final @NonNull StringTypeDefinition type) {
+        return Objects.hash(type.getPath(), type.getUnknownSchemaNodes(), type.getBaseType(),
+            type.getUnits().orElse(null), type.getDefaultValue().orElse(null), type.getLengthConstraint().orElse(null),
+            type.getPatternConstraints());
+    }
+
+    static boolean equals(final @NonNull StringTypeDefinition type, final @Nullable Object obj) {
+        if (type == obj) {
+            return true;
+        }
+
+        final StringTypeDefinition other = TypeDefinitions.castIfEquals(StringTypeDefinition.class, type, obj);
+        return other != null && type.getLengthConstraint().equals(other.getLengthConstraint())
+                && type.getPatternConstraints().equals(other.getPatternConstraints());
+    }
+
+    static String toString(final @NonNull StringTypeDefinition type) {
+        return TypeDefinitions.toStringHelper(type).add("length", type.getLengthConstraint().orElse(null))
+                .add("patterns", type.getPatternConstraints()).toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/TypeDefinitions.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/TypeDefinitions.java
new file mode 100644 (file)
index 0000000..5428d60
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.api.type;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+final class TypeDefinitions {
+    private TypeDefinitions() {
+        throw new UnsupportedOperationException();
+    }
+
+    static int basicHashCode(final @NonNull TypeDefinition<?> type) {
+        return Objects.hash(type.getPath(), type.getUnknownSchemaNodes(), type.getBaseType(),
+            type.getUnits().orElse(null), type.getDefaultValue().orElse(null));
+    }
+
+    static int hashCode(final @NonNull RangeRestrictedTypeDefinition<?, ?> type) {
+        return Objects.hash(type.getPath(), type.getUnknownSchemaNodes(), type.getBaseType(),
+            type.getUnits().orElse(null), type.getDefaultValue().orElse(null), type.getRangeConstraint().orElse(null));
+    }
+
+    static <T extends RangeRestrictedTypeDefinition<T, ?>> boolean equals(final @NonNull Class<T> clazz,
+            final @NonNull T type, final @Nullable Object obj) {
+        if (type == obj) {
+            return true;
+        }
+
+        final @Nullable T other = castIfEquals(clazz, type, obj);
+        return other != null && type.getRangeConstraint().equals(other.getRangeConstraint());
+    }
+
+    static @NonNull String toString(final @NonNull RangeRestrictedTypeDefinition<?, ?> type) {
+        return toStringHelper(type).toString();
+    }
+
+    static <T extends TypeDefinition<T>> @Nullable T castIfEquals(final @NonNull Class<T> clazz, final @NonNull T type,
+            final @Nullable Object obj) {
+        if (!clazz.isInstance(obj)) {
+            return null;
+        }
+
+        final @NonNull T other = clazz.cast(obj);
+        return Objects.equals(type.getPath(), other.getPath())
+                && Objects.equals(type.getBaseType(), other.getBaseType())
+                && Objects.equals(type.getDefaultValue(), other.getDefaultValue())
+                && Objects.equals(type.getUnknownSchemaNodes(), other.getUnknownSchemaNodes())
+                && Objects.equals(type.getUnits(), other.getUnits()) ? other : null;
+    }
+
+    static @NonNull ToStringHelper toStringHelper(final @NonNull TypeDefinition<?> type) {
+        return MoreObjects.toStringHelper(type).omitNullValues()
+                .add("path", type.getPath())
+                .add("baseType", type.getBaseType())
+                .add("default", type.getDefaultValue().orElse(null))
+                .add("description", type.getDescription().orElse(null))
+                .add("reference", type.getReference().orElse(null))
+                .add("status", type.getStatus())
+                .add("units", type.getUnits().orElse(null));
+    }
+
+    static @NonNull ToStringHelper toStringHelper(final @NonNull RangeRestrictedTypeDefinition<?, ?> type) {
+        return toStringHelper((TypeDefinition<?>) type).add("range", type.getRangeConstraint().orElse(null));
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Uint16TypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Uint16TypeDefinition.java
new file mode 100644 (file)
index 0000000..9c42a0a
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.Uint16;
+
+/**
+ * Type definition derived from uint16 type.
+ *
+ * @author Robert Varga
+ */
+public interface Uint16TypeDefinition extends RangeRestrictedTypeDefinition<Uint16TypeDefinition, Uint16> {
+
+    static int hashCode(final @NonNull Uint16TypeDefinition type) {
+        return TypeDefinitions.hashCode(type);
+    }
+
+    static boolean equals(final @NonNull Uint16TypeDefinition type, final @Nullable Object obj) {
+        return TypeDefinitions.equals(Uint16TypeDefinition.class, type, obj);
+    }
+
+    static String toString(final @NonNull Uint16TypeDefinition type) {
+        return TypeDefinitions.toString(type);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Uint32TypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Uint32TypeDefinition.java
new file mode 100644 (file)
index 0000000..d143bfb
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.Uint32;
+
+/**
+ * Type definition derived from uint32 type.
+ *
+ * @author Robert Varga
+ */
+public interface Uint32TypeDefinition extends RangeRestrictedTypeDefinition<Uint32TypeDefinition, Uint32> {
+
+    static int hashCode(final @NonNull Uint32TypeDefinition type) {
+        return TypeDefinitions.hashCode(type);
+    }
+
+    static boolean equals(final @NonNull Uint32TypeDefinition type, final @Nullable Object obj) {
+        return TypeDefinitions.equals(Uint32TypeDefinition.class, type, obj);
+    }
+
+    static String toString(final @NonNull Uint32TypeDefinition type) {
+        return TypeDefinitions.toString(type);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Uint64TypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Uint64TypeDefinition.java
new file mode 100644 (file)
index 0000000..fc30fbd
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.Uint64;
+
+/**
+ * Type definition derived from uint64 type.
+ *
+ * @author Robert Varga
+ */
+public interface Uint64TypeDefinition extends RangeRestrictedTypeDefinition<Uint64TypeDefinition, Uint64> {
+
+    static int hashCode(final @NonNull Uint64TypeDefinition type) {
+        return TypeDefinitions.hashCode(type);
+    }
+
+    static boolean equals(final @NonNull Uint64TypeDefinition type, final @Nullable Object obj) {
+        return TypeDefinitions.equals(Uint64TypeDefinition.class, type, obj);
+    }
+
+    static String toString(final @NonNull Uint64TypeDefinition type) {
+        return TypeDefinitions.toString(type);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Uint8TypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/Uint8TypeDefinition.java
new file mode 100644 (file)
index 0000000..a4fe95c
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.Uint8;
+
+/**
+ * Type definition derived from uint8 type.
+ *
+ * @author Robert Varga
+ */
+public interface Uint8TypeDefinition extends RangeRestrictedTypeDefinition<Uint8TypeDefinition, Uint8> {
+    static int hashCode(final @NonNull Uint8TypeDefinition type) {
+        return TypeDefinitions.hashCode(type);
+    }
+
+    static boolean equals(final @NonNull Uint8TypeDefinition type, final @Nullable Object obj) {
+        return TypeDefinitions.equals(Uint8TypeDefinition.class, type, obj);
+    }
+
+    static String toString(final @NonNull Uint8TypeDefinition type) {
+        return TypeDefinitions.toStringHelper(type).add("range", type.getRangeConstraint().orElse(null)).toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/UnionTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/UnionTypeDefinition.java
new file mode 100644 (file)
index 0000000..f647cf5
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.model.api.type;
+
+import java.util.List;
+import java.util.Objects;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Contains the method which access union item in the union type.
+ */
+public interface UnionTypeDefinition extends TypeDefinition<UnionTypeDefinition> {
+
+    /**
+     * Returns type definitions which represent the values of the arguments for
+     * all YANG <code>type</code> substatement in the main <code>union</code>
+     * statement.
+     *
+     * @return list of the type definition which contains the union items.
+     */
+    List<TypeDefinition<?>> getTypes();
+
+    static int hashCode(final UnionTypeDefinition type) {
+        return Objects.hash(type.getPath(), type.getUnknownSchemaNodes(), type.getBaseType(),
+            type.getUnits().orElse(null), type.getDefaultValue().orElse(null), type.getTypes());
+    }
+
+    static boolean equals(final UnionTypeDefinition type, final Object obj) {
+        if (type == obj) {
+            return true;
+        }
+
+        final UnionTypeDefinition other = TypeDefinitions.castIfEquals(UnionTypeDefinition.class, type, obj);
+        return other != null && type.getTypes().equals(other.getTypes());
+    }
+
+    static String toString(final UnionTypeDefinition type) {
+        return TypeDefinitions.toStringHelper(type).add("types", type.getTypes()).toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/UnknownTypeDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/type/UnknownTypeDefinition.java
new file mode 100644 (file)
index 0000000..de7dc7d
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.model.api.type;
+
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+public interface UnknownTypeDefinition extends TypeDefinition<UnknownTypeDefinition> {
+
+    List<RangeConstraint<?>> getRangeConstraints();
+
+    List<LengthConstraint> getLengthConstraints();
+
+    List<PatternConstraint> getPatternConstraints();
+
+    Integer getFractionDigits();
+
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/DelegatedYangTextSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/DelegatedYangTextSchemaSource.java
new file mode 100644 (file)
index 0000000..c2c6d02
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.io.ByteSource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Delegator;
+
+final class DelegatedYangTextSchemaSource extends YangTextSchemaSource implements Delegator<ByteSource> {
+    private final @NonNull ByteSource delegate;
+
+    DelegatedYangTextSchemaSource(final SourceIdentifier identifier, final ByteSource delegate) {
+        super(identifier);
+        this.delegate = requireNonNull(delegate);
+    }
+
+    @Override
+    public ByteSource getDelegate() {
+        return delegate;
+    }
+
+    @Override
+    public InputStream openStream() throws IOException {
+        return delegate.openStream();
+    }
+
+    @Override
+    public Optional<String> getSymbolicName() {
+        return Optional.of("[" + delegate.toString() + "]");
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("delegate", delegate);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/DelegatedYinTextSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/DelegatedYinTextSchemaSource.java
new file mode 100644 (file)
index 0000000..d88b912
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.io.ByteSource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Delegator;
+
+final class DelegatedYinTextSchemaSource extends YinTextSchemaSource implements Delegator<ByteSource> {
+    private final @NonNull ByteSource delegate;
+
+    DelegatedYinTextSchemaSource(final SourceIdentifier identifier, final ByteSource delegate) {
+        super(identifier);
+        this.delegate = requireNonNull(delegate);
+    }
+
+    @Override
+    public ByteSource getDelegate() {
+        return delegate;
+    }
+
+    @Override
+    public InputStream openStream() throws IOException {
+        return delegate.openStream();
+    }
+
+    @Override
+    public Optional<String> getSymbolicName() {
+        return Optional.of("[" + delegate.toString() + "]");
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("delegate", delegate);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/EffectiveModelContextFactory.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/EffectiveModelContextFactory.java
new file mode 100644 (file)
index 0000000..b6d52d0
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.api;
+
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.Arrays;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * An asynchronous factory for building {@link SchemaContext} instances based on a specification of what
+ * {@link SourceIdentifier}s are required and dynamic recursive resolution.
+ */
+@Beta
+//FIXME: 5.0.0: evaluate if we still need to extend SchemaContext here
+public interface EffectiveModelContextFactory extends SchemaContextFactory {
+    /**
+     * Create a new schema context containing specified sources, pulling in any dependencies they may have.
+     *
+     * @param requiredSources a collection of sources which are required to be present
+     * @return A checked future, which will produce a schema context, or fail with an explanation why the creation
+     *         of the schema context failed.
+     */
+    @NonNull ListenableFuture<EffectiveModelContext> createEffectiveModelContext(
+            @NonNull Collection<SourceIdentifier> requiredSources);
+
+    default @NonNull ListenableFuture<EffectiveModelContext> createEffectiveModelContext(
+            final SourceIdentifier... requiredSources) {
+        return createEffectiveModelContext(Arrays.asList(requiredSources));
+    }
+
+    @Override
+    @Deprecated
+    default ListenableFuture<SchemaContext> createSchemaContext(
+            final Collection<SourceIdentifier> requiredSources) {
+        return Futures.transform(createEffectiveModelContext(requiredSources), ctx -> ctx,
+            MoreExecutors.directExecutor());
+    }
+}
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..cc9cae3
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import static java.util.Objects.requireNonNull;
+
+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;
+
+    private final SourceIdentifier id;
+
+    public MissingSchemaSourceException(final String message, final SourceIdentifier id) {
+        this(message, id, null);
+    }
+
+    public MissingSchemaSourceException(final String message, final SourceIdentifier id, final Throwable cause) {
+        super(requireNonNull(message), cause);
+        this.id = requireNonNull(id);
+    }
+
+    public SourceIdentifier getSourceId() {
+        return id;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/ResourceYangTextSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/ResourceYangTextSchemaSource.java
new file mode 100644 (file)
index 0000000..8250fed
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Delegator;
+
+/**
+ * A resource-backed {@link YinTextSchemaSource}.
+ */
+final class ResourceYangTextSchemaSource extends YangTextSchemaSource implements Delegator<URL> {
+    private final @NonNull URL url;
+
+    ResourceYangTextSchemaSource(final SourceIdentifier identifier, final URL url) {
+        super(identifier);
+        this.url = requireNonNull(url);
+    }
+
+    @Override
+    public URL getDelegate() {
+        return url;
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("url", url);
+    }
+
+    @Override
+    public InputStream openStream() throws IOException {
+        return url.openStream();
+    }
+
+    @Override
+    public Optional<String> getSymbolicName() {
+        return Optional.of(url.toString());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/ResourceYinTextSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/ResourceYinTextSchemaSource.java
new file mode 100644 (file)
index 0000000..e929ae9
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Delegator;
+
+/**
+ * A resource-backed {@link YangTextSchemaSource}.
+ */
+final class ResourceYinTextSchemaSource extends YinTextSchemaSource implements Delegator<URL> {
+    private final @NonNull URL url;
+
+    ResourceYinTextSchemaSource(final SourceIdentifier identifier, final URL url) {
+        super(identifier);
+        this.url = requireNonNull(url);
+    }
+
+    @Override
+    public URL getDelegate() {
+        return url;
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("url", url);
+    }
+
+    @Override
+    public InputStream openStream() throws IOException {
+        return url.openStream();
+    }
+
+    @Override
+    public Optional<String> getSymbolicName() {
+        return Optional.of(url.toString());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/RevisionSourceIdentifier.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/RevisionSourceIdentifier.java
new file mode 100644 (file)
index 0000000..080155a
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2016 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.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.Revision;
+
+/**
+ * YANG Schema revision source identifier.
+ *
+ * <p>
+ * Simple transfer object represents revision identifier of source for YANG schema (module or submodule), which consists
+ * of
+ * <ul>
+ * <li>YANG schema name ({@link #getName()}
+ * <li>Module revision (optional) ({link {@link #getRevision()})
+ * </ul>
+ *
+ * <p>
+ * Revision source identifier is designated to be carry only necessary information to look-up YANG model source
+ * and to be used by various SchemaSourceProviders.
+ *
+ * <p>
+ * <b>Note:</b>On source retrieval layer it is impossible to distinguish between YANG module and/or submodule unless
+ * source is present.
+ *
+ * <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 RevisionSourceIdentifier extends SourceIdentifier {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Creates new YANG Schema revision source identifier for sources without a revision.
+     *
+     * @param name Name of schema
+     */
+    RevisionSourceIdentifier(final String name) {
+        super(name);
+    }
+
+    /**
+     * Creates new YANG Schema revision source identifier.
+     *
+     * @param name Name of schema
+     * @param revision Revision of source, may be null
+     */
+    RevisionSourceIdentifier(final String name, final @Nullable Revision revision) {
+        super(name, revision);
+    }
+
+    /**
+     * Creates new YANG Schema revision source identifier.
+     *
+     * @param name Name of schema
+     * @param formattedRevision Revision of source, potentially not present
+     */
+    private RevisionSourceIdentifier(final String name, final Optional<Revision> revision) {
+        super(name, revision);
+    }
+
+    /**
+     * Creates new YANG Schema revision source identifier.
+     *
+     * @param moduleName Name of schema
+     * @param revision Revision of source in format YYYY-mm-dd. If not present, default value will be used.
+     */
+    public static @NonNull RevisionSourceIdentifier create(final String moduleName, final Optional<Revision> revision) {
+        return new RevisionSourceIdentifier(moduleName, revision);
+    }
+
+    /**
+     * Creates new YANG Schema revision source identifier.
+     *
+     * @param moduleName Name of schema
+     * @param revision Revision of source, may be null
+     */
+    public static @NonNull RevisionSourceIdentifier create(final String moduleName, final @Nullable Revision revision) {
+        return new RevisionSourceIdentifier(moduleName, revision);
+    }
+
+    /**
+     * Creates new YANG Schema revision source identifier for sources without
+     * a revision.
+     *
+     * @param moduleName
+     *            Name of schema
+     */
+    public static @NonNull RevisionSourceIdentifier create(final String moduleName) {
+        return new RevisionSourceIdentifier(moduleName);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getName());
+        result = prime * result + Objects.hashCode(getRevision());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof RevisionSourceIdentifier)) {
+            return false;
+        }
+        final RevisionSourceIdentifier other = (RevisionSourceIdentifier) obj;
+        return Objects.equals(getName(), other.getName()) && Objects.equals(getRevision(), other.getRevision());
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("RevisionSourceIdentifier [name=");
+        sb.append(getName());
+
+        final Optional<Revision> rev = getRevision();
+        if (rev.isPresent()) {
+            sb.append('@').append(rev.get());
+        }
+        return sb.append(']').toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactory.java
new file mode 100644 (file)
index 0000000..8bb15c4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.api;
+
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Arrays;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * An asynchronous factory for building {@link SchemaContext} instances based on a specification of what
+ * {@link SourceIdentifier}s are required and dynamic recursive resolution.
+ *
+ * @deprecated Use {@link EffectiveModelContextFactory} instead.
+ */
+@Beta
+@Deprecated
+public interface SchemaContextFactory {
+    /**
+     * Create a new schema context containing specified sources, pulling in any dependencies they may have.
+     *
+     * @param requiredSources a collection of sources which are required to be present
+     * @return A checked future, which will produce a schema context, or fail with an explanation why the creation
+     *         of the schema context failed.
+     */
+    @NonNull ListenableFuture<SchemaContext> createSchemaContext(
+            @NonNull Collection<SourceIdentifier> requiredSources);
+
+    default @NonNull ListenableFuture<SchemaContext> createSchemaContext(
+            final SourceIdentifier... requiredSources) {
+        return createSchemaContext(Arrays.asList(requiredSources));
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactoryConfiguration.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaContextFactoryConfiguration.java
new file mode 100644 (file)
index 0000000..80cc985
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2017 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.SetMultimap;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+/**
+ * SchemaContextFactory configuration class.
+ *
+ * <p>
+ * SchemaContextFactoryConfiguration supports currently the following options to be set:
+ * <ul>
+ * <li>schema source filter</li>
+ * <li>statement parser mode</li>
+ * <li>supported features</li>
+ * <li>supported deviations</li>
+ * </ul>
+ */
+@Beta
+public final class SchemaContextFactoryConfiguration implements Immutable {
+    private static final @NonNull SchemaContextFactoryConfiguration DEFAULT_CONFIGURATION = new Builder().build();
+
+    private final @NonNull SchemaSourceFilter filter;
+    private final @NonNull StatementParserMode statementParserMode;
+    private final @Nullable ImmutableSet<QName> supportedFeatures;
+    private final @Nullable ImmutableSetMultimap<QNameModule, QNameModule> modulesDeviatedByModules;
+
+    private SchemaContextFactoryConfiguration(final @NonNull SchemaSourceFilter filter,
+            final @NonNull StatementParserMode statementParserMode,
+            final @Nullable ImmutableSet<QName> supportedFeatures,
+            final @Nullable ImmutableSetMultimap<QNameModule, QNameModule> modulesDeviatedByModules) {
+        this.filter = requireNonNull(filter);
+        this.statementParserMode = requireNonNull(statementParserMode);
+        this.supportedFeatures = supportedFeatures;
+        this.modulesDeviatedByModules = modulesDeviatedByModules;
+    }
+
+    public @NonNull SchemaSourceFilter getSchemaSourceFilter() {
+        return filter;
+    }
+
+    public @NonNull StatementParserMode getStatementParserMode() {
+        return statementParserMode;
+    }
+
+    public Optional<Set<QName>> getSupportedFeatures() {
+        return Optional.ofNullable(supportedFeatures);
+    }
+
+    public Optional<SetMultimap<QNameModule, QNameModule>> getModulesDeviatedByModules() {
+        return Optional.ofNullable(modulesDeviatedByModules);
+    }
+
+    public static @NonNull SchemaContextFactoryConfiguration getDefault() {
+        return DEFAULT_CONFIGURATION;
+    }
+
+    public static @NonNull Builder builder() {
+        return new Builder();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(filter, statementParserMode, supportedFeatures, modulesDeviatedByModules);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof SchemaContextFactoryConfiguration)) {
+            return false;
+        }
+        final SchemaContextFactoryConfiguration other = (SchemaContextFactoryConfiguration) obj;
+        return filter.equals(other.filter) && statementParserMode.equals(other.statementParserMode)
+                && Objects.equals(supportedFeatures, other.supportedFeatures)
+                && Objects.equals(modulesDeviatedByModules, other.modulesDeviatedByModules);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).omitNullValues().add("schemaSourceFilter", filter)
+                .add("statementParserMode", statementParserMode).add("supportedFeatures", supportedFeatures)
+                .add("modulesDeviatedByModules", modulesDeviatedByModules).toString();
+    }
+
+    public static class Builder
+            implements org.opendaylight.yangtools.concepts.Builder<SchemaContextFactoryConfiguration> {
+        private SchemaSourceFilter filter = SchemaSourceFilter.ALWAYS_ACCEPT;
+        private StatementParserMode statementParserMode = StatementParserMode.DEFAULT_MODE;
+        private ImmutableSetMultimap<QNameModule, QNameModule> modulesDeviatedByModules;
+        private ImmutableSet<QName> supportedFeatures;
+
+        /**
+         * Set schema source filter which will filter available schema sources using the provided filter.
+         *
+         * @param filter schema source filter which acts as the gating function before a schema source is considered
+         *               by the factory for inclusion in the SchemaContext it produces.
+         * @return this builder
+         */
+        public @NonNull Builder setFilter(final @NonNull SchemaSourceFilter filter) {
+            this.filter = requireNonNull(filter);
+            return this;
+        }
+
+        /**
+         * Set YANG statement parser mode.
+         *
+         * @param statementParserMode mode of yang statement parser
+         * @return this builder
+         */
+        public @NonNull Builder setStatementParserMode(final @NonNull StatementParserMode statementParserMode) {
+            this.statementParserMode = requireNonNull(statementParserMode);
+            return this;
+        }
+
+        /**
+         * Set supported features based on which all if-feature statements in the parsed YANG modules will be resolved.
+         *
+         * @param supportedFeatures Set of supported features in the final SchemaContext. If the set is empty, no
+         *                          features encountered will be supported.
+         * @return this builder
+         */
+        public @NonNull Builder setSupportedFeatures(final Set<QName> supportedFeatures) {
+            this.supportedFeatures = supportedFeatures != null ? ImmutableSet.copyOf(supportedFeatures) : null;
+            return this;
+        }
+
+        /**
+         * Set YANG modules which can be deviated by specified modules during the parsing process. Map key (QNameModule)
+         * denotes a module which can be deviated by the modules in the Map value.
+         *
+         * @param modulesDeviatedByModules Map of YANG modules (Map key) which can be deviated by specified modules
+         *                                 (Map values) in the final SchemaContext. If the map is empty, no deviations
+         *                                 encountered will be supported. If the map is null, all deviations will be
+         *                                 applied.
+         * @return this builder
+         */
+        public @NonNull Builder setModulesDeviatedByModules(
+                final @Nullable SetMultimap<QNameModule, QNameModule> modulesDeviatedByModules) {
+            this.modulesDeviatedByModules = modulesDeviatedByModules != null
+                    ? ImmutableSetMultimap.copyOf(modulesDeviatedByModules) : null;
+            return this;
+        }
+
+        @Override
+        public @NonNull SchemaContextFactoryConfiguration build() {
+            return new SchemaContextFactoryConfiguration(filter, statementParserMode, supportedFeatures,
+                    modulesDeviatedByModules);
+        }
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaRepository.java
new file mode 100644 (file)
index 0000000..a886f84
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.eclipse.jdt.annotation.NonNull;
+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 {
+    /**
+     * Returns {@link SchemaContextFactory} with supplied configuration.
+     *
+     * @param config configuration of schema context factory.
+     * @return schema context factory.
+     */
+    @NonNull EffectiveModelContextFactory createEffectiveModelContextFactory(
+            @NonNull SchemaContextFactoryConfiguration config);
+
+    /**
+     * Returns {@link SchemaContextFactory} with {@link SchemaContextFactoryConfiguration#getDefault()}.
+     *
+     * @return schema context factory.
+     */
+    default @NonNull EffectiveModelContextFactory createEffectiveModelContextFactory() {
+        return createEffectiveModelContextFactory(SchemaContextFactoryConfiguration.getDefault());
+    }
+
+    <T extends SchemaSourceRepresentation> @NonNull ListenableFuture<T> getSchemaSource(@NonNull SourceIdentifier id,
+            @NonNull Class<T> represetation);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaResolutionException.java
new file mode 100644 (file)
index 0000000..b6128bf
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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.api;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+
+/**
+ * Exception thrown when a Schema Source fails to resolve.
+ */
+@Beta
+public class SchemaResolutionException extends SchemaSourceException {
+    private static final long serialVersionUID = 1L;
+
+    private final SourceIdentifier failedSource;
+    private final @NonNull ImmutableMultimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
+    private final @NonNull ImmutableList<SourceIdentifier> resolvedSources;
+
+    public SchemaResolutionException(final @NonNull String message) {
+        this(message, null);
+    }
+
+    public SchemaResolutionException(final @NonNull String message, final Throwable cause) {
+        this(message, null, cause, ImmutableList.of(), ImmutableMultimap.of());
+    }
+
+    public SchemaResolutionException(final @NonNull String message, final SourceIdentifier failedSource,
+            final Throwable cause) {
+        this(message, failedSource, cause, ImmutableList.of(), ImmutableMultimap.of());
+    }
+
+    public SchemaResolutionException(final @NonNull String message,
+            final @NonNull Collection<SourceIdentifier> resolvedSources,
+            final @NonNull Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
+        this(message, null, null, resolvedSources, unsatisfiedImports);
+    }
+
+    public SchemaResolutionException(final @NonNull String message, final SourceIdentifier failedSource,
+            final Throwable cause, final @NonNull Collection<SourceIdentifier> resolvedSources,
+            final @NonNull Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
+        super(formatMessage(message, failedSource, resolvedSources, unsatisfiedImports), cause);
+        this.failedSource = failedSource;
+        this.unsatisfiedImports = ImmutableMultimap.copyOf(unsatisfiedImports);
+        this.resolvedSources = ImmutableList.copyOf(resolvedSources);
+    }
+
+    private static String formatMessage(final String message, final SourceIdentifier failedSource,
+            final Collection<SourceIdentifier> resolvedSources,
+            final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
+        return String.format("%s, failed source: %s, resolved sources: %s, unsatisfied imports: %s", message,
+                failedSource, resolvedSources, unsatisfiedImports);
+    }
+
+    /**
+     * Return YANG schema source identifier consisting of name and revision of the module which caused this exception.
+     *
+     * @return YANG schema source identifier
+     */
+    public final SourceIdentifier getFailedSource() {
+        return this.failedSource;
+    }
+
+    /**
+     * Return the list of sources which failed to resolve along with reasons why they were not resolved.
+     *
+     * @return Source/reason map.
+     */
+    public final @NonNull Multimap<SourceIdentifier, ModuleImport> getUnsatisfiedImports() {
+        return unsatisfiedImports;
+    }
+
+    // FIXME: should be leak actual mapping?
+    public final @NonNull Collection<SourceIdentifier> getResolvedSources() {
+        return resolvedSources;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).add("unsatisfiedImports", unsatisfiedImports))
+            .toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper;
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceException.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceException.java
new file mode 100644 (file)
index 0000000..d307213
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.repo.api;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Exception thrown when a failure to acquire a schema source occurs.
+ */
+@Beta
+public class SchemaSourceException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public SchemaSourceException(final String message) {
+        super(message);
+    }
+
+    public SchemaSourceException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceFilter.java
new file mode 100644 (file)
index 0000000..cccfc92
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.api;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.concurrent.FluentFutures;
+
+/**
+ * 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.
+     */
+    @NonNull SchemaSourceFilter ALWAYS_ACCEPT = new SchemaSourceFilter() {
+        private final ImmutableList<Class<? extends SchemaSourceRepresentation>> representations =
+                ImmutableList.of(SchemaSourceRepresentation.class);
+
+        @Override
+        public ImmutableList<Class<? extends SchemaSourceRepresentation>> supportedRepresentations() {
+            return representations;
+        }
+
+        @Override
+        public FluentFuture<Boolean> apply(final SchemaSourceRepresentation schemaSource) {
+            return FluentFutures.immediateTrueFluentFuture();
+        }
+    };
+
+    /**
+     * 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);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SchemaSourceRepresentation.java
new file mode 100644 (file)
index 0000000..431aac4
--- /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.model.repo.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Common interface for schema source representations. A schema source is an atomic piece of the overall schema context.
+ * In YANG terms, a schema source is semantically equivalent to a single YANG text file, be it a module or a submodule.
+ *
+ * <p>
+ * A schema source can exist in various forms, which we call representations. Again, in YANG terms, each representation
+ * is semantically equivalent, but from implementation perspective certain operations on a schema source may require it
+ * to be first transformed into a particular representation before they can be applied. Such transformations are
+ * affected via instances of SchemaSourceTransformation.
+ *
+ * <p>
+ * Typical examples of a schema source representation include:
+ * <ul>
+ * <li>a {@link java.lang.String} - textual representation of source code
+ * <li>a {@link java.io.InputStream} - input stream containing source code
+ * <li>a {@link com.google.common.io.ByteSource} - source for input streams
+ * containing source code
+ * <li>Parsed abstract syntax tree (AST), which is the result of a syntactic parser
+ * </ul>
+ *
+ * <p>
+ * Implementations of this interface expected to comply with the {@link Immutable} contract.
+ */
+@Beta
+public interface SchemaSourceRepresentation extends Identifiable<SourceIdentifier>, Immutable {
+    @Override
+    SourceIdentifier getIdentifier();
+
+    /**
+     * Return the concrete representation type.
+     *
+     * @return The type of representation.
+     */
+    @NonNull Class<? extends SchemaSourceRepresentation> getType();
+
+    /**
+     * Return the symbolic name, if available. This name has no semantic meaning beyond being useful for debugging
+     * by humans.
+     *
+     * @return Symbolic name, if available
+     */
+    default Optional<String> getSymbolicName() {
+        return Optional.empty();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SemVerSourceIdentifier.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SemVerSourceIdentifier.java
new file mode 100644 (file)
index 0000000..a67f166
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2016 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.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.common.Revision;
+
+/**
+ * YANG Schema source identifier with specified semantic version.
+ *
+ * <p>
+ * Simple transfer object represents identifier of source for YANG schema (module or submodule), which consists of
+ * <ul>
+ * <li>YANG schema name {@link #getName()}
+ * <li>Semantic version of yang schema {@link #getSemanticVersion()}
+ * <li>(Optional) Module revision ({link {@link #getRevision()}
+ * </ul>
+ *
+ * <p>
+ * Source identifier is designated to be carry only necessary information to look-up YANG model source and to be used
+ * by various SchemaSourceProviders.
+ *
+ * <p>
+ * <b>Note:</b>On source retrieval layer it is impossible to distinguish between YANG module and/or submodule unless
+ * source is present.
+ *
+ * <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 SemVerSourceIdentifier extends SourceIdentifier {
+    private static final long serialVersionUID = 1L;
+    private final SemVer semVer;
+
+    /**
+     * Creates new YANG Schema semVer source identifier.
+     *
+     * @param name Name of schema
+     * @param revision Revision of source, possibly not present
+     * @param semVer semantic version of source
+     */
+    SemVerSourceIdentifier(final String name, final Optional<Revision> revision, final @Nullable SemVer semVer) {
+        super(name, revision);
+        this.semVer = semVer;
+    }
+
+    /**
+     * Creates new YANG Schema semVer source identifier.
+     *
+     * @param name Name of schema
+     * @param semVer semantic version of source
+     */
+    SemVerSourceIdentifier(final String name, final @Nullable SemVer semVer) {
+        this(name, Optional.empty(), semVer);
+    }
+
+    /**
+     * Returns semantic version of source if it was specified.
+     *
+     * @return revision of source.
+     */
+    public Optional<SemVer> getSemanticVersion() {
+        return Optional.ofNullable(semVer);
+    }
+
+    /**
+     * Creates new YANG Schema semVer source identifier.
+     *
+     * @param moduleName Name of schema
+     * @param semVer semantic version of source
+     */
+    public static @NonNull SemVerSourceIdentifier create(final String moduleName, final SemVer semVer) {
+        return new SemVerSourceIdentifier(moduleName, semVer);
+    }
+
+    /**
+     * Creates new YANG Schema semVer source identifier.
+     *
+     * @param moduleName Name of schema
+     * @param revision Revision of source in format YYYY-mm-dd
+     * @param semVer semantic version of source
+     */
+    public static @NonNull SemVerSourceIdentifier create(final String moduleName, final Revision revision,
+            final SemVer semVer) {
+        return new SemVerSourceIdentifier(moduleName, Optional.ofNullable(revision), semVer);
+    }
+
+    /**
+     * Creates new YANG Schema semVer source identifier.
+     *
+     * @param moduleName Name of schema
+     * @param revision Optional of source revision in format YYYY-mm-dd. If not present, default value will be used.
+     * @param semVer semantic version of source
+     */
+    public static @NonNull SemVerSourceIdentifier create(final String moduleName, final Optional<Revision> revision,
+            final SemVer semVer) {
+        return new SemVerSourceIdentifier(moduleName, revision, semVer);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getName());
+        result = prime * result + Objects.hashCode(semVer);
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof SemVerSourceIdentifier)) {
+            return false;
+        }
+        final SemVerSourceIdentifier other = (SemVerSourceIdentifier) obj;
+        return Objects.equals(getName(), other.getName()) && Objects.equals(semVer, other.semVer);
+    }
+
+    @Override
+    public String toString() {
+        return "SemVerSourceIdentifier [name=" + getName() + "(" + semVer + ")" + "@" + getRevision() + "]";
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/SourceIdentifier.java
new file mode 100644 (file)
index 0000000..a6f7ba5
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Identifier;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+
+/**
+ * Base class of YANG Schema source identifiers.
+ *
+ * <p>
+ * Source identifiers are designated to be carry only necessary information to
+ * look-up YANG model source and to be used by various SchemaSourceProviders.
+ *
+ * <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 abstract class SourceIdentifier implements Identifier, Immutable {
+    private static final Interner<SourceIdentifier> INTERNER = Interners.newWeakInterner();
+    private static final long serialVersionUID = 2L;
+
+    private final @Nullable Revision revision;
+    private final @NonNull String name;
+
+    /**
+     * Creates new YANG Schema source identifier for sources without revision.
+     *
+     * @param name Name of schema
+     */
+    SourceIdentifier(final String name) {
+        this(name, (Revision) null);
+    }
+
+    /**
+     * Creates new YANG Schema source identifier.
+     *
+     * @param name Name of schema
+     * @param revision Revision of source, may be null
+     */
+    SourceIdentifier(final String name, final @Nullable Revision revision) {
+        this.name = requireNonNull(name);
+        this.revision = revision;
+    }
+
+    /**
+     * Creates new YANG Schema source identifier.
+     *
+     * @param name Name of schema
+     * @param revision Revision of source, possibly not present
+     */
+    SourceIdentifier(final String name, final Optional<Revision> revision) {
+        this(name, revision.orElse(null));
+    }
+
+    /**
+     * Return an interned reference to a equivalent SemVerSourceIdentifier.
+     *
+     * @return Interned reference, or this object if it was interned.
+     */
+    public @NonNull SourceIdentifier intern() {
+        return INTERNER.intern(this);
+    }
+
+    /**
+     * Returns model name.
+     *
+     * @return model name
+     */
+    public @NonNull String getName() {
+        return name;
+    }
+
+    /**
+     * Returns revision of source or null if revision was not supplied.
+     *
+     * @return revision of source or null if revision was not supplied.
+     */
+    public Optional<Revision> getRevision() {
+        return Optional.ofNullable(revision);
+    }
+
+    /**
+     * Returns filename for this YANG module as specified in RFC 6020.
+     *
+     * <p>
+     * Returns filename in format <code>name ['@' revision] '.yang'</code>, where revision is date in format YYYY-mm-dd.
+     *
+     * <p>
+     * @see <a href="http://tools.ietf.org/html/rfc6020#section-5.2">RFC6020</a>
+     *
+     * @return Filename for this source identifier.
+     */
+    public @NonNull String toYangFilename() {
+        return toYangFileName(name, Optional.ofNullable(revision));
+    }
+
+    /**
+     * Returns filename for this YANG module as specified in RFC 6020.
+     *
+     * <p>
+     * Returns filename in format <code>moduleName ['@' revision] '.yang'</code>,
+     * where Where revision-date is in format YYYY-mm-dd.
+     *
+     * <p>
+     * See http://tools.ietf.org/html/rfc6020#section-5.2
+     *
+     * @return Filename for this source identifier.
+     */
+    public static @NonNull String toYangFileName(final String moduleName, final Optional<Revision> revision) {
+        StringBuilder filename = new StringBuilder(moduleName);
+        if (revision.isPresent()) {
+            filename.append('@');
+            filename.append(revision.get());
+        }
+        filename.append(YangConstants.RFC6020_YANG_FILE_EXTENSION);
+        return filename.toString();
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/StatementParserMode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/StatementParserMode.java
new file mode 100644 (file)
index 0000000..e2e71a2
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2016 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.api;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public enum StatementParserMode {
+    /**
+     * Default mode of statement parser.
+     */
+    DEFAULT_MODE,
+    /**
+     * Semantic version mode of statement parser. If it is enabled, module
+     * imports are processed on the basis of semantic versions.
+     */
+    SEMVER_MODE
+}
\ No newline at end of file
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangSchemaSourceRepresentation.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangSchemaSourceRepresentation.java
new file mode 100644 (file)
index 0000000..d927c8c
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.api;
+
+/**
+ * A YANG {@link SchemaSourceRepresentation}.
+ */
+public interface YangSchemaSourceRepresentation extends SchemaSourceRepresentation {
+    @Override
+    Class<? extends YangSchemaSourceRepresentation> getType();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextFileSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextFileSchemaSource.java
new file mode 100644 (file)
index 0000000..de5f0d0
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Delegator;
+
+/**
+ * A {@link YangTextSchemaSource} backed by a file.
+ *
+ * @author Robert Varga
+ */
+final class YangTextFileSchemaSource extends YangTextSchemaSource implements Delegator<File> {
+    private final @NonNull File file;
+
+    YangTextFileSchemaSource(final SourceIdentifier identifier, final File file) {
+        super(identifier);
+        this.file = requireNonNull(file);
+    }
+
+    @Override
+    public File getDelegate() {
+        return file;
+    }
+
+    @Override
+    public InputStream openStream() throws IOException {
+        return Files.newInputStream(file.toPath());
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("file", file);
+    }
+
+    @Override
+    public Optional<String> getSymbolicName() {
+        return Optional.of(file.toString());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YangTextSchemaSource.java
new file mode 100644 (file)
index 0000000..0e52156
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * 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.api;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.common.YangConstants.RFC6020_YANG_FILE_EXTENSION;
+import static org.opendaylight.yangtools.yang.common.YangNames.parseFilename;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.io.ByteSource;
+import com.google.common.io.Resources;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Map.Entry;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Revision;
+
+/**
+ * YANG text schema source representation. Exposes an RFC6020 or RFC7950 text representation as an {@link InputStream}.
+ */
+@Beta
+public abstract class YangTextSchemaSource extends ByteSource implements YangSchemaSourceRepresentation {
+    private final @NonNull SourceIdentifier identifier;
+
+    protected YangTextSchemaSource(final SourceIdentifier identifier) {
+        this.identifier = requireNonNull(identifier);
+    }
+
+    public static @NonNull SourceIdentifier identifierFromFilename(final String name) {
+        checkArgument(name.endsWith(RFC6020_YANG_FILE_EXTENSION), "Filename %s does not end with '%s'",
+            RFC6020_YANG_FILE_EXTENSION, name);
+
+        final String baseName = name.substring(0, name.length() - RFC6020_YANG_FILE_EXTENSION.length());
+        final Entry<String, String> parsed = parseFilename(baseName);
+        return RevisionSourceIdentifier.create(parsed.getKey(), Revision.ofNullable(parsed.getValue()));
+    }
+
+    /**
+     * Create a new YangTextSchemaSource with a specific source identifier and backed
+     * by ByteSource, which provides the actual InputStreams.
+     *
+     * @param identifier SourceIdentifier of the resulting schema source
+     * @param delegate Backing ByteSource instance
+     * @return A new YangTextSchemaSource
+     */
+    public static @NonNull YangTextSchemaSource delegateForByteSource(final SourceIdentifier identifier,
+            final ByteSource delegate) {
+        return new DelegatedYangTextSchemaSource(identifier, delegate);
+    }
+
+    /**
+     * Create a new YangTextSchemaSource with {@link SourceIdentifier} derived from a supplied filename and backed
+     * by ByteSource, which provides the actual InputStreams.
+     *
+     * @param fileName File name
+     * @param delegate Backing ByteSource instance
+     * @return A new YangTextSchemaSource
+     * @throws IllegalArgumentException if the file name has invalid format
+     */
+    public static @NonNull YangTextSchemaSource delegateForByteSource(final String fileName,
+            final ByteSource delegate) {
+        return new DelegatedYangTextSchemaSource(identifierFromFilename(fileName), delegate);
+    }
+
+    /**
+     * Create a new YangTextSchemaSource backed by a {@link File} with {@link SourceIdentifier} derived from the file
+     * name.
+     *
+     * @param file Backing File
+     * @return A new YangTextSchemaSource
+     * @throws IllegalArgumentException if the file name has invalid format or if the supplied File is not a file
+     * @throws NullPointerException if file is null
+     */
+    public static @NonNull YangTextSchemaSource forFile(final File file) {
+        checkArgument(file.isFile(), "Supplied file %s is not a file", file);
+        return new YangTextFileSchemaSource(identifierFromFilename(file.getName()), file);
+    }
+
+    /**
+     * Create a new {@link YangTextSchemaSource} backed by a resource available in the ClassLoader where this
+     * class resides.
+     *
+     * @param resourceName Resource name
+     * @return A new instance.
+     * @throws IllegalArgumentException if the resource does not exist or if the name has invalid format
+     */
+    public static @NonNull YangTextSchemaSource forResource(final String resourceName) {
+        return forResource(YangTextSchemaSource.class, resourceName);
+    }
+
+    /**
+     * Create a new {@link YangTextSchemaSource} backed by a resource by a resource available on the ClassLoader
+     * which loaded the specified class.
+     *
+     * @param clazz Class reference
+     * @param resourceName Resource name
+     * @return A new instance.
+     * @throws IllegalArgumentException if the resource does not exist or if the name has invalid format
+     */
+    public static @NonNull YangTextSchemaSource forResource(final Class<?> clazz, final String resourceName) {
+        final String fileName = resourceName.substring(resourceName.lastIndexOf('/') + 1);
+        final SourceIdentifier identifier = identifierFromFilename(fileName);
+        final URL url = Resources.getResource(clazz, resourceName);
+        return new ResourceYangTextSchemaSource(identifier, url);
+    }
+
+    @Override
+    public final SourceIdentifier getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public Class<? extends YangTextSchemaSource> getType() {
+        return YangTextSchemaSource.class;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).add("identifier", identifier)).toString();
+    }
+
+    /**
+     * Add subclass-specific attributes to the output {@link #toString()} output. Since
+     * subclasses are prevented from overriding {@link #toString()} for consistency
+     * reasons, they can add their specific attributes to the resulting string by attaching
+     * attributes to the supplied {@link ToStringHelper}.
+     *
+     * @param toStringHelper ToStringHelper onto the attributes can be added
+     * @return ToStringHelper supplied as input argument.
+     */
+    protected abstract ToStringHelper addToStringAttributes(ToStringHelper toStringHelper);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinDomSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinDomSchemaSource.java
new file mode 100644 (file)
index 0000000..d32ccd0
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2015 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.api;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.common.YangConstants.RFC6020_YIN_MODULE;
+import static org.opendaylight.yangtools.yang.model.api.YangStmtMapping.MODULE;
+import static org.opendaylight.yangtools.yang.model.api.YangStmtMapping.REVISION;
+import static org.opendaylight.yangtools.yang.model.api.YangStmtMapping.SUBMODULE;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Utility {@link YinXmlSchemaSource} exposing a W3C {@link DOMSource} representation of YIN model.
+ */
+public abstract class YinDomSchemaSource implements YinXmlSchemaSource {
+    private static final Logger LOG = LoggerFactory.getLogger(YinDomSchemaSource.class);
+    private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
+    private static final QName REVISION_STMT = REVISION.getStatementName();
+    private static final String MODULE_ARG = MODULE.getArgumentDefinition().get().getArgumentName().getLocalName();
+    private static final String REVISION_ARG = REVISION.getArgumentDefinition().get().getArgumentName().getLocalName();
+
+    YinDomSchemaSource() {
+        // Prevent outside instantiation
+    }
+
+    /**
+     * Create a new {@link YinDomSchemaSource} using an identifier and a source.
+     *
+     * @param identifier Schema source identifier
+     * @param source W3C DOM source
+     * @return A new {@link YinDomSchemaSource} instance.
+     */
+    public static @NonNull YinDomSchemaSource create(final @NonNull SourceIdentifier identifier,
+            final @NonNull DOMSource source) {
+
+        final Node root = source.getNode().getFirstChild();
+        final String rootNs = root.getNamespaceURI();
+        if (rootNs == null) {
+            // Let whoever is using this deal with this
+            return new Simple(identifier, source);
+        }
+
+        final QName qname = QName.create(rootNs, root.getLocalName());
+        checkArgument(RFC6020_YIN_MODULE.equals(qname.getModule()),
+            "Root node namepsace %s does not match %s", rootNs, YangConstants.RFC6020_YIN_NAMESPACE);
+        checkArgument(MODULE.getStatementName().equals(qname)
+            || SUBMODULE.getStatementName().equals(qname), "Root element %s is not a module nor a submodule", qname);
+
+        checkArgument(root instanceof Element, "Root node %s is not an element", root);
+        final Element element = (Element)root;
+
+        final Attr nameAttr = element.getAttributeNode(MODULE_ARG);
+        checkArgument(nameAttr != null, "No %s name argument found in %s", element.getLocalName());
+
+        final NodeList revisions = element.getElementsByTagNameNS(REVISION_STMT.getNamespace().toString(),
+            REVISION_STMT.getLocalName());
+        if (revisions.getLength() == 0) {
+            // FIXME: is module name important (as that may have changed)
+            return new Simple(identifier, source);
+        }
+
+        final Element revisionStmt = (Element) revisions.item(0);
+        final Attr dateAttr = revisionStmt.getAttributeNode(REVISION_ARG);
+        checkArgument(dateAttr != null, "No revision statement argument found in %s", revisionStmt);
+
+        final SourceIdentifier parsedId = RevisionSourceIdentifier.create(nameAttr.getValue(),
+            Revision.of(dateAttr.getValue()));
+        final SourceIdentifier id;
+        if (!parsedId.equals(identifier)) {
+            LOG.debug("Changed identifier from {} to {}", identifier, parsedId);
+            id = parsedId;
+        } else {
+            id = identifier;
+        }
+
+        return new Simple(id, source);
+    }
+
+    /**
+     * Create a {@link YinDomSchemaSource} from a {@link YinXmlSchemaSource}. If the argument is already a
+     * YinDomSchemaSource, this method returns the same instance. The source will be translated on first access,
+     * at which point an {@link IllegalStateException} may be raised.
+     *
+     * @param xmlSchemaSource Backing schema source
+     * @return A {@link YinDomSchemaSource} instance
+     */
+    public static @NonNull YinDomSchemaSource lazyTransform(final YinXmlSchemaSource xmlSchemaSource) {
+        final YinDomSchemaSource cast = castSchemaSource(xmlSchemaSource);
+        return cast != null ? cast : new Transforming(xmlSchemaSource);
+    }
+
+    /**
+     * Create a {@link YinDomSchemaSource} from a {@link YinXmlSchemaSource}. If the argument is already a
+     * YinDomSchemaSource, this method returns the same instance. The source will be translated immediately.
+     *
+     * @param xmlSchemaSource Backing schema source
+     * @return A {@link YinDomSchemaSource} instance
+     * @throws TransformerException when the provided source fails to transform
+     */
+    public static @NonNull YinDomSchemaSource transform(final YinXmlSchemaSource xmlSchemaSource)
+            throws TransformerException {
+        final YinDomSchemaSource cast = castSchemaSource(xmlSchemaSource);
+        return cast != null ? cast :
+            create(xmlSchemaSource.getIdentifier(), transformSource(xmlSchemaSource.getSource()));
+    }
+
+    @Override
+    public abstract DOMSource getSource();
+
+    @Override
+    public final Class<? extends YinXmlSchemaSource> getType() {
+        return YinDomSchemaSource.class;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).add("identifier", getIdentifier())).toString();
+    }
+
+    /**
+     * Add subclass-specific attributes to the output {@link #toString()} output. Since
+     * subclasses are prevented from overriding {@link #toString()} for consistency
+     * reasons, they can add their specific attributes to the resulting string by attaching
+     * attributes to the supplied {@link ToStringHelper}.
+     *
+     * @param toStringHelper ToStringHelper onto the attributes can be added
+     * @return ToStringHelper supplied as input argument.
+     */
+    protected abstract ToStringHelper addToStringAttributes(ToStringHelper toStringHelper);
+
+    static @NonNull DOMSource transformSource(final Source source) throws TransformerException {
+        final DOMResult result = new DOMResult();
+        TRANSFORMER_FACTORY.newTransformer().transform(source, result);
+
+        return new DOMSource(result.getNode(), result.getSystemId());
+    }
+
+    private static @Nullable YinDomSchemaSource castSchemaSource(final YinXmlSchemaSource xmlSchemaSource) {
+        if (xmlSchemaSource instanceof YinDomSchemaSource) {
+            return (YinDomSchemaSource) xmlSchemaSource;
+        }
+
+        final Source source = xmlSchemaSource.getSource();
+        if (source instanceof DOMSource) {
+            return create(xmlSchemaSource.getIdentifier(), (DOMSource) source);
+        }
+
+        return null;
+    }
+
+    private static final class Simple extends YinDomSchemaSource {
+        private final @NonNull SourceIdentifier identifier;
+        private final @NonNull DOMSource source;
+
+        Simple(final @NonNull SourceIdentifier identifier, final @NonNull DOMSource source) {
+            this.identifier = requireNonNull(identifier);
+            this.source = requireNonNull(source);
+        }
+
+        @Override
+        public DOMSource getSource() {
+            return source;
+        }
+
+        @Override
+        public SourceIdentifier getIdentifier() {
+            return identifier;
+        }
+
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+            return toStringHelper.add("source", source);
+        }
+    }
+
+    private static final class Transforming extends YinDomSchemaSource {
+        private final YinXmlSchemaSource xmlSchemaSource;
+        private volatile DOMSource source;
+
+        Transforming(final YinXmlSchemaSource xmlSchemaSource) {
+            this.xmlSchemaSource = requireNonNull(xmlSchemaSource);
+        }
+
+        @Override
+        public DOMSource getSource() {
+            DOMSource ret = source;
+            if (ret == null) {
+                synchronized (this) {
+                    ret = source;
+                    if (ret == null) {
+                        try {
+                            ret = transformSource(xmlSchemaSource.getSource());
+                        } catch (TransformerException e) {
+                            throw new IllegalStateException("Failed to transform schema source " + xmlSchemaSource, e);
+                        }
+                        source = ret;
+                    }
+                }
+            }
+
+            return ret;
+        }
+
+        @Override
+        public SourceIdentifier getIdentifier() {
+            return xmlSchemaSource.getIdentifier();
+        }
+
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+            return toStringHelper.add("xmlSchemaSource", xmlSchemaSource);
+        }
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSourceRepresentation.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinSchemaSourceRepresentation.java
new file mode 100644 (file)
index 0000000..5b2d6ce
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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.api;
+
+/**
+ * A YIN {@link SchemaSourceRepresentation}.
+ */
+public interface YinSchemaSourceRepresentation extends SchemaSourceRepresentation {
+    @Override
+    Class<? extends YinSchemaSourceRepresentation> getType();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinTextFileSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinTextFileSchemaSource.java
new file mode 100644 (file)
index 0000000..f900b8c
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Delegator;
+
+/**
+ * A {@link YinTextSchemaSource} backed by a file.
+ *
+ * @author Robert Varga
+ */
+final class YinTextFileSchemaSource extends YinTextSchemaSource implements Delegator<File> {
+    private final @NonNull File file;
+
+    YinTextFileSchemaSource(final @NonNull SourceIdentifier identifier, final @NonNull File file) {
+        super(identifier);
+        this.file = requireNonNull(file);
+    }
+
+    @Override
+    public File getDelegate() {
+        return file;
+    }
+
+    @Override
+    public InputStream openStream() throws IOException {
+        return Files.newInputStream(file.toPath());
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("file", file);
+    }
+
+    @Override
+    public Optional<String> getSymbolicName() {
+        return Optional.of(file.toString());
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinTextSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinTextSchemaSource.java
new file mode 100644 (file)
index 0000000..cead5fe
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2015 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.api;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.io.ByteSource;
+import com.google.common.io.Resources;
+import java.io.File;
+import java.io.InputStream;
+import java.util.Map.Entry;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.common.YangNames;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * YIN text schema source representation. Exposes an RFC6020 or RFC7950 XML representation as an {@link InputStream}.
+ */
+@Beta
+public abstract class YinTextSchemaSource extends ByteSource implements YinSchemaSourceRepresentation {
+    private static final Logger LOG = LoggerFactory.getLogger(YinTextSchemaSource.class);
+    private static final String XML_EXTENSION = ".xml";
+
+    private final @NonNull SourceIdentifier identifier;
+
+    protected YinTextSchemaSource(final SourceIdentifier identifier) {
+        this.identifier = requireNonNull(identifier);
+    }
+
+    public static @NonNull SourceIdentifier identifierFromFilename(final String name) {
+        final String baseName;
+        if (name.endsWith(YangConstants.RFC6020_YIN_FILE_EXTENSION)) {
+            baseName = name.substring(0, name.length() - YangConstants.RFC6020_YIN_FILE_EXTENSION.length());
+        } else if (name.endsWith(XML_EXTENSION)) {
+            // FIXME: BUG-7061: remove this once we do not need it
+            LOG.warn("XML file {} being loaded as YIN", name);
+            baseName = name.substring(0, name.length() - XML_EXTENSION.length());
+        } else {
+            throw new IllegalArgumentException("Filename " + name + " does not have a .yin or .xml extension");
+        }
+
+        final Entry<String, String> parsed = YangNames.parseFilename(baseName);
+        return RevisionSourceIdentifier.create(parsed.getKey(), Revision.ofNullable(parsed.getValue()));
+    }
+
+    @Override
+    public final SourceIdentifier getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public Class<? extends YinTextSchemaSource> getType() {
+        return YinTextSchemaSource.class;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).add("identifier", identifier)).toString();
+    }
+
+    /**
+     * Add subclass-specific attributes to the output {@link #toString()} output. Since
+     * subclasses are prevented from overriding {@link #toString()} for consistency
+     * reasons, they can add their specific attributes to the resulting string by attaching
+     * attributes to the supplied {@link ToStringHelper}.
+     *
+     * @param toStringHelper ToStringHelper onto the attributes can be added
+     * @return ToStringHelper supplied as input argument.
+     */
+    protected abstract ToStringHelper addToStringAttributes(@NonNull ToStringHelper toStringHelper);
+
+    /**
+     * Create a new YinTextSchemaSource with a specific source identifier and backed
+     * by ByteSource, which provides the actual InputStreams.
+     *
+     * @param identifier SourceIdentifier of the resulting schema source
+     * @param delegate Backing ByteSource instance
+     * @return A new YinTextSchemaSource
+     */
+    public static @NonNull YinTextSchemaSource delegateForByteSource(final SourceIdentifier identifier,
+            final ByteSource delegate) {
+        return new DelegatedYinTextSchemaSource(identifier, delegate);
+    }
+
+    public static @NonNull YinTextSchemaSource forFile(final File file) {
+        checkArgument(file.isFile(), "Supplied file %s is not a file", file);
+        return new YinTextFileSchemaSource(identifierFromFilename(file.getName()), file);
+    }
+
+    public static @NonNull YinTextSchemaSource forResource(final Class<?> clazz, final String resourceName) {
+        final String fileName = resourceName.substring(resourceName.lastIndexOf('/') + 1);
+        return new ResourceYinTextSchemaSource(identifierFromFilename(fileName),
+            Resources.getResource(clazz, resourceName));
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinXmlSchemaSource.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/api/YinXmlSchemaSource.java
new file mode 100644 (file)
index 0000000..4bd18ee
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 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.api;
+
+import com.google.common.annotations.Beta;
+import javax.xml.transform.Source;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * YIN text schema source representation. Exposes an RFC6020 or RFC7950 XML representation as an XML {@link Source}.
+ */
+@Beta
+public interface YinXmlSchemaSource extends YinSchemaSourceRepresentation {
+    @Override
+    Class<? extends YinXmlSchemaSource> getType();
+
+    /**
+     * Return an XML {@link Source} of the YIN document.
+     *
+     * @return An XML {@link Source} instance.
+     */
+    @NonNull Source getSource();
+}
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..c02c767
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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.spi;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import java.util.Objects;
+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;
+
+        Costs(final int value) {
+            this.value = value;
+        }
+
+        /**
+         * The the cost value.
+         *
+         * @return A constant cost.
+         */
+        public int getValue() {
+            return value;
+        }
+    }
+
+    private static final Interner<PotentialSchemaSource<?>> INTERNER = Interners.newWeakInterner();
+    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 = requireNonNull(representation);
+        this.sourceIdentifier = requireNonNull(sourceIdentifier);
+        checkArgument(cost >= 0, "cost has to be non-negative");
+        this.cost = cost;
+    }
+
+    public static <T extends SchemaSourceRepresentation> PotentialSchemaSource<T> create(
+            final SourceIdentifier sourceIdentifier, final Class<? extends T> representation, final int cost) {
+        return new PotentialSchemaSource<>(sourceIdentifier, representation, cost);
+    }
+
+    public static <T extends SchemaSourceRepresentation> PotentialSchemaSource<T> create(
+            final SourceIdentifier sourceIdentifier, final Class<? extends T> representation, final Costs cost) {
+        return new PotentialSchemaSource<>(sourceIdentifier, representation, cost.getValue());
+    }
+
+    /**
+     * Return a cached reference to an object equal to this object.
+     *
+     * @return A potentially shared reference, not guaranteed to be unique.
+     */
+    @SuppressWarnings("unchecked")
+    public PotentialSchemaSource<T> cachedReference() {
+        return (PotentialSchemaSource<T>) INTERNER.intern(this);
+    }
+
+    public SourceIdentifier getSourceIdentifier() {
+        return sourceIdentifier;
+    }
+
+    public Class<? extends T> getRepresentation() {
+        return representation;
+    }
+
+    public int getCost() {
+        return cost;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(cost, representation, sourceIdentifier);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof PotentialSchemaSource)) {
+            return false;
+        }
+        final PotentialSchemaSource<?> other = (PotentialSchemaSource<?>) obj;
+        return cost == other.cost && representation.equals(other.representation)
+                && sourceIdentifier.equals(other.sourceIdentifier);
+    }
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaListenerRegistration.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaListenerRegistration.java
new file mode 100644 (file)
index 0000000..7e480b9
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * 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.spi;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * Registration of a SchemaSourceListener.
+ */
+@Beta
+public interface SchemaListenerRegistration extends ListenerRegistration<SchemaSourceListener> {
+
+}
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..71ca203
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.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);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceProvider.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceProvider.java
new file mode 100644 (file)
index 0000000..06c0a2a
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.spi;
+
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Schema source provider implementations take care of resolving a {@link SourceIdentifier}
+ * into a particular representation of the schema source. Examples of resolution include
+ * fetching the source from an external source, opening a classpath resource, or similar.
+ *
+ * @param <T> Schema source representation type provided by this implementation
+ */
+@Beta
+public interface SchemaSourceProvider<T extends SchemaSourceRepresentation> {
+    /**
+     * Returns a representation a for supplied YANG source identifier. The resolution
+     * criteria are as follows:
+     *
+     * <ul>
+     * <li> If the source identifier specifies a revision, this method returns either
+     * 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 throws {@link MissingSchemaSourceException}.
+     * </ul>
+     *
+     * <p>
+     * In either case the returned representation is required to report a non-null
+     * revision in the {@link SourceIdentifier} returned from
+     * {@link SchemaSourceRepresentation#getIdentifier()}.
+     *
+     * <p>
+     * Implementations are not required to provide constant behavior in time, notably
+     * this different invocation of this method may produce different results.
+     *
+     * @param sourceIdentifier source identifier
+     * @return future source representation, if supplied YANG module is available
+     */
+    ListenableFuture<? extends T> getSource(SourceIdentifier sourceIdentifier);
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistration.java
new file mode 100644 (file)
index 0000000..781bfd5
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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.spi;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+
+/**
+ * Registration of a schema source.
+ */
+@Beta
+public interface SchemaSourceRegistration<T extends SchemaSourceRepresentation>
+        extends ObjectRegistration<PotentialSchemaSource<T>> {
+    @Override
+    void close();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/repo/spi/SchemaSourceRegistry.java
new file mode 100644 (file)
index 0000000..dfa34b7
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.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;
+
+/**
+ * Registry of all potentially available schema sources. Processes capable of
+ * dynamic schema discovery, such as OSGi registry scanners, NETCONF clients
+ * (with NETCONF monitoring extension) and similar can register
+ * {@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 provider Resolver which can potentially resolve the identifier
+     * @param source Schema source details
+     * @return A registration handle. Invoking {@link SchemaSourceRegistration#close()}
+     *         will cancel the registration.
+     */
+    <T extends SchemaSourceRepresentation> SchemaSourceRegistration<T> registerSchemaSource(
+            SchemaSourceProvider<? super T> provider, PotentialSchemaSource<T> source);
+
+    /**
+     * Register a schema source listener. The listener will be notified as new
+     * sources and their representations become available, subject to the provided
+     * filter.
+     *
+     * @param listener Schema source listener
+     * @return A registration handle. Invoking {@link SchemaListenerRegistration#close()}
+     *         will cancel the registration.
+     */
+    SchemaListenerRegistration registerSchemaSourceListener(SchemaSourceListener listener);
+}
diff --git a/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatement1.java b/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatement1.java
new file mode 100644 (file)
index 0000000..4b6469a
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.meta;
+
+import com.google.common.annotations.VisibleForTesting;
+
+@VisibleForTesting
+public interface DeclaredStatement1 extends DeclaredStatement<String> {
+
+}
\ No newline at end of file
diff --git a/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatement2.java b/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatement2.java
new file mode 100644 (file)
index 0000000..4ccbbe0
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.meta;
+
+import com.google.common.annotations.VisibleForTesting;
+
+@VisibleForTesting
+public interface DeclaredStatement2 extends DeclaredStatement<String> {
+
+}
\ No newline at end of file
diff --git a/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatementTest.java b/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatementTest.java
new file mode 100644 (file)
index 0000000..8dea691
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.meta;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doReturn;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class DeclaredStatementTest {
+    @Mock
+    private DeclaredStatement1 stmt;
+    @Mock
+    private DeclaredStatement1 stmt1;
+    @Mock
+    private DeclaredStatement2 stmt2;
+
+    @Before
+    public void before() {
+        doReturn("one").when(stmt1).argument();
+        doReturn("two").when(stmt2).argument();
+        doReturn(ImmutableList.of(stmt1, stmt2)).when(stmt).declaredSubstatements();
+        doCallRealMethod().when(stmt).declaredSubstatements(any());
+        doCallRealMethod().when(stmt).findFirstDeclaredSubstatement(any());
+        doCallRealMethod().when(stmt).findFirstDeclaredSubstatementArgument(any());
+        doCallRealMethod().when(stmt).streamDeclaredSubstatements(any());
+    }
+
+    @Test
+    public void testDeclaredSubstatements() {
+        assertEquals(ImmutableList.of(stmt1), ImmutableList.copyOf(stmt.declaredSubstatements(
+            DeclaredStatement1.class)));
+        assertEquals(ImmutableList.of(stmt2), ImmutableList.copyOf(stmt.declaredSubstatements(
+            DeclaredStatement2.class)));
+    }
+
+    @Test
+    public void testFindFirstDeclaredSubstatement() {
+        assertEquals(Optional.of(stmt1), stmt.findFirstDeclaredSubstatement(DeclaredStatement1.class));
+        assertEquals(Optional.of(stmt2), stmt.findFirstDeclaredSubstatement(DeclaredStatement2.class));
+    }
+
+    @Test
+    public void testFindFirstDeclaredSubstatementArgument() {
+        assertEquals(Optional.of("one"), stmt.findFirstDeclaredSubstatementArgument(DeclaredStatement1.class));
+        assertEquals(Optional.of("two"), stmt.findFirstDeclaredSubstatementArgument(DeclaredStatement2.class));
+    }
+}
diff --git a/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/EffectiveStatement1.java b/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/EffectiveStatement1.java
new file mode 100644 (file)
index 0000000..3ed3266
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.meta;
+
+import com.google.common.annotations.VisibleForTesting;
+
+@VisibleForTesting
+public interface EffectiveStatement1 extends EffectiveStatement<String, DeclaredStatement1> {
+
+}
\ No newline at end of file
diff --git a/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/EffectiveStatementTest.java b/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/EffectiveStatementTest.java
new file mode 100644 (file)
index 0000000..dbcf36f
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.meta;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doReturn;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class EffectiveStatementTest {
+    @Mock
+    private EffectiveStatement1 stmt;
+    @Mock
+    private EffectiveStatement1 stmt1;
+    @Mock
+    private Effectivestatement2 stmt2;
+    @Mock
+    private Map<?, ?> mockNamespace;
+
+    @Before
+    public void before() {
+        doReturn("one").when(stmt1).argument();
+        doReturn("two").when(stmt2).argument();
+        doReturn(ImmutableList.of(stmt1, stmt2)).when(stmt).effectiveSubstatements();
+        doCallRealMethod().when(stmt).findFirstEffectiveSubstatement(any());
+        doCallRealMethod().when(stmt).findFirstEffectiveSubstatementArgument(any());
+        doCallRealMethod().when(stmt).streamEffectiveSubstatements(any());
+    }
+
+    @Test
+    public void testFindFirstDeclaredSubstatement() {
+        assertEquals(Optional.of(stmt1), stmt.findFirstEffectiveSubstatement(EffectiveStatement1.class));
+        assertEquals(Optional.of(stmt2), stmt.findFirstEffectiveSubstatement(Effectivestatement2.class));
+    }
+
+    @Test
+    public void testFindFirstDeclaredSubstatementArgument() {
+        assertEquals(Optional.of("one"), stmt.findFirstEffectiveSubstatementArgument(EffectiveStatement1.class));
+        assertEquals(Optional.of("two"), stmt.findFirstEffectiveSubstatementArgument(Effectivestatement2.class));
+    }
+
+    @Test
+    public void testStreamEffectiveSubstatements() {
+        assertEquals(ImmutableList.of(stmt1), stmt.streamEffectiveSubstatements(EffectiveStatement1.class)
+            .collect(Collectors.toList()));
+        assertEquals(ImmutableList.of(stmt2), stmt.streamEffectiveSubstatements(Effectivestatement2.class)
+            .collect(Collectors.toList()));
+    }
+}
diff --git a/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/Effectivestatement2.java b/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/meta/Effectivestatement2.java
new file mode 100644 (file)
index 0000000..ae964d7
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.meta;
+
+import com.google.common.annotations.VisibleForTesting;
+
+@VisibleForTesting
+public interface Effectivestatement2 extends EffectiveStatement<String, DeclaredStatement2> {
+
+}
\ No newline at end of file
diff --git a/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/stmt/ActionStatementAwareDeclaredStatementTest.java b/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/api/stmt/ActionStatementAwareDeclaredStatementTest.java
new file mode 100644 (file)
index 0000000..5d9bf85
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.api.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doReturn;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement1;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class ActionStatementAwareDeclaredStatementTest {
+    @Mock
+    ActionStatementAwareDeclaredStatement<?> stmt;
+    @Mock
+    private DeclaredStatement1 stmt1;
+    @Mock
+    private ActionStatement stmt2;
+
+    @Before
+    public void before() {
+        doReturn(ImmutableList.of(stmt1, stmt2)).when(stmt).declaredSubstatements();
+        doCallRealMethod().when(stmt).declaredSubstatements(any());
+        doCallRealMethod().when(stmt).getActions();
+    }
+
+    @Test
+    public void testGetActions() {
+        assertEquals(ImmutableList.of(stmt2), ImmutableList.copyOf(stmt.getActions()));
+    }
+}
diff --git a/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSourceTest.java b/yang/yang-model-api/src/test/java/org/opendaylight/yangtools/yang/model/repo/spi/PotentialSchemaSourceTest.java
new file mode 100644 (file)
index 0000000..95cf664
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.spi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class PotentialSchemaSourceTest {
+    private interface TestSchemaSourceRepresentation extends SchemaSourceRepresentation {
+
+    }
+
+    @Mock
+    private SourceIdentifier sourceIdentifier;
+    private PotentialSchemaSource<TestSchemaSourceRepresentation> source;
+    private PotentialSchemaSource<TestSchemaSourceRepresentation> same;
+
+    @Before
+    public void before() {
+        source = PotentialSchemaSource.create(sourceIdentifier, TestSchemaSourceRepresentation.class,
+            PotentialSchemaSource.Costs.LOCAL_IO.getValue());
+        same = PotentialSchemaSource.create(source.getSourceIdentifier(), source.getRepresentation(),
+            source.getCost());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNegativeCost() {
+        PotentialSchemaSource.create(sourceIdentifier, TestSchemaSourceRepresentation.class, -1);
+    }
+
+    @Test
+    public void testMethods() {
+        assertEquals(PotentialSchemaSource.Costs.LOCAL_IO.getValue(), source.getCost());
+        assertSame(sourceIdentifier, source.getSourceIdentifier());
+        assertSame(TestSchemaSourceRepresentation.class, source.getRepresentation());
+        assertEquals(same.hashCode(), source.hashCode());
+        assertFalse(source.equals(null));
+        assertTrue(source.equals(source));
+        assertTrue(source.equals(same));
+    }
+
+    @Test
+    public void testIntern() {
+        assertSame(source, source.cachedReference());
+        assertSame(source, same.cachedReference());
+    }
+}
diff --git a/yang/yang-model-export/pom.xml b/yang/yang-model-export/pom.xml
new file mode 100644 (file)
index 0000000..435bbc1
--- /dev/null
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2015 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-model-export</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-test-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.xmlunit</groupId>
+            <artifactId>xmlunit-legacy</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.model.export</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/DeclaredStatementFormatter.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/DeclaredStatementFormatter.java
new file mode 100644 (file)
index 0000000..040f0c9
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.export;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement.QNameModuleToPrefixNamespace;
+
+/**
+ * Utility class for formatting {@link DeclaredStatement}s.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public final class DeclaredStatementFormatter implements Immutable {
+    private static final DeclaredStatementFormatter DEFAULT = new DeclaredStatementFormatter(ImmutableSet.of(), true);
+
+    private final Set<StatementDefinition> ignoredStatements;
+    private final boolean omitDefaultStatements;
+
+    DeclaredStatementFormatter(final Set<StatementDefinition> ignoredStatements, final boolean omitDefaultStatements) {
+        this.ignoredStatements = requireNonNull(ignoredStatements);
+        this.omitDefaultStatements = omitDefaultStatements;
+    }
+
+    /**
+     * Format specified statement into a {@link YangTextSnippet}.
+     *
+     * @param module parent module
+     * @param statement statement to format
+     * @return A {@link YangTextSnippet}
+     * @throws NullPointerException if any of the arguments is null
+     */
+    public YangTextSnippet toYangTextSnippet(final ModuleEffectiveStatement module,
+            final DeclaredStatement<?> statement) {
+        return new YangTextSnippet(statement, module.findAll(QNameModuleToPrefixNamespace.class), ignoredStatements,
+            omitDefaultStatements);
+    }
+
+    /**
+     * Return the default DeclaredStatementFormatter instance. The instance suppresses statements with default values
+     * and does not ignore any statements.
+     *
+     * @return Default-configured instance.
+     */
+    public static DeclaredStatementFormatter defaultInstance() {
+        return DEFAULT;
+    }
+
+    /**
+     * Create a new {@link Builder}, which can be used to create customized DeclaredStatementFormatter instances.
+     *
+     * @return A new Builder.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder class for instantiation of a customized {@link DeclaredStatementFormatter}.
+     */
+    @Beta
+    public static final class Builder
+            implements org.opendaylight.yangtools.concepts.Builder<DeclaredStatementFormatter> {
+        private final Set<StatementDefinition> ignoredStatements = new HashSet<>(4);
+        private boolean retainDefaultStatements;
+
+        Builder() {
+
+        }
+
+        /**
+         * Add a statement which should be skipped along with any of its children.
+         *
+         * @param statementDef Statement to be ignored
+         * @return This builder
+         */
+        public Builder addIgnoredStatement(final StatementDefinition statementDef) {
+            ignoredStatements.add(requireNonNull(statementDef));
+            return this;
+        }
+
+        /**
+         * Retain common known statements whose argument matches semantics of not being present. By default these
+         * statements are omitted from output.
+         *
+         * @return This builder
+         */
+        public Builder retainDefaultStatements() {
+            retainDefaultStatements = true;
+            return this;
+        }
+
+        @Override
+        public DeclaredStatementFormatter build() {
+            return new DeclaredStatementFormatter(ImmutableSet.copyOf(ignoredStatements), !retainDefaultStatements);
+        }
+    }
+}
diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ExportUtils.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ExportUtils.java
new file mode 100644 (file)
index 0000000..585cfea
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.export;
+
+import static com.google.common.base.Verify.verify;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+
+/**
+ * Internal shared helpers.
+ * @author Robert Varga
+ *
+ */
+final class ExportUtils {
+    private ExportUtils() {
+        // Hidden on purpose
+    }
+
+    static Optional<String> statementPrefix(final Map<QNameModule, String> namespaces, final QName stmtName) {
+        final QNameModule namespace = stmtName.getModule();
+        if (YangConstants.RFC6020_YIN_MODULE.equals(namespace)) {
+            return Optional.empty();
+        }
+
+        // Non-default namespace, a prefix is needed
+        @Nullable String prefix = namespaces.get(namespace);
+        if (prefix == null && !namespace.getRevision().isPresent()) {
+            // FIXME: this is an artifact of commonly-bound statements in parser, which means a statement's name
+            //        does not have a Revision. We'll need to find a solution to this which is acceptable. There
+            //        are multiple ways of fixing this:
+            //        - perhaps EffectiveModuleStatement should be giving us a statement-to-EffectiveModule map?
+            //        - or DeclaredStatement should provide the prefix?
+            //        The second one seems cleaner, as that means we would not have perform any lookup at all...
+            Entry<QNameModule, @NonNull String> match = null;
+            for (Entry<QNameModule, @NonNull String> entry : namespaces.entrySet()) {
+                final QNameModule ns = entry.getKey();
+                if (namespace.equals(ns.withoutRevision()) && (match == null
+                        || Revision.compare(match.getKey().getRevision(), ns.getRevision()) < 0)) {
+                    match = entry;
+                }
+            }
+
+            if (match != null) {
+                prefix = match.getValue();
+            }
+        }
+
+        if (prefix == null) {
+            throw new IllegalArgumentException("Failed to find prefix for statement " + stmtName);
+        }
+
+        verify(!prefix.isEmpty(), "Empty prefix for statement %s", stmtName);
+        return Optional.of(prefix);
+    }
+}
diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ModuleNamespaceContext.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ModuleNamespaceContext.java
new file mode 100644 (file)
index 0000000..c74562e
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.export;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableListMultimap.Builder;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Maps;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement.PrefixToEffectiveModuleNamespace;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement.QNameModuleToPrefixNamespace;
+
+final class ModuleNamespaceContext implements NamespaceContext {
+    private static final Entry<String, String> YIN_PREFIX_AND_NAMESPACE =
+            new SimpleImmutableEntry<>(XMLConstants.DEFAULT_NS_PREFIX, YangConstants.RFC6020_YIN_NAMESPACE_STRING);
+
+    private final ListMultimap<@NonNull String, @NonNull String> namespaceToPrefix;
+    private final Map<String, @NonNull ModuleEffectiveStatement> prefixToModule;
+    private final Map<QNameModule, @NonNull String> moduleToPrefix;
+
+    ModuleNamespaceContext(final ModuleEffectiveStatement module) {
+        this.prefixToModule = requireNonNull(module.getAll(PrefixToEffectiveModuleNamespace.class));
+        this.moduleToPrefix = requireNonNull(module.getAll(QNameModuleToPrefixNamespace.class));
+
+        final Builder<String, String> namespaces = ImmutableListMultimap.builder();
+        for (Entry<QNameModule, @NonNull String> e : moduleToPrefix.entrySet()) {
+            namespaces.put(e.getKey().getNamespace().toString(), e.getValue());
+        }
+        namespaceToPrefix = namespaces.build();
+    }
+
+    @Override
+    public String getNamespaceURI(final String prefix) {
+        checkArgument(prefix != null);
+
+        switch (prefix) {
+            case XMLConstants.DEFAULT_NS_PREFIX:
+                return YangConstants.RFC6020_YIN_NAMESPACE_STRING;
+            case XMLConstants.XML_NS_PREFIX:
+                return XMLConstants.XML_NS_URI;
+            case XMLConstants.XMLNS_ATTRIBUTE:
+                return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
+            default:
+                final ModuleEffectiveStatement module = prefixToModule.get(prefix);
+                return module != null ? module.localQNameModule().getNamespace().toString()
+                        : XMLConstants.NULL_NS_URI;
+        }
+    }
+
+    @Override
+    public String getPrefix(final String namespaceURI) {
+        checkArgument(namespaceURI != null);
+
+        switch (namespaceURI) {
+            case YangConstants.RFC6020_YIN_NAMESPACE_STRING:
+                return XMLConstants.DEFAULT_NS_PREFIX;
+            case XMLConstants.XML_NS_URI:
+                return XMLConstants.XML_NS_PREFIX;
+            case XMLConstants.XMLNS_ATTRIBUTE_NS_URI:
+                return XMLConstants.XMLNS_ATTRIBUTE;
+            default:
+                final List<@NonNull String> prefixes = namespaceToPrefix.get(namespaceURI);
+                return prefixes.isEmpty() ? null : prefixes.get(0);
+        }
+    }
+
+    @Override
+    public Iterator<String> getPrefixes(final String namespaceURI) {
+        checkArgument(namespaceURI != null);
+
+        switch (namespaceURI) {
+            case YangConstants.RFC6020_YIN_NAMESPACE_STRING:
+                return Iterators.singletonIterator(XMLConstants.DEFAULT_NS_PREFIX);
+            case XMLConstants.XML_NS_URI:
+                return Iterators.singletonIterator(XMLConstants.XML_NS_PREFIX);
+            case XMLConstants.XMLNS_ATTRIBUTE_NS_URI:
+                return Iterators.singletonIterator(XMLConstants.XMLNS_ATTRIBUTE);
+            default:
+                return namespaceToPrefix.get(namespaceURI).iterator();
+        }
+    }
+
+    Entry<String, String> prefixAndNamespaceFor(final QNameModule module) {
+        if (YangConstants.RFC6020_YIN_MODULE.equals(module)) {
+            return YIN_PREFIX_AND_NAMESPACE;
+        }
+
+        final String prefix = moduleToPrefix.get(module);
+        checkArgument(prefix != null, "Module %s does not map to a prefix", module);
+        return new SimpleImmutableEntry<>(prefix, module.getNamespace().toString());
+    }
+
+    Entry<String, String> prefixAndNamespaceForStatement(final QName stmtName) {
+        final Optional<String> prefix = ExportUtils.statementPrefix(moduleToPrefix, stmtName);
+        if (!prefix.isPresent()) {
+            return YIN_PREFIX_AND_NAMESPACE;
+        }
+
+        return new SimpleImmutableEntry<>(prefix.get(), stmtName.getNamespace().toString());
+    }
+
+    Map<String, String> prefixesAndNamespaces() {
+        return Maps.transformValues(prefixToModule, module -> module.localQNameModule().getNamespace().toString());
+    }
+}
diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YangTextSnippet.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YangTextSnippet.java
new file mode 100644 (file)
index 0000000..9355e1e
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.export;
+
+import static java.util.Objects.requireNonNull;
+import static org.eclipse.jdt.annotation.DefaultLocation.PARAMETER;
+import static org.eclipse.jdt.annotation.DefaultLocation.RETURN_TYPE;
+
+import com.google.common.annotations.Beta;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+/**
+ * A YANG text snippet generated from a {@link DeclaredStatement}. Generated {@link #stream()} or {@link #iterator()}
+ * are guaranteed to not contain null nor empty strings. Furthermore, newlines are always emitted at the end
+ * on the generated string -- which can be checked with {@link #isEolString(String)} utility method.
+ *
+ * <p>
+ * This allows individual strings to be escaped as needed and external indentation can be accounted for by inserting
+ * outer document indentation after the string which matched {@link #isEolString(String)} is emitted to the stream.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault({ PARAMETER, RETURN_TYPE })
+public final class YangTextSnippet implements Immutable, Iterable<@NonNull String> {
+    private final Set<@NonNull StatementDefinition> ignoredStatements;
+    private final Map<QNameModule, @NonNull String> mapper;
+    private final DeclaredStatement<?> statement;
+    private final boolean omitDefaultStatements;
+
+    YangTextSnippet(final DeclaredStatement<?> statement, final Map<QNameModule, @NonNull String> namespaces,
+            final Set<@NonNull StatementDefinition> ignoredStatements, final boolean omitDefaultStatements) {
+        this.statement = requireNonNull(statement);
+        this.mapper = requireNonNull(namespaces);
+        this.ignoredStatements = requireNonNull(ignoredStatements);
+        this.omitDefaultStatements = omitDefaultStatements;
+    }
+
+    @Override
+    public Iterator<@NonNull String> iterator() {
+        return new YangTextSnippetIterator(statement, mapper, ignoredStatements, omitDefaultStatements);
+    }
+
+    @Override
+    @SuppressWarnings("null")
+    public Spliterator<@NonNull String> spliterator() {
+        return Spliterators.spliteratorUnknownSize(iterator(),
+            Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL);
+    }
+
+    @SuppressWarnings("null")
+    public Stream<@NonNull String> stream() {
+        return StreamSupport.stream(spliterator(), false);
+    }
+
+    /**
+     * Check if an emitted string contains End-Of-Line character.
+     *
+     * @param str String to be checked
+     * @return True if the string contains end of line.
+     * @throws NullPointerException if str is null
+     */
+    public static boolean isEolString(final String str) {
+        return !str.isEmpty() && str.charAt(str.length() - 1) == '\n';
+    }
+
+    @Override
+    @SuppressWarnings("null")
+    public String toString() {
+        return stream().collect(Collectors.joining());
+    }
+}
diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YangTextSnippetIterator.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YangTextSnippetIterator.java
new file mode 100644 (file)
index 0000000..b97e0c5
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.export;
+
+import static java.util.Objects.requireNonNull;
+import static org.eclipse.jdt.annotation.DefaultLocation.PARAMETER;
+import static org.eclipse.jdt.annotation.DefaultLocation.RETURN_TYPE;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Queue;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+/**
+ * This is an iterator over strings needed to assemble a YANG snippet.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault({ PARAMETER, RETURN_TYPE })
+final class YangTextSnippetIterator extends AbstractIterator<@NonNull String> {
+    // https://tools.ietf.org/html/rfc7950#section-6.1.3
+    //            An unquoted string is any sequence of characters that does not
+    //            contain any space, tab, carriage return, or line feed characters, a
+    //            single or double quote character, a semicolon (";"), braces ("{" or
+    //            "}"), or comment sequences ("//", "/*", or "*/").
+    // Newline is treated separately, so it is not included here
+    private static final CharMatcher NEED_QUOTE_MATCHER = CharMatcher.anyOf(" \t\r'\";{}");
+    private static final CharMatcher DQUOT_MATCHER = CharMatcher.is('"');
+    private static final Splitter NEWLINE_SPLITTER = Splitter.on('\n');
+    private static final ImmutableSet<StatementDefinition> QUOTE_MULTILINE_STATEMENTS = ImmutableSet.of(
+        YangStmtMapping.CONTACT,
+        YangStmtMapping.DESCRIPTION,
+        YangStmtMapping.ERROR_MESSAGE,
+        YangStmtMapping.ORGANIZATION,
+        YangStmtMapping.REFERENCE);
+
+    /*
+     * https://tools.ietf.org/html/rfc6087#section-4.3:
+     *            In general, it is suggested that substatements containing very common
+     *            default values SHOULD NOT be present.  The following substatements
+     *            are commonly used with the default value, which would make the module
+     *            difficult to read if used everywhere they are allowed.
+     */
+    private static final ImmutableMap<StatementDefinition, String> DEFAULT_STATEMENTS =
+            ImmutableMap.<StatementDefinition, String>builder()
+            .put(YangStmtMapping.CONFIG, "true")
+            .put(YangStmtMapping.MANDATORY, "true")
+            .put(YangStmtMapping.MAX_ELEMENTS, "unbounded")
+            .put(YangStmtMapping.MIN_ELEMENTS, "0")
+            .put(YangStmtMapping.ORDERED_BY, "system")
+            .put(YangStmtMapping.REQUIRE_INSTANCE, "true")
+            .put(YangStmtMapping.STATUS, "current")
+            .put(YangStmtMapping.YIN_ELEMENT, "false")
+            .build();
+
+    private static final String INDENT = "  ";
+    private static final int INDENT_STRINGS_SIZE = 16;
+    private static final String[] INDENT_STRINGS = new String[INDENT_STRINGS_SIZE];
+
+    static {
+        for (int i = 0; i < INDENT_STRINGS_SIZE; i++) {
+            INDENT_STRINGS[i] = INDENT.repeat(i).intern();
+        }
+    }
+
+    private enum Quoting {
+        /**
+         * No quoting necessary.
+         */
+        NONE,
+        /**
+         * Argument is empty, quote an empty string.
+         */
+        EMPTY,
+        /**
+         * Quote on the same line.
+         */
+        SIMPLE,
+        /**
+         * Quote starting on next line.
+         */
+        MULTILINE;
+    }
+
+    /*
+     * We normally have up to 10 strings:
+     *               <indent>
+     *               <prefix>
+     *               ":"
+     *               <name>
+     *               " \n"
+     *               <indent>
+     *               "\""
+     *               <argument>
+     *               "\""
+     *               ";\n"
+     *
+     * But all of this is typically not used:
+     * - statements usually do not have a prefix, saving two items
+     * - arguments are not typically quoted, saving another two
+     *
+     * In case we get into a multi-line argument, we are already splitting strings, so the cost of growing
+     * the queue is negligible
+     */
+    private final Queue<String> strings = new ArrayDeque<>(8);
+    // Let's be modest, 16-level deep constructs are not exactly common.
+    private final Deque<Iterator<? extends DeclaredStatement<?>>> stack = new ArrayDeque<>(8);
+    private final Map<QNameModule, @NonNull String> namespaces;
+    private final Set<StatementDefinition> ignoredStatements;
+    private final boolean omitDefaultStatements;
+
+    YangTextSnippetIterator(final DeclaredStatement<?> stmt, final Map<QNameModule, @NonNull String> namespaces,
+        final Set<StatementDefinition> ignoredStatements, final boolean omitDefaultStatements) {
+        this.namespaces = requireNonNull(namespaces);
+        this.ignoredStatements = requireNonNull(ignoredStatements);
+        this.omitDefaultStatements = omitDefaultStatements;
+        pushStatement(requireNonNull(stmt));
+    }
+
+    @Override
+    protected @NonNull String computeNext() {
+        // We may have some strings stashed, take one out, if that is the case
+        final String nextString = strings.poll();
+        if (nextString != null) {
+            return nextString;
+        }
+
+        final Iterator<? extends DeclaredStatement<?>> it = stack.peek();
+        if (it == null) {
+            endOfData();
+            // Post-end of data, the user will never see this
+            return "";
+        }
+
+        // Try to push next child
+        while (it.hasNext()) {
+            if (pushStatement(it.next())) {
+                return strings.remove();
+            }
+        }
+
+        // End of children, close the parent statement
+        stack.pop();
+        addIndent();
+        strings.add("}\n");
+        return strings.remove();
+    }
+
+    /**
+     * Push a statement to the stack. A successfully-pushed statement results in strings not being empty.
+     *
+     * @param stmt Statement to push into strings
+     * @return True if the statement was pushed. False if the statement was suppressed.
+     */
+    private boolean pushStatement(final DeclaredStatement<?> stmt) {
+        final StatementDefinition def = stmt.statementDefinition();
+        if (ignoredStatements.contains(def)) {
+            return false;
+        }
+
+        final Collection<? extends DeclaredStatement<?>> children = stmt.declaredSubstatements();
+        if (omitDefaultStatements && children.isEmpty()) {
+            // This statement does not have substatements, check if its value matches the declared default, like
+            // "config true", "mandatory false", etc.
+            final String suppressValue = DEFAULT_STATEMENTS.get(def);
+            if (suppressValue != null && suppressValue.equals(stmt.rawArgument())) {
+                return false;
+            }
+        }
+
+        // New statement: push indent
+        addIndent();
+
+        // Add statement prefixed with namespace if needed
+        final QName stmtName = def.getStatementName();
+        final Optional<String> prefix = ExportUtils.statementPrefix(namespaces, stmtName);
+        if (prefix.isPresent()) {
+            strings.add(prefix.get());
+            strings.add(":");
+        }
+        strings.add(stmtName.getLocalName());
+
+        // Add argument, quoted and properly indented if need be
+        addArgument(def, stmt.rawArgument());
+
+        if (!children.isEmpty()) {
+            // Open the statement and push child iterator
+            strings.add(" {\n");
+            stack.push(children.iterator());
+        } else {
+            // Close the statement
+            strings.add(";\n");
+        }
+
+        return true;
+    }
+
+    private void addIndent() {
+        int depth = stack.size();
+        while (depth >= INDENT_STRINGS_SIZE) {
+            strings.add(INDENT_STRINGS[INDENT_STRINGS_SIZE - 1]);
+            depth -= INDENT_STRINGS_SIZE;
+        }
+        if (depth > 0) {
+            strings.add(INDENT_STRINGS[depth]);
+        }
+    }
+
+    private void addArgument(final StatementDefinition def, final @Nullable String arg) {
+        if (arg == null) {
+            // No argument, nothing to do
+            return;
+        }
+
+        switch (quoteKind(def, arg)) {
+            case EMPTY:
+                strings.add(" \"\"");
+                break;
+            case NONE:
+                strings.add(" ");
+                strings.add(arg);
+                break;
+            case SIMPLE:
+                strings.add(" \"");
+                strings.add(DQUOT_MATCHER.replaceFrom(arg, "\\\""));
+                strings.add("\"");
+                break;
+            case MULTILINE:
+                strings.add("\n");
+                addIndent();
+                strings.add(INDENT + '\"');
+
+                final Iterator<String> it = NEWLINE_SPLITTER.split(DQUOT_MATCHER.replaceFrom(arg, "\\\"")).iterator();
+                final String first = it.next();
+                if (!first.isEmpty()) {
+                    strings.add(first);
+                }
+
+                while (it.hasNext()) {
+                    strings.add("\n");
+                    final String str = it.next();
+                    if (!str.isEmpty()) {
+                        addIndent();
+                        strings.add(INDENT + ' ');
+                        strings.add(str);
+                    }
+                }
+                strings.add("\"");
+                break;
+            default:
+                throw new IllegalStateException("Illegal quoting for " + def + " argument \"" + arg + "\"");
+        }
+    }
+
+    private static Quoting quoteKind(final StatementDefinition def, final String str) {
+        if (str.isEmpty()) {
+            return Quoting.EMPTY;
+        }
+        if (QUOTE_MULTILINE_STATEMENTS.contains(def) || str.indexOf('\n') != -1) {
+            return Quoting.MULTILINE;
+        }
+        if (NEED_QUOTE_MATCHER.matchesAnyOf(str) || str.contains("//") || str.contains("/*") || str.contains("*/")) {
+            return Quoting.SIMPLE;
+        }
+
+        return Quoting.NONE;
+    }
+}
diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinExportUtils.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinExportUtils.java
new file mode 100644 (file)
index 0000000..a64582a
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015 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.export;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.io.OutputStream;
+import java.util.Optional;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stax.StAXSource;
+import javax.xml.transform.stream.StreamResult;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+
+public final class YinExportUtils {
+    private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
+
+    private YinExportUtils() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    /**
+     * Returns well-formed file name of YIN file as defined in RFC6020.
+     *
+     * @param name Module or submodule name
+     * @param revision Revision of module or submodule
+     * @return well-formed file name of YIN file as defined in RFC6020.
+     */
+    public static String wellFormedYinName(final String name, final Optional<Revision> revision) {
+        return wellFormedYinName(name, revision.map(Revision::toString).orElse(null));
+    }
+
+    /**
+     * Returns well-formed file name of YIN file as defined in RFC6020.
+     *
+     * @param name name Module or submodule name
+     * @param revision Revision of module or submodule
+     * @return well-formed file name of YIN file as defined in RFC6020.
+     */
+    public static String wellFormedYinName(final String name, final String revision) {
+        if (revision == null) {
+            return name + YangConstants.RFC6020_YIN_FILE_EXTENSION;
+        }
+        return requireNonNull(name) + '@' + revision +  YangConstants.RFC6020_YIN_FILE_EXTENSION;
+    }
+
+    /**
+     * Write a module as a YIN text into specified {@link OutputStream}. Supplied module must have the
+     * {@link ModuleEffectiveStatement} trait.
+     *
+     * @param module Module to be exported
+     * @throws IllegalArgumentException if the module is not an ModuleEffectiveStatement or if it declared
+     *                                  representation is not available.
+     * @throws NullPointerException if any of of the parameters is null
+     * @throws XMLStreamException if an input-output error occurs
+     */
+    @Beta
+    public static void writeModuleAsYinText(final Module module, final OutputStream output) throws XMLStreamException {
+        requireNonNull(module);
+        checkArgument(module instanceof ModuleEffectiveStatement, "Module %s is not a ModuleEffectiveStatement",
+            module);
+        final ModuleEffectiveStatement effective = (ModuleEffectiveStatement) module;
+        writeReaderToOutput(YinXMLEventReaderFactory.defaultInstance().createXMLEventReader(effective), output);
+    }
+
+    /**
+     * Write a submodule as a YIN text into specified {@link OutputStream}. Supplied submodule must have the
+     * {@link SubmoduleEffectiveStatement} trait.
+     *
+     * @param parentModule Parent module
+     * @param submodule Submodule to be exported
+     * @throws IllegalArgumentException if the parent module is not a ModuleEffectiveStatement, if the submodule is not
+     *                                  a SubmoduleEffectiveStatement or if its declared representation is not available
+     * @throws NullPointerException if any of of the parameters is null
+     * @throws XMLStreamException if an input-output error occurs
+     */
+    @Beta
+    public static void writeSubmoduleAsYinText(final Module parentModule, final Module submodule,
+            final OutputStream output) throws XMLStreamException {
+        requireNonNull(parentModule);
+        checkArgument(parentModule instanceof ModuleEffectiveStatement, "Parent %s is not a ModuleEffectiveStatement",
+            parentModule);
+        requireNonNull(submodule);
+        checkArgument(submodule instanceof SubmoduleEffectiveStatement,
+            "Submodule %s is not a SubmoduleEffectiveStatement", submodule);
+        writeReaderToOutput(YinXMLEventReaderFactory.defaultInstance().createXMLEventReader(
+            (ModuleEffectiveStatement) parentModule, (SubmoduleEffectiveStatement)submodule), output);
+    }
+
+    private static void writeReaderToOutput(final XMLEventReader reader, final OutputStream output)
+            throws XMLStreamException {
+        try {
+            final Transformer transformer = TRANSFORMER_FACTORY.newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
+            transformer.transform(new StAXSource(reader), new StreamResult(output));
+        } catch (TransformerException e) {
+            throw new XMLStreamException("Failed to stream XML events", e);
+        }
+    }
+}
diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinXMLEventReader.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinXMLEventReader.java
new file mode 100644 (file)
index 0000000..75ecf97
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.export;
+
+import static com.google.common.collect.Iterators.singletonIterator;
+import static com.google.common.collect.Iterators.transform;
+import static java.util.Collections.emptyIterator;
+import static java.util.Objects.requireNonNull;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.Queue;
+import javax.xml.XMLConstants;
+import javax.xml.stream.XMLEventFactory;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.Characters;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+final class YinXMLEventReader implements XMLEventReader {
+    private static final class OpenElement {
+        final Iterator<? extends DeclaredStatement<?>> children;
+        final QName name;
+
+        OpenElement(final Iterator<? extends DeclaredStatement<?>> children) {
+            this.children = requireNonNull(children);
+            this.name = null;
+        }
+
+        OpenElement(final QName name, final Iterator<? extends DeclaredStatement<?>> children) {
+            this.children = requireNonNull(children);
+            this.name = requireNonNull(name);
+        }
+    }
+
+    private final Deque<OpenElement> stack = new ArrayDeque<>(8);
+    private final Queue<XMLEvent> events = new ArrayDeque<>();
+    private final ModuleNamespaceContext namespaceContext;
+    private final XMLEventFactory eventFactory;
+
+    YinXMLEventReader(final XMLEventFactory eventFactory, final ModuleNamespaceContext namespaceContext,
+            final DeclaredStatement<?> root) {
+        this.eventFactory = requireNonNull(eventFactory);
+        this.namespaceContext = requireNonNull(namespaceContext);
+
+        events.add(eventFactory.createStartDocument(StandardCharsets.UTF_8.name()));
+
+        final StatementDefinition def = root.statementDefinition();
+        final QName name = def.getStatementName();
+        final ArgumentDefinition arg = def.getArgumentDefinition().get();
+
+        events.add(eventFactory.createStartElement(XMLConstants.DEFAULT_NS_PREFIX, name.getNamespace().toString(),
+            name.getLocalName(), singletonIterator(attribute(arg.getArgumentName(), root.rawArgument())),
+            transform(namespaceContext.prefixesAndNamespaces().entrySet().iterator(),
+                e -> eventFactory.createNamespace(e.getKey(), e.getValue())),
+            namespaceContext));
+
+        stack.push(new OpenElement(name, root.declaredSubstatements().iterator()));
+    }
+
+    @Override
+    public XMLEvent next() {
+        XMLEvent event = events.poll();
+        if (event != null) {
+            return event;
+        }
+
+        nextStatement();
+        event = events.poll();
+        if (event == null) {
+            throw new NoSuchElementException("All events have been processed");
+        }
+        return event;
+    }
+
+    @Override
+    public XMLEvent nextEvent() {
+        return next();
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (events.isEmpty()) {
+            nextStatement();
+            return events.isEmpty();
+        }
+        return true;
+    }
+
+    @Override
+    public XMLEvent peek() throws XMLStreamException {
+        if (events.isEmpty()) {
+            nextStatement();
+        }
+
+        return events.peek();
+    }
+
+    @Override
+    public String getElementText() throws XMLStreamException {
+        XMLEvent current = peek();
+        if (current == null) {
+            throw new XMLStreamException("End of event stream");
+        }
+        if (!(current instanceof StartElement)) {
+            throw new XMLStreamException("Current event is " + current);
+        }
+
+        current = next();
+        if (!(current instanceof Characters)) {
+            throw new XMLStreamException("Encountered non-text event " + current);
+        }
+        final String ret = ((Characters)current).getData();
+
+        current = next();
+        if (!(current instanceof EndElement)) {
+            throw new XMLStreamException("Encountered unexpected event " + current);
+        }
+        return ret;
+    }
+
+    @Override
+    public XMLEvent nextTag() throws XMLStreamException {
+        final XMLEvent next = next();
+        if (next instanceof Characters) {
+            throw new XMLStreamException("Significant characters encountered: " + next);
+        }
+        return next;
+    }
+
+    @Override
+    public Object getProperty(final String name) {
+        throw new IllegalArgumentException("Property " + name + " not supported");
+    }
+
+    @Override
+    public void close() {
+        events.clear();
+        stack.clear();
+    }
+
+    private Attribute attribute(final QName qname, final String value) {
+        final Entry<String, String> ns = namespaceContext.prefixAndNamespaceFor(qname.getModule());
+        return eventFactory.createAttribute(ns.getKey(), ns.getValue(), qname.getLocalName(), value);
+    }
+
+    private StartElement startElement(final QName qname) {
+        final Entry<String, String> ns = namespaceContext.prefixAndNamespaceFor(qname.getModule());
+        return eventFactory.createStartElement(ns.getKey(), ns.getValue(), qname.getLocalName(), emptyIterator(),
+            emptyIterator(), namespaceContext);
+    }
+
+    private EndElement endElement(final QName qname) {
+        final Entry<String, String> ns = namespaceContext.prefixAndNamespaceFor(qname.getModule());
+        return eventFactory.createEndElement(ns.getKey(), ns.getValue(), qname.getLocalName());
+    }
+
+    private void nextStatement() {
+        OpenElement current = stack.peek();
+        if (current == null) {
+            return;
+        }
+
+        do {
+            while (current.children.hasNext()) {
+                // We have to mind child statement source and not emit empty implicit children
+                final DeclaredStatement<?> child = current.children.next();
+                switch (child.getStatementSource()) {
+                    case CONTEXT:
+                        final Iterator<? extends DeclaredStatement<?>> it = child.declaredSubstatements().iterator();
+                        if (it.hasNext()) {
+                            current = new OpenElement(it);
+                        }
+                        break;
+                    case DECLARATION:
+                        addStatement(child);
+                        return;
+                    default:
+                        throw new IllegalStateException("Unhandled statement source " + child.getStatementSource());
+                }
+            }
+
+            if (current.name != null) {
+                events.add(endElement(current.name));
+            }
+            stack.pop();
+            if (stack.isEmpty()) {
+                events.add(eventFactory.createEndDocument());
+            }
+        } while (events.isEmpty());
+    }
+
+    private void addStatement(final DeclaredStatement<?> statement) {
+        final StatementDefinition def = statement.statementDefinition();
+        final QName name = def.getStatementName();
+        final Optional<ArgumentDefinition> optArgDef = def.getArgumentDefinition();
+        if (optArgDef.isPresent()) {
+            final ArgumentDefinition argDef = optArgDef.get();
+            final QName argName = argDef.getArgumentName();
+            if (argDef.isYinElement()) {
+                events.addAll(Arrays.asList(startElement(name), startElement(argName),
+                    eventFactory.createCharacters(statement.rawArgument()), endElement(argName)));
+            } else {
+                final Entry<String, String> ns = namespaceContext.prefixAndNamespaceFor(name.getModule());
+                events.add(eventFactory.createStartElement(ns.getKey(), ns.getValue(), name.getLocalName(),
+                    singletonIterator(attribute(argName, statement.rawArgument())), emptyIterator(), namespaceContext));
+            }
+        } else {
+            // No attributes: just emit a start
+            events.add(startElement(name));
+        }
+
+        stack.push(new OpenElement(name, statement.declaredSubstatements().iterator()));
+    }
+}
diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinXMLEventReaderFactory.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinXMLEventReaderFactory.java
new file mode 100644 (file)
index 0000000..7485dd7
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.export;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLEventFactory;
+import javax.xml.stream.XMLEventReader;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
+
+/**
+ * Factory for creating {@link XMLEventReader} instances reporting events equivalent to reading a YIN document defining
+ * a specified {@link ModuleEffectiveStatement}. This class is thread-safe.
+ */
+@Beta
+public final class YinXMLEventReaderFactory {
+    private static final Location DUMMY_LOCATION = new Location() {
+
+        @Override
+        public int getLineNumber() {
+            return -1;
+        }
+
+        @Override
+        public int getColumnNumber() {
+            return -1;
+        }
+
+        @Override
+        public int getCharacterOffset() {
+            return -1;
+        }
+
+        @Override
+        public String getPublicId() {
+            return null;
+        }
+
+        @Override
+        public String getSystemId() {
+            return null;
+        }
+    };
+
+    private static final YinXMLEventReaderFactory DEFAULT;
+
+    static {
+        final XMLEventFactory eventFactory = XMLEventFactory.newFactory();
+        eventFactory.setLocation(DUMMY_LOCATION);
+        DEFAULT = new YinXMLEventReaderFactory(eventFactory);
+    }
+
+    private final XMLEventFactory eventFactory;
+
+    private YinXMLEventReaderFactory(final XMLEventFactory eventFactory) {
+        this.eventFactory = requireNonNull(eventFactory);
+    }
+
+    /**
+     * Get the system-wide default instance, backed by system-wide default XMLEventFactory.
+     *
+     * @return Default instance.
+     */
+    public static YinXMLEventReaderFactory defaultInstance() {
+        return DEFAULT;
+    }
+
+    public static YinXMLEventReaderFactory ofEventFactory(final XMLEventFactory factory) {
+        return new YinXMLEventReaderFactory(factory);
+    }
+
+    /**
+     * Create a new XMLEventReader iterating of the YIN document equivalent of an effective module.
+     *
+     * @param module Effective module
+     * @return A new XMLEventReader.
+     * @throws NullPointerException if module is null
+     * @throws IllegalArgumentException if the specified module does not expose declared model
+     */
+    public XMLEventReader createXMLEventReader(final ModuleEffectiveStatement module) {
+        final ModuleStatement declared = module.getDeclared();
+        checkArgument(declared != null, "Module %s does not expose declared model", module);
+
+        return new YinXMLEventReader(eventFactory, new ModuleNamespaceContext(module), declared);
+    }
+
+    /**
+     * Create a new XMLEventReader iterating of the YIN document equivalent of an effective submodule.
+     *
+     * @param module Effective module
+     * @param submodule Effective submodule
+     * @return A new XMLEventReader.
+     * @throws NullPointerException if any argument is null
+     * @throws IllegalArgumentException if the specified submodule does not expose declared model
+     */
+    public XMLEventReader createXMLEventReader(final ModuleEffectiveStatement module,
+            final SubmoduleEffectiveStatement submodule) {
+        final SubmoduleStatement declared = submodule.getDeclared();
+        checkArgument(declared != null, "Submodule %s does not expose declared model", submodule);
+        return new YinXMLEventReader(eventFactory, new ModuleNamespaceContext(module), declared);
+    }
+}
diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/AbstractYinExportTest.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/AbstractYinExportTest.java
new file mode 100644 (file)
index 0000000..7cc317c
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o..  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.export;
+
+import static org.junit.Assert.assertNotEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import javax.xml.stream.XMLStreamException;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier;
+import org.custommonkey.xmlunit.XMLAssert;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+abstract class AbstractYinExportTest {
+    final void exportYinModules(final String yangDir, final String yinDir) throws IOException, SAXException,
+            XMLStreamException {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResourceDirectory(yangDir);
+        final Collection<Module> modules = schemaContext.getModules();
+        assertNotEquals(0, modules.size());
+
+        for (Module module : modules) {
+            readAndValidateModule(schemaContext, module, yinDir);
+
+            for (Module submodule : module.getSubmodules()) {
+                readAndValidateSubmodule(schemaContext, module, submodule, yinDir);
+            }
+        }
+    }
+
+    void validateOutput(final String yinDir, final String fileName, final String fileBody) throws IOException,
+            SAXException {
+        assertNotEquals(0, fileBody.length());
+        if (yinDir != null) {
+            final Document doc = YinExportTestUtils.loadDocument(yinDir + "/" + fileName);
+            assertXMLEquals(fileName, doc, fileBody);
+        }
+    }
+
+    private void readAndValidateModule(final SchemaContext schemaContext, final Module module, final String yinDir)
+            throws XMLStreamException, IOException, SAXException {
+        final String fileName = YinExportUtils.wellFormedYinName(module.getName(), module.getRevision());
+        validateOutput(yinDir, fileName, export(module));
+    }
+
+    private void readAndValidateSubmodule(final SchemaContext schemaContext, final Module module,
+            final Module submodule, final String yinDir) throws XMLStreamException, IOException, SAXException {
+        final String fileName = YinExportUtils.wellFormedYinName(submodule.getName(), submodule.getRevision());
+        validateOutput(yinDir, fileName, export(module, submodule));
+    }
+
+    private static String export(final Module module) throws XMLStreamException {
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        YinExportUtils.writeModuleAsYinText(module, bos);
+        return new String(bos.toByteArray(), StandardCharsets.UTF_8);
+    }
+
+    private static String export(final Module module, final Module submodule) throws XMLStreamException {
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        YinExportUtils.writeSubmoduleAsYinText(module, submodule, bos);
+        return new String(bos.toByteArray(), StandardCharsets.UTF_8);
+    }
+
+    private static void assertXMLEquals(final String fileName, final Document expectedXMLDoc, final String output)
+            throws SAXException, IOException {
+        final String expected = YinExportTestUtils.toString(expectedXMLDoc.getDocumentElement());
+
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setNormalize(true);
+        XMLUnit.setNormalizeWhitespace(true);
+
+        final Diff diff = new Diff(expected, output);
+        diff.overrideElementQualifier(new ElementNameAndAttributeQualifier());
+        XMLAssert.assertXMLEqual(fileName, diff, true);
+    }
+}
diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug2444Test.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug2444Test.java
new file mode 100644 (file)
index 0000000..834550f
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 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.export;
+
+import org.junit.Test;
+
+public class Bug2444Test extends AbstractYinExportTest {
+    @Test
+    public void test() throws Exception {
+        exportYinModules("/bugs/bug2444/yang", "/bugs/bug2444/yin");
+    }
+}
diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug4504Test.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug4504Test.java
new file mode 100644 (file)
index 0000000..1e42469
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.export;
+
+import java.io.IOException;
+import javax.xml.stream.XMLStreamException;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+public class Bug4504Test extends AbstractYinExportTest {
+    @Test
+    public void test() throws IOException, SAXException, XMLStreamException {
+        exportYinModules("/bugs/bug4504", null);
+    }
+}
diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug5531Test.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug5531Test.java
new file mode 100644 (file)
index 0000000..c232036
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016 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.export;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug5531Test {
+    @Test
+    public void test() throws Exception {
+        SchemaContext schema = YangParserTestUtils.parseYangResourceDirectory("/bugs/bug5531");
+
+        assertNotNull(schema);
+        assertNotNull(schema.getModules());
+        assertEquals(1, schema.getModules().size());
+
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream);
+
+        // write small module of size less than 8kB
+        for (Module module : schema.getModules()) {
+            YinExportUtils.writeModuleAsYinText(module, bufferedOutputStream);
+        }
+
+        String output = byteArrayOutputStream.toString();
+
+        // if all changes were flushed then following conditions are satisfied
+        assertNotEquals("Output should not be empty", 0, output.length());
+        assertTrue("Output should contains start of the module", output.contains("<module"));
+        assertTrue("Output should contains end of the module", output.contains("</module>"));
+    }
+}
diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug6856Test.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug6856Test.java
new file mode 100644 (file)
index 0000000..8241ec3
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017 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.export;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug6856Test {
+
+    @Test
+    public void testImplicitInputAndOutputInRpc() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(Bug6856Test.class,
+            "/bugs/bug6856/foo.yang");
+        assertNotNull(schemaContext);
+
+        final OutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream);
+
+        final Module fooModule = schemaContext.findModule("foo", Revision.of("2017-02-28")).get();
+        YinExportUtils.writeModuleAsYinText(fooModule, bufferedOutputStream);
+
+        final String output = byteArrayOutputStream.toString();
+        assertNotNull(output);
+        assertFalse(output.isEmpty());
+
+        assertFalse(output.contains("<input>"));
+        assertFalse(output.contains("<output>"));
+    }
+
+    @Test
+    public void testExplicitInputAndOutputInRpc() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResources(Bug6856Test.class,
+            "/bugs/bug6856/bar.yang");
+        assertNotNull(schemaContext);
+
+        final OutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream);
+
+        final Module barModule = schemaContext.findModule("bar", Revision.of("2017-02-28")).get();
+        YinExportUtils.writeModuleAsYinText(barModule, bufferedOutputStream);
+
+        final String output = byteArrayOutputStream.toString();
+        assertNotNull(output);
+        assertFalse(output.isEmpty());
+
+        assertTrue(output.contains("<input>"));
+        assertTrue(output.contains("<output>"));
+    }
+}
diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/SchemaContextEmitterTest.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/SchemaContextEmitterTest.java
new file mode 100644 (file)
index 0000000..f1c151d
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2016 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.export;
+
+import java.io.IOException;
+import javax.xml.stream.XMLStreamException;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+public class SchemaContextEmitterTest extends AbstractYinExportTest {
+
+    @Test
+    public void testSchemaContextEmitter() throws IOException, XMLStreamException, SAXException {
+        exportYinModules("/schema-context-emitter-test", "/schema-context-emitter-test");
+    }
+}
diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/SimpleModuleTest.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/SimpleModuleTest.java
new file mode 100644 (file)
index 0000000..4a3b352
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015 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.export;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
+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.parser.repo.SharedSchemaRepository;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
+
+public class SimpleModuleTest {
+    private SharedSchemaRepository schemaRegistry;
+    private EffectiveModelContextFactory schemaContextFactory;
+    private Set<SourceIdentifier> allTestSources;
+
+    @Before
+    public void init() {
+        schemaRegistry = new SharedSchemaRepository("test");
+        final TextToASTTransformer astTransformer = TextToASTTransformer.create(schemaRegistry, schemaRegistry);
+        schemaRegistry.registerSchemaSourceListener(astTransformer);
+
+        schemaContextFactory = schemaRegistry.createEffectiveModelContextFactory();
+        allTestSources = new HashSet<>();
+        final SchemaListenerRegistration reg = schemaRegistry.registerSchemaSourceListener(new SchemaSourceListener() {
+
+            @Override
+            public void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
+                // NOOP
+            }
+
+            @Override
+            public void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
+                for (final PotentialSchemaSource<?> source : sources) {
+                    allTestSources.add(source.getSourceIdentifier());
+                }
+            }
+
+            @Override
+            public void schemaSourceEncountered(final SchemaSourceRepresentation source) {
+                // NOOP
+            }
+        });
+        reg.close();
+    }
+
+    @Test
+    public void testGenerateAll() throws Exception {
+        testSetOfModules(allTestSources);
+    }
+
+    private void testSetOfModules(final Collection<SourceIdentifier> source) throws Exception {
+        final SchemaContext schemaContext = schemaContextFactory.createSchemaContext(source).get();
+        final File outDir = new File("target/collection");
+        outDir.mkdirs();
+        for (final Module module : schemaContext.getModules()) {
+            exportModule(module, outDir);
+        }
+    }
+
+    private static File exportModule(final Module module, final File outDir)
+            throws Exception {
+        final File outFile = new File(outDir, YinExportUtils.wellFormedYinName(module.getName(), module.getRevision()));
+        try (OutputStream output = new FileOutputStream(outFile)) {
+            YinExportUtils.writeModuleAsYinText(module, output);
+        }
+        return outFile;
+    }
+}
diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/YangTextSnippetTest.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/YangTextSnippetTest.java
new file mode 100644 (file)
index 0000000..f1740cb
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.export;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class YangTextSnippetTest {
+    @Test
+    public void testNotification() {
+        final SchemaContext schema = YangParserTestUtils.parseYangResource("/bugs/bug2444/yang/notification.yang");
+
+        for (Module module : schema.getModules()) {
+            assertTrue(module instanceof ModuleEffectiveStatement);
+            final ModuleEffectiveStatement stmt = (ModuleEffectiveStatement) module;
+
+            final String str = DeclaredStatementFormatter.defaultInstance().toYangTextSnippet(stmt, stmt.getDeclared())
+                    .toString();
+            assertNotNull(str);
+        }
+    }
+}
diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/YinExportTestUtils.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/YinExportTestUtils.java
new file mode 100644 (file)
index 0000000..126eb2b
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017 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.export;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.Optional;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+public final class YinExportTestUtils {
+    private YinExportTestUtils() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    public static Document loadDocument(final String prefix, final Module module) throws IOException, SAXException {
+        final Optional<Revision> rev = module.getRevision();
+        final String fileName = !rev.isPresent() ? module.getName() : module.getName() + '@' + rev.get().toString();
+        return loadDocument(prefix + '/' + fileName + YangConstants.RFC6020_YIN_FILE_EXTENSION);
+    }
+
+    public static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+        final InputStream resourceAsStream = SchemaContextEmitterTest.class.getResourceAsStream(xmlPath);
+        return requireNonNull(readXmlToDocument(resourceAsStream));
+    }
+
+    static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+        final Document doc = UntrustedXML.newDocumentBuilder().parse(xmlContent);
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    public static String toString(final Node xml) {
+        try {
+            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+            final StreamResult result = new StreamResult(new StringWriter());
+            final DOMSource source = new DOMSource(xml);
+            transformer.transform(source, result);
+
+            return result.getWriter().toString();
+        } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+            throw new RuntimeException("Unable to serialize xml element " + xml, e);
+        }
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/action.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/action.yang
new file mode 100644 (file)
index 0000000..ee7882e
--- /dev/null
@@ -0,0 +1,37 @@
+module action {
+    namespace action;
+    prefix ac;
+    yang-version 1.1;
+
+    feature my-feature-1;
+    feature my-feature-2;
+
+    container root {
+        action a {
+            if-feature "my-feature-1 or my-feature-2";
+            status deprecated;
+            description "decs";
+            reference "ref";
+            typedef my-type {
+                type string;
+            }
+            grouping my-grp {
+                container con-in-grp {
+                }
+            }
+            input {
+                container i {
+                }
+            }
+            output {
+                container o {
+                }
+            }
+            ac:my-ext "arg-val";
+        }
+    }
+    
+    extension my-ext {
+        argument my-arg;
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/anydata.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/anydata.yang
new file mode 100644 (file)
index 0000000..a08088f
--- /dev/null
@@ -0,0 +1,24 @@
+module anydata {
+    namespace anydata;
+    prefix ad;
+    yang-version 1.1;
+
+    feature my-feature-1;
+    feature my-feature-2;
+
+    anydata my-anydata {
+        when "1!=0";
+        must "1=1";
+        config true;
+        mandatory true;
+        if-feature "my-feature-1 or my-feature-2";
+        status deprecated;
+        description "decs";
+        reference "ref";
+        ad:my-ext "arg-val";
+    }
+
+    extension my-ext {
+        argument my-arg;
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/binary-spec.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/binary-spec.yang
new file mode 100644 (file)
index 0000000..3aba0a1
--- /dev/null
@@ -0,0 +1,15 @@
+module binary-spec {
+    namespace "binary-spec";
+    prefix bin;
+
+    leaf bin-leaf {
+        type binary {
+            length "1..99";
+        }
+    }
+
+    leaf bin-leaf-2 {
+        type binary {
+        }
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/default.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/default.yang
new file mode 100644 (file)
index 0000000..d8ef249
--- /dev/null
@@ -0,0 +1,18 @@
+module default {
+    namespace default;
+    prefix def;
+    yang-version 1.1;
+
+    leaf-list my-leaflist-1 {
+        type string;
+        default "def-val-1";
+        default "def-val-2";
+        default "def-val-3";
+        ordered-by user;
+    }
+
+    leaf-list my-leaflist-2 {
+        type string;
+        status "deprecated";
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/deviation-target.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/deviation-target.yang
new file mode 100644 (file)
index 0000000..a405a32
--- /dev/null
@@ -0,0 +1,76 @@
+module deviation-target {
+    namespace deviation-target;
+    prefix tgt;
+    yang-version 1.1;
+
+    revision 2017-01-20;
+
+    leaf-list my-leaf-list {
+        type int32;
+        default 50;
+    }
+
+    list my-list {
+        key key-leaf;
+
+        leaf key-leaf {
+            type string;
+        }
+
+        leaf my-leaf-a {
+            type string;
+        }
+
+        leaf my-leaf-b {
+            type string;
+        }
+
+        leaf my-leaf-c {
+            type string;
+        }
+
+        leaf my-leaf-d {
+            type string;
+        }
+    }
+
+    choice my-choice {
+        case c1 {
+            leaf c1-leaf {
+                type string;
+                default "heaven";
+            }
+        }
+
+        case c2 {
+            leaf c2-leaf {
+                type string;
+                default "hell";
+            }
+        }
+        container shortcase {
+        }
+    }
+
+    rpc my-rpc {
+        input {
+            leaf my-rpc-input-leaf {
+                type string;
+            }
+        }
+
+        output {
+            leaf my-rpc-output-leaf {
+                type string;
+            }
+        }
+    }
+
+    notification my-notification {
+
+    }
+
+    anyxml my-anyxml {
+
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/deviation.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/deviation.yang
new file mode 100644 (file)
index 0000000..077ac6b
--- /dev/null
@@ -0,0 +1,69 @@
+module deviation {
+    namespace "deviation";
+    prefix "dev";
+    yang-version 1.1;
+
+    import deviation-target {
+        prefix dev-tgt;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/dev-tgt:my-leaf-list" {
+        deviate add {
+            config false;
+            min-elements 5;
+            max-elements 10;
+            default 100;
+            default 200;
+            units "hours";
+        }
+    }
+
+    deviation "/dev-tgt:my-list" {
+        deviate add {
+            unique "my-leaf-a my-leaf-b";
+            unique "my-leaf-c my-leaf-d";
+        }
+    }
+
+    deviation "/dev-tgt:my-choice" {
+        deviate add {
+            default c2;
+        }
+    }
+
+    deviation "/dev-tgt:my-rpc/dev-tgt:input" {
+        deviate add {
+            must "day or night";
+            must "black and white";
+        }
+    }
+
+    deviation "/dev-tgt:my-rpc/dev-tgt:output" {
+        deviate add {
+            must "day or night";
+            must "black and white";
+        }
+    }
+
+    deviation "/dev-tgt:my-notification" {
+        deviate add {
+            must "day or night";
+            must "black and white";
+        }
+    }
+
+    deviation "/dev-tgt:my-anyxml" {
+        deviate add {
+            mandatory true;
+            dev:custom-property "arg";
+            dev:custom-property "another arg";
+        }
+    }
+
+    extension custom-property {
+        argument name;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/identities.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/identities.yang
new file mode 100644 (file)
index 0000000..d1b676b
--- /dev/null
@@ -0,0 +1,33 @@
+module identities {
+    namespace identities;
+    prefix iden;
+    yang-version 1.1;
+
+    identity id-a;
+    identity id-b;
+    identity id-c {
+        status "deprecated";
+    }
+
+    identity derived-id {
+        base id-a;
+        base id-b;
+        base id-c;
+    }
+
+    leaf idref-leaf {
+        type identityref {
+            base id-a;
+            base id-b;
+            base id-c;
+        }
+        mandatory true;
+    }
+
+    leaf idref-leaf-2 {
+        type identityref {
+            base derived-id;
+        }
+        config true;
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/import-yang1.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/import-yang1.yang
new file mode 100644 (file)
index 0000000..3e708e2
--- /dev/null
@@ -0,0 +1,9 @@
+module import-yang1 {
+    namespace "import-yang1";
+    prefix iy1;
+    yang-version 1;
+
+    import action { prefix ac; }
+    
+    revision 1970-01-01;
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/import.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/import.yang
new file mode 100644 (file)
index 0000000..f8fc24b
--- /dev/null
@@ -0,0 +1,7 @@
+module import {
+    namespace "import";
+    prefix i;
+    yang-version 1.1;
+
+    import action { description "txt"; reference "ref"; prefix ac; }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/include.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/include.yang
new file mode 100644 (file)
index 0000000..09f7425
--- /dev/null
@@ -0,0 +1,33 @@
+module include {
+    yang-version 1.1;
+    namespace include;
+    prefix inc;
+
+    import deviation-target { prefix dev-tgt; revision-date 2017-01-20; reference "import ref"; description "import desc"; }
+
+    include submodule {
+        reference "include ref";
+        description "include desc";
+    }
+
+    reference "module ref";
+    description "module desc";
+
+    revision 2017-06-26 {
+        description "2017-06-26 revision desc";
+        reference "2017-06-26 revision ref";
+    }
+
+    revision 2016-06-26 {
+        description "2016-06-26 revision desc";
+        reference "2016-06-26 revision ref";
+    }
+
+    revision 2015-06-26 {
+        description "2015-06-26 revision desc";
+        reference "2015-06-26 revision ref";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/leafref-yang1.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/leafref-yang1.yang
new file mode 100644 (file)
index 0000000..2e66c27
--- /dev/null
@@ -0,0 +1,18 @@
+module leafref-yang1 {
+    namespace leafref-yang1;
+    prefix lr;
+    yang-version 1;
+
+    leaf my-leafref-3 {
+        type leafref {
+            path "/target";
+        }
+        status current;
+        mandatory true;
+        config false;
+    }
+
+    leaf target {
+        type string;
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/leafref.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/leafref.yang
new file mode 100644 (file)
index 0000000..6ab20b1
--- /dev/null
@@ -0,0 +1,29 @@
+module leafref {
+    namespace leafref;
+    prefix lr;
+    yang-version 1.1;
+
+    leaf my-leafref {
+        type leafref {
+            path "/target";
+            require-instance false;
+        }
+    }
+
+    leaf my-leafref-2 {
+        type leafref {
+            path "/target";
+            require-instance true;
+        }
+    }
+
+    leaf my-leafref-3 {
+        type leafref {
+            path "/target";
+        }
+    }
+
+    leaf target {
+        type string;
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/modifier-yang1.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/modifier-yang1.yang
new file mode 100644 (file)
index 0000000..21f425c
--- /dev/null
@@ -0,0 +1,18 @@
+module modifier-yang1 {
+    namespace modifier-yang1;
+    prefix my1;
+    yang-version 1;
+
+    leaf my-leaf {
+        type string {
+            length 0..2 {
+                error-message "Error message length";
+                error-app-tag "length-out";
+            }
+            pattern ".*" {
+                error-message "Error message pattern";
+                error-app-tag "regex-err";
+            }
+        }
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/modifier.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/modifier.yang
new file mode 100644 (file)
index 0000000..b3002dd
--- /dev/null
@@ -0,0 +1,13 @@
+module modifier {
+    namespace modifier;
+    prefix m;
+    yang-version 1.1;
+
+    leaf my-leaf {
+        type string {
+            pattern ".*" {
+                modifier invert-match;
+            }
+        }
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/must-yang1.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/must-yang1.yang
new file mode 100644 (file)
index 0000000..015a342
--- /dev/null
@@ -0,0 +1,15 @@
+module must-yang1 {
+    namespace "must-yang1";
+    prefix muy1;
+    yang-version 1;
+
+    notification n {
+    }
+
+    rpc r {
+        input {
+        }
+        output {
+        }
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/must.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/must.yang
new file mode 100644 (file)
index 0000000..66cab84
--- /dev/null
@@ -0,0 +1,30 @@
+module must {
+    namespace "must";
+    prefix mu;
+    yang-version 1.1;
+
+    notification n {
+        must "1!=0";
+    }
+
+    rpc r {
+        input {
+            must "1!=0";
+        }
+        output {
+            must "1!=0";
+        }
+    }
+
+    container root {
+        presence "This is a presence container.";
+        action a {
+            input {
+                must "1!=0";
+            }
+            output {
+                must "1!=0";
+            }
+        }
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/notification.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/notification.yang
new file mode 100644 (file)
index 0000000..be8285a
--- /dev/null
@@ -0,0 +1,38 @@
+module notification {
+    namespace "notification";
+    prefix n;
+    yang-version 1.1;
+
+    notification n1 {
+        status deprecated;
+        container c {
+            config true;
+            status current;
+        }
+    }
+
+    container r {
+        notification n2 {
+        }
+    }
+
+    list l {
+        key "id";
+        leaf id {
+            type int16;
+        }
+        notification n3 {
+        }
+    }
+
+    augment "/r" {
+        uses grp;
+        notification n4 {
+        }
+    }
+
+    grouping grp {
+        notification n5 {
+        }
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/submodule.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/submodule.yang
new file mode 100644 (file)
index 0000000..1347d67
--- /dev/null
@@ -0,0 +1,8 @@
+submodule submodule {
+    belongs-to include {
+        prefix incl;
+    }
+
+    container sub-con {
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/unique.yang b/yang/yang-model-export/src/test/resources/bugs/bug2444/yang/unique.yang
new file mode 100644 (file)
index 0000000..34e41a6
--- /dev/null
@@ -0,0 +1,29 @@
+module unique {
+    namespace unique;
+    prefix u;
+
+    list l {
+        key id;
+        leaf id {
+            type string;
+        }
+        ordered-by user;
+        unique "c1/c2/my-leaf-2";
+        unique "my-container/my-leaf-3";
+        leaf my-leaf-1 {
+            type string;
+        }
+        container c1 {
+            container c2 {
+                leaf my-leaf-2 {
+                    type string;
+                }
+            }
+        }
+        container my-container {
+            leaf my-leaf-3 {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin-effective-emitter/notification.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin-effective-emitter/notification.yin
new file mode 100644 (file)
index 0000000..2d89662
--- /dev/null
@@ -0,0 +1,50 @@
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:n="notification"
+    name="notification">
+    <yang-version value="1.1"></yang-version>
+    <namespace uri="notification"></namespace>
+    <prefix value="n"></prefix>
+    <grouping name="grp">
+        <status value="current"></status>
+        <notification name="n5">
+            <status value="current"></status>
+        </notification>
+    </grouping>
+    <container name="r">
+        <presence value="false"></presence>
+        <config value="true"></config>
+        <status value="current"></status>
+        <notification name="n2">
+            <status value="current"></status>
+        </notification>
+    </container>
+    <list name="l">
+        <key value="id"></key>
+        <config value="true"></config>
+        <ordered-by value="system"></ordered-by>
+        <status value="current"></status>
+        <leaf name="id">
+            <type name="int16"></type>
+            <config value="true"></config>
+            <mandatory value="false"></mandatory>
+            <status value="current"></status>
+        </leaf>
+        <notification name="n3">
+            <status value="current"></status>
+        </notification>
+    </list>
+    <augment target-node="/r">
+        <status value="current"></status>
+        <uses name="grp"></uses>
+        <notification name="n4">
+            <status value="current"></status>
+        </notification>
+    </augment>
+    <notification name="n1">
+        <status value="deprecated"></status>
+        <container name="c">
+            <presence value="false"></presence>
+            <config value="true"></config>
+            <status value="current"></status>
+        </container>
+    </notification>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/action.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/action.yin
new file mode 100644 (file)
index 0000000..ca6ad40
--- /dev/null
@@ -0,0 +1,36 @@
+<module name="action" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:ac="action">
+    <namespace uri="action" />
+    <prefix value="ac" />
+    <yang-version value="1.1" />
+    <feature name="my-feature-1"/>
+    <feature name="my-feature-2"/>
+    <container name="root">
+        <action name="a">
+            <if-feature name="my-feature-1 or my-feature-2" />
+            <status value="deprecated" />
+            <description>
+                <text>decs</text>
+            </description>
+            <reference>
+                <text>ref</text>
+            </reference>
+            <typedef name="my-type">
+                <type name="string" />
+            </typedef>
+            <grouping name="my-grp">
+                <container name="con-in-grp" />
+            </grouping>
+            <input>
+                <container name="i" />
+            </input>
+            <output>
+                <container name="o" />
+            </output>
+            <ac:my-ext ac:my-arg="arg-val" />
+        </action>
+    </container>
+    <extension name="my-ext">
+        <argument name="my-arg" />
+    </extension>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/anydata.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/anydata.yin
new file mode 100644 (file)
index 0000000..5bd1497
--- /dev/null
@@ -0,0 +1,26 @@
+<module name="anydata" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:ad="anydata">
+    <namespace uri="anydata" />
+    <prefix value="ad" />
+    <yang-version value="1.1" />
+    <feature name="my-feature-1"/>
+    <feature name="my-feature-2"/>
+    <anydata name="my-anydata">
+        <when condition="1!=0" />
+        <must condition="1=1" />
+        <config value="true" />
+        <mandatory value="true" />
+        <if-feature name="my-feature-1 or my-feature-2" />
+        <status value="deprecated" />
+        <description>
+            <text>decs</text>
+        </description>
+        <reference>
+            <text>ref</text>
+        </reference>
+        <ad:my-ext ad:my-arg="arg-val" />
+    </anydata>
+    <extension name="my-ext">
+        <argument name="my-arg" />
+    </extension>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/binary-spec.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/binary-spec.yin
new file mode 100644 (file)
index 0000000..4495cbf
--- /dev/null
@@ -0,0 +1,13 @@
+<module name="binary-spec" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:bin="binary-spec">
+    <namespace uri="binary-spec" />
+    <prefix value="bin" />
+    <leaf name="bin-leaf">
+        <type name="binary">
+            <length value="1..99" />
+        </type>
+    </leaf>
+    <leaf name="bin-leaf-2">
+        <type name="binary" />
+    </leaf>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/default.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/default.yin
new file mode 100644 (file)
index 0000000..d2b439e
--- /dev/null
@@ -0,0 +1,17 @@
+<module name="default" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:def="default">
+    <namespace uri="default" />
+    <prefix value="def" />
+    <yang-version value="1.1" />
+    <leaf-list name="my-leaflist-1">
+        <type name="string" />
+        <default value="def-val-1" />
+        <default value="def-val-2" />
+        <default value="def-val-3" />
+        <ordered-by value="user" />
+    </leaf-list>
+    <leaf-list name="my-leaflist-2">
+        <type name="string" />
+        <status value="deprecated" />
+    </leaf-list>
+</module>
\ No newline at end of file
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/deviation-target@2017-01-20.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/deviation-target@2017-01-20.yin
new file mode 100644 (file)
index 0000000..e261b27
--- /dev/null
@@ -0,0 +1,58 @@
+<module name="deviation-target" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:tgt="deviation-target">
+    <namespace uri="deviation-target" />
+    <prefix value="tgt" />
+    <yang-version value="1.1" />
+    <revision date="2017-01-20" />
+    <leaf-list name="my-leaf-list">
+        <type name="int32" />
+        <default value="50" />
+    </leaf-list>
+    <list name="my-list">
+        <key value="key-leaf" />
+        <leaf name="key-leaf">
+            <type name="string" />
+        </leaf>
+        <leaf name="my-leaf-a">
+            <type name="string" />
+        </leaf>
+        <leaf name="my-leaf-b">
+            <type name="string" />
+        </leaf>
+        <leaf name="my-leaf-c">
+            <type name="string" />
+        </leaf>
+        <leaf name="my-leaf-d">
+            <type name="string" />
+        </leaf>
+    </list>
+    <choice name="my-choice">
+        <case name="c1">
+            <leaf name="c1-leaf">
+                <type name="string" />
+                <default value="heaven" />
+            </leaf>
+        </case>
+        <case name="c2">
+            <leaf name="c2-leaf">
+                <type name="string" />
+                <default value="hell" />
+            </leaf>
+        </case>
+        <container name="shortcase" />
+    </choice>
+    <rpc name="my-rpc">
+        <input>
+            <leaf name="my-rpc-input-leaf">
+                <type name="string" />
+            </leaf>
+        </input>
+        <output>
+            <leaf name="my-rpc-output-leaf">
+                <type name="string" />
+            </leaf>
+        </output>
+    </rpc>
+    <notification name="my-notification" />
+    <anyxml name="my-anyxml" />
+</module>
\ No newline at end of file
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/deviation@2017-01-20.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/deviation@2017-01-20.yin
new file mode 100644 (file)
index 0000000..698a656
--- /dev/null
@@ -0,0 +1,60 @@
+<module name="deviation" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:dev="deviation" xmlns:dev-tgt="deviation-target">
+    <namespace uri="deviation" />
+    <prefix value="dev" />
+    <yang-version value="1.1" />
+    <import module="deviation-target">
+        <prefix value="dev-tgt" />
+        <revision-date date="2017-01-20" />
+    </import>
+    <revision date="2017-01-20" />
+    <deviation target-node="/dev-tgt:my-leaf-list">
+        <deviate value="add">
+            <config value="false" />
+            <min-elements value="5" />
+            <max-elements value="10" />
+            <default value="100" />
+            <default value="200" />
+            <units name="hours" />
+        </deviate>
+    </deviation>
+    <deviation target-node="/dev-tgt:my-list">
+        <deviate value="add">
+            <unique tag="my-leaf-a my-leaf-b" />
+            <unique tag="my-leaf-c my-leaf-d" />
+        </deviate>
+    </deviation>
+    <deviation target-node="/dev-tgt:my-choice">
+        <deviate value="add">
+            <default value="c2" />
+        </deviate>
+    </deviation>
+    <deviation target-node="/dev-tgt:my-rpc/dev-tgt:input">
+        <deviate value="add">
+            <must condition="day or night" />
+            <must condition="black and white" />
+        </deviate>
+    </deviation>
+    <deviation target-node="/dev-tgt:my-rpc/dev-tgt:output">
+        <deviate value="add">
+            <must condition="day or night" />
+            <must condition="black and white" />
+        </deviate>
+    </deviation>
+    <deviation target-node="/dev-tgt:my-notification">
+        <deviate value="add">
+            <must condition="day or night" />
+            <must condition="black and white" />
+        </deviate>
+    </deviation>
+    <deviation target-node="/dev-tgt:my-anyxml">
+        <deviate value="add">
+            <mandatory value="true" />
+            <dev:custom-property dev:name="arg" />
+            <dev:custom-property dev:name="another arg" />
+        </deviate>
+    </deviation>
+    <extension name="custom-property">
+        <argument name="name" />
+    </extension>
+</module>
\ No newline at end of file
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/identities.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/identities.yin
new file mode 100644 (file)
index 0000000..6ccd525
--- /dev/null
@@ -0,0 +1,30 @@
+<module name="identities" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:iden="identities">
+    <namespace uri="identities" />
+    <prefix value="iden" />
+    <yang-version value="1.1" />
+    <identity name="id-a" />
+    <identity name="id-b" />
+    <identity name="id-c">
+        <status value="deprecated" />
+    </identity>
+    <identity name="derived-id">
+        <base name="id-a" />
+        <base name="id-b" />
+        <base name="id-c" />
+    </identity>
+    <leaf name="idref-leaf">
+        <type name="identityref">
+            <base name="id-a" />
+            <base name="id-b" />
+            <base name="id-c" />
+        </type>
+        <mandatory value="true" />
+    </leaf>
+    <leaf name="idref-leaf-2">
+        <type name="identityref">
+            <base name="derived-id" />
+        </type>
+        <config value="true" />
+    </leaf>
+</module>
\ No newline at end of file
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/import-yang1@1970-01-01.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/import-yang1@1970-01-01.yin
new file mode 100644 (file)
index 0000000..3406313
--- /dev/null
@@ -0,0 +1,10 @@
+<module name="import-yang1" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:iy1="import-yang1">
+    <namespace uri="import-yang1" />
+    <prefix value="iy1" />
+    <yang-version value="1" />
+    <import module="action">
+        <prefix value="ac" />
+    </import>
+    <revision date="1970-01-01" />
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/import.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/import.yin
new file mode 100644 (file)
index 0000000..1dc8085
--- /dev/null
@@ -0,0 +1,15 @@
+<module name="import" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:i="import">
+    <namespace uri="import" />
+    <prefix value="i" />
+    <yang-version value="1.1" />
+    <import module="action">
+        <description>
+            <text>txt</text>
+        </description>
+        <reference>
+            <text>ref</text>
+        </reference>
+        <prefix value="ac" />
+    </import>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/include@2017-06-26.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/include@2017-06-26.yin
new file mode 100644 (file)
index 0000000..d94492a
--- /dev/null
@@ -0,0 +1,55 @@
+<module name="include" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:inc="include" xmlns:dev-tgt="deviation-target">
+    <yang-version value="1.1" />
+    <namespace uri="include" />
+    <prefix value="inc" />
+    <import module="deviation-target">
+        <prefix value="dev-tgt" />
+        <revision-date date="2017-01-20" />
+        <reference>
+            <text>import ref</text>
+        </reference>
+        <description>
+            <text>import desc</text>
+        </description>
+    </import>
+    <include module="submodule">
+        <reference>
+            <text>include ref</text>
+        </reference>
+        <description>
+            <text>include desc</text>
+        </description>
+    </include>
+    <reference>
+        <text>module ref</text>
+    </reference>
+    <description>
+        <text>module desc</text>
+    </description>
+    <revision date="2017-06-26">
+        <description>
+            <text>2017-06-26 revision desc</text>
+        </description>
+        <reference>
+            <text>2017-06-26 revision ref</text>
+        </reference>
+    </revision>
+    <revision date="2016-06-26">
+        <description>
+            <text>2016-06-26 revision desc</text>
+        </description>
+        <reference>
+            <text>2016-06-26 revision ref</text>
+        </reference>
+    </revision>
+    <revision date="2015-06-26">
+        <description>
+            <text>2015-06-26 revision desc</text>
+        </description>
+        <reference>
+            <text>2015-06-26 revision ref</text>
+        </reference>
+    </revision>
+    <container name="root" />
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/leafref-yang1.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/leafref-yang1.yin
new file mode 100644 (file)
index 0000000..7c940f7
--- /dev/null
@@ -0,0 +1,17 @@
+<module name="leafref-yang1" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:lr="leafref-yang1">
+    <namespace uri="leafref-yang1" />
+    <prefix value="lr" />
+    <yang-version value="1" />
+    <leaf name="my-leafref-3">
+        <type name="leafref">
+            <path value="/target" />
+        </type>
+        <status value="current" />
+        <mandatory value="true" />
+        <config value="false" />
+    </leaf>
+    <leaf name="target">
+        <type name="string" />
+    </leaf>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/leafref.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/leafref.yin
new file mode 100644 (file)
index 0000000..d006ac3
--- /dev/null
@@ -0,0 +1,26 @@
+<module name="leafref" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:lr="leafref">
+    <namespace uri="leafref" />
+    <prefix value="lr" />
+    <yang-version value="1.1" />
+    <leaf name="my-leafref">
+        <type name="leafref">
+            <path value="/target" />
+            <require-instance value="false" />
+        </type>
+    </leaf>
+    <leaf name="my-leafref-2">
+        <type name="leafref">
+            <path value="/target" />
+            <require-instance value="true" />
+        </type>
+    </leaf>
+    <leaf name="my-leafref-3">
+        <type name="leafref">
+            <path value="/target" />
+        </type>
+    </leaf>
+    <leaf name="target">
+        <type name="string" />
+    </leaf>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/modifier-yang1.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/modifier-yang1.yin
new file mode 100644 (file)
index 0000000..601ba77
--- /dev/null
@@ -0,0 +1,22 @@
+<module name="modifier-yang1" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:my1="modifier-yang1">
+    <namespace uri="modifier-yang1" />
+    <prefix value="my1" />
+    <yang-version value="1" />
+    <leaf name="my-leaf">
+        <type name="string">
+            <length value="0..2">
+                <error-message>
+                    <value>Error message length</value>
+                </error-message>
+                <error-app-tag value="length-out" />
+            </length>
+            <pattern value=".*">
+                <error-message>
+                    <value>Error message pattern</value>
+                </error-message>
+                <error-app-tag value="regex-err" />
+            </pattern>
+        </type>
+    </leaf>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/modifier.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/modifier.yin
new file mode 100644 (file)
index 0000000..fcf5787
--- /dev/null
@@ -0,0 +1,13 @@
+<module name="modifier" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:m="modifier">
+    <namespace uri="modifier" />
+    <prefix value="m" />
+    <yang-version value="1.1" />
+    <leaf name="my-leaf">
+        <type name="string">
+            <pattern value=".*">
+                <modifier value="invert-match" />
+            </pattern>
+        </type>
+    </leaf>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/must-yang1.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/must-yang1.yin
new file mode 100644 (file)
index 0000000..c4934d0
--- /dev/null
@@ -0,0 +1,11 @@
+<module name="must-yang1" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:muy1="must-yang1">
+    <namespace uri="must-yang1" />
+    <prefix value="muy1" />
+    <yang-version value="1" />
+    <notification name="n" />
+    <rpc name="r">
+        <input />
+        <output />
+    </rpc>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/must.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/must.yin
new file mode 100644 (file)
index 0000000..2008e01
--- /dev/null
@@ -0,0 +1,28 @@
+<module name="must" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:mu="must">
+    <namespace uri="must" />
+    <prefix value="mu" />
+    <yang-version value="1.1" />
+    <notification name="n">
+        <must condition="1!=0" />
+    </notification>
+    <rpc name="r">
+        <input>
+            <must condition="1!=0" />
+        </input>
+        <output>
+            <must condition="1!=0" />
+        </output>
+    </rpc>
+    <container name="root">
+        <presence value="This is a presence container." />
+        <action name="a">
+            <input>
+                <must condition="1!=0" />
+            </input>
+            <output>
+                <must condition="1!=0" />
+            </output>
+        </action>
+    </container>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/notification.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/notification.yin
new file mode 100644 (file)
index 0000000..5d51bf1
--- /dev/null
@@ -0,0 +1,30 @@
+<module name="notification" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:n="notification">
+    <namespace uri="notification" />
+    <prefix value="n" />
+    <yang-version value="1.1" />
+    <notification name="n1">
+        <status value="deprecated" />
+        <container name="c">
+            <config value="true" />
+            <status value="current" />
+        </container>
+    </notification>
+    <container name="r">
+        <notification name="n2" />
+    </container>
+    <list name="l">
+        <key value="id" />
+        <leaf name="id">
+            <type name="int16" />
+        </leaf>
+        <notification name="n3" />
+    </list>
+    <augment target-node="/r">
+        <uses name="grp" />
+        <notification name="n4" />
+    </augment>
+    <grouping name="grp">
+        <notification name="n5" />
+    </grouping>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/submodule.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/submodule.yin
new file mode 100644 (file)
index 0000000..7be82fb
--- /dev/null
@@ -0,0 +1,7 @@
+<submodule name="submodule" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:incl="include">
+    <belongs-to module="include">
+        <prefix value="incl" />
+    </belongs-to>
+    <container name="sub-con" />
+</submodule>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/unique.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin/unique.yin
new file mode 100644 (file)
index 0000000..a878d26
--- /dev/null
@@ -0,0 +1,29 @@
+<module name="unique" xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:u="unique">
+    <namespace uri="unique" />
+    <prefix value="u" />
+    <list name="l">
+        <key value="id" />
+        <leaf name="id">
+            <type name="string" />
+        </leaf>
+        <ordered-by value="user" />
+        <unique tag="c1/c2/my-leaf-2" />
+        <unique tag="my-container/my-leaf-3" />
+        <leaf name="my-leaf-1">
+            <type name="string" />
+        </leaf>
+        <container name="c1">
+            <container name="c2">
+                <leaf name="my-leaf-2">
+                    <type name="string" />
+                </leaf>
+            </container>
+        </container>
+        <container name="my-container">
+            <leaf name="my-leaf-3">
+                <type name="string" />
+            </leaf>
+        </container>
+    </list>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug4504/bar.yang b/yang/yang-model-export/src/test/resources/bugs/bug4504/bar.yang
new file mode 100644 (file)
index 0000000..0000813
--- /dev/null
@@ -0,0 +1,21 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    import foo { prefix foo; revision-date 2015-01-01; }
+
+    revision 2015-02-02 {
+        description "test";
+    }
+
+    extension my-ext {
+        argument name;
+    }
+
+    augment "/foo:target" {
+        bar:my-ext "arg";
+        leaf my-leaf {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug4504/foo.yang b/yang/yang-model-export/src/test/resources/bugs/bug4504/foo.yang
new file mode 100644 (file)
index 0000000..e8ad06b
--- /dev/null
@@ -0,0 +1,11 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    revision 2015-01-01 {
+        description "test";
+    }
+
+    container target {
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug5531/foo.yang b/yang/yang-model-export/src/test/resources/bugs/bug5531/foo.yang
new file mode 100644 (file)
index 0000000..2c6c2e3
--- /dev/null
@@ -0,0 +1,8 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    revision 2015-01-01 {
+        description "test";
+    }
+}
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug6856/bar.yang b/yang/yang-model-export/src/test/resources/bugs/bug6856/bar.yang
new file mode 100644 (file)
index 0000000..a14733d
--- /dev/null
@@ -0,0 +1,19 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-02-28;
+
+    rpc bar-rpc {
+        input {
+            leaf input-leaf {
+                type string;
+            }
+        }
+        output {
+            leaf output-leaf {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-model-export/src/test/resources/bugs/bug6856/foo.yang b/yang/yang-model-export/src/test/resources/bugs/bug6856/foo.yang
new file mode 100644 (file)
index 0000000..efe2f1c
--- /dev/null
@@ -0,0 +1,8 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    revision 2017-02-28;
+
+    rpc foo-rpc {}
+}
\ No newline at end of file
diff --git a/yang/yang-model-export/src/test/resources/schema-context-emitter-test/foo@2016-08-05.yang b/yang/yang-model-export/src/test/resources/schema-context-emitter-test/foo@2016-08-05.yang
new file mode 100644 (file)
index 0000000..f96f696
--- /dev/null
@@ -0,0 +1,206 @@
+module foo {
+    namespace foo-namespace;
+    prefix foo-prefix;
+
+    revision 2016-08-05;
+
+    feature test-feature {
+        status current;
+        description "test-feature description";
+        reference "test-feature reference";
+    }
+
+    typedef test-uint32-typedef {
+        type uint32 {
+            range "50..100";
+        }
+        units seconds;
+    }
+
+    typedef test-int32-typedef {
+        type int32 {
+            range "50..100";
+        }
+    }
+
+    typedef test-leafref-typedef {
+        type leafref {
+            path "../leafref-target-leaf";
+        }
+    }
+
+    typedef test-iid-typedef {
+        type instance-identifier {
+            require-instance false;
+        }
+    }
+
+    identity test-base-identity;
+
+    identity test-identity {
+        base test-base-identity;
+        status current;
+        description "test-identity description";
+        reference "test-identity reference";
+    }
+
+    extension test-extension;
+
+    rpc test-rpc {
+        input {
+            leaf input-leaf {
+                type string;
+            }
+        }
+        output {
+            leaf output-leaf {
+                type string;
+            }
+        }
+
+        grouping rpc-grouping {
+            leaf rpc-grouping-leaf {
+                type string;
+            }
+        }
+    }
+
+    notification test-notification {
+        uses test-grouping-2 {
+            refine test-anyxml-2 {
+                config false;
+            }
+            refine test-choice-2 {
+                config false;
+            }
+        }
+    }
+
+    anyxml test-anyxml {
+        when "foo != 'bar'";
+        must "bar != 'foo'";
+
+        foo-prefix:test-extension;
+    }
+
+    leaf leafref-target-leaf {
+        type string;
+    }
+
+    container test-container-1 {
+        must "bar != 'foo'";
+    }
+
+    container test-container-3 {
+        choice test-choice {
+            case a {
+                leaf case-a-leaf {
+                    type int32;
+                }
+            }
+            case b {
+                leaf case-b-leaf {
+                    type decimal64 {
+                        fraction-digits 3;
+                    }
+                }
+            }
+        }
+        leaf bits-leaf {
+            type bits {
+                bit one {
+                    position 1;
+                }
+                bit two {
+                    position 2;
+                }
+            }
+        }
+        leaf identityref-leaf {
+            type identityref {
+                base test-base-identity;
+            }
+        }
+    }
+
+    augment "/test-container-3/test-choice" {
+        case c {
+            leaf case-c-leaf {
+                type string;
+            }
+        }
+    }
+
+    augment "/test-container-1" {
+        uses test-grouping-1 {
+            refine test-leaf-1 {
+                default "def-val";
+            }
+            refine test-leaf-list {
+                config false;
+            }
+            refine test-list {
+                min-elements 5;
+            }
+            refine test-container-2 {
+                config false;
+            }
+            augment test-container-2 {
+                leaf test-leaf-2 {
+                    type string;
+                }
+            }
+        }
+    }
+
+    grouping test-grouping-1 {
+        leaf test-leaf-1 {
+            type string;
+            default "def-val";
+        }
+        leaf-list test-leaf-list {
+            type string;
+            config false;
+            ordered-by user;
+        }
+        list test-list {
+            key "key-leaf-1 key-leaf-2";
+            min-elements 5;
+
+            leaf key-leaf-1 {
+                type string;
+            }
+
+            leaf key-leaf-2 {
+                type string;
+            }
+        }
+        container test-container-2 {
+            config false;
+        }
+    }
+
+    grouping test-grouping-2 {
+        anyxml test-anyxml-2 {
+            config false;
+        }
+        choice test-choice-2 {
+            config false;
+
+            case first {
+                leaf first-case-leaf {
+                    type string {
+                        length "10..10 | 15";
+                    }
+                }
+            }
+            case second {
+                leaf second-case-leaf {
+                    type int32 {
+                        range "10..10 | 15";
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-model-export/src/test/resources/schema-context-emitter-test/foo@2016-08-05.yin b/yang/yang-model-export/src/test/resources/schema-context-emitter-test/foo@2016-08-05.yin
new file mode 100644 (file)
index 0000000..6464ee4
--- /dev/null
@@ -0,0 +1,197 @@
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+    xmlns:foo-prefix="foo-namespace" name="foo">
+    <namespace uri="foo-namespace"></namespace>
+    <prefix value="foo-prefix"></prefix>
+    <revision date="2016-08-05"></revision>
+    <extension name="test-extension">
+    </extension>
+    <feature name="test-feature">
+        <status value="current"></status>
+        <description>
+            <text>test-feature description</text>
+        </description>
+        <reference>
+            <text>test-feature reference</text>
+        </reference>
+    </feature>
+    <identity name="test-identity">
+        <base name="test-base-identity"></base>
+        <status value="current"></status>
+        <description>
+            <text>test-identity description</text>
+        </description>
+        <reference>
+            <text>test-identity reference</text>
+        </reference>
+    </identity>
+    <identity name="test-base-identity">
+    </identity>
+    <typedef name="test-uint32-typedef">
+        <type name="uint32">
+            <range value="50..100">
+            </range>
+        </type>
+        <units name="seconds"></units>
+    </typedef>
+    <typedef name="test-int32-typedef">
+        <type name="int32">
+            <range value="50..100">
+            </range>
+        </type>
+    </typedef>
+    <typedef name="test-leafref-typedef">
+        <type name="leafref">
+            <path value="../leafref-target-leaf"></path>
+        </type>
+    </typedef>
+    <typedef name="test-iid-typedef">
+        <type name="instance-identifier">
+            <require-instance value="false"></require-instance>
+        </type>
+    </typedef>
+    <grouping name="test-grouping-1">
+        <leaf name="test-leaf-1">
+            <type name="string"></type>
+            <default value="def-val"></default>
+        </leaf>
+        <leaf-list name="test-leaf-list">
+            <type name="string"></type>
+            <config value="false"></config>
+            <ordered-by value="user"></ordered-by>
+        </leaf-list>
+        <list name="test-list">
+            <key value="key-leaf-1 key-leaf-2"></key>
+            <min-elements value="5"></min-elements>
+            <leaf name="key-leaf-1">
+                <type name="string"></type>
+            </leaf>
+            <leaf name="key-leaf-2">
+                <type name="string"></type>
+            </leaf>
+        </list>
+        <container name="test-container-2">
+            <config value="false"></config>
+        </container>
+    </grouping>
+    <grouping name="test-grouping-2">
+        <anyxml name="test-anyxml-2">
+            <config value="false"></config>
+        </anyxml>
+        <choice name="test-choice-2">
+            <config value="false"></config>
+            <case name="first">
+                <leaf name="first-case-leaf">
+                    <type name="string">
+                        <length value="10..10 | 15">
+                        </length>
+                    </type>
+                </leaf>
+            </case>
+            <case name="second">
+                <leaf name="second-case-leaf">
+                    <type name="int32">
+                        <range value="10..10 | 15">
+                        </range>
+                    </type>
+                </leaf>
+            </case>
+        </choice>
+    </grouping>
+    <anyxml name="test-anyxml">
+        <when condition="foo != 'bar'"></when>
+        <must condition="bar != 'foo'"></must>
+        <foo-prefix:test-extension></foo-prefix:test-extension>
+    </anyxml>
+    <leaf name="leafref-target-leaf">
+        <type name="string"></type>
+    </leaf>
+    <container name="test-container-1">
+        <must condition="bar != 'foo'"></must>
+    </container>
+    <container name="test-container-3">
+        <choice name="test-choice">
+            <case name="a">
+                <leaf name="case-a-leaf">
+                    <type name="int32"></type>
+                </leaf>
+            </case>
+            <case name="b">
+                <leaf name="case-b-leaf">
+                    <type name="decimal64">
+                        <fraction-digits value="3"></fraction-digits>
+                    </type>
+                </leaf>
+            </case>
+        </choice>
+        <leaf name="bits-leaf">
+            <type name="bits">
+                <bit name="one">
+                    <position value="1"></position>
+                </bit>
+                <bit name="two">
+                    <position value="2"></position>
+                </bit>
+            </type>
+        </leaf>
+        <leaf name="identityref-leaf">
+            <type name="identityref">
+                <base name="test-base-identity"></base>
+            </type>
+        </leaf>
+    </container>
+    <augment target-node="/test-container-3/test-choice">
+        <case name="c">
+            <leaf name="case-c-leaf">
+                <type name="string"></type>
+            </leaf>
+        </case>
+    </augment>
+    <augment target-node="/test-container-1">
+        <uses name="test-grouping-1">
+            <refine target-node="test-list">
+                <min-elements value="5"></min-elements>
+            </refine>
+            <refine target-node="test-leaf-list">
+                <config value="false"></config>
+            </refine>
+            <refine target-node="test-leaf-1">
+                <default value="def-val"></default>
+            </refine>
+            <refine target-node="test-container-2">
+                <config value="false"></config>
+            </refine>
+            <augment target-node="test-container-2">
+                <leaf name="test-leaf-2">
+                    <type name="string"></type>
+                </leaf>
+            </augment>
+        </uses>
+    </augment>
+    <rpc name="test-rpc">
+        <grouping name="rpc-grouping">
+            <leaf name="rpc-grouping-leaf">
+                <type name="string"></type>
+            </leaf>
+        </grouping>
+        <input>
+            <leaf name="input-leaf">
+                <type name="string"></type>
+            </leaf>
+        </input>
+        <output>
+            <leaf name="output-leaf">
+                <type name="string"></type>
+            </leaf>
+        </output>
+    </rpc>
+    <notification name="test-notification">
+        <uses name="test-grouping-2">
+            <refine target-node="test-choice-2">
+                <config value="false"></config>
+            </refine>
+            <refine target-node="test-anyxml-2">
+                <config value="false"></config>
+            </refine>
+        </uses>
+    </notification>
+</module>
diff --git a/yang/yang-model-export/src/test/resources/yang/ietf-inet-types@2010-09-24.yang b/yang/yang-model-export/src/test/resources/yang/ietf-inet-types@2010-09-24.yang
new file mode 100644 (file)
index 0000000..edd285d
--- /dev/null
@@ -0,0 +1,427 @@
+module ietf-inet-types {
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
+  prefix "inet";
+
+  organization
+   "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+   "WG Web:   <http://tools.ietf.org/wg/netmod/>
+    WG List:  <mailto:netmod@ietf.org>
+
+    WG Chair: David Partain
+              <mailto:david.partain@ericsson.com>
+
+    WG Chair: David Kessens
+              <mailto:david.kessens@nsn.com>
+
+    Editor:   Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>";
+
+  description
+   "This module contains a collection of generally useful derived
+    YANG data types for Internet addresses and related things.
+
+    Copyright (c) 2010 IETF Trust and the persons identified as
+    authors of the code.  All rights reserved.
+
+
+
+    Redistribution and use in source and binary forms, with or without
+    modification, is permitted pursuant to, and subject to the license
+    terms contained in, the Simplified BSD License set forth in Section
+    4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+    (http://trustee.ietf.org/license-info).
+
+    This version of this YANG module is part of RFC 6021; see
+    the RFC itself for full legal notices.";
+
+  revision 2010-09-24 {
+    description
+     "Initial revision.";
+    reference
+     "RFC 6021: Common YANG Data Types";
+  }
+
+  /*** collection of protocol field related types ***/
+
+  typedef ip-version {
+    type enumeration {
+      enum unknown {
+        value "0";
+        description
+         "An unknown or unspecified version of the Internet protocol.";
+      }
+      enum ipv4 {
+        value "1";
+        description
+         "The IPv4 protocol as defined in RFC 791.";
+      }
+      enum ipv6 {
+        value "2";
+        description
+         "The IPv6 protocol as defined in RFC 2460.";
+      }
+    }
+    description
+     "This value represents the version of the IP protocol.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetVersion textual convention of the SMIv2.";
+    reference
+     "RFC  791: Internet Protocol
+      RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+      RFC 4001: Textual Conventions for Internet Network Addresses";
+  }
+
+  typedef dscp {
+    type uint8 {
+      range "0..63";
+    }
+    description
+     "The dscp type represents a Differentiated Services Code-Point
+      that may be used for marking packets in a traffic stream.
+
+      In the value set and its semantics, this type is equivalent
+      to the Dscp textual convention of the SMIv2.";
+    reference
+     "RFC 3289: Management Information Base for the Differentiated
+                Services Architecture
+      RFC 2474: Definition of the Differentiated Services Field
+                (DS Field) in the IPv4 and IPv6 Headers
+      RFC 2780: IANA Allocation Guidelines For Values In
+                the Internet Protocol and Related Headers";
+  }
+
+  typedef ipv6-flow-label {
+    type uint32 {
+      range "0..1048575";
+    }
+    description
+     "The flow-label type represents flow identifier or Flow Label
+      in an IPv6 packet header that may be used to discriminate
+      traffic flows.
+
+      In the value set and its semantics, this type is equivalent
+      to the IPv6FlowLabel textual convention of the SMIv2.";
+    reference
+     "RFC 3595: Textual Conventions for IPv6 Flow Label
+      RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
+  }
+
+  typedef port-number {
+    type uint16 {
+      range "0..65535";
+    }
+    description
+     "The port-number type represents a 16-bit port number of an
+      Internet transport layer protocol such as UDP, TCP, DCCP, or
+      SCTP.  Port numbers are assigned by IANA.  A current list of
+      all assignments is available from <http://www.iana.org/>.
+
+      Note that the port number value zero is reserved by IANA.  In
+      situations where the value zero does not make sense, it can
+      be excluded by subtyping the port-number type.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetPortNumber textual convention of the SMIv2.";
+    reference
+     "RFC  768: User Datagram Protocol
+      RFC  793: Transmission Control Protocol
+      RFC 4960: Stream Control Transmission Protocol
+      RFC 4340: Datagram Congestion Control Protocol (DCCP)
+      RFC 4001: Textual Conventions for Internet Network Addresses";
+  }
+
+  /*** collection of autonomous system related types ***/
+
+  typedef as-number {
+    type uint32;
+    description
+     "The as-number type represents autonomous system numbers
+      which identify an Autonomous System (AS).  An AS is a set
+      of routers under a single technical administration, using
+      an interior gateway protocol and common metrics to route
+      packets within the AS, and using an exterior gateway
+      protocol to route packets to other ASs'.  IANA maintains
+      the AS number space and has delegated large parts to the
+      regional registries.
+
+      Autonomous system numbers were originally limited to 16
+      bits.  BGP extensions have enlarged the autonomous system
+      number space to 32 bits.  This type therefore uses an uint32
+      base type without a range restriction in order to support
+      a larger autonomous system number space.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetAutonomousSystemNumber textual convention of
+      the SMIv2.";
+    reference
+     "RFC 1930: Guidelines for creation, selection, and registration
+                of an Autonomous System (AS)
+      RFC 4271: A Border Gateway Protocol 4 (BGP-4)
+      RFC 4893: BGP Support for Four-octet AS Number Space
+      RFC 4001: Textual Conventions for Internet Network Addresses";
+  }
+
+  /*** collection of IP address and hostname related types ***/
+
+  typedef ip-address {
+    type union {
+      type inet:ipv4-address;
+      type inet:ipv6-address;
+    }
+    description
+     "The ip-address type represents an IP address and is IP
+      version neutral.  The format of the textual representations
+      implies the IP version.";
+  }
+
+  typedef ipv4-address {
+    type string {
+      pattern
+        '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+      +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+      + '(%[\p{N}\p{L}]+)?';
+    }
+    description
+      "The ipv4-address type represents an IPv4 address in
+       dotted-quad notation.  The IPv4 address may include a zone
+       index, separated by a % sign.
+
+       The zone index is used to disambiguate identical address
+       values.  For link-local addresses, the zone index will
+       typically be the interface index number or the name of an
+       interface.  If the zone index is not present, the default
+       zone of the device will be used.
+
+       The canonical format for the zone index is the numerical
+       format";
+  }
+
+  typedef ipv6-address {
+    type string {
+      pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+            + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+            + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+            + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+            + '(%[\p{N}\p{L}]+)?';
+      pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+            + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+            + '(%.+)?';
+    }
+    description
+     "The ipv6-address type represents an IPv6 address in full,
+      mixed, shortened, and shortened-mixed notation.  The IPv6
+      address may include a zone index, separated by a % sign.
+
+
+
+
+
+      The zone index is used to disambiguate identical address
+      values.  For link-local addresses, the zone index will
+      typically be the interface index number or the name of an
+      interface.  If the zone index is not present, the default
+      zone of the device will be used.
+
+      The canonical format of IPv6 addresses uses the compressed
+      format described in RFC 4291, Section 2.2, item 2 with the
+      following additional rules: the :: substitution must be
+      applied to the longest sequence of all-zero 16-bit chunks
+      in an IPv6 address.  If there is a tie, the first sequence
+      of all-zero 16-bit chunks is replaced by ::.  Single
+      all-zero 16-bit chunks are not compressed.  The canonical
+      format uses lowercase characters and leading zeros are
+      not allowed.  The canonical format for the zone index is
+      the numerical format as described in RFC 4007, Section
+      11.2.";
+    reference
+     "RFC 4291: IP Version 6 Addressing Architecture
+      RFC 4007: IPv6 Scoped Address Architecture
+      RFC 5952: A Recommendation for IPv6 Address Text Representation";
+  }
+
+  typedef ip-prefix {
+    type union {
+      type inet:ipv4-prefix;
+      type inet:ipv6-prefix;
+    }
+    description
+     "The ip-prefix type represents an IP prefix and is IP
+      version neutral.  The format of the textual representations
+      implies the IP version.";
+  }
+
+  typedef ipv4-prefix {
+    type string {
+      pattern
+         '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+       +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+       + '/(([0-9])|([1-2][0-9])|(3[0-2]))';
+    }
+    description
+     "The ipv4-prefix type represents an IPv4 address prefix.
+      The prefix length is given by the number following the
+      slash character and must be less than or equal to 32.
+
+
+
+      A prefix length value of n corresponds to an IP address
+      mask that has n contiguous 1-bits from the most
+      significant bit (MSB) and all other bits set to 0.
+
+      The canonical format of an IPv4 prefix has all bits of
+      the IPv4 address set to zero that are not part of the
+      IPv4 prefix.";
+  }
+
+  typedef ipv6-prefix {
+    type string {
+      pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+            + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+            + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+            + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+            + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
+      pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+            + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+            + '(/.+)';
+    }
+    description
+     "The ipv6-prefix type represents an IPv6 address prefix.
+      The prefix length is given by the number following the
+      slash character and must be less than or equal 128.
+
+      A prefix length value of n corresponds to an IP address
+      mask that has n contiguous 1-bits from the most
+      significant bit (MSB) and all other bits set to 0.
+
+      The IPv6 address should have all bits that do not belong
+      to the prefix set to zero.
+
+      The canonical format of an IPv6 prefix has all bits of
+      the IPv6 address set to zero that are not part of the
+      IPv6 prefix.  Furthermore, IPv6 address is represented
+      in the compressed format described in RFC 4291, Section
+      2.2, item 2 with the following additional rules: the ::
+      substitution must be applied to the longest sequence of
+      all-zero 16-bit chunks in an IPv6 address.  If there is
+      a tie, the first sequence of all-zero 16-bit chunks is
+      replaced by ::.  Single all-zero 16-bit chunks are not
+      compressed.  The canonical format uses lowercase
+      characters and leading zeros are not allowed.";
+    reference
+     "RFC 4291: IP Version 6 Addressing Architecture";
+  }
+
+
+  /*** collection of domain name and URI types ***/
+
+  typedef domain-name {
+    type string {
+      pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+           +  '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+           +  '|\.';
+      length "1..253";
+    }
+    description
+     "The domain-name type represents a DNS domain name.  The
+      name SHOULD be fully qualified whenever possible.
+
+      Internet domain names are only loosely specified.  Section
+      3.5 of RFC 1034 recommends a syntax (modified in Section
+      2.1 of RFC 1123).  The pattern above is intended to allow
+      for current practice in domain name use, and some possible
+      future expansion.  It is designed to hold various types of
+      domain names, including names used for A or AAAA records
+      (host names) and other records, such as SRV records.  Note
+      that Internet host names have a stricter syntax (described
+      in RFC 952) than the DNS recommendations in RFCs 1034 and
+      1123, and that systems that want to store host names in
+      schema nodes using the domain-name type are recommended to
+      adhere to this stricter standard to ensure interoperability.
+
+      The encoding of DNS names in the DNS protocol is limited
+      to 255 characters.  Since the encoding consists of labels
+      prefixed by a length bytes and there is a trailing NULL
+      byte, only 253 characters can appear in the textual dotted
+      notation.
+
+      The description clause of schema nodes using the domain-name
+      type MUST describe when and how these names are resolved to
+      IP addresses.  Note that the resolution of a domain-name value
+      may require to query multiple DNS records (e.g., A for IPv4
+      and AAAA for IPv6).  The order of the resolution process and
+      which DNS record takes precedence can either be defined
+      explicitely or it may depend on the configuration of the
+      resolver.
+
+      Domain-name values use the US-ASCII encoding.  Their canonical
+      format uses lowercase US-ASCII characters.  Internationalized
+      domain names MUST be encoded in punycode as described in RFC
+      3492";
+    reference
+     "RFC  952: DoD Internet Host Table Specification
+      RFC 1034: Domain Names - Concepts and Facilities
+      RFC 1123: Requirements for Internet Hosts -- Application
+                and Support
+      RFC 2782: A DNS RR for specifying the location of services
+                (DNS SRV)
+      RFC 3492: Punycode: A Bootstring encoding of Unicode for
+                Internationalized Domain Names in Applications
+                (IDNA)
+      RFC 5891: Internationalizing Domain Names in Applications
+                (IDNA): Protocol";
+  }
+
+  typedef host {
+    type union {
+      type inet:ip-address;
+      type inet:domain-name;
+    }
+    description
+     "The host type represents either an IP address or a DNS
+      domain name.";
+  }
+
+  typedef uri {
+    type string;
+    description
+     "The uri type represents a Uniform Resource Identifier
+      (URI) as defined by STD 66.
+
+      Objects using the uri type MUST be in US-ASCII encoding,
+      and MUST be normalized as described by RFC 3986 Sections
+      6.2.1, 6.2.2.1, and 6.2.2.2.  All unnecessary
+      percent-encoding is removed, and all case-insensitive
+      characters are set to lowercase except for hexadecimal
+      digits, which are normalized to uppercase as described in
+      Section 6.2.2.1.
+
+      The purpose of this normalization is to help provide
+      unique URIs.  Note that this normalization is not
+      sufficient to provide uniqueness.  Two URIs that are
+      textually distinct after this normalization may still be
+      equivalent.
+
+      Objects using the uri type may restrict the schemes that
+      they permit.  For example, 'data:' and 'urn:' schemes
+      might not be appropriate.
+
+      A zero-length URI is not a valid URI.  This can be used to
+      express 'URI absent' where required.
+
+      In the value set and its semantics, this type is equivalent
+      to the Uri SMIv2 textual convention defined in RFC 5017.";
+    reference
+     "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
+      RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
+                Group: Uniform Resource Identifiers (URIs), URLs,
+                and Uniform Resource Names (URNs): Clarifications
+                and Recommendations
+      RFC 5017: MIB Textual Conventions for Uniform Resource
+                Identifiers (URIs)";
+  }
+
+}
diff --git a/yang/yang-model-util/pom.xml b/yang/yang-model-util/pom.xml
new file mode 100644 (file)
index 0000000..0f53575
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-model-util</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-model-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.model.util</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
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..336f634
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * 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 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..e0b1a2e
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * 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 static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+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.MoreExecutors;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.checkerframework.checker.lock.qual.GuardedBy;
+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.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 #createEffectiveModelContextFactory()} implementation.
+ */
+@Beta
+public abstract class AbstractSchemaRepository implements SchemaRepository, SchemaSourceRegistry {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractSchemaRepository.class);
+
+    /*
+     * Source identifier -> representation -> provider map. We usually are looking for
+     * a specific representation of a source.
+     */
+    @GuardedBy("this")
+    private final Map<SourceIdentifier, ListMultimap<Class<? extends SchemaSourceRepresentation>,
+            AbstractSchemaSourceRegistration<?>>> sources = new HashMap<>();
+
+    /*
+     * Schema source listeners.
+     */
+    @GuardedBy("this")
+    private final List<SchemaListenerRegistration> listeners = new ArrayList<>();
+
+    @SuppressWarnings("unchecked")
+    private static <T extends SchemaSourceRepresentation> ListenableFuture<T> fetchSource(
+            final SourceIdentifier id, final Iterator<AbstractSchemaSourceRegistration<?>> it) {
+        final AbstractSchemaSourceRegistration<?> reg = it.next();
+
+        return Futures.catchingAsync(((SchemaSourceProvider<T>)reg.getProvider()).getSource(id), Throwable.class,
+            input -> {
+                LOG.debug("Failed to acquire source from {}", reg, input);
+
+                if (it.hasNext()) {
+                    return fetchSource(id, it);
+                }
+
+                throw new MissingSchemaSourceException("All available providers exhausted", id, input);
+            }, MoreExecutors.directExecutor());
+    }
+
+    @Override
+    public <T extends SchemaSourceRepresentation> ListenableFuture<T> getSchemaSource(final SourceIdentifier id,
+            final Class<T> representation) {
+        final ArrayList<AbstractSchemaSourceRegistration<?>> sortedSchemaSourceRegistrations;
+
+        synchronized (this) {
+            final ListMultimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> srcs =
+                sources.get(id);
+            if (srcs == null) {
+                return immediateFailedFluentFuture(new MissingSchemaSourceException(
+                    "No providers registered for source" + id, id));
+            }
+
+            sortedSchemaSourceRegistrations = Lists.newArrayList(srcs.get(representation));
+        }
+
+        // TODO, remove and make sources keep sorted multimap (e.g. ArrayListMultimap with SortedLists)
+        sortedSchemaSourceRegistrations.sort(SchemaProviderCostComparator.INSTANCE);
+
+        final Iterator<AbstractSchemaSourceRegistration<?>> regs = sortedSchemaSourceRegistrations.iterator();
+        if (!regs.hasNext()) {
+            return immediateFailedFluentFuture(new MissingSchemaSourceException(
+                        "No providers for source " + id + " representation " + representation + " available", id));
+        }
+
+        final ListenableFuture<T> fetchSourceFuture = fetchSource(id, regs);
+        // Add callback to notify cache listeners about encountered schema
+        Futures.addCallback(fetchSourceFuture, new FutureCallback<T>() {
+            @Override
+            public void onSuccess(final T result) {
+                for (final SchemaListenerRegistration listener : listeners) {
+                    listener.getInstance().schemaSourceEncountered(result);
+                }
+            }
+
+            @Override
+            @SuppressWarnings("checkstyle:parameterName")
+            public void onFailure(final Throwable t) {
+                LOG.trace("Skipping notification for encountered source {}, fetching source failed", id, t);
+            }
+        }, MoreExecutors.directExecutor());
+
+        return fetchSourceFuture;
+    }
+
+    private synchronized <T extends SchemaSourceRepresentation> void addSource(final PotentialSchemaSource<T> source,
+            final AbstractSchemaSourceRegistration<T> reg) {
+        ListMultimap<Class<? extends SchemaSourceRepresentation>, AbstractSchemaSourceRegistration<?>> map =
+            sources.get(source.getSourceIdentifier());
+        if (map == null) {
+            map = ArrayListMultimap.create();
+            sources.put(source.getSourceIdentifier(), map);
+        }
+
+        map.put(source.getRepresentation(), reg);
+
+        final Collection<PotentialSchemaSource<?>> reps = Collections.singleton(source);
+        for (SchemaListenerRegistration l : listeners) {
+            l.getInstance().schemaSourceRegistered(reps);
+        }
+    }
+
+    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+            justification = "https://github.com/spotbugs/spotbugs/issues/811")
+    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(source.getSourceIdentifier());
+            }
+        }
+    }
+
+    @Override
+    public <T extends SchemaSourceRepresentation> SchemaSourceRegistration<T> registerSchemaSource(
+            final SchemaSourceProvider<? super T> provider, final PotentialSchemaSource<T> source) {
+        final PotentialSchemaSource<T> src = source.cachedReference();
+
+        final AbstractSchemaSourceRegistration<T> ret = new AbstractSchemaSourceRegistration<>(provider, src) {
+            @Override
+            protected void removeRegistration() {
+                removeSource(src, this);
+            }
+        };
+
+        addSource(src, 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;
+    }
+
+    private static class SchemaProviderCostComparator implements Comparator<AbstractSchemaSourceRegistration<?>>,
+            Serializable {
+        static final SchemaProviderCostComparator INSTANCE = new SchemaProviderCostComparator();
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public int compare(final AbstractSchemaSourceRegistration<?> o1, final AbstractSchemaSourceRegistration<?> o2) {
+            return o1.getInstance().getCost() - o2.getInstance().getCost();
+        }
+
+        private Object readResolve() {
+            return INSTANCE;
+        }
+    }
+}
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..92567fd
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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 static java.util.Objects.requireNonNull;
+
+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 = requireNonNull(consumer);
+        this.representation = requireNonNull(representation);
+        this.cost = requireNonNull(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..a6e12b7
--- /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.model.repo.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+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 = requireNonNull(provider);
+    }
+
+    public final SchemaSourceProvider<?> getProvider() {
+        return provider;
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return super.addToStringAttributes(toStringHelper).add("provider", provider);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/FilesystemSchemaSourceCache.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/FilesystemSchemaSourceCache.java
new file mode 100644 (file)
index 0000000..1c6b3c5
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * 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 static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.util.concurrent.FluentFuture;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+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.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Cache implementation that stores schemas in form of files under provided folder.
+ */
+public final class FilesystemSchemaSourceCache<T extends SchemaSourceRepresentation>
+        extends AbstractSchemaSourceCache<T> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(FilesystemSchemaSourceCache.class);
+
+    // Init storage adapters
+    private static final Map<Class<? extends SchemaSourceRepresentation>,
+            StorageAdapter<? extends SchemaSourceRepresentation>> STORAGE_ADAPTERS = Collections.singletonMap(
+                    YangTextSchemaSource.class, new YangTextSchemaStorageAdapter());
+
+    private static final Pattern CACHED_FILE_PATTERN =
+            Pattern.compile("(?<moduleName>[^@]+)" + "(@(?<revision>" + Revision.STRING_FORMAT_PATTERN + "))?");
+
+    private final Class<T> representation;
+    private final File storageDirectory;
+
+    public FilesystemSchemaSourceCache(
+            final SchemaSourceRegistry consumer, final Class<T> representation, final File storageDirectory) {
+        super(consumer, representation, Costs.LOCAL_IO);
+        this.representation = representation;
+        this.storageDirectory = requireNonNull(storageDirectory);
+
+        checkSupportedRepresentation(representation);
+
+        checkArgument(storageDirectory.mkdirs() || storageDirectory.isDirectory(),
+                "Unable to create cache directory at %s", storageDirectory);
+        checkArgument(storageDirectory.canWrite());
+        checkArgument(storageDirectory.canRead());
+
+        init();
+    }
+
+    private static void checkSupportedRepresentation(final Class<? extends SchemaSourceRepresentation> representation) {
+        for (final Class<? extends SchemaSourceRepresentation> supportedRepresentation : STORAGE_ADAPTERS.keySet()) {
+            if (supportedRepresentation.isAssignableFrom(representation)) {
+                return;
+            }
+        }
+
+        throw new IllegalArgumentException(String.format(
+                   "This cache does not support representation: %s, supported representations are: %s",
+                   representation, STORAGE_ADAPTERS.keySet()));
+    }
+
+    /**
+     * Restore cache state.
+     */
+    private void init() {
+
+        final CachedModulesFileVisitor fileVisitor = new CachedModulesFileVisitor();
+        try {
+            Files.walkFileTree(storageDirectory.toPath(), fileVisitor);
+        } catch (final IOException e) {
+            LOG.warn("Unable to restore cache from {}. Starting with an empty cache", storageDirectory, e);
+            return;
+        }
+
+        fileVisitor.getCachedSchemas().stream().forEach(this::register);
+    }
+
+    @Override
+    public synchronized FluentFuture<? extends T> getSource(final SourceIdentifier sourceIdentifier) {
+        final File file = sourceIdToFile(sourceIdentifier, storageDirectory);
+        if (file.exists() && file.canRead()) {
+            LOG.trace("Source {} found in cache as {}", sourceIdentifier, file);
+            final SchemaSourceRepresentation restored = STORAGE_ADAPTERS.get(representation).restore(sourceIdentifier,
+                    file);
+            return immediateFluentFuture(representation.cast(restored));
+        }
+
+        LOG.debug("Source {} not found in cache as {}", sourceIdentifier, file);
+        return immediateFailedFluentFuture(new MissingSchemaSourceException("Source not found", sourceIdentifier));
+    }
+
+    @Override
+    protected synchronized void offer(final T source) {
+        LOG.trace("Source {} offered to cache", source.getIdentifier());
+        final File file = sourceIdToFile(source);
+        if (file.exists()) {
+            LOG.debug("Source {} already in cache as {}", source.getIdentifier(), file);
+            return;
+        }
+
+        storeSource(file, source);
+        register(source.getIdentifier());
+        LOG.trace("Source {} stored in cache as {}", source.getIdentifier(), file);
+    }
+
+    private File sourceIdToFile(final T source) {
+        return sourceIdToFile(source.getIdentifier(), storageDirectory);
+    }
+
+    static File sourceIdToFile(final SourceIdentifier identifier, final File storageDirectory) {
+        final Optional<Revision> rev = identifier.getRevision();
+        final File file;
+        if (rev.isEmpty()) {
+            // FIXME: this does not look right
+            file = findFileWithNewestRev(identifier, storageDirectory);
+        } else {
+            file = new File(storageDirectory, identifier.toYangFilename());
+        }
+        return file;
+    }
+
+    @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE",
+            justification = "listFiles is analyzed to return null")
+    private static File findFileWithNewestRev(final SourceIdentifier identifier, final File storageDirectory) {
+        File[] files = storageDirectory.listFiles(new FilenameFilter() {
+            final Pattern pat = Pattern.compile(Pattern.quote(identifier.getName())
+                    + "(\\.yang|@\\d\\d\\d\\d-\\d\\d-\\d\\d.yang)");
+
+            @Override
+            public boolean accept(final File dir, final String name) {
+                return pat.matcher(name).matches();
+            }
+        });
+
+        if (files.length == 0) {
+            return new File(storageDirectory, identifier.toYangFilename());
+        }
+        if (files.length == 1) {
+            return files[0];
+        }
+
+        File file = null;
+        TreeMap<Optional<Revision>, File> map = new TreeMap<>(Revision::compare);
+        for (File sorted : files) {
+            String fileName = sorted.getName();
+            Matcher match = Revision.STRING_FORMAT_PATTERN.matcher(fileName);
+            if (match.find()) {
+                String revStr = match.group();
+                Revision rev;
+                try {
+                    rev = Revision.of(revStr);
+                } catch (final DateTimeParseException e) {
+                    LOG.info("Unable to parse date from yang file name {}, falling back to not-present", fileName, e);
+                    rev = null;
+                }
+
+                map.put(Optional.ofNullable(rev), sorted);
+
+            } else {
+                map.put(Optional.empty(), sorted);
+            }
+        }
+        file = map.lastEntry().getValue();
+
+        return file;
+    }
+
+    private void storeSource(final File file, final T schemaRepresentation) {
+        STORAGE_ADAPTERS.get(representation).store(file, schemaRepresentation);
+    }
+
+    private abstract static class StorageAdapter<T extends SchemaSourceRepresentation> {
+
+        private final Class<T> supportedType;
+
+        protected StorageAdapter(final Class<T> supportedType) {
+            this.supportedType = supportedType;
+        }
+
+        void store(final File file, final SchemaSourceRepresentation schemaSourceRepresentation) {
+            checkArgument(supportedType.isAssignableFrom(schemaSourceRepresentation.getClass()),
+                    "Cannot store schema source %s, this adapter only supports %s", schemaSourceRepresentation,
+                    supportedType);
+
+            storeAsType(file, supportedType.cast(schemaSourceRepresentation));
+        }
+
+        protected abstract void storeAsType(File file, T cast);
+
+        public T restore(final SourceIdentifier sourceIdentifier, final File cachedSource) {
+            checkArgument(cachedSource.isFile());
+            checkArgument(cachedSource.exists());
+            checkArgument(cachedSource.canRead());
+            return restoreAsType(sourceIdentifier, cachedSource);
+        }
+
+        protected abstract T restoreAsType(SourceIdentifier sourceIdentifier, File cachedSource);
+    }
+
+    private static final class YangTextSchemaStorageAdapter extends StorageAdapter<YangTextSchemaSource> {
+
+        protected YangTextSchemaStorageAdapter() {
+            super(YangTextSchemaSource.class);
+        }
+
+        @Override
+        @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE",
+            justification = "https://github.com/spotbugs/spotbugs/issues/600")
+        protected void storeAsType(final File file, final YangTextSchemaSource cast) {
+            try (InputStream castStream = cast.openStream()) {
+                Files.copy(castStream, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
+            } catch (final IOException e) {
+                throw new IllegalStateException("Cannot store schema source " + cast.getIdentifier() + " to " + file,
+                        e);
+            }
+        }
+
+        @Override
+        public YangTextSchemaSource restoreAsType(final SourceIdentifier sourceIdentifier, final File cachedSource) {
+            return new YangTextSchemaSource(sourceIdentifier) {
+
+                @Override
+                protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+                    return toStringHelper;
+                }
+
+                @Override
+                public InputStream openStream() throws IOException {
+                    return Files.newInputStream(cachedSource.toPath());
+                }
+            };
+        }
+    }
+
+    private static final class CachedModulesFileVisitor extends SimpleFileVisitor<Path> {
+        private final List<SourceIdentifier> cachedSchemas = new ArrayList<>();
+
+        @Override
+        public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+            final FileVisitResult fileVisitResult = super.visitFile(file, attrs);
+            String fileName = file.toFile().getName();
+            fileName = com.google.common.io.Files.getNameWithoutExtension(fileName);
+
+            final Optional<SourceIdentifier> si = getSourceIdentifier(fileName);
+            if (si.isPresent()) {
+                LOG.trace("Restoring cached file {} as {}", file, si.get());
+                cachedSchemas.add(si.get());
+            } else {
+                LOG.debug("Skipping cached file {}, cannot restore source identifier from filename: {},"
+                        + " does not match {}", file, fileName, CACHED_FILE_PATTERN);
+            }
+            return fileVisitResult;
+        }
+
+        private static Optional<SourceIdentifier> getSourceIdentifier(final String fileName) {
+            final Matcher matcher = CACHED_FILE_PATTERN.matcher(fileName);
+            if (matcher.matches()) {
+                final String moduleName = matcher.group("moduleName");
+                final String revision = matcher.group("revision");
+                return Optional.of(RevisionSourceIdentifier.create(moduleName, Revision.ofNullable(revision)));
+            }
+            return Optional.empty();
+        }
+
+        @Override
+        public FileVisitResult visitFileFailed(final Path file, final IOException exc) throws IOException {
+            LOG.warn("Unable to restore cached file {}. Ignoring", file, exc);
+            return FileVisitResult.CONTINUE;
+        }
+
+        public List<SourceIdentifier> getCachedSchemas() {
+            return cachedSchemas;
+        }
+    }
+}
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..1e2fc94
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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.annotations.Beta;
+import com.google.common.base.FinalizablePhantomReference;
+import com.google.common.base.FinalizableReferenceQueue;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.util.concurrent.FluentFuture;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.yangtools.util.concurrent.FluentFutures;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+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;
+
+@Beta
+public class InMemorySchemaSourceCache<T extends SchemaSourceRepresentation> extends AbstractSchemaSourceCache<T>
+        implements AutoCloseable {
+    private final List<FinalizablePhantomReference<T>> regs = Collections.synchronizedList(new ArrayList<>());
+    private final FinalizableReferenceQueue queue = new FinalizableReferenceQueue();
+    private final Cache<SourceIdentifier, T> cache;
+
+    protected InMemorySchemaSourceCache(final SchemaSourceRegistry consumer, final Class<T> representation,
+            final CacheBuilder<Object, Object> builder) {
+        super(consumer, representation, Costs.IMMEDIATE);
+        cache = builder.build();
+    }
+
+    public static <R extends SchemaSourceRepresentation> InMemorySchemaSourceCache<R> createSoftCache(
+            final SchemaSourceRegistry consumer, final Class<R> representation) {
+        return new InMemorySchemaSourceCache<>(consumer, representation, CacheBuilder.newBuilder().softValues());
+    }
+
+    public static <R extends SchemaSourceRepresentation> InMemorySchemaSourceCache<R> createSoftCache(
+            final SchemaSourceRegistry consumer, final Class<R> representation, final long lifetime,
+            final TimeUnit units) {
+        return new InMemorySchemaSourceCache<>(consumer, representation, CacheBuilder.newBuilder().softValues()
+                .expireAfterAccess(lifetime, units));
+    }
+
+    @Override
+    public FluentFuture<? extends T> getSource(final SourceIdentifier sourceIdentifier) {
+        final T present = cache.getIfPresent(sourceIdentifier);
+        return present != null ? FluentFutures.immediateFluentFuture(present)
+                : FluentFutures.immediateFailedFluentFuture(new MissingSchemaSourceException("Source not found",
+                    sourceIdentifier));
+    }
+
+    @Override
+    protected void offer(final T source) {
+        final T present = cache.getIfPresent(source.getIdentifier());
+        if (present == null) {
+            cache.put(source.getIdentifier(), source);
+
+            final SchemaSourceRegistration<T> reg = register(source.getIdentifier());
+            final FinalizablePhantomReference<T> ref = new FinalizablePhantomReference<T>(source, queue) {
+                @Override
+                public void finalizeReferent() {
+                    reg.close();
+                    regs.remove(this);
+                }
+            };
+
+            regs.add(ref);
+        }
+    }
+
+    @Override
+    public void close() {
+        while (!regs.isEmpty()) {
+            final FinalizablePhantomReference<?> ref = regs.get(0);
+            ref.finalizeReferent();
+        }
+
+        cache.invalidateAll();
+        queue.close();
+    }
+}
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..087104f
--- /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.model.repo.util;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+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 = requireNonNull(reg);
+    }
+
+    public void incRef() {
+        refcount++;
+    }
+
+    public boolean decRef() {
+        checkState(refcount > 0, "Refcount underflow: %s", refcount);
+
+        if (0 == --refcount) {
+            reg.close();
+            return true;
+        }
+
+        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..28539bb
--- /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 static java.util.Objects.requireNonNull;
+
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+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> {
+
+    @FunctionalInterface
+    public interface Transformation<S extends SchemaSourceRepresentation, D extends SchemaSourceRepresentation>
+            extends AsyncFunction<S, D> {
+        @Override
+        ListenableFuture<D> apply(S input) throws Exception;
+    }
+
+    private final Map<PotentialSchemaSource<?>, RefcountedRegistration> availableSources = 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 = requireNonNull(provider);
+        this.consumer = requireNonNull(consumer);
+        this.function = requireNonNull(function);
+        this.srcClass = requireNonNull(srcClass);
+        this.dstClass = requireNonNull(dstClass);
+    }
+
+    @Override
+    public ListenableFuture<D> getSource(final SourceIdentifier sourceIdentifier) {
+        return Futures.transformAsync(provider.getSchemaSource(sourceIdentifier, srcClass), function,
+            MoreExecutors.directExecutor());
+    }
+
+    @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 = availableSources.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);
+        availableSources.put(src, new RefcountedRegistration(r));
+    }
+
+    private void unregisterSource(final PotentialSchemaSource<?> src) {
+        final RefcountedRegistration reg = availableSources.get(src);
+        if (reg != null && reg.decRef()) {
+            availableSources.remove(src);
+        }
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractIdentifiableSchemaContextProvider.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractIdentifiableSchemaContextProvider.java
new file mode 100644 (file)
index 0000000..1ca132f
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+@Beta
+@NonNullByDefault
+public abstract class AbstractIdentifiableSchemaContextProvider<T> extends AbstractSchemaContextProvider
+        implements Identifiable<T> {
+    private final @NonNull T identifier;
+
+    protected AbstractIdentifiableSchemaContextProvider(final SchemaContext schemaContext, final T identifier) {
+        super(schemaContext);
+        this.identifier = requireNonNull(identifier);
+    }
+
+    @Override
+    public final T getIdentifier() {
+        return identifier;
+    }
+
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractPathExpression.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractPathExpression.java
new file mode 100644 (file)
index 0000000..a097612
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+
+@Beta
+@NonNullByDefault
+public abstract class AbstractPathExpression implements PathExpression {
+    private final String originalString;
+
+    protected AbstractPathExpression(final String originalString) {
+        this.originalString = requireNonNull(originalString);
+    }
+
+    @Override
+    public final String getOriginalString() {
+        return originalString;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("originalString", originalString);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractSchemaContext.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractSchemaContext.java
new file mode 100644 (file)
index 0000000..0351735
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.SetMultimap;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeSet;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+public abstract class AbstractSchemaContext implements SchemaContext {
+    /**
+     * A {@link Module} comparator based on {@link Module#getRevision()}, placing latest revision first. Note this
+     * comparator does not take into account module name and so two modules with different names but same revisions
+     * compare as equal.
+     */
+    protected static final Comparator<Module> REVISION_COMPARATOR =
+        (first, second) -> Revision.compare(second.getRevision(), first.getRevision());
+
+    /**
+     * A {@link Module} comparator based on {@link Module#getName()} and {@link Module#getRevision()}, ordering modules
+     * lexicographically by their name and then in order of descending revision. This comparator assumes that
+     * the combination of these two attributes is sufficient to be consistent with hashCode/equals.
+     */
+    protected static final Comparator<Module> NAME_REVISION_COMPARATOR = (first, second) -> {
+        final int cmp = first.getName().compareTo(second.getName());
+        return cmp != 0 ? cmp : REVISION_COMPARATOR.compare(first, second);
+    };
+
+    /**
+     * Create a TreeSet for containing Modules with the same name, such that the set is ordered
+     * by {@link #REVISION_COMPARATOR}.
+     *
+     * @return A fresh TreeSet instance.
+     */
+    protected static final TreeSet<Module> createModuleSet() {
+        return new TreeSet<>(REVISION_COMPARATOR);
+    }
+
+    /**
+     * Returns the namespace-to-module mapping.
+     *
+     * @return Map of modules where key is namespace
+     */
+    protected abstract SetMultimap<URI, Module> getNamespaceToModules();
+
+    /**
+     * Returns the module name-to-module mapping.
+     *
+     * @return Map of modules where key is name of module
+     */
+    protected abstract SetMultimap<String, Module> getNameToModules();
+
+    /**
+     * Returns the namespace+revision-to-module mapping.
+     *
+     * @return Map of modules where key is Module's QNameModule.
+     */
+    protected abstract Map<QNameModule, Module> getModuleMap();
+
+    @Override
+    public Set<DataSchemaNode> getDataDefinitions() {
+        final Set<DataSchemaNode> dataDefs = new HashSet<>();
+        for (Module m : getModules()) {
+            dataDefs.addAll(m.getChildNodes());
+        }
+        return dataDefs;
+    }
+
+    @Override
+    public Set<NotificationDefinition> getNotifications() {
+        final Set<NotificationDefinition> notifications = new HashSet<>();
+        for (Module m : getModules()) {
+            notifications.addAll(m.getNotifications());
+        }
+        return notifications;
+    }
+
+    @Override
+    public Set<RpcDefinition> getOperations() {
+        final Set<RpcDefinition> rpcs = new HashSet<>();
+        for (Module m : getModules()) {
+            rpcs.addAll(m.getRpcs());
+        }
+        return rpcs;
+    }
+
+    @Override
+    public Set<ExtensionDefinition> getExtensions() {
+        final Set<ExtensionDefinition> extensions = new HashSet<>();
+        for (Module m : getModules()) {
+            extensions.addAll(m.getExtensionSchemaNodes());
+        }
+        return extensions;
+    }
+
+    @Override
+    public Optional<Module> findModule(final String name, final Optional<Revision> revision) {
+        for (final Module module : getNameToModules().get(name)) {
+            if (revision.equals(module.getRevision())) {
+                return Optional.of(module);
+            }
+        }
+
+        return Optional.empty();
+    }
+
+    @Override
+    public Optional<Module> findModule(final QNameModule qnameModule) {
+        return Optional.ofNullable(getModuleMap().get(qnameModule));
+    }
+
+    @Override
+    public Set<Module> findModules(final URI namespace) {
+        return getNamespaceToModules().get(namespace);
+    }
+
+    @Override
+    public Set<Module> findModules(final String name) {
+        return getNameToModules().get(name);
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAugmenting() {
+        return false;
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAddedByUses() {
+        return false;
+    }
+
+    @Override
+    public boolean isConfiguration() {
+        return false;
+    }
+
+    @Override
+    public QName getQName() {
+        return SchemaContext.NAME;
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return SchemaPath.ROOT;
+    }
+
+    @Override
+    public Status getStatus() {
+        return Status.CURRENT;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        final List<UnknownSchemaNode> result = new ArrayList<>();
+        for (Module module : getModules()) {
+            result.addAll(module.getUnknownSchemaNodes());
+        }
+        return Collections.unmodifiableList(result);
+    }
+
+    @Override
+    public Set<TypeDefinition<?>> getTypeDefinitions() {
+        final Set<TypeDefinition<?>> result = new LinkedHashSet<>();
+        for (Module module : getModules()) {
+            result.addAll(module.getTypeDefinitions());
+        }
+        return Collections.unmodifiableSet(result);
+    }
+
+    @Override
+    public Set<DataSchemaNode> getChildNodes() {
+        final Set<DataSchemaNode> result = new LinkedHashSet<>();
+        for (Module module : getModules()) {
+            result.addAll(module.getChildNodes());
+        }
+        return Collections.unmodifiableSet(result);
+    }
+
+    @Override
+    public Set<GroupingDefinition> getGroupings() {
+        final Set<GroupingDefinition> result = new LinkedHashSet<>();
+        for (Module module : getModules()) {
+            result.addAll(module.getGroupings());
+        }
+        return Collections.unmodifiableSet(result);
+    }
+
+    @Override
+    public Optional<DataSchemaNode> findDataChildByName(final QName name) {
+        requireNonNull(name);
+        for (Module module : getModules()) {
+            final Optional<DataSchemaNode> result = module.findDataChildByName(name);
+            if (result.isPresent()) {
+                return result;
+            }
+        }
+        return Optional.empty();
+    }
+
+    @Override
+    public Set<UsesNode> getUses() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public boolean isPresenceContainer() {
+        return false;
+    }
+
+    @Override
+    public Set<AugmentationSchemaNode> getAvailableAugmentations() {
+        return Collections.emptySet();
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractSchemaContextProvider.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractSchemaContextProvider.java
new file mode 100644 (file)
index 0000000..138947a
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech s.r.o. 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+/**
+ * Utility superclass for classes returning a constant {@link SchemaContext}.
+ */
+@Beta
+@NonNullByDefault
+public abstract class AbstractSchemaContextProvider implements SchemaContextProvider {
+    private final SchemaContext schemaContext;
+
+    protected AbstractSchemaContextProvider(final SchemaContext schemaContext) {
+        this.schemaContext = requireNonNull(schemaContext);
+    }
+
+    @Override
+    public final SchemaContext getSchemaContext() {
+        return schemaContext;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("schemaContext", schemaContext);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/BaseConstraints.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/BaseConstraints.java
new file mode 100644 (file)
index 0000000..020c3b1
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.model.util;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+
+/**
+ * Utility class which provides factory methods to construct Constraints.
+ *
+ * <p>
+ * Provides static factory methods which constructs instances of
+ * <ul>
+ * <li>{@link PatternConstraint} - {@link #newPatternConstraint(String, Optional, Optional)}
+ * </ul>
+ */
+public final class BaseConstraints {
+    private BaseConstraints() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Creates a {@link PatternConstraint}.
+     *
+     * <p>
+     * Creates an instance of Pattern constraint based on supplied parameters
+     * with additional behaviour:
+     * <ul>
+     * <li>{@link PatternConstraint#getErrorAppTag()} returns
+     * <code>invalid-regular-expression</code>
+     * </ul>
+     *
+     * @see PatternConstraint
+     *
+     * @param pattern
+     *            Regular expression, MUST NOT BE null.
+     * @param description
+     *            Description associated with constraint.
+     * @param reference
+     *            Reference associated with constraint.
+     * @return Instance of {@link PatternConstraint}
+     */
+    public static PatternConstraint newPatternConstraint(final String pattern, final Optional<String> description,
+            final Optional<String> reference) {
+        return new PatternConstraintImpl(pattern, description, reference);
+    }
+
+    /**
+     * Creates a {@link PatternConstraint}.
+     *
+     * <p>
+     * Creates an instance of Pattern constraint based on supplied parameters
+     * with additional behaviour:
+     * <ul>
+     * <li>{@link PatternConstraint#getErrorAppTag()} returns
+     * <code>invalid-regular-expression</code>
+     * </ul>
+     *
+     * @see PatternConstraint
+     *
+     * @param pattern
+     *            Regular expression, MUST NOT BE null.
+     * @param description
+     *            Description associated with constraint.
+     * @param reference
+     *            Reference associated with constraint.
+     * @param errorAppTag
+     *            error-app-tag associated with constraint.
+     * @param errorMessage
+     *            error message associated with constraint.
+     * @param modifier
+     *            Modifier of pattern constraint.
+     * @return Instance of {@link PatternConstraint}
+     */
+    public static PatternConstraint newPatternConstraint(final String pattern, final Optional<String> description,
+            final Optional<String> reference, final String errorAppTag, final String errorMessage,
+            final Optional<ModifierKind> modifier) {
+        return new PatternConstraintImpl(pattern, description, reference, errorAppTag, errorMessage, modifier);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/BaseTypes.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/BaseTypes.java
new file mode 100644 (file)
index 0000000..1d96038
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * 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.model.util;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Utility methods and constants to work with built-in YANG types.
+ */
+public final class BaseTypes {
+
+    private BaseTypes() {
+    }
+
+    public static final QName BINARY_QNAME = constructQName("binary");
+    public static final QName BITS_QNAME = constructQName("bits");
+    public static final QName BOOLEAN_QNAME = constructQName("boolean");
+    public static final QName DECIMAL64_QNAME = constructQName("decimal64");
+    public static final QName EMPTY_QNAME = constructQName("empty");
+    public static final QName ENUMERATION_QNAME = constructQName("enumeration");
+    public static final QName IDENTITYREF_QNAME = constructQName("identityref");
+    public static final QName INSTANCE_IDENTIFIER_QNAME = constructQName("instance-identifier");
+    public static final QName INT8_QNAME = constructQName("int8");
+    public static final QName INT16_QNAME = constructQName("int16");
+    public static final QName INT32_QNAME = constructQName("int32");
+    public static final QName INT64_QNAME = constructQName("int64");
+    public static final QName LEAFREF_QNAME = constructQName("leafref");
+    public static final QName STRING_QNAME = constructQName("string");
+    public static final QName UINT8_QNAME = constructQName("uint8");
+    public static final QName UINT16_QNAME = constructQName("uint16");
+    public static final QName UINT32_QNAME = constructQName("uint32");
+    public static final QName UINT64_QNAME = constructQName("uint64");
+    public static final QName UNION_QNAME = constructQName("union");
+
+    private static final ImmutableSet<QName> BUILT_IN_TYPES = ImmutableSet.<QName>builder()
+            .add(BINARY_QNAME)
+            .add(BITS_QNAME)
+            .add(BOOLEAN_QNAME)
+            .add(DECIMAL64_QNAME)
+            .add(EMPTY_QNAME)
+            .add(ENUMERATION_QNAME)
+            .add(IDENTITYREF_QNAME)
+            .add(INSTANCE_IDENTIFIER_QNAME)
+            .add(INT8_QNAME)
+            .add(INT16_QNAME)
+            .add(INT32_QNAME)
+            .add(INT64_QNAME)
+            .add(LEAFREF_QNAME)
+            .add(STRING_QNAME)
+            .add(UINT8_QNAME)
+            .add(UINT16_QNAME)
+            .add(UINT32_QNAME)
+            .add(UINT64_QNAME)
+            .add(UNION_QNAME)
+            .build();
+
+    /**
+     * Construct QName for Built-in base Yang type. The namespace for built-in
+     * base yang types is defined as: urn:ietf:params:xml:ns:yang:1
+     *
+     * @param typeName
+     *            yang type name
+     * @return built-in base yang type QName.
+     */
+    public static QName constructQName(final String typeName) {
+        return QName.create(YangConstants.RFC6020_YANG_MODULE, typeName).intern();
+    }
+
+    /**
+     * Returns true if supplied type is representation of built-in YANG type as
+     * per RFC 6020.
+     *
+     * <p>
+     * See package documentation for description of base types.
+     *
+     * @param type A type name
+     * @return true if type is built-in YANG Types.
+     */
+    public static boolean isYangBuildInType(final String type) {
+        return !Strings.isNullOrEmpty(type) && BUILT_IN_TYPES.contains(
+                QName.create(YangConstants.RFC6020_YANG_MODULE, type));
+    }
+
+    /**
+     * Returns true if supplied type is representation of built-in YANG type as
+     * per RFC 6020.
+     *
+     * <p>
+     * See package documentation for description of base types.
+     *
+     * @param type Type definition
+     * @return true if type is built-in YANG Types.
+     */
+    public static boolean isYangBuildInType(final TypeDefinition<?> type) {
+        return type != null && BUILT_IN_TYPES.contains(type.getQName());
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/DataNodeIterator.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/DataNodeIterator.java
new file mode 100644 (file)
index 0000000..d0b0eb3
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * 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.model.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * DataNodeIterator is iterator, which walks down whole YANG DataNodeContainer
+ * and walks all instances of {@link DataSchemaNode} present in subtree.
+ *
+ * <p>
+ * Iterator instance is eagerly created, walking happens on initialization. Iteration is not ordered.
+ */
+public class DataNodeIterator implements Iterator<DataSchemaNode> {
+
+    private final List<ListSchemaNode> allLists = new ArrayList<>();
+    private final List<ContainerSchemaNode> allContainers = new ArrayList<>();
+    private final List<ChoiceSchemaNode> allChoices = new ArrayList<>();
+    private final List<DataSchemaNode> allChilds = new ArrayList<>();
+    private final List<GroupingDefinition> allGroupings = new ArrayList<>();
+    private final List<TypeDefinition<?>> allTypedefs = new ArrayList<>();
+    private final DataNodeContainer container;
+
+    public DataNodeIterator(final DataNodeContainer container) {
+        if (container == null) {
+            throw new IllegalArgumentException("Data Node Container MUST be specified and cannot be NULL!");
+        }
+
+        this.container = container;
+        traverse(this.container);
+    }
+
+    /**
+     * Returns list all containers present in subtree.
+     *
+     * @return Returns list all containers present in subtree.
+     */
+    public List<ContainerSchemaNode> allContainers() {
+        return allContainers;
+    }
+
+    /**
+     * Returns list all lists present in subtree.
+     *
+     * @return Returns list all containers present in subtree.
+     */
+    public List<ListSchemaNode> allLists() {
+        return allLists;
+    }
+
+    /**
+     * Returns list all choices present in subtree.
+     *
+     * @return Returns list all containers present in subtree.
+     */
+    public List<ChoiceSchemaNode> allChoices() {
+        return allChoices;
+    }
+
+    /**
+     * Returns list all groupings present in subtree.
+     *
+     * @return Returns list all containers present in subtree.
+     */
+    public List<GroupingDefinition> allGroupings() {
+        return allGroupings;
+    }
+
+    /**
+     * Returns list all typedefs present in subtree.
+     *
+     * @return Returns list all containers present in subtree.
+     */
+    public List<TypeDefinition<?>> allTypedefs() {
+        return allTypedefs;
+    }
+
+    private void traverse(final DataNodeContainer dataNode) {
+        if (dataNode == null) {
+            return;
+        }
+
+        final Iterable<DataSchemaNode> childNodes = dataNode.getChildNodes();
+        if (childNodes != null) {
+            for (DataSchemaNode childNode : childNodes) {
+                if (childNode.isAugmenting()) {
+                    continue;
+                }
+                allChilds.add(childNode);
+                if (childNode instanceof ContainerSchemaNode) {
+                    final ContainerSchemaNode containerNode = (ContainerSchemaNode) childNode;
+                    allContainers.add(containerNode);
+                    traverse(containerNode);
+                } else if (childNode instanceof ListSchemaNode) {
+                    final ListSchemaNode list = (ListSchemaNode) childNode;
+                    allLists.add(list);
+                    traverse(list);
+                } else if (childNode instanceof ChoiceSchemaNode) {
+                    final ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) childNode;
+                    allChoices.add(choiceNode);
+                    for (final CaseSchemaNode caseNode : choiceNode.getCases().values()) {
+                        traverse(caseNode);
+                    }
+                }
+            }
+        }
+
+        this.allTypedefs.addAll(dataNode.getTypeDefinitions());
+        traverseModule(dataNode);
+        traverseGroupings(dataNode);
+
+    }
+
+    private void traverseModule(final DataNodeContainer dataNode) {
+        final Module module;
+        if (dataNode instanceof Module) {
+            module = (Module) dataNode;
+        } else {
+            return;
+        }
+
+        final Set<NotificationDefinition> notifications = module.getNotifications();
+        for (NotificationDefinition notificationDefinition : notifications) {
+            traverse(notificationDefinition);
+        }
+
+        final Set<RpcDefinition> rpcs = module.getRpcs();
+        for (RpcDefinition rpcDefinition : rpcs) {
+            this.allTypedefs.addAll(rpcDefinition.getTypeDefinitions());
+            ContainerSchemaNode input = rpcDefinition.getInput();
+            if (input != null) {
+                traverse(input);
+            }
+            ContainerSchemaNode output = rpcDefinition.getOutput();
+            if (output != null) {
+                traverse(output);
+            }
+        }
+    }
+
+    private void traverseGroupings(final DataNodeContainer dataNode) {
+        final Set<GroupingDefinition> groupings = dataNode.getGroupings();
+        if (groupings != null) {
+            for (GroupingDefinition grouping : groupings) {
+                allGroupings.add(grouping);
+                traverse(grouping);
+            }
+        }
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (container.getChildNodes() != null) {
+            final Collection<DataSchemaNode> childNodes = container.getChildNodes();
+
+            if (childNodes != null && !childNodes.isEmpty()) {
+                return childNodes.iterator().hasNext();
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public DataSchemaNode next() {
+        return allChilds.iterator().next();
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/EffectiveAugmentationSchema.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/EffectiveAugmentationSchema.java
new file mode 100644 (file)
index 0000000..af7762a
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+/**
+ * Proxy for AugmentationSchema. Child node schemas are replaced with actual schemas from parent.
+ */
+public final class EffectiveAugmentationSchema implements AugmentationSchemaNode {
+    private final AugmentationSchemaNode delegate;
+    private final ImmutableSet<DataSchemaNode> realChildSchemas;
+    private final ImmutableMap<QName, DataSchemaNode> mappedChildSchemas;
+
+    public EffectiveAugmentationSchema(final AugmentationSchemaNode augmentSchema,
+            final Set<DataSchemaNode> realChildSchemas) {
+        this.delegate = requireNonNull(augmentSchema);
+        this.realChildSchemas = ImmutableSet.copyOf(realChildSchemas);
+
+        final Map<QName, DataSchemaNode> m = new HashMap<>(realChildSchemas.size());
+        for (DataSchemaNode realChildSchema : realChildSchemas) {
+            m.put(realChildSchema.getQName(), realChildSchema);
+        }
+
+        this.mappedChildSchemas = ImmutableMap.copyOf(m);
+    }
+
+    /**
+     * Returns an AugmentationSchemaNode as effective in a parent node.
+     *
+     * @param schema Augmentation schema
+     * @param parent Parent schema
+     * @return Adjusted Augmentation schema
+     * @throws NullPointerException if any of the arguments is null
+     */
+    public static AugmentationSchemaNode create(final AugmentationSchemaNode schema, final DataNodeContainer parent) {
+        Set<DataSchemaNode> children = new HashSet<>();
+        for (DataSchemaNode augNode : schema.getChildNodes()) {
+            children.add(parent.getDataChildByName(augNode.getQName()));
+        }
+        return new EffectiveAugmentationSchema(schema, children);
+    }
+
+    @Override
+    public Optional<RevisionAwareXPath> getWhenCondition() {
+        return delegate.getWhenCondition();
+    }
+
+    @Override
+    public Optional<String> getDescription() {
+        return delegate.getDescription();
+    }
+
+    @Override
+    public Optional<String> getReference() {
+        return delegate.getReference();
+    }
+
+    @Override
+    public Status getStatus() {
+        return delegate.getStatus();
+    }
+
+    @Override
+    public SchemaPath getTargetPath() {
+        return delegate.getTargetPath();
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return delegate.getUnknownSchemaNodes();
+    }
+
+    @Override
+    public Set<TypeDefinition<?>> getTypeDefinitions() {
+        return delegate.getTypeDefinitions();
+    }
+
+    @Override
+    public Set<DataSchemaNode> getChildNodes() {
+        return realChildSchemas;
+    }
+
+    @Override
+    public Set<GroupingDefinition> getGroupings() {
+        return delegate.getGroupings();
+    }
+
+    @Override
+    public Optional<DataSchemaNode> findDataChildByName(final QName name) {
+        return Optional.ofNullable(mappedChildSchemas.get(requireNonNull(name)));
+    }
+
+    @Override
+    public Set<UsesNode> getUses() {
+        return delegate.getUses();
+    }
+
+    @Override
+    public Optional<AugmentationSchemaNode> getOriginalDefinition() {
+        return delegate.getOriginalDefinition();
+    }
+
+    @Override
+    public Set<ActionDefinition> getActions() {
+        return delegate.getActions();
+    }
+
+    @Override
+    public Optional<ActionDefinition> findAction(final QName qname) {
+        return delegate.findAction(qname);
+    }
+
+    @Override
+    public Set<NotificationDefinition> getNotifications() {
+        return delegate.getNotifications();
+    }
+
+    @Override
+    public Optional<NotificationDefinition> findNotification(final QName qname) {
+        return delegate.findNotification(qname);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/FilteringSchemaContextProxy.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/FilteringSchemaContextProxy.java
new file mode 100644 (file)
index 0000000..d749490
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.TreeMultimap;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.function.Function;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public final class FilteringSchemaContextProxy extends AbstractSchemaContext {
+    private final ImmutableMap<QNameModule, Module> moduleMap;
+
+    //collection to be filled with filtered modules
+    private final ImmutableSet<Module> filteredModules;
+
+    //collections to be filled in with filtered data
+    private final ImmutableSetMultimap<URI, Module> namespaceToModules;
+    private final ImmutableSetMultimap<String, Module> nameToModules;
+
+    /**
+     * Filters SchemaContext for yang modules.
+     *
+     * @param delegate original SchemaContext
+     * @param rootModules modules (yang schemas) to be available and all their dependencies (modules importing
+     *                    rootModule and whole chain of their imports)
+     * @param additionalModuleIds (additional) modules (yang schemas) to be available and whole chain of their imports
+     */
+    public FilteringSchemaContextProxy(final SchemaContext delegate, final Collection<ModuleId> rootModules,
+            final Set<ModuleId> additionalModuleIds) {
+        requireNonNull(rootModules, "Base modules cannot be null.");
+        requireNonNull(additionalModuleIds, "Additional modules cannot be null.");
+
+        final Builder<Module> filteredModulesBuilder = new Builder<>();
+
+        // preparing map to get all modules with one name but difference in revision
+        final TreeMultimap<String, Module> nameToModulesAll = TreeMultimap.create(String::compareTo,
+            REVISION_COMPARATOR);
+
+        nameToModulesAll.putAll(getStringModuleMap(delegate));
+
+        // in case there is a particular dependency to view filteredModules/YANG models dependency is checked
+        // for module name and imports
+        processForRootModules(delegate, rootModules, filteredModulesBuilder);
+
+        // adding additional modules
+        processForAdditionalModules(delegate, additionalModuleIds, filteredModulesBuilder);
+
+        filteredModulesBuilder.addAll(getImportedModules(
+                Maps.uniqueIndex(delegate.getModules(), ModuleId.MODULE_TO_MODULE_ID::apply),
+                filteredModulesBuilder.build(), nameToModulesAll));
+
+        /**
+         * Instead of doing this on each invocation of getModules(), pre-compute it once and keep it around.
+         */
+        final List<Module> sortedModules = new ArrayList<>(filteredModulesBuilder.build());
+        sortedModules.sort(NAME_REVISION_COMPARATOR);
+        this.filteredModules = ImmutableSet.copyOf(sortedModules);
+
+        final SetMultimap<URI, Module> nsMap = Multimaps.newSetMultimap(new TreeMap<>(),
+            AbstractSchemaContext::createModuleSet);
+        final SetMultimap<String, Module> nameMap = Multimaps.newSetMultimap(new TreeMap<>(),
+            AbstractSchemaContext::createModuleSet);
+        final ImmutableMap.Builder<QNameModule, Module> moduleMapBuilder = ImmutableMap.builder();
+        for (final Module module : filteredModules) {
+            nameMap.put(module.getName(), module);
+            nsMap.put(module.getNamespace(), module);
+            moduleMapBuilder.put(module.getQNameModule(), module);
+        }
+
+        namespaceToModules = ImmutableSetMultimap.copyOf(nsMap);
+        nameToModules = ImmutableSetMultimap.copyOf(nameMap);
+        moduleMap = moduleMapBuilder.build();
+    }
+
+    private static void processForAdditionalModules(final SchemaContext delegate,
+            final Set<ModuleId> additionalModuleIds, final Builder<Module> filteredModulesBuilder) {
+        filteredModulesBuilder.addAll(Collections2.filter(delegate.getModules(),
+            module -> selectAdditionalModules(module, additionalModuleIds)));
+    }
+
+    private void processForRootModules(final SchemaContext delegate, final Collection<ModuleId> rootModules,
+            final Builder<Module> filteredModulesBuilder) {
+        filteredModulesBuilder.addAll(Collections2.filter(delegate.getModules(),
+            module -> checkModuleDependency(module, rootModules)));
+    }
+
+    private static Multimap<String, Module> getStringModuleMap(final SchemaContext delegate) {
+        return Multimaps.index(delegate.getModules(), Module::getName);
+    }
+
+    //dealing with imported module other than root and directly importing root
+    private static Collection<Module> getImportedModules(final Map<ModuleId, Module> allModules,
+            final Set<Module> baseModules, final TreeMultimap<String, Module> nameToModulesAll) {
+
+        List<Module> relatedModules = new LinkedList<>();
+
+        for (Module module : baseModules) {
+            for (ModuleImport moduleImport : module.getImports()) {
+                Optional<Revision> revisionDate = moduleImport.getRevision();
+                if (revisionDate.isEmpty()) {
+                    revisionDate = nameToModulesAll.get(moduleImport.getModuleName()).first().getRevision();
+                }
+
+                ModuleId key = new ModuleId(moduleImport.getModuleName(), revisionDate);
+                Module importedModule = allModules.get(key);
+
+                Preconditions.checkArgument(importedModule != null,
+                        "Invalid schema, cannot find imported module: %s from module: %s, %s, modules:%s", key,
+                        module.getQNameModule(), module.getName(), allModules);
+                relatedModules.add(importedModule);
+
+                //calling imports recursive
+                relatedModules.addAll(getImportedModules(allModules, Collections.singleton(importedModule),
+                            nameToModulesAll));
+            }
+        }
+
+        return relatedModules;
+    }
+
+    @Override
+    public Set<Module> getModules() {
+        return filteredModules;
+    }
+
+    @Override
+    protected Map<QNameModule, Module> getModuleMap() {
+        return moduleMap;
+    }
+
+    @Override
+    protected SetMultimap<URI, Module> getNamespaceToModules() {
+        return namespaceToModules;
+    }
+
+    @Override
+    protected SetMultimap<String, Module> getNameToModules() {
+        return nameToModules;
+    }
+
+    private static boolean selectAdditionalModules(final Module module, final Set<ModuleId> additionalModules) {
+        return additionalModules.contains(new ModuleId(module.getName(), module.getRevision()));
+    }
+
+    //check for any dependency regarding given string
+    private boolean checkModuleDependency(final Module module, final Collection<ModuleId> rootModules) {
+        for (ModuleId rootModule : rootModules) {
+            if (rootModule.equals(new ModuleId(module.getName(), module.getRevision()))) {
+                return true;
+            }
+
+            //handling/checking imports regarding root modules
+            for (ModuleImport moduleImport : module.getImports()) {
+                if (moduleImport.getModuleName().equals(rootModule.getName())) {
+                    return moduleImport.getRevision().isEmpty()
+                            || moduleImport.getRevision().equals(rootModule.getRev());
+                }
+            }
+
+            //submodules handling
+            for (Module moduleSub : module.getSubmodules()) {
+                return checkModuleDependency(moduleSub, rootModules);
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("SchemaContextProxyImpl{filteredModules=%s}", filteredModules);
+    }
+
+    public static final class ModuleId {
+        private final String name;
+        private final Revision rev;
+
+        public ModuleId(final String name, final Optional<Revision> rev) {
+            checkArgument(!Strings.isNullOrEmpty(name), "No module dependency name given. Nothing to do.");
+            this.name = name;
+            checkArgument(rev.isPresent(), "No revision date given. Nothing to do.");
+            this.rev = rev.get();
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Optional<Revision> getRev() {
+            return Optional.ofNullable(rev);
+        }
+
+        public static final Function<Module, ModuleId> MODULE_TO_MODULE_ID = input -> new ModuleId(input.getName(),
+            input.getRevision());
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof ModuleId)) {
+                return false;
+            }
+
+            ModuleId moduleId = (ModuleId) obj;
+            if (name != null ? !name.equals(moduleId.name) : moduleId.name != null) {
+                return false;
+            }
+            if (rev != null ? !rev.equals(moduleId.rev) : moduleId.rev != null) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = name != null ? name.hashCode() : 0;
+            result = 31 * result + (rev != null ? rev.hashCode() : 0);
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("ModuleId{name='%s', rev=%s}",name,rev);
+        }
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/ModuleDependencySort.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/ModuleDependencySort.java
new file mode 100644 (file)
index 0000000..15f6d9b
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * 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.model.util;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Table;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import org.opendaylight.yangtools.util.TopologicalSort;
+import org.opendaylight.yangtools.util.TopologicalSort.Node;
+import org.opendaylight.yangtools.util.TopologicalSort.NodeImpl;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Creates a module dependency graph from provided {@link Module}s and provides a {@link #sort(Collection)} method.
+ * It is topological sort and returns modules in order in which they should be processed (e.g. if A imports B, sort
+ * returns {B, A}).
+ */
+@Beta
+public final class ModuleDependencySort {
+    private static final Logger LOG = LoggerFactory.getLogger(ModuleDependencySort.class);
+
+    /**
+     * It is not desirable to instance this class.
+     */
+    private ModuleDependencySort() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Topological sort of module dependency graph.
+     *
+     * @param modules YANG modules
+     * @return Sorted list of Modules. Modules can be further processed in returned order.
+     * @throws IllegalArgumentException when provided modules are not consistent.
+     */
+    public static List<Module> sort(final Module... modules) {
+        return sort(Arrays.asList(modules));
+    }
+
+    /**
+     * Topological sort of module dependency graph.
+     *
+     * @param modules YANG modules
+     * @return Sorted list of Modules. Modules can be further processed in returned order.
+     * @throws IllegalArgumentException when provided modules are not consistent.
+     */
+    public static List<Module> sort(final Collection<Module> modules) {
+        final List<Node> sorted = sortInternal(modules);
+        // Cast to Module from Node and return
+        return Lists.transform(sorted, input -> input == null ? null : ((ModuleNodeImpl) input).getReference());
+    }
+
+    private static List<Node> sortInternal(final Collection<Module> modules) {
+        final Table<String, Optional<Revision>, ModuleNodeImpl> moduleGraph = createModuleGraph(modules);
+        return TopologicalSort.sort(new HashSet<>(moduleGraph.values()));
+    }
+
+    private static Table<String, Optional<Revision>, ModuleNodeImpl> createModuleGraph(
+            final Collection<Module> builders) {
+        final Table<String, Optional<Revision>, ModuleNodeImpl> moduleGraph = HashBasedTable.create();
+
+        processModules(moduleGraph, builders);
+        processDependencies(moduleGraph, builders);
+
+        return moduleGraph;
+    }
+
+    /**
+     * Extract module:revision from modules.
+     */
+    private static void processDependencies(final Table<String, Optional<Revision>, ModuleNodeImpl> moduleGraph,
+            final Collection<Module> mmbs) {
+        final Map<URI, Module> allNS = new HashMap<>();
+
+        // Create edges in graph
+        for (final Module module : mmbs) {
+            final Map<String, Optional<Revision>> imported = new HashMap<>();
+            final String fromName = module.getName();
+            final URI ns = module.getNamespace();
+            final Optional<Revision> fromRevision = module.getRevision();
+
+            // check for existence of module with same namespace
+            final Module prev = allNS.putIfAbsent(ns, module);
+            if (prev != null) {
+                final String name = prev.getName();
+                if (!fromName.equals(name)) {
+                    LOG.warn("Error while sorting module [{}, {}]: module with same namespace ({}) already loaded:"
+                        + " [{}, {}]", fromName, fromRevision, ns, name, prev.getRevision());
+                }
+            }
+
+            // no need to check if other Type of object, check is performed in process modules
+            for (final ModuleImport imprt : allImports(module)) {
+                final String toName = imprt.getModuleName();
+                final Optional<Revision> toRevision = imprt.getRevision();
+
+                final ModuleNodeImpl from = moduleGraph.get(fromName, fromRevision);
+                final ModuleNodeImpl to = getModuleByNameAndRevision(moduleGraph, fromName, fromRevision, toName,
+                    toRevision);
+
+                /*
+                 * If it is an yang 1 module, check imports: If module is imported twice with different
+                 * revisions then throw exception
+                 */
+                if (module.getYangVersion() == YangVersion.VERSION_1) {
+                    final Optional<Revision> impRevision = imported.get(toName);
+                    if (impRevision != null && impRevision.isPresent() && !impRevision.equals(toRevision)
+                            && toRevision.isPresent()) {
+                        throw new IllegalArgumentException(String.format(
+                            "Module:%s imported twice with different revisions:%s, %s", toName,
+                            formatRevDate(impRevision), formatRevDate(toRevision)));
+                    }
+                }
+
+                imported.put(toName, toRevision);
+
+                from.addEdge(to);
+            }
+        }
+    }
+
+    private static Collection<ModuleImport> allImports(final Module mod) {
+        if (mod.getSubmodules().isEmpty()) {
+            return mod.getImports();
+        }
+
+        final Collection<ModuleImport> concat = new LinkedHashSet<>();
+        concat.addAll(mod.getImports());
+        for (Module sub : mod.getSubmodules()) {
+            concat.addAll(sub.getImports());
+        }
+        return concat;
+    }
+
+    /**
+     * Get imported module by its name and revision from moduleGraph.
+     */
+    private static ModuleNodeImpl getModuleByNameAndRevision(
+            final Table<String, Optional<Revision>, ModuleNodeImpl> moduleGraph, final String fromName,
+            final Optional<Revision> fromRevision, final String toName, final Optional<Revision> toRevision) {
+
+        final ModuleNodeImpl exact = moduleGraph.get(toName, toRevision);
+        if (exact != null) {
+            return exact;
+        }
+
+        // If revision is not specified in import, but module exists with different revisions, take first one
+        if (toRevision.isEmpty()) {
+            final Map<Optional<Revision>, ModuleNodeImpl> modulerevs = moduleGraph.row(toName);
+
+            if (!modulerevs.isEmpty()) {
+                final ModuleNodeImpl first = modulerevs.values().iterator().next();
+                if (LOG.isTraceEnabled()) {
+                    LOG.trace("Import:{}:{} by module:{}:{} does not specify revision, using:{}:{}"
+                            + " for module dependency sort", toName, formatRevDate(toRevision), fromName,
+                            formatRevDate(fromRevision), first.getName(), formatRevDate(first.getRevision()));
+                }
+                return first;
+            }
+        }
+
+        LOG.warn("Not existing module imported:{}:{} by:{}:{}", toName, formatRevDate(toRevision), fromName,
+            formatRevDate(fromRevision));
+        LOG.warn("Available models: {}", moduleGraph);
+        throw new IllegalArgumentException(String.format("Not existing module imported:%s:%s by:%s:%s", toName,
+            formatRevDate(toRevision), fromName, formatRevDate(fromRevision)));
+    }
+
+    /**
+     * Extract dependencies from modules to fill dependency graph.
+     */
+    private static void processModules(final Table<String, Optional<Revision>, ModuleNodeImpl> moduleGraph,
+            final Iterable<Module> modules) {
+
+        // Process nodes
+        for (final Module momb : modules) {
+
+            final String name = momb.getName();
+            final Optional<Revision> rev = momb.getRevision();
+            final Map<Optional<Revision>, ModuleNodeImpl> revs = moduleGraph.row(name);
+            if (revs.containsKey(rev)) {
+                throw new IllegalArgumentException(String.format("Module:%s with revision:%s declared twice", name,
+                    formatRevDate(rev)));
+            }
+
+            revs.put(rev, new ModuleNodeImpl(name, rev.orElse(null), momb));
+        }
+    }
+
+    private static String formatRevDate(final Optional<Revision> rev) {
+        return rev.map(Revision::toString).orElse("default");
+    }
+
+    private static final class ModuleNodeImpl extends NodeImpl {
+        private final String name;
+        private final Revision revision;
+        private final Module originalObject;
+
+        ModuleNodeImpl(final String name, final Revision revision, final Module module) {
+            this.name = name;
+            this.revision = revision;
+            this.originalObject = module;
+        }
+
+        String getName() {
+            return name;
+        }
+
+        Optional<Revision> getRevision() {
+            return Optional.ofNullable(revision);
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + Objects.hashCode(name);
+            result = prime * result + Objects.hashCode(revision);
+            return result;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            final ModuleNodeImpl other = (ModuleNodeImpl) obj;
+            if (name == null) {
+                if (other.name != null) {
+                    return false;
+                }
+            } else if (!name.equals(other.name)) {
+                return false;
+            }
+            if (revision == null) {
+                if (other.revision != null) {
+                    return false;
+                }
+            } else if (!revision.equals(other.revision)) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "Module [name=" + name + ", revision=" + formatRevDate(getRevision()) + "]";
+        }
+
+        public Module getReference() {
+            return originalObject;
+        }
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/ModuleNameNamespaceContext.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/ModuleNameNamespaceContext.java
new file mode 100644 (file)
index 0000000..d5e10a2
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableBiMap.Builder;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.opendaylight.yangtools.yang.common.BiMapYangNamespaceContext;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+/**
+ * Utility {@link YangNamespaceContext} backed by a SchemaContext, resolving namespaces to their module names. This
+ * is useful for implementing namespace resolution according to
+ * <a href="https://tools.ietf.org/html/rfc7951#section-4">RFC7951 Section 4</a>.
+ *
+ * <p>
+ * When multiple revisions of a particular namespace are present in the backing SchemaContext, this ambiguity is
+ * resolved by using the latest revision available.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class ModuleNameNamespaceContext implements YangNamespaceContext, SchemaContextProvider {
+    private static final long serialVersionUID = 1L;
+
+    @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "Handled through writeReplace()")
+    private final SchemaContext schemaContext;
+
+    public ModuleNameNamespaceContext(final SchemaContext schemaContext) {
+        this.schemaContext = requireNonNull(schemaContext);
+    }
+
+    /**
+     * Convert this object to an equivalent {@link BiMapYangNamespaceContext}.
+     *
+     * @return A BiMapYangNamespaceContext.
+     */
+    public BiMapYangNamespaceContext toBiMap() {
+        final Builder<String, QNameModule> builder = ImmutableBiMap.builder();
+        for (String name : schemaContext.getModules().stream().map(Module::getName).collect(Collectors.toSet())) {
+            builder.put(name, findNamespaceForPrefix(name).get());
+        }
+        return new BiMapYangNamespaceContext(builder.build());
+    }
+
+    @Override
+    public SchemaContext getSchemaContext() {
+        return schemaContext;
+    }
+
+    @Override
+    public Optional<QNameModule> findNamespaceForPrefix(final String prefix) {
+        return schemaContext.findModules(prefix).stream().findFirst().map(Module::getQNameModule);
+    }
+
+    @Override
+    public Optional<String> findPrefixForNamespace(final QNameModule namespace) {
+        return schemaContext.findModule(namespace).map(Module::getName);
+    }
+
+    private Object writeReplace() {
+        return toBiMap();
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java
new file mode 100644 (file)
index 0000000..ca35335
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.util;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+
+/**
+ * A simple XPathExpression implementation.
+ *
+ * @deprecated This is a transitional class to transition from {@link RevisionAwareXPathImpl}. Users are advised to
+ *             supply their own implementation of PathExpression.
+ */
+@Deprecated
+@NonNullByDefault
+public final class PathExpressionImpl extends AbstractPathExpression {
+    private final @Nullable YangLocationPath location;
+    private final boolean absolute;
+
+    @SuppressFBWarnings(value = "NP_STORE_INTO_NONNULL_FIELD", justification = "Non-grok on SpotBugs part")
+    public PathExpressionImpl(final String xpath, final boolean absolute) {
+        super(xpath);
+        this.absolute = absolute;
+        this.location = null;
+    }
+
+    public PathExpressionImpl(final String xpath, final YangLocationPath location) {
+        super(xpath);
+        this.absolute = location.isAbsolute();
+        this.location = location;
+    }
+
+    @Override
+    public boolean isAbsolute() {
+        return absolute;
+    }
+
+    @Override
+    public YangLocationPath getLocation() {
+        final YangLocationPath loc = location;
+        if (loc == null) {
+            throw new UnsupportedOperationException("Location has not been provided");
+        }
+        return loc;
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper.add("absolute", absolute).add("location", location));
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PatternConstraintImpl.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PatternConstraintImpl.java
new file mode 100644 (file)
index 0000000..85af82b
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import java.util.Objects;
+import java.util.Optional;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+
+/**
+ * {@link Immutable} implementation of {@link PatternConstraint}.
+ *
+ * <p>
+ * Creates an instance of Range constraint based on supplied parameters with
+ * additional behaviour:
+ * <ul>
+ * <li>{@link PatternConstraint#getErrorAppTag()} returns
+ * <code>invalid-regular-expression</code>
+ * </ul>
+ */
+final class PatternConstraintImpl implements PatternConstraint, Immutable {
+    private final String regex;
+    private final String description;
+    private final String reference;
+
+    private final String errorAppTag;
+    private final String errorMessage;
+    private final ModifierKind modifier;
+
+    PatternConstraintImpl(final String regex, final Optional<String> description, final Optional<String> reference) {
+        this(regex, description, reference, null, null, Optional.empty());
+    }
+
+    PatternConstraintImpl(final String regex, final Optional<String> description, final Optional<String> reference,
+            final String errorAppTag, final String errorMessage, final Optional<ModifierKind> modifier) {
+        this.regex = requireNonNull(regex, "regex must not be null.");
+        this.description = description.orElse(null);
+        this.reference = reference.orElse(null);
+        this.errorAppTag = errorAppTag != null ? errorAppTag : "invalid-regular-expression";
+        this.errorMessage = errorMessage;
+        this.modifier = modifier.orElse(null);
+    }
+
+    @Override
+    public Optional<String> getDescription() {
+        return Optional.ofNullable(description);
+    }
+
+    @Override
+    public Optional<String> getErrorAppTag() {
+        return Optional.ofNullable(errorAppTag);
+    }
+
+    @Override
+    public Optional<String> getErrorMessage() {
+        return Optional.ofNullable(errorMessage);
+    }
+
+    @Override
+    public Optional<String> getReference() {
+        return Optional.ofNullable(reference);
+    }
+
+    @Override
+    public String getJavaPatternString() {
+        return regex;
+    }
+
+    @Override
+    public String getRegularExpressionString() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Optional<ModifierKind> getModifier() {
+        return Optional.ofNullable(modifier);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(description, errorAppTag, errorMessage, reference, regex, modifier);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof PatternConstraintImpl)) {
+            return false;
+        }
+        final PatternConstraintImpl other = (PatternConstraintImpl) obj;
+        return Objects.equals(description, other.description) && Objects.equals(errorAppTag, other.errorAppTag)
+                && Objects.equals(errorMessage, other.errorMessage) && Objects.equals(reference, other.reference)
+                && Objects.equals(regex, other.regex) && Objects.equals(modifier, other.modifier);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).omitNullValues().add("regex", regex).add("description", description)
+                .add("reference", reference).add("errorAppTag", errorAppTag).add("errorMessage", errorMessage)
+                .add("modifier", modifier).toString();
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/RegexUtils.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/RegexUtils.java
new file mode 100644 (file)
index 0000000..a5e5d70
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2017 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.util;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utilities for converting YANG XSD regexes into Java-compatible regexes.
+ */
+public final class RegexUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(RegexUtils.class);
+    private static final Pattern BETWEEN_CURLY_BRACES_PATTERN = Pattern.compile("\\{(.+?)\\}");
+    private static final ImmutableSet<String> JAVA_UNICODE_BLOCKS = ImmutableSet.<String>builder()
+            .add("AegeanNumbers")
+            .add("AlchemicalSymbols")
+            .add("AlphabeticPresentationForms")
+            .add("AncientGreekMusicalNotation")
+            .add("AncientGreekNumbers")
+            .add("AncientSymbols")
+            .add("Arabic")
+            .add("ArabicPresentationForms-A")
+            .add("ArabicPresentationForms-B")
+            .add("ArabicSupplement")
+            .add("Armenian")
+            .add("Arrows")
+            .add("Avestan")
+            .add("Balinese")
+            .add("Bamum")
+            .add("BamumSupplement")
+            .add("BasicLatin")
+            .add("Batak")
+            .add("Bengali")
+            .add("BlockElements")
+            .add("Bopomofo")
+            .add("BopomofoExtended")
+            .add("BoxDrawing")
+            .add("Brahmi")
+            .add("BraillePatterns")
+            .add("Buginese")
+            .add("Buhid")
+            .add("ByzantineMusicalSymbols")
+            .add("Carian")
+            .add("Cham")
+            .add("Cherokee")
+            .add("CJKCompatibility")
+            .add("CJKCompatibilityForms")
+            .add("CJKCompatibilityIdeographs")
+            .add("CJKCompatibilityIdeographsSupplement")
+            .add("CJKRadicalsSupplement")
+            .add("CJKStrokes")
+            .add("CJKSymbolsandPunctuation")
+            .add("CJKUnifiedIdeographs")
+            .add("CJKUnifiedIdeographsExtensionA")
+            .add("CJKUnifiedIdeographsExtensionB")
+            .add("CJKUnifiedIdeographsExtensionC")
+            .add("CJKUnifiedIdeographsExtensionD")
+            .add("CombiningDiacriticalMarks")
+            .add("CombiningDiacriticalMarksSupplement")
+            .add("CombiningHalfMarks")
+            .add("CombiningDiacriticalMarksforSymbols")
+            .add("CommonIndicNumberForms")
+            .add("ControlPictures")
+            .add("Coptic")
+            .add("CountingRodNumerals")
+            .add("Cuneiform")
+            .add("CuneiformNumbersandPunctuation")
+            .add("CurrencySymbols")
+            .add("CypriotSyllabary")
+            .add("Cyrillic")
+            .add("CyrillicExtended-A")
+            .add("CyrillicExtended-B")
+            .add("CyrillicSupplementary")
+            .add("Deseret")
+            .add("Devanagari")
+            .add("DevanagariExtended")
+            .add("Dingbats")
+            .add("DominoTiles")
+            .add("EgyptianHieroglyphs")
+            .add("Emoticons")
+            .add("EnclosedAlphanumericSupplement")
+            .add("EnclosedAlphanumerics")
+            .add("EnclosedCJKLettersandMonths")
+            .add("EnclosedIdeographicSupplement")
+            .add("Ethiopic")
+            .add("EthiopicExtended")
+            .add("EthiopicExtended-A")
+            .add("EthiopicSupplement")
+            .add("GeneralPunctuation")
+            .add("GeometricShapes")
+            .add("Georgian")
+            .add("GeorgianSupplement")
+            .add("Glagolitic")
+            .add("Gothic")
+            .add("GreekandCoptic")
+            .add("GreekExtended")
+            .add("Gujarati")
+            .add("Gurmukhi")
+            .add("HalfwidthandFullwidthForms")
+            .add("HangulCompatibilityJamo")
+            .add("HangulJamo")
+            .add("HangulJamoExtended-A")
+            .add("HangulJamoExtended-B")
+            .add("HangulSyllables")
+            .add("Hanunoo")
+            .add("Hebrew")
+            .add("HighPrivateUseSurrogates")
+            .add("HighSurrogates")
+            .add("Hiragana")
+            .add("IdeographicDescriptionCharacters")
+            .add("ImperialAramaic")
+            .add("InscriptionalPahlavi")
+            .add("InscriptionalParthian")
+            .add("IPAExtensions")
+            .add("Javanese")
+            .add("Kaithi")
+            .add("KanaSupplement")
+            .add("Kanbun")
+            .add("Kangxi Radicals")
+            .add("Kannada")
+            .add("Katakana")
+            .add("KatakanaPhoneticExtensions")
+            .add("KayahLi")
+            .add("Kharoshthi")
+            .add("Khmer")
+            .add("KhmerSymbols")
+            .add("Lao")
+            .add("Latin-1Supplement")
+            .add("LatinExtended-A")
+            .add("LatinExtendedAdditional")
+            .add("LatinExtended-B")
+            .add("LatinExtended-C")
+            .add("LatinExtended-D")
+            .add("Lepcha")
+            .add("LetterlikeSymbols")
+            .add("Limbu")
+            .add("LinearBIdeograms")
+            .add("LinearBSyllabary")
+            .add("Lisu")
+            .add("LowSurrogates")
+            .add("Lycian")
+            .add("Lydian")
+            .add("MahjongTiles")
+            .add("Malayalam")
+            .add("Mandaic")
+            .add("MathematicalAlphanumericSymbols")
+            .add("MathematicalOperators")
+            .add("MeeteiMayek")
+            .add("MiscellaneousMathematicalSymbols-A")
+            .add("MiscellaneousMathematicalSymbols-B")
+            .add("MiscellaneousSymbols")
+            .add("MiscellaneousSymbolsandArrows")
+            .add("MiscellaneousSymbolsAndPictographs")
+            .add("MiscellaneousTechnical")
+            .add("ModifierToneLetters")
+            .add("Mongolian")
+            .add("MusicalSymbols")
+            .add("Myanmar")
+            .add("MyanmarExtended-A")
+            .add("NewTaiLue")
+            .add("NKo")
+            .add("NumberForms")
+            .add("Ogham")
+            .add("OlChiki")
+            .add("OldItalic")
+            .add("OldPersian")
+            .add("OldSouthArabian")
+            .add("OldTurkic")
+            .add("OpticalCharacterRecognition")
+            .add("Oriya")
+            .add("Osmanya")
+            .add("Phags-pa")
+            .add("PhaistosDisc")
+            .add("Phoenician")
+            .add("PhoneticExtensions")
+            .add("PhoneticExtensionsSupplement")
+            .add("PlayingCards")
+            .add("PrivateUseArea")
+            .add("Rejang")
+            .add("RumiNumeralSymbols")
+            .add("Runic")
+            .add("Samaritan")
+            .add("Saurashtra")
+            .add("Shavian")
+            .add("Sinhala")
+            .add("SmallFormVariants")
+            .add("SpacingModifierLetters")
+            .add("Specials")
+            .add("Sundanese")
+            .add("SuperscriptsandSubscripts")
+            .add("SupplementalArrows-A")
+            .add("SupplementalArrows-B")
+            .add("SupplementalMathematicalOperators")
+            .add("SupplementalPunctuation")
+            .add("SupplementaryPrivateUseArea-A")
+            .add("SupplementaryPrivateUseArea-B")
+            .add("SylotiNagri")
+            .add("Syriac")
+            .add("Tagalog")
+            .add("Tagbanwa")
+            .add("Tags")
+            .add("TaiLe")
+            .add("TaiTham")
+            .add("TaiViet")
+            .add("TaiXuanJingSymbols")
+            .add("Tamil")
+            .add("Telugu")
+            .add("Thaana")
+            .add("Thai")
+            .add("Tibetan")
+            .add("Tifinagh")
+            .add("TransportAndMapSymbols")
+            .add("Ugaritic")
+            .add("UnifiedCanadianAboriginalSyllabics")
+            .add("UnifiedCanadianAboriginalSyllabicsExtended")
+            .add("Vai")
+            .add("VariationSelectors")
+            .add("VariationSelectorsSupplement")
+            .add("VedicExtensions")
+            .add("VerticalForms")
+            .add("YiRadicals")
+            .add("YiSyllables")
+            .add("YijingHexagramSymbols").build();
+
+    private static final int UNICODE_SCRIPT_FIX_COUNTER = 30;
+
+    private RegexUtils() {
+        throw new UnsupportedOperationException("Utility class should not be instantiated.");
+    }
+
+    /**
+     * Converts XSD regex to Java-compatible regex.
+     *
+     * @param xsdRegex XSD regex pattern as it is defined in a YANG source
+     * @return Java-compatible regex
+     */
+    public static String getJavaRegexFromXSD(final String xsdRegex) {
+        // Note: we are using a non-capturing group to deal with internal structure issues, like branches and similar.
+        return "^(?:" + fixUnicodeScriptPattern(escapeChars(xsdRegex)) + ")$";
+    }
+
+    /*
+     * As both '^' and '$' are special anchor characters in java regular
+     * expressions which are implicitly present in XSD regular expressions,
+     * we need to escape them in case they are not defined as part of
+     * character ranges i.e. inside regular square brackets.
+     */
+    private static String escapeChars(final String regex) {
+        final StringBuilder result = new StringBuilder(regex.length());
+        int bracket = 0;
+        boolean escape = false;
+        for (int i = 0; i < regex.length(); i++) {
+            final char ch = regex.charAt(i);
+            switch (ch) {
+                case '[':
+                    if (!escape) {
+                        bracket++;
+                    }
+                    escape = false;
+                    result.append(ch);
+                    break;
+                case ']':
+                    if (!escape) {
+                        bracket--;
+                    }
+                    escape = false;
+                    result.append(ch);
+                    break;
+                case '\\':
+                    escape = !escape;
+                    result.append(ch);
+                    break;
+                case '^':
+                case '$':
+                    if (bracket == 0) {
+                        result.append('\\');
+                    }
+                    escape = false;
+                    result.append(ch);
+                    break;
+                default:
+                    escape = false;
+                    result.append(ch);
+            }
+        }
+        return result.toString();
+    }
+
+    private static String fixUnicodeScriptPattern(String rawPattern) {
+        for (int i = 0; i < UNICODE_SCRIPT_FIX_COUNTER; i++) {
+            try {
+                Pattern.compile(rawPattern);
+                return rawPattern;
+            } catch (final PatternSyntaxException ex) {
+                LOG.debug("Invalid regex pattern syntax in: {}", rawPattern, ex);
+                final String msg = ex.getMessage();
+                if (msg.startsWith("Unknown character script name")
+                        || msg.startsWith("Unknown character property name")) {
+                    rawPattern = fixUnknownScripts(msg, rawPattern);
+                } else {
+                    return rawPattern;
+                }
+            }
+        }
+
+        LOG.warn("Regex pattern could not be fixed: {}", rawPattern);
+        return rawPattern;
+    }
+
+    private static String fixUnknownScripts(final String exMessage, final String rawPattern) {
+        StringBuilder result = new StringBuilder(rawPattern);
+        final Matcher matcher = BETWEEN_CURLY_BRACES_PATTERN.matcher(exMessage);
+        if (matcher.find()) {
+            String capturedGroup = matcher.group(1);
+            if (capturedGroup.startsWith("In/Is")) {
+                // Java 9 changed the reporting string
+                capturedGroup = capturedGroup.substring(5);
+            }
+
+            if (JAVA_UNICODE_BLOCKS.contains(capturedGroup)) {
+                final int idx = rawPattern.indexOf("Is" + capturedGroup);
+                result = result.replace(idx, idx + 2, "In");
+            }
+        }
+        return result.toString();
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/RevisionAwareXPathImpl.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/RevisionAwareXPathImpl.java
new file mode 100644 (file)
index 0000000..a4e81b7
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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.model.util;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+
+/**
+ * The <code>helper</code> implementation of Instance Revision Aware XPath interface.
+ *
+ * @see RevisionAwareXPath
+ */
+public class RevisionAwareXPathImpl implements RevisionAwareXPath {
+    private final @NonNull String xpath;
+    private final boolean absolute;
+
+    public RevisionAwareXPathImpl(final String xpath, final boolean absolute) {
+        this.xpath = requireNonNull(xpath);
+        this.absolute = absolute;
+    }
+
+    @Override
+    public boolean isAbsolute() {
+        return absolute;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(xpath);
+        result = prime * result + Boolean.hashCode(absolute);
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        RevisionAwareXPathImpl other = (RevisionAwareXPathImpl) obj;
+        return absolute == other.absolute && Objects.equals(xpath, other.xpath);
+    }
+
+    @Override
+    public String getOriginalString() {
+        return xpath;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java
new file mode 100644 (file)
index 0000000..7aa3984
--- /dev/null
@@ -0,0 +1,853 @@
+/*
+ * 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.model.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.regex.Pattern;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.DerivableSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Schema Context Util contains support methods for searching through Schema Context modules for specified schema
+ * nodes via Schema Path or Revision Aware XPath. The Schema Context Util is designed as mixin, so it is not
+ * instantiable.
+ */
+public final class SchemaContextUtil {
+    private static final Logger LOG = LoggerFactory.getLogger(SchemaContextUtil.class);
+    private static final Splitter COLON_SPLITTER = Splitter.on(':');
+    private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings();
+
+    private SchemaContextUtil() {
+    }
+
+    /**
+     * Method attempts to find DataSchemaNode in Schema Context via specified
+     * Schema Path. The returned DataSchemaNode from method will be the node at
+     * the end of the SchemaPath. If the DataSchemaNode is not present in the
+     * Schema Context the method will return <code>null</code>. <br>
+     * In case that Schema Context or Schema Path are not specified correctly
+     * (i.e. contains <code>null</code> values) the method will throw
+     * IllegalArgumentException.
+     *
+     * @param context
+     *            Schema Context
+     * @param schemaPath
+     *            Schema Path to search for
+     * @return SchemaNode from the end of the Schema Path or <code>null</code>
+     *         if the Node is not present.
+     * @throws NullPointerException if context or schemaPath is null
+     */
+    public static SchemaNode findDataSchemaNode(final SchemaContext context, final SchemaPath schemaPath) {
+        final Iterable<QName> prefixedPath = schemaPath.getPathFromRoot();
+        if (prefixedPath == null) {
+            LOG.debug("Schema path {} has null path", schemaPath);
+            return null;
+        }
+
+        LOG.trace("Looking for path {} in context {}", schemaPath, context);
+        return findNodeInSchemaContext(context, prefixedPath);
+    }
+
+    /**
+     * Method attempts to find DataSchemaNode inside of provided Schema Context
+     * and Yang Module accordingly to Non-conditional Revision Aware XPath. The
+     * specified Module MUST be present in Schema Context otherwise the
+     * operation would fail and return <code>null</code>. <br>
+     * The Revision Aware XPath MUST be specified WITHOUT the conditional
+     * statement (i.e. without [cond]) in path, because in this state the Schema
+     * Context is completely unaware of data state and will be not able to
+     * properly resolve XPath. If the XPath contains condition the method will
+     * return IllegalArgumentException. <br>
+     * If the Revision Aware XPath is correct and desired Data Schema Node is
+     * present in Yang module or in depending module in Schema Context the
+     * method will return specified Data Schema Node, otherwise the operation
+     * will fail and method will return <code>null</code>.
+     *
+     * @param context
+     *            Schema Context
+     * @param module
+     *            Yang Module
+     * @param nonCondXPath
+     *            Non Conditional Revision Aware XPath
+     * @return Returns Data Schema Node for specified Schema Context for given
+     *         Non-conditional Revision Aware XPath, or <code>null</code> if the
+     *         DataSchemaNode is not present in Schema Context.
+     * @throws NullPointerException if any of the arguments is null
+     */
+    // FIXME: This entire method is ill-defined, as the resolution process depends on  where the XPath is defined --
+    //        notably RPCs, actions and notifications modify the data tree temporarily. See sections 6.4.1 and 9.9.2
+    //        of RFC7950.
+    //
+    //        Most notably we need to understand whether the XPath is being resolved in the data tree, or as part of
+    //        a notification/action/RPC, as then the SchemaContext grows tentative nodes ... which could be addressed
+    //        via a derived SchemaContext (i.e. this class would have to have a
+    //
+    //            SchemaContext notificationSchemaContext(SchemaContext delegate, NotificationDefinition notif)
+    //
+    //        which would then be passed in to a method similar to this one. In static contexts, like MD-SAL codegen,
+    //        that feels like an overkill.
+    public static SchemaNode findDataSchemaNode(final SchemaContext context, final Module module,
+            final PathExpression nonCondXPath) {
+        requireNonNull(context, "context");
+        requireNonNull(module, "module");
+
+        final String strXPath = nonCondXPath.getOriginalString();
+        checkArgument(strXPath.indexOf('[') == -1, "Revision Aware XPath may not contain a condition");
+        if (nonCondXPath.isAbsolute()) {
+            final List<QName> path = xpathToQNamePath(context, module, strXPath);
+
+            // We do not have enough information about resolution context, hence cannot account for actions, RPCs
+            // and notifications. We therefore attempt to make a best estimate, but this can still fail.
+            final Optional<DataSchemaNode> pureData = context.findDataTreeChild(path);
+            return pureData.isPresent() ? pureData.get() : findNodeInSchemaContext(context, path);
+        }
+        return null;
+    }
+
+    /**
+     * Method attempts to find DataSchemaNode inside of provided Schema Context
+     * and Yang Module accordingly to Non-conditional relative Revision Aware
+     * XPath. The specified Module MUST be present in Schema Context otherwise
+     * the operation would fail and return <code>null</code>. <br>
+     * The relative Revision Aware XPath MUST be specified WITHOUT the
+     * conditional statement (i.e. without [cond]) in path, because in this
+     * state the Schema Context is completely unaware of data state and will be
+     * not able to properly resolve XPath. If the XPath contains condition the
+     * method will return IllegalArgumentException. <br>
+     * The Actual Schema Node MUST be specified correctly because from this
+     * Schema Node will search starts. If the Actual Schema Node is not correct
+     * the operation will simply fail, because it will be unable to find desired
+     * DataSchemaNode. <br>
+     * If the Revision Aware XPath doesn't have flag
+     * <code>isAbsolute == false</code> the method will throw
+     * IllegalArgumentException. <br>
+     * If the relative Revision Aware XPath is correct and desired Data Schema
+     * Node is present in Yang module or in depending module in Schema Context
+     * the method will return specified Data Schema Node, otherwise the
+     * operation will fail and method will return <code>null</code>.
+     *
+     * @param context
+     *            Schema Context
+     * @param module
+     *            Yang Module
+     * @param actualSchemaNode
+     *            Actual Schema Node
+     * @param relativeXPath
+     *            Relative Non Conditional Revision Aware XPath
+     * @return DataSchemaNode if is present in specified Schema Context for
+     *         given relative Revision Aware XPath, otherwise will return
+     *         <code>null</code>.
+     * @throws NullPointerException if any argument is null
+     */
+    // FIXME: This entire method is ill-defined, as the resolution process depends on  where the XPath is defined --
+    //        notably RPCs, actions and notifications modify the data tree temporarily. See sections 6.4.1 and 9.9.2
+    //        of RFC7950.
+    //
+    //        Most notably we need to understand whether the XPath is being resolved in the data tree, or as part of
+    //        a notification/action/RPC, as then the SchemaContext grows tentative nodes ... which could be addressed
+    //        via a derived SchemaContext (i.e. this class would have to have a
+    //
+    //            SchemaContext notificationSchemaContext(SchemaContext delegate, NotificationDefinition notif)
+    //
+    //        which would then be passed in to a method similar to this one. In static contexts, like MD-SAL codegen,
+    //        that feels like an overkill.
+    public static SchemaNode findDataSchemaNodeForRelativeXPath(final SchemaContext context, final Module module,
+            final SchemaNode actualSchemaNode, final PathExpression relativeXPath) {
+        checkState(!relativeXPath.isAbsolute(), "Revision Aware XPath MUST be relative i.e. MUST contains ../, "
+                + "for non relative Revision Aware XPath use findDataSchemaNode method");
+
+        final Iterable<QName> qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualSchemaNode);
+
+        // We do not have enough information about resolution context, hence cannot account for actions, RPCs
+        // and notifications. We therefore attempt to make a best estimate, but this can still fail.
+        final Optional<DataSchemaNode> pureData = context.findDataTreeChild(qnamePath);
+        return pureData.isPresent() ? pureData.get() : findNodeInSchemaContext(context, qnamePath);
+    }
+
+    /**
+     * Returns parent Yang Module for specified Schema Context in which Schema
+     * Node is declared. If the Schema Node is not present in Schema Context the
+     * operation will return <code>null</code>.
+     *
+     * @param context Schema Context
+     * @param schemaNode Schema Node
+     * @return Yang Module for specified Schema Context and Schema Node, if Schema Node is NOT present, the method will
+     *         return <code>null</code>
+     * @throws NullPointerException if any of the arguments is null
+     */
+    public static Module findParentModule(final SchemaContext context, final SchemaNode schemaNode) {
+        final QName qname = schemaNode.getPath().getLastComponent();
+        checkState(qname != null, "Schema Path contains invalid state of path parts. "
+                + "The Schema Path MUST contain at least ONE QName  which defines namespace and Local name of path.");
+        return context.findModule(qname.getModule()).orElse(null);
+    }
+
+    public static SchemaNode findNodeInSchemaContext(final SchemaContext context, final Iterable<QName> path) {
+        final QName current = path.iterator().next();
+
+        LOG.trace("Looking up module {} in context {}", current, path);
+        final Optional<Module> module = context.findModule(current.getModule());
+        if (module.isEmpty()) {
+            LOG.debug("Module {} not found", current);
+            return null;
+        }
+
+        return findNodeInModule(module.get(), path);
+    }
+
+    /**
+     * Returns NotificationDefinition from Schema Context.
+     *
+     * @param schema SchemaContext in which lookup should be performed.
+     * @param path Schema Path of notification
+     * @return Notification schema or null, if notification is not present in schema context.
+     */
+    @Beta
+    public static @Nullable NotificationDefinition getNotificationSchema(final @NonNull SchemaContext schema,
+            final @NonNull SchemaPath path) {
+        requireNonNull(schema, "Schema context must not be null.");
+        requireNonNull(path, "Schema path must not be null.");
+        for (final NotificationDefinition potential : schema.getNotifications()) {
+            if (path.equals(potential.getPath())) {
+                return potential;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns RPC Input or Output Data container from RPC definition.
+     *
+     * @param schema SchemaContext in which lookup should be performed.
+     * @param path Schema path of RPC input/output data container
+     * @return Notification schema or null, if notification is not present in schema context.
+     */
+    @Beta
+    public static @Nullable ContainerSchemaNode getRpcDataSchema(final @NonNull SchemaContext schema,
+            final @NonNull SchemaPath path) {
+        requireNonNull(schema, "Schema context must not be null.");
+        requireNonNull(path, "Schema path must not be null.");
+        final Iterator<QName> it = path.getPathFromRoot().iterator();
+        checkArgument(it.hasNext(), "Rpc must have QName.");
+        final QName rpcName = it.next();
+        checkArgument(it.hasNext(), "input or output must be part of path.");
+        final QName inOrOut = it.next();
+        for (final RpcDefinition potential : schema.getOperations()) {
+            if (rpcName.equals(potential.getQName())) {
+                return SchemaNodeUtils.getRpcDataSchema(potential, inOrOut);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Extract the identifiers of all modules and submodules which were used to create a particular SchemaContext.
+     *
+     * @param context SchemaContext to be examined
+     * @return Set of ModuleIdentifiers.
+     */
+    public static Set<SourceIdentifier> getConstituentModuleIdentifiers(final SchemaContext context) {
+        final Set<SourceIdentifier> ret = new HashSet<>();
+
+        for (Module module : context.getModules()) {
+            ret.add(moduleToIdentifier(module));
+
+            for (Module submodule : module.getSubmodules()) {
+                ret.add(moduleToIdentifier(submodule));
+            }
+        }
+
+        return ret;
+    }
+
+    private static SourceIdentifier moduleToIdentifier(final Module module) {
+        return RevisionSourceIdentifier.create(module.getName(), module.getRevision());
+    }
+
+    private static SchemaNode findNodeInModule(final Module module, final Iterable<QName> path) {
+        if (!path.iterator().hasNext()) {
+            LOG.debug("No node matching {} found in node {}", path, module);
+            return null;
+        }
+
+        final QName current = path.iterator().next();
+        LOG.trace("Looking for node {} in module {}", current, module);
+
+        SchemaNode foundNode = null;
+        final Iterable<QName> nextPath = nextLevel(path);
+
+        foundNode = module.getDataChildByName(current);
+        if (foundNode != null && nextPath.iterator().hasNext()) {
+            foundNode = findNodeIn(foundNode, nextPath);
+        }
+
+        if (foundNode == null) {
+            foundNode = getGroupingByName(module, current);
+            if (foundNode != null && nextPath.iterator().hasNext()) {
+                foundNode = findNodeIn(foundNode, nextPath);
+            }
+        }
+
+        if (foundNode == null) {
+            foundNode = getRpcByName(module, current);
+            if (foundNode != null && nextPath.iterator().hasNext()) {
+                foundNode = findNodeIn(foundNode, nextPath);
+            }
+        }
+
+        if (foundNode == null) {
+            foundNode = getNotificationByName(module, current);
+            if (foundNode != null && nextPath.iterator().hasNext()) {
+                foundNode = findNodeIn(foundNode, nextPath);
+            }
+        }
+
+        if (foundNode == null) {
+            LOG.debug("No node matching {} found in node {}", path, module);
+        }
+
+        return foundNode;
+    }
+
+    private static SchemaNode findNodeIn(final SchemaNode parent, final Iterable<QName> path) {
+        if (!path.iterator().hasNext()) {
+            LOG.debug("No node matching {} found in node {}", path, parent);
+            return null;
+        }
+
+        final QName current = path.iterator().next();
+        LOG.trace("Looking for node {} in node {}", current, parent);
+
+        SchemaNode foundNode = null;
+        final Iterable<QName> nextPath = nextLevel(path);
+
+        if (parent instanceof DataNodeContainer) {
+            final DataNodeContainer parentDataNodeContainer = (DataNodeContainer) parent;
+
+            foundNode = parentDataNodeContainer.getDataChildByName(current);
+            if (foundNode != null && nextPath.iterator().hasNext()) {
+                foundNode = findNodeIn(foundNode, nextPath);
+            }
+
+            if (foundNode == null) {
+                foundNode = getGroupingByName(parentDataNodeContainer, current);
+                if (foundNode != null && nextPath.iterator().hasNext()) {
+                    foundNode = findNodeIn(foundNode, nextPath);
+                }
+            }
+        }
+
+        if (foundNode == null && parent instanceof ActionNodeContainer) {
+            foundNode = ((ActionNodeContainer) parent).getActions().stream()
+                    .filter(act -> current.equals(act.getQName())).findFirst().orElse(null);
+            if (foundNode != null && nextPath.iterator().hasNext()) {
+                foundNode = findNodeIn(foundNode, nextPath);
+            }
+        }
+
+        if (foundNode == null && parent instanceof NotificationNodeContainer) {
+            foundNode = ((NotificationNodeContainer) parent).getNotifications().stream()
+                    .filter(notif -> current.equals(notif.getQName())).findFirst().orElse(null);
+            if (foundNode != null && nextPath.iterator().hasNext()) {
+                foundNode = findNodeIn(foundNode, nextPath);
+            }
+        }
+
+        if (foundNode == null && parent instanceof OperationDefinition) {
+            final OperationDefinition parentRpcDefinition = (OperationDefinition) parent;
+
+            if (current.getLocalName().equals("input")) {
+                foundNode = parentRpcDefinition.getInput();
+                if (foundNode != null && nextPath.iterator().hasNext()) {
+                    foundNode = findNodeIn(foundNode, nextPath);
+                }
+            }
+
+            if (current.getLocalName().equals("output")) {
+                foundNode = parentRpcDefinition.getOutput();
+                if (foundNode != null && nextPath.iterator().hasNext()) {
+                    foundNode = findNodeIn(foundNode, nextPath);
+                }
+            }
+
+            if (foundNode == null) {
+                foundNode = getGroupingByName(parentRpcDefinition, current);
+                if (foundNode != null && nextPath.iterator().hasNext()) {
+                    foundNode = findNodeIn(foundNode, nextPath);
+                }
+            }
+        }
+
+        if (foundNode == null && parent instanceof ChoiceSchemaNode) {
+            foundNode = ((ChoiceSchemaNode) parent).getCaseNodeByName(current);
+
+            if (foundNode != null && nextPath.iterator().hasNext()) {
+                foundNode = findNodeIn(foundNode, nextPath);
+            }
+
+            if (foundNode == null) {
+                // fallback that tries to map into one of the child cases
+                for (final CaseSchemaNode caseNode : ((ChoiceSchemaNode) parent).getCases().values()) {
+                    final DataSchemaNode maybeChild = caseNode.getDataChildByName(current);
+                    if (maybeChild != null) {
+                        foundNode = findNodeIn(maybeChild, nextPath);
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (foundNode == null) {
+            LOG.debug("No node matching {} found in node {}", path, parent);
+        }
+
+        return foundNode;
+
+    }
+
+    private static Iterable<QName> nextLevel(final Iterable<QName> path) {
+        return Iterables.skip(path, 1);
+    }
+
+    private static RpcDefinition getRpcByName(final Module module, final QName name) {
+        for (final RpcDefinition rpc : module.getRpcs()) {
+            if (rpc.getQName().equals(name)) {
+                return rpc;
+            }
+        }
+        return null;
+    }
+
+    private static NotificationDefinition getNotificationByName(final Module module, final QName name) {
+        for (final NotificationDefinition notification : module.getNotifications()) {
+            if (notification.getQName().equals(name)) {
+                return notification;
+            }
+        }
+        return null;
+    }
+
+    private static GroupingDefinition getGroupingByName(final DataNodeContainer dataNodeContainer, final QName name) {
+        for (final GroupingDefinition grouping : dataNodeContainer.getGroupings()) {
+            if (grouping.getQName().equals(name)) {
+                return grouping;
+            }
+        }
+        return null;
+    }
+
+    private static GroupingDefinition getGroupingByName(final OperationDefinition rpc, final QName name) {
+        for (final GroupingDefinition grouping : rpc.getGroupings()) {
+            if (grouping.getQName().equals(name)) {
+                return grouping;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Transforms string representation of XPath to Queue of QNames. The XPath
+     * is split by "/" and for each part of XPath is assigned correct module in
+     * Schema Path. <br>
+     * If Schema Context, Parent Module or XPath string contains
+     * <code>null</code> values, the method will throws IllegalArgumentException
+     *
+     * @param context
+     *            Schema Context
+     * @param parentModule
+     *            Parent Module
+     * @param xpath
+     *            XPath String
+     * @return return a list of QName
+     */
+    private static List<QName> xpathToQNamePath(final SchemaContext context, final Module parentModule,
+            final String xpath) {
+        final List<QName> path = new ArrayList<>();
+        for (final String pathComponent : SLASH_SPLITTER.split(xpath)) {
+            path.add(stringPathPartToQName(context, parentModule, pathComponent));
+        }
+        return path;
+    }
+
+    /**
+     * Transforms part of Prefixed Path as java String to QName. <br>
+     * If the string contains module prefix separated by ":" (i.e.
+     * mod:container) this module is provided from from Parent Module list of
+     * imports. If the Prefixed module is present in Schema Context the QName
+     * can be constructed. <br>
+     * If the Prefixed Path Part does not contains prefix the Parent's Module
+     * namespace is taken for construction of QName. <br>
+     *
+     * @param context Schema Context
+     * @param parentModule Parent Module
+     * @param prefixedPathPart Prefixed Path Part string
+     * @return QName from prefixed Path Part String.
+     * @throws NullPointerException if any arguments are null
+     */
+    private static QName stringPathPartToQName(final SchemaContext context, final Module parentModule,
+            final String prefixedPathPart) {
+        requireNonNull(context, "context");
+
+        if (prefixedPathPart.indexOf(':') != -1) {
+            final Iterator<String> prefixedName = COLON_SPLITTER.split(prefixedPathPart).iterator();
+            final String modulePrefix = prefixedName.next();
+
+            final Module module = resolveModuleForPrefix(context, parentModule, modulePrefix);
+            checkArgument(module != null, "Failed to resolve xpath: no module found for prefix %s in module %s",
+                    modulePrefix, parentModule.getName());
+
+            return QName.create(module.getQNameModule(), prefixedName.next());
+        }
+
+        return QName.create(parentModule.getNamespace(), parentModule.getRevision(), prefixedPathPart);
+    }
+
+    /**
+     * Method will attempt to resolve and provide Module reference for specified module prefix. Each Yang module could
+     * contains multiple imports which MUST be associated with corresponding module prefix. The method simply looks into
+     * module imports and returns the module that is bounded with specified prefix. If the prefix is not present
+     * in module or the prefixed module is not present in specified Schema Context, the method will return {@code null}.
+     * <br>
+     * If String prefix is the same as prefix of the specified Module the reference to this module is returned.<br>
+     *
+     * @param context Schema Context
+     * @param module Yang Module
+     * @param prefix Module Prefix
+     * @return Module for given prefix in specified Schema Context if is present, otherwise returns <code>null</code>
+     * @throws NullPointerException if any arguments are null
+     */
+    private static Module resolveModuleForPrefix(final SchemaContext context, final Module module,
+            final String prefix) {
+        requireNonNull(context, "context");
+
+        if (prefix.equals(module.getPrefix())) {
+            return module;
+        }
+
+        final Set<ModuleImport> imports = module.getImports();
+        for (final ModuleImport mi : imports) {
+            if (prefix.equals(mi.getPrefix())) {
+                return context.findModule(mi.getModuleName(), mi.getRevision()).orElse(null);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Resolve a relative XPath into a set of QNames.
+     *
+     * @param context
+     *            Schema Context
+     * @param module
+     *            Yang Module
+     * @param relativeXPath
+     *            Non conditional Revision Aware Relative XPath
+     * @param actualSchemaNode
+     *            actual schema node
+     * @return list of QName
+     * @throws IllegalArgumentException if any arguments are null
+     */
+    private static Iterable<QName> resolveRelativeXPath(final SchemaContext context, final Module module,
+            final PathExpression relativeXPath, final SchemaNode actualSchemaNode) {
+        checkState(!relativeXPath.isAbsolute(), "Revision Aware XPath MUST be relative i.e. MUST contains ../, "
+                + "for non relative Revision Aware XPath use findDataSchemaNode method");
+        checkState(actualSchemaNode.getPath() != null, "Schema Path reference for Leafref cannot be NULL");
+
+        List<String> xpaths = new ArrayList<>();
+        splitXPath(relativeXPath.getOriginalString(), xpaths);
+
+        // Find out how many "parent" components there are and trim them
+        final int colCount = normalizeXPath(xpaths);
+        if (colCount != 0) {
+            xpaths = xpaths.subList(colCount, xpaths.size());
+        }
+
+        final Iterable<QName> schemaNodePath = actualSchemaNode.getPath().getPathFromRoot();
+
+        if (Iterables.size(schemaNodePath) - colCount >= 0) {
+            return Iterables.concat(Iterables.limit(schemaNodePath, Iterables.size(schemaNodePath) - colCount),
+                Iterables.transform(xpaths, input -> stringPathPartToQName(context, module, input)));
+        }
+        return Iterables.concat(schemaNodePath,
+                Iterables.transform(xpaths, input -> stringPathPartToQName(context, module, input)));
+    }
+
+    @VisibleForTesting
+    static int normalizeXPath(final List<String> xpath) {
+        LOG.trace("Normalize {}", xpath);
+
+        // We need to make multiple passes here, as the leading XPaths as we can have "../abc/../../def", which really
+        // is "../../def"
+        while (true) {
+            // Next up: count leading ".." components
+            int leadingParents = 0;
+            while (true) {
+                if (leadingParents == xpath.size()) {
+                    return leadingParents;
+                }
+                if (!"..".equals(xpath.get(leadingParents))) {
+                    break;
+                }
+
+                ++leadingParents;
+            }
+
+            // Now let's see if there there is a '..' in the rest
+            final int dots = findDots(xpath, leadingParents + 1);
+            if (dots == -1) {
+                return leadingParents;
+            }
+
+            xpath.remove(dots - 1);
+            xpath.remove(dots - 1);
+            LOG.trace("Next iteration {}", xpath);
+        }
+    }
+
+    private static int findDots(final List<String> xpath, final int startIndex) {
+        for (int i = startIndex; i < xpath.size(); ++i) {
+            if ("..".equals(xpath.get(i))) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    private static void splitXPath(final String xpath, final List<String> output) {
+        // This is a major hack, but should do the trick for now.
+        final int deref = xpath.indexOf("deref(");
+        if (deref == -1) {
+            doSplitXPath(xpath, output);
+            return;
+        }
+
+        // Interpret leading part
+        doSplitXPath(xpath.substring(0, deref), output);
+
+        // Find matching parentheses
+        final int start = deref + 6;
+        final int paren = xpath.indexOf(')', start);
+        checkArgument(paren != -1, "Cannot find matching parentheses in %s", xpath);
+
+        // Interpret the argument
+        doSplitXPath(xpath.substring(start, paren), output);
+
+        // And now the last bit
+        splitXPath(xpath.substring(paren + 1), output);
+    }
+
+    private static void doSplitXPath(final String xpath, final List<String> output) {
+        SLASH_SPLITTER.split(xpath).forEach(output::add);
+    }
+
+    /**
+     * Extracts the base type of node on which schema node points to. If target node is again of type
+     * LeafrefTypeDefinition, methods will be call recursively until it reach concrete type definition.
+     *
+     * @param typeDefinition
+     *            type of node which will be extracted
+     * @param schemaContext
+     *            Schema Context
+     * @param schema
+     *            Schema Node
+     * @return recursively found type definition this leafref is pointing to or null if the xpath is incorrect (null
+     *         is there to preserve backwards compatibility)
+     */
+    public static TypeDefinition<?> getBaseTypeForLeafRef(final LeafrefTypeDefinition typeDefinition,
+            final SchemaContext schemaContext, final SchemaNode schema) {
+        PathExpression pathStatement = typeDefinition.getPathStatement();
+        pathStatement = new PathExpressionImpl(stripConditionsFromXPathString(pathStatement),
+            pathStatement.isAbsolute());
+
+        final DataSchemaNode dataSchemaNode;
+        if (pathStatement.isAbsolute()) {
+            SchemaNode baseSchema = schema;
+            while (baseSchema instanceof DerivableSchemaNode) {
+                final Optional<? extends SchemaNode> basePotential = ((DerivableSchemaNode) baseSchema).getOriginal();
+                if (basePotential.isPresent()) {
+                    baseSchema = basePotential.get();
+                } else {
+                    break;
+                }
+            }
+
+            Module parentModule = findParentModuleOfReferencingType(schemaContext, baseSchema);
+            dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNode(schemaContext, parentModule,
+                    pathStatement);
+        } else {
+            Module parentModule = findParentModule(schemaContext, schema);
+            dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemaContext,
+                    parentModule, schema, pathStatement);
+        }
+
+        // FIXME this is just to preserve backwards compatibility since yangtools do not mind wrong leafref xpaths
+        // and current expected behaviour for such cases is to just use pure string
+        // This should throw an exception about incorrect XPath in leafref
+        if (dataSchemaNode == null) {
+            return null;
+        }
+
+        final TypeDefinition<?> targetTypeDefinition = typeDefinition(dataSchemaNode);
+
+        if (targetTypeDefinition instanceof LeafrefTypeDefinition) {
+            return getBaseTypeForLeafRef((LeafrefTypeDefinition) targetTypeDefinition, schemaContext, dataSchemaNode);
+        }
+
+        return targetTypeDefinition;
+    }
+
+    /**
+     * Returns base type for {@code typeDefinition} which belongs to module specified via {@code qname}. This handle
+     * the case when leafref type isn't specified as type substatement of leaf or leaf-list but is defined in other
+     * module as typedef which is then imported to referenced module.
+     *
+     * <p>
+     * Because {@code typeDefinition} is definied via typedef statement, only absolute path is meaningful.
+     */
+    public static TypeDefinition<?> getBaseTypeForLeafRef(final LeafrefTypeDefinition typeDefinition,
+            final SchemaContext schemaContext, final QName qname) {
+        final PathExpression pathStatement = typeDefinition.getPathStatement();
+        final PathExpression strippedPathStatement = new PathExpressionImpl(
+            stripConditionsFromXPathString(pathStatement), pathStatement.isAbsolute());
+        if (!strippedPathStatement.isAbsolute()) {
+            return null;
+        }
+
+        final Optional<Module> parentModule = schemaContext.findModule(qname.getModule());
+        checkArgument(parentModule.isPresent(), "Failed to find parent module for %s", qname);
+
+        final DataSchemaNode dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNode(schemaContext,
+            parentModule.get(), strippedPathStatement);
+        final TypeDefinition<?> targetTypeDefinition = typeDefinition(dataSchemaNode);
+        if (targetTypeDefinition instanceof LeafrefTypeDefinition) {
+            return getBaseTypeForLeafRef((LeafrefTypeDefinition) targetTypeDefinition, schemaContext, dataSchemaNode);
+        }
+
+        return targetTypeDefinition;
+    }
+
+    private static Module findParentModuleOfReferencingType(final SchemaContext schemaContext,
+            final SchemaNode schemaNode) {
+        checkArgument(schemaContext != null, "Schema Context reference cannot be NULL!");
+        checkArgument(schemaNode instanceof TypedDataSchemaNode, "Unsupported node %s", schemaNode);
+
+        TypeDefinition<?> nodeType = ((TypedDataSchemaNode) schemaNode).getType();
+        if (nodeType.getBaseType() != null) {
+            while (nodeType.getBaseType() != null) {
+                nodeType = nodeType.getBaseType();
+            }
+
+            return schemaContext.findModule(nodeType.getQName().getModule()).orElse(null);
+        }
+
+        return SchemaContextUtil.findParentModule(schemaContext, schemaNode);
+    }
+
+    private static final Pattern STRIP_PATTERN = Pattern.compile("\\[[^\\[\\]]*\\]");
+
+    /**
+     * Removes conditions from xPath pointed to target node.
+     *
+     * @param pathStatement
+     *            xPath to target node
+     * @return string representation of xPath without conditions
+     */
+    @VisibleForTesting
+    static String stripConditionsFromXPathString(final PathExpression pathStatement) {
+        return STRIP_PATTERN.matcher(pathStatement.getOriginalString()).replaceAll("");
+    }
+
+    /**
+     * Extracts the base type of leaf schema node until it reach concrete type of TypeDefinition.
+     *
+     * @param node
+     *            a node representing LeafSchemaNode
+     * @return concrete type definition of node value
+     */
+    private static TypeDefinition<?> typeDefinition(final LeafSchemaNode node) {
+        TypeDefinition<?> baseType = node.getType();
+        while (baseType.getBaseType() != null) {
+            baseType = baseType.getBaseType();
+        }
+        return baseType;
+    }
+
+    /**
+     * Extracts the base type of leaf schema node until it reach concrete type of TypeDefinition.
+     *
+     * @param node
+     *            a node representing LeafListSchemaNode
+     * @return concrete type definition of node value
+     */
+    private static TypeDefinition<?> typeDefinition(final LeafListSchemaNode node) {
+        TypeDefinition<?> baseType = node.getType();
+        while (baseType.getBaseType() != null) {
+            baseType = baseType.getBaseType();
+        }
+        return baseType;
+    }
+
+    /**
+     * Gets the base type of DataSchemaNode value.
+     *
+     * @param node
+     *            a node representing DataSchemaNode
+     * @return concrete type definition of node value
+     */
+    private static TypeDefinition<?> typeDefinition(final DataSchemaNode node) {
+        if (node instanceof LeafListSchemaNode) {
+            return typeDefinition((LeafListSchemaNode) node);
+        } else if (node instanceof LeafSchemaNode) {
+            return typeDefinition((LeafSchemaNode) node);
+        } else {
+            throw new IllegalArgumentException("Unhandled parameter type: " + node);
+        }
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaNodeUtils.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaNodeUtils.java
new file mode 100644 (file)
index 0000000..2b21b23
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+public final class SchemaNodeUtils {
+    private SchemaNodeUtils() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    public static Optional<SchemaNode> getOriginalIfPossible(final SchemaNode node) {
+        if (node instanceof DerivableSchemaNode) {
+            @SuppressWarnings("unchecked")
+            final Optional<SchemaNode> ret  = (Optional<SchemaNode>) ((DerivableSchemaNode) node).getOriginal();
+            return ret;
+        }
+        return Optional.empty();
+    }
+
+    public static SchemaNode getRootOriginalIfPossible(final SchemaNode data) {
+        Optional<SchemaNode> previous = Optional.empty();
+        Optional<SchemaNode> next = getOriginalIfPossible(data);
+        while (next.isPresent()) {
+            previous = next;
+            next = getOriginalIfPossible(next.get());
+        }
+        return previous.orElse(null);
+    }
+
+    /**
+     * Returns RPC input or output schema based on supplied QName.
+     *
+     * @param rpc RPC Definition
+     * @param qname input or output QName with namespace same as RPC
+     * @return input or output schema. Returns null if RPC does not have input/output specified.
+     */
+    public static @Nullable ContainerSchemaNode getRpcDataSchema(final @NonNull RpcDefinition rpc,
+            final @NonNull QName qname) {
+        requireNonNull(rpc, "Rpc Schema must not be null");
+        switch (requireNonNull(qname, "QName must not be null").getLocalName()) {
+            case "input":
+                return rpc.getInput();
+            case "output":
+                return rpc.getOutput();
+            default:
+                throw new IllegalArgumentException("Supplied qname " + qname
+                        + " does not represent rpc input or output.");
+        }
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SimpleSchemaContext.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SimpleSchemaContext.java
new file mode 100644 (file)
index 0000000..fe4e17c
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeMap;
+import org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNode;
+import org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNodeAwareSchemaContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+/**
+ * Simple subclass of {@link AbstractSchemaContext} which performs some amount of indexing to speed up common
+ * SchemaContext operations. This implementation assumes input modules are consistent and does not perform
+ * any extensive analysis to ensure the resulting object complies to SchemaContext interface.
+ */
+@Beta
+public class SimpleSchemaContext extends AbstractSchemaContext implements AnnotationSchemaNodeAwareSchemaContext {
+    private final ImmutableSetMultimap<URI, Module> namespaceToModules;
+    private final ImmutableSetMultimap<String, Module> nameToModules;
+    private final ImmutableMap<QNameModule, Module> moduleMap;
+    private final ImmutableSet<Module> modules;
+    private final ImmutableMap<QName, AnnotationSchemaNode> annotations;
+
+    protected SimpleSchemaContext(final Set<Module> modules) {
+        /*
+         * Instead of doing this on each invocation of getModules(), pre-compute it once and keep it around -- better
+         * than the set we got in.
+         *
+         * Note we are performing two sort operations: the dependency sort takes care of detecting multiple imports,
+         * performing sorting as a side-effect, but we really want the modules sorted to comply with getModules().
+         */
+        final List<Module> sortedModules = new ArrayList<>(ModuleDependencySort.sort(modules));
+        sortedModules.sort(NAME_REVISION_COMPARATOR);
+        this.modules = ImmutableSet.copyOf(sortedModules);
+
+        /*
+         * The most common lookup is from Namespace->Module.
+         *
+         * RESTCONF performs lookups based on module name only, where it wants
+         * to receive the latest revision
+         *
+         * Invest some quality time in building up lookup tables for both.
+         */
+        final SetMultimap<URI, Module> nsMap = Multimaps.newSetMultimap(new TreeMap<>(),
+            AbstractSchemaContext::createModuleSet);
+        final SetMultimap<String, Module> nameMap = Multimaps.newSetMultimap(new TreeMap<>(),
+            AbstractSchemaContext::createModuleSet);
+        final Builder<QNameModule, Module> moduleMapBuilder = ImmutableMap.builder();
+        for (Module m : modules) {
+            nameMap.put(m.getName(), m);
+            nsMap.put(m.getNamespace(), m);
+            moduleMapBuilder.put(m.getQNameModule(), m);
+        }
+
+        namespaceToModules = ImmutableSetMultimap.copyOf(nsMap);
+        nameToModules = ImmutableSetMultimap.copyOf(nameMap);
+        moduleMap = moduleMapBuilder.build();
+        annotations = ImmutableMap.copyOf(AnnotationSchemaNode.findAll(this));
+    }
+
+    /**
+     * Create a new instance from specified modules. Note that no module validation is done and hence the consistency
+     * of the resulting SchemaContext is completely in hands of the caller.
+     */
+    public static SimpleSchemaContext forModules(final Set<Module> modules) {
+        return new SimpleSchemaContext(modules);
+    }
+
+    @Override
+    public final Set<Module> getModules() {
+        return modules;
+    }
+
+    @Override
+    public final Optional<AnnotationSchemaNode> findAnnotation(final QName qname) {
+        return Optional.ofNullable(annotations.get(requireNonNull(qname)));
+    }
+
+    @Override
+    protected Map<QNameModule, Module> getModuleMap() {
+        return moduleMap;
+    }
+
+    @Override
+    protected final SetMultimap<URI, Module> getNamespaceToModules() {
+        return namespaceToModules;
+    }
+
+    @Override
+    protected final SetMultimap<String, Module> getNameToModules() {
+        return nameToModules;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("modules", modules);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/package-info.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/package-info.java
new file mode 100644 (file)
index 0000000..e034fd1
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * 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
+ */
+/**
+ * Utility classes and implementations for concepts defined in yang-model-api.
+ */
+package org.opendaylight.yangtools.yang.model.util;
\ No newline at end of file
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractBaseType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractBaseType.java
new file mode 100644 (file)
index 0000000..043ebcd
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+abstract class AbstractBaseType<T extends TypeDefinition<T>> extends AbstractTypeDefinition<T> {
+    AbstractBaseType(final QName qname) {
+        this(SchemaPath.ROOT.createChild(qname), ImmutableList.of());
+    }
+
+    AbstractBaseType(final SchemaPath path, final List<UnknownSchemaNode> unknownSchemaNodes) {
+        super(path, unknownSchemaNodes);
+    }
+
+    @Override
+    public final T getBaseType() {
+        return null;
+    }
+
+    @Override
+    public final Optional<String> getUnits() {
+        return Optional.empty();
+    }
+
+    @Override
+    public final Optional<? extends Object> getDefaultValue() {
+        return Optional.empty();
+    }
+
+    @Override
+    public final Optional<String> getDescription() {
+        return Optional.empty();
+    }
+
+    @Override
+    public final Optional<String> getReference() {
+        return Optional.empty();
+    }
+
+    @Override
+    public final Status getStatus() {
+        return Status.CURRENT;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractDerivedType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractDerivedType.java
new file mode 100644 (file)
index 0000000..1425ed4
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+abstract class AbstractDerivedType<T extends TypeDefinition<T>> extends AbstractTypeDefinition<T> {
+    private final @NonNull T baseType;
+    private final Object defaultValue;
+    private final String description;
+    private final String reference;
+    private final @NonNull Status status;
+    private final String units;
+
+    AbstractDerivedType(final T baseType, final SchemaPath path, final Object defaultValue, final String description,
+            final String reference, final Status status, final String units,
+            final Collection<UnknownSchemaNode> unknownSchemNodes) {
+        super(path, unknownSchemNodes);
+        this.baseType = requireNonNull(baseType);
+        this.status = requireNonNull(status);
+        this.defaultValue = defaultValue;
+        this.description = description;
+        this.reference = reference;
+        this.units = units;
+    }
+
+    @Override
+    public final T getBaseType() {
+        return baseType instanceof AbstractRestrictedType ? baseType.getBaseType() : baseType;
+    }
+
+    /**
+     * Return the stored base type. Unlike {@link #getBaseType()}, this method returns the underlying restricted type.
+     */
+    final T baseType() {
+        return baseType;
+    }
+
+    @Override
+    public final Optional<? extends Object> getDefaultValue() {
+        return defaultValue != null ? Optional.of(defaultValue) : baseType.getDefaultValue();
+    }
+
+    @Override
+    public final Optional<String> getDescription() {
+        return Optional.ofNullable(description);
+    }
+
+    @Override
+    public final Optional<String> getReference() {
+        return Optional.ofNullable(reference);
+    }
+
+    @Override
+    public final Status getStatus() {
+        return status;
+    }
+
+    @Override
+    public final Optional<String> getUnits() {
+        return units != null ? Optional.of(units) : baseType.getUnits();
+    }
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(this).omitNullValues()
+                .add("baseType", baseType)
+                .add("default", defaultValue)
+                .add("description", description)
+                .add("path", getPath())
+                .add("reference", reference)
+                .add("status", status)
+                .add("units", units).toString();
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractLengthRestrictedBaseType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractLengthRestrictedBaseType.java
new file mode 100644 (file)
index 0000000..537d56b
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.LengthRestrictedTypeDefinition;
+
+abstract class AbstractLengthRestrictedBaseType<T extends LengthRestrictedTypeDefinition<T>> extends AbstractBaseType<T>
+        implements LengthRestrictedTypeDefinition<T> {
+
+    AbstractLengthRestrictedBaseType(final QName qname) {
+        super(qname);
+    }
+
+    @Override
+    public final Optional<LengthConstraint> getLengthConstraint() {
+        return Optional.empty();
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractLengthRestrictedDerivedType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractLengthRestrictedDerivedType.java
new file mode 100644 (file)
index 0000000..27cfaf7
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.LengthRestrictedTypeDefinition;
+
+abstract class AbstractLengthRestrictedDerivedType<T extends LengthRestrictedTypeDefinition<T>>
+        extends AbstractDerivedType<T> implements LengthRestrictedTypeDefinition<T> {
+
+    AbstractLengthRestrictedDerivedType(final T baseType, final SchemaPath path,
+            final Object defaultValue, final String description, final String reference, final Status status,
+            final String units, final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public final Optional<LengthConstraint> getLengthConstraint() {
+        return baseType().getLengthConstraint();
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractLengthRestrictedType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractLengthRestrictedType.java
new file mode 100644 (file)
index 0000000..3aaaea1
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.LengthRestrictedTypeDefinition;
+
+abstract class AbstractLengthRestrictedType<T extends LengthRestrictedTypeDefinition<T>>
+        extends AbstractRestrictedType<T> implements LengthRestrictedTypeDefinition<T> {
+    private final @Nullable LengthConstraint lengthConstraint;
+
+    AbstractLengthRestrictedType(final T baseType, final SchemaPath path,
+        final Collection<UnknownSchemaNode> unknownSchemaNodes, final @Nullable LengthConstraint lengthConstraint) {
+        super(baseType, path, unknownSchemaNodes);
+        this.lengthConstraint = lengthConstraint;
+    }
+
+    @Override
+    public final Optional<LengthConstraint> getLengthConstraint() {
+        return Optional.ofNullable(lengthConstraint);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRangeRestrictedBaseType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRangeRestrictedBaseType.java
new file mode 100644 (file)
index 0000000..4d767e6
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.Range;
+import java.util.List;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
+
+abstract class AbstractRangeRestrictedBaseType<T extends RangeRestrictedTypeDefinition<T, N>,
+    N extends Number & Comparable<N>> extends AbstractBaseType<T> implements RangeRestrictedTypeDefinition<T, N> {
+    private static final ConstraintMetaDefinition BUILTIN_CONSTRAINT = new ConstraintMetaDefinition() {
+
+        @Override
+        public Optional<String> getReference() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<String> getDescription() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<String> getErrorMessage() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<String> getErrorAppTag() {
+            return Optional.empty();
+        }
+    };
+
+    private final RangeConstraint<N> rangeConstraint;
+
+    AbstractRangeRestrictedBaseType(final QName qname, final N minValue, final N maxValue) {
+        super(qname);
+        this.rangeConstraint = new ResolvedRangeConstraint<>(BUILTIN_CONSTRAINT, ImmutableRangeSet.of(
+            Range.closed(minValue, maxValue)));
+    }
+
+    AbstractRangeRestrictedBaseType(final SchemaPath path, final List<UnknownSchemaNode> unknownSchemaNodes,
+        final RangeConstraint<N> rangeConstraint) {
+        super(path, unknownSchemaNodes);
+        this.rangeConstraint = requireNonNull(rangeConstraint);
+    }
+
+    @Override
+    public final Optional<RangeConstraint<N>> getRangeConstraint() {
+        return Optional.of(rangeConstraint);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRangeRestrictedDerivedType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRangeRestrictedDerivedType.java
new file mode 100644 (file)
index 0000000..86674eb
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
+
+abstract class AbstractRangeRestrictedDerivedType<T extends RangeRestrictedTypeDefinition<T, N>,
+        N extends Number & Comparable<N>> extends AbstractDerivedType<T>
+        implements RangeRestrictedTypeDefinition<T, N> {
+
+    AbstractRangeRestrictedDerivedType(final T baseType, final SchemaPath path,
+            final Object defaultValue, final String description, final String reference, final Status status,
+            final String units, final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public final Optional<RangeConstraint<N>> getRangeConstraint() {
+        return baseType().getRangeConstraint();
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRangeRestrictedType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRangeRestrictedType.java
new file mode 100644 (file)
index 0000000..dcd4609
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
+
+abstract class AbstractRangeRestrictedType<T extends RangeRestrictedTypeDefinition<T, N>,
+        N extends Number & Comparable<N>> extends AbstractRestrictedType<T>
+        implements RangeRestrictedTypeDefinition<T, N> {
+    private final @Nullable RangeConstraint<N> rangeConstraint;
+
+    AbstractRangeRestrictedType(final T baseType, final SchemaPath path,
+        final Collection<UnknownSchemaNode> unknownSchemaNodes, final @Nullable RangeConstraint<N> rangeConstraint) {
+        super(baseType, path, unknownSchemaNodes);
+        this.rangeConstraint = rangeConstraint;
+    }
+
+    @Override
+    public final Optional<RangeConstraint<N>> getRangeConstraint() {
+        return Optional.ofNullable(rangeConstraint);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRestrictedType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRestrictedType.java
new file mode 100644 (file)
index 0000000..77262c2
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+abstract class AbstractRestrictedType<T extends TypeDefinition<T>> extends AbstractTypeDefinition<T> {
+    private final @NonNull T baseType;
+
+    AbstractRestrictedType(final T baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(path, unknownSchemaNodes);
+        this.baseType = requireNonNull(baseType);
+    }
+
+    @Override
+    public final T getBaseType() {
+        return baseType;
+    }
+
+    @Override
+    public final Optional<String> getUnits() {
+        return baseType.getUnits();
+    }
+
+    @Override
+    public final Optional<? extends Object> getDefaultValue() {
+        return baseType.getDefaultValue();
+    }
+
+    @Override
+    public final Optional<String> getDescription() {
+        return baseType.getDescription();
+    }
+
+    @Override
+    public final Optional<String> getReference() {
+        return baseType.getReference();
+    }
+
+    @Override
+    public final Status getStatus() {
+        return baseType.getStatus();
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRestrictedTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractRestrictedTypeBuilder.java
new file mode 100644 (file)
index 0000000..cd172c8
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verifyNotNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+abstract class AbstractRestrictedTypeBuilder<T extends TypeDefinition<T>> extends TypeBuilder<T> {
+    private boolean touched;
+
+    AbstractRestrictedTypeBuilder(final T baseType, final SchemaPath path) {
+        super(baseType, path);
+        if (baseType != null) {
+            checkArgument(baseType instanceof AbstractBaseType || baseType instanceof AbstractDerivedType,
+                "Restricted type has to be based on either a base or derived type, not %s", baseType);
+        } else {
+            touched = true;
+        }
+    }
+
+    final void touch() {
+        touched = true;
+    }
+
+    abstract @NonNull T buildType();
+
+    @Override
+    public final T build() {
+        return touched ? buildType() : verifyNotNull(getBaseType());
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractTypeDefinition.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/AbstractTypeDefinition.java
new file mode 100644 (file)
index 0000000..fe0abdd
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+abstract class AbstractTypeDefinition<T extends TypeDefinition<T>> implements Immutable, TypeDefinition<T> {
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownSchemaNodes;
+    private final @NonNull SchemaPath path;
+
+    AbstractTypeDefinition(final SchemaPath path, final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        this.path = requireNonNull(path);
+        this.unknownSchemaNodes = ImmutableList.copyOf(unknownSchemaNodes);
+    }
+
+    @Override
+    public final QName getQName() {
+        return path.getLastComponent();
+    }
+
+    @Override
+    public final SchemaPath getPath() {
+        return path;
+    }
+
+    @Override
+    public final List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownSchemaNodes;
+    }
+
+    @Override
+    public abstract String toString();
+
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals(Object obj);
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseBinaryType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseBinaryType.java
new file mode 100644 (file)
index 0000000..cf38120
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseBinaryType extends AbstractLengthRestrictedBaseType<BinaryTypeDefinition>
+        implements BinaryTypeDefinition {
+    static final @NonNull BaseBinaryType INSTANCE = new BaseBinaryType();
+
+    private BaseBinaryType() {
+        super(BaseTypes.BINARY_QNAME);
+    }
+
+    @Override
+    public int hashCode() {
+        return BinaryTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return BinaryTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return BinaryTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseBitsType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseBitsType.java
new file mode 100644 (file)
index 0000000..eeba56f
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+
+final class BaseBitsType extends AbstractBaseType<BitsTypeDefinition> implements BitsTypeDefinition {
+    private final @NonNull ImmutableList<Bit> bits;
+
+    BaseBitsType(final SchemaPath path, final List<UnknownSchemaNode> unknownSchemaNodes, final Collection<Bit> bits) {
+        super(path, unknownSchemaNodes);
+        this.bits = ImmutableList.copyOf(bits);
+    }
+
+    @Override
+    public List<Bit> getBits() {
+        return bits;
+    }
+
+    @Override
+    public int hashCode() {
+        return BitsTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return BitsTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return BitsTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseBooleanType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseBooleanType.java
new file mode 100644 (file)
index 0000000..e3961dd
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseBooleanType extends AbstractBaseType<BooleanTypeDefinition> implements BooleanTypeDefinition {
+    static final @NonNull BaseBooleanType INSTANCE = new BaseBooleanType();
+
+    private BaseBooleanType() {
+        super(BaseTypes.BOOLEAN_QNAME);
+    }
+
+    @Override
+    public int hashCode() {
+        return BooleanTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return BooleanTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return BooleanTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseDecimalType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseDecimalType.java
new file mode 100644 (file)
index 0000000..9196c2f
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.Range;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+
+final class BaseDecimalType extends AbstractRangeRestrictedBaseType<DecimalTypeDefinition, BigDecimal>
+        implements DecimalTypeDefinition {
+    private static final ConstraintMetaDefinition BUILTIN_CONSTRAINT = new ConstraintMetaDefinition() {
+
+        @Override
+        public Optional<String> getReference() {
+            return Optional.of("https://tools.ietf.org/html/rfc6020#section-9.3.4");
+        }
+
+        @Override
+        public Optional<String> getDescription() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<String> getErrorMessage() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<String> getErrorAppTag() {
+            return Optional.empty();
+        }
+    };
+
+    private static final ImmutableList<RangeConstraint<BigDecimal>> IMPLICIT_RANGE_STATEMENTS = ImmutableList.of(
+        createRangeConstraint("-922337203685477580.8", "922337203685477580.7"),
+        createRangeConstraint("-92233720368547758.08", "92233720368547758.07"),
+        createRangeConstraint("-9223372036854775.808", "9223372036854775.807"),
+        createRangeConstraint("-922337203685477.5808", "922337203685477.5807"),
+        createRangeConstraint("-92233720368547.75808", "92233720368547.75807"),
+        createRangeConstraint("-9223372036854.775808", "9223372036854.775807"),
+        createRangeConstraint("-922337203685.4775808", "922337203685.4775807"),
+        createRangeConstraint("-92233720368.54775808", "92233720368.54775807"),
+        createRangeConstraint("-9223372036.854775808", "9223372036.854775807"),
+        createRangeConstraint("-922337203.6854775808", "922337203.6854775807"),
+        createRangeConstraint("-92233720.36854775808", "92233720.36854775807"),
+        createRangeConstraint("-9223372.036854775808", "9223372.036854775807"),
+        createRangeConstraint("-922337.2036854775808", "922337.2036854775807"),
+        createRangeConstraint("-92233.72036854775808", "92233.72036854775807"),
+        createRangeConstraint("-9223.372036854775808", "9223.372036854775807"),
+        createRangeConstraint("-922.3372036854775808", "922.3372036854775807"),
+        createRangeConstraint("-92.23372036854775808", "92.23372036854775807"),
+        createRangeConstraint("-9.223372036854775808", "9.223372036854775807"));
+
+    private static RangeConstraint<BigDecimal> createRangeConstraint(final String min, final String max) {
+        return new ResolvedRangeConstraint<>(BUILTIN_CONSTRAINT, ImmutableRangeSet.of(
+            Range.closed(new BigDecimal(min), new BigDecimal(max))));
+    }
+
+    static RangeConstraint<BigDecimal> constraintsForDigits(final int fractionDigits) {
+        return verifyNotNull(IMPLICIT_RANGE_STATEMENTS.get(fractionDigits - 1));
+    }
+
+    private final int fractionDigits;
+
+    BaseDecimalType(final SchemaPath path, final List<UnknownSchemaNode> unknownSchemaNodes,
+            final int fractionDigits, final RangeConstraint<BigDecimal> rangeConstraint) {
+        super(path, unknownSchemaNodes, rangeConstraint);
+        this.fractionDigits = fractionDigits;
+    }
+
+    @Override
+    public int getFractionDigits() {
+        return fractionDigits;
+    }
+
+    @Override
+    public int hashCode() {
+        return DecimalTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return DecimalTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return DecimalTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseEmptyType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseEmptyType.java
new file mode 100644 (file)
index 0000000..12fe0ee
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseEmptyType extends AbstractBaseType<EmptyTypeDefinition> implements EmptyTypeDefinition {
+    static final @NonNull BaseEmptyType INSTANCE = new BaseEmptyType();
+
+    private BaseEmptyType() {
+        super(BaseTypes.EMPTY_QNAME);
+    }
+
+    @Override
+    public int hashCode() {
+        return EmptyTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return EmptyTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return EmptyTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseEnumerationType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseEnumerationType.java
new file mode 100644 (file)
index 0000000..b019bf1
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+
+final class BaseEnumerationType extends AbstractBaseType<EnumTypeDefinition> implements EnumTypeDefinition {
+    private final @NonNull ImmutableList<EnumPair> values;
+
+    BaseEnumerationType(final SchemaPath path, final List<UnknownSchemaNode> unknownSchemaNodes,
+            final Collection<EnumPair> values) {
+        super(path, unknownSchemaNodes);
+        this.values = ImmutableList.copyOf(values);
+    }
+
+    @Override
+    public List<EnumPair> getValues() {
+        return values;
+    }
+
+    @Override
+    public int hashCode() {
+        return EnumTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return EnumTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return EnumTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseIdentityrefType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseIdentityrefType.java
new file mode 100644 (file)
index 0000000..a746727
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+
+final class BaseIdentityrefType extends AbstractBaseType<IdentityrefTypeDefinition>
+        implements IdentityrefTypeDefinition {
+    private final @NonNull Set<IdentitySchemaNode> identities;
+
+    BaseIdentityrefType(final SchemaPath path, final List<UnknownSchemaNode> unknownSchemaNodes,
+            final Set<IdentitySchemaNode> identities) {
+        super(path, unknownSchemaNodes);
+        this.identities = requireNonNull(identities);
+    }
+
+    @Override
+    public Set<IdentitySchemaNode> getIdentities() {
+        return identities;
+    }
+
+    @Override
+    public int hashCode() {
+        return IdentityrefTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return IdentityrefTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return IdentityrefTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInstanceIdentifierType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInstanceIdentifierType.java
new file mode 100644 (file)
index 0000000..10c2600
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseInstanceIdentifierType extends AbstractBaseType<InstanceIdentifierTypeDefinition>
+        implements InstanceIdentifierTypeDefinition {
+    static final @NonNull BaseInstanceIdentifierType INSTANCE = new BaseInstanceIdentifierType();
+
+    private BaseInstanceIdentifierType() {
+        super(BaseTypes.INSTANCE_IDENTIFIER_QNAME);
+    }
+
+    @Override
+    public boolean requireInstance() {
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return InstanceIdentifierTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return InstanceIdentifierTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return InstanceIdentifierTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInt16Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInt16Type.java
new file mode 100644 (file)
index 0000000..af4d06a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseInt16Type extends AbstractRangeRestrictedBaseType<Int16TypeDefinition, Short>
+        implements Int16TypeDefinition {
+    static final @NonNull BaseInt16Type INSTANCE = new BaseInt16Type();
+
+    private BaseInt16Type() {
+        super(BaseTypes.INT16_QNAME, Short.MIN_VALUE, Short.MAX_VALUE);
+    }
+
+    @Override
+    public int hashCode() {
+        return Int16TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Int16TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Int16TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInt32Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInt32Type.java
new file mode 100644 (file)
index 0000000..0893590
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseInt32Type extends AbstractRangeRestrictedBaseType<Int32TypeDefinition, Integer>
+        implements Int32TypeDefinition {
+    static final @NonNull BaseInt32Type INSTANCE = new BaseInt32Type();
+
+    private BaseInt32Type() {
+        super(BaseTypes.INT32_QNAME, Integer.MIN_VALUE, Integer.MAX_VALUE);
+    }
+
+    @Override
+    public int hashCode() {
+        return Int32TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Int32TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Int32TypeDefinition.toString(this);
+    }
+
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInt64Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInt64Type.java
new file mode 100644 (file)
index 0000000..cf8c033
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseInt64Type extends AbstractRangeRestrictedBaseType<Int64TypeDefinition, Long>
+        implements Int64TypeDefinition {
+    static final @NonNull BaseInt64Type INSTANCE = new BaseInt64Type();
+
+    private BaseInt64Type() {
+        super(BaseTypes.INT64_QNAME, Long.MIN_VALUE, Long.MAX_VALUE);
+    }
+
+    @Override
+    public int hashCode() {
+        return Int64TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Int64TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Int64TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInt8Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseInt8Type.java
new file mode 100644 (file)
index 0000000..bc11699
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseInt8Type extends AbstractRangeRestrictedBaseType<Int8TypeDefinition, Byte>
+        implements Int8TypeDefinition {
+    static final @NonNull BaseInt8Type INSTANCE = new BaseInt8Type();
+
+    private BaseInt8Type() {
+        super(BaseTypes.INT8_QNAME, Byte.MIN_VALUE, Byte.MAX_VALUE);
+    }
+
+    @Override
+    public int hashCode() {
+        return Int8TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Int8TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Int8TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseLeafrefType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseLeafrefType.java
new file mode 100644 (file)
index 0000000..b48ce54
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+
+final class BaseLeafrefType extends AbstractBaseType<LeafrefTypeDefinition> implements LeafrefTypeDefinition {
+    private final PathExpression pathStatement;
+    private final boolean requireInstance;
+
+    BaseLeafrefType(final SchemaPath path, final PathExpression pathStatement, final boolean requireInstance,
+            final List<UnknownSchemaNode> unknownSchemaNodes) {
+        super(path, unknownSchemaNodes);
+        this.pathStatement = requireNonNull(pathStatement);
+        this.requireInstance = requireInstance;
+    }
+
+    @Override
+    public PathExpression getPathStatement() {
+        return pathStatement;
+    }
+
+    @Override
+    public boolean requireInstance() {
+        return requireInstance;
+    }
+
+    @Override
+    public int hashCode() {
+        return LeafrefTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return LeafrefTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return LeafrefTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseStringType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseStringType.java
new file mode 100644 (file)
index 0000000..1cb7cfa
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseStringType extends AbstractLengthRestrictedBaseType<StringTypeDefinition>
+        implements StringTypeDefinition {
+    static final @NonNull BaseStringType INSTANCE = new BaseStringType();
+
+    private BaseStringType() {
+        super(BaseTypes.STRING_QNAME);
+    }
+
+    @Override
+    public List<PatternConstraint> getPatternConstraints() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public int hashCode() {
+        return StringTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return StringTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return StringTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseTypes.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseTypes.java
new file mode 100644 (file)
index 0000000..6deaa15
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+
+/**
+ * Utility access methods for creating and accessing YANG base type definitions. YANG types come in two basic variants,
+ * depending on whether they fully define their base instance or model input is required to fully-form the type.
+ *
+ * <p>
+ * The following types have their base type fully specified and are exposed as appropriate TypeDefinition
+ * sub-interfaces:
+ * <ul>
+ *     <li>boolean</li>
+ *     <li>empty</li>
+ *     <li>binary</li>
+ *     <li>int{8,16,32,64}</li>
+ *     <li>string</li>
+ *     <li>uint{8,16,32,64}</li>
+ * </ul>
+ *
+ * <p>
+ * The following types require additional specification in the model and are exposed by means of a specialized
+ * {@link TypeBuilder}s for each type:
+ * <ul>
+ *    <li>decimal64</li>
+ *    <li>instance-identifier</li>
+ *    <li>enumeration</li>
+ *    <li>identityref</li>
+ *    <li>leafref</li>
+ *    <li>union</li>
+ * </ul>
+ */
+@Beta
+public final class BaseTypes {
+    private BaseTypes() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static @NonNull BinaryTypeDefinition binaryType() {
+        return BaseBinaryType.INSTANCE;
+    }
+
+    public static @NonNull BitsTypeBuilder bitsTypeBuilder(final SchemaPath path) {
+        return new BitsTypeBuilder(path);
+    }
+
+    public static @NonNull BooleanTypeDefinition booleanType() {
+        return BaseBooleanType.INSTANCE;
+    }
+
+    public static @NonNull DecimalTypeBuilder decimalTypeBuilder(final SchemaPath path) {
+        return new DecimalTypeBuilder(path);
+    }
+
+    public static @NonNull EmptyTypeDefinition emptyType() {
+        return BaseEmptyType.INSTANCE;
+    }
+
+    public static @NonNull EnumerationTypeBuilder enumerationTypeBuilder(final SchemaPath path) {
+        return new EnumerationTypeBuilder(path);
+    }
+
+    public static @NonNull IdentityrefTypeBuilder identityrefTypeBuilder(final SchemaPath path) {
+        return new IdentityrefTypeBuilder(path);
+    }
+
+    public static @NonNull InstanceIdentifierTypeDefinition instanceIdentifierType() {
+        return BaseInstanceIdentifierType.INSTANCE;
+    }
+
+    public static @NonNull Int8TypeDefinition int8Type() {
+        return BaseInt8Type.INSTANCE;
+    }
+
+    /**
+     * Check if a particular type definition represents the built-in int8 type.
+     *
+     * @param type Type definition
+     * @return True if the definition is the built-in int8 type.
+     */
+    public static boolean isInt8(final TypeDefinition<?> type) {
+        return BaseInt8Type.INSTANCE.getPath().equals(type.getPath());
+    }
+
+    public static @NonNull Int16TypeDefinition int16Type() {
+        return BaseInt16Type.INSTANCE;
+    }
+
+    /**
+     * Check if a particular type definition represents the built-in int16 type.
+     *
+     * @param type Type definition
+     * @return True if the definition is the built-in int16 type.
+     */
+    public static boolean isInt16(final TypeDefinition<?> type) {
+        return BaseInt16Type.INSTANCE.getPath().equals(type.getPath());
+    }
+
+    public static @NonNull Int32TypeDefinition int32Type() {
+        return BaseInt32Type.INSTANCE;
+    }
+
+    /**
+     * Check if a particular type definition represents the built-in int32 type.
+     *
+     * @param type Type definition
+     * @return True if the definition is the built-in int32 type.
+     */
+
+    public static boolean isInt32(final TypeDefinition<?> type) {
+        return BaseInt32Type.INSTANCE.getPath().equals(type.getPath());
+    }
+
+    public static @NonNull Int64TypeDefinition int64Type() {
+        return BaseInt64Type.INSTANCE;
+    }
+
+    /**
+     * Check if a particular type definition represents the built-in int64 type.
+     *
+     * @param type Type definition
+     * @return True if the definition is the built-in int64 type.
+     */
+    public static boolean isInt64(final TypeDefinition<?> type) {
+        return BaseInt64Type.INSTANCE.getPath().equals(type.getPath());
+    }
+
+    public static @NonNull LeafrefTypeBuilder leafrefTypeBuilder(final SchemaPath path) {
+        return new LeafrefTypeBuilder(path);
+    }
+
+    public static @NonNull StringTypeDefinition stringType() {
+        return BaseStringType.INSTANCE;
+    }
+
+    public static UnionTypeBuilder unionTypeBuilder(final SchemaPath path) {
+        return new UnionTypeBuilder(path);
+    }
+
+    public static @NonNull Uint8TypeDefinition uint8Type() {
+        return BaseUint8Type.INSTANCE;
+    }
+
+    /**
+     * Check if a particular type is the base type for uint8.
+     *
+     * @param type The type to check
+     * @return If the type corresponds to the base uint8 type.
+     * @throws NullPointerException if type is null
+     */
+    public static boolean isUint8(final @NonNull TypeDefinition<?> type) {
+        return BaseUint8Type.INSTANCE.getPath().equals(type.getPath());
+    }
+
+    public static @NonNull Uint16TypeDefinition uint16Type() {
+        return BaseUint16Type.INSTANCE;
+    }
+
+    /**
+     * Check if a particular type is the base type for uint16.
+     *
+     * @param type The type to check
+     * @return If the type corresponds to the base uint16 type.
+     * @throws NullPointerException if type is null
+     */
+    public static boolean isUint16(final @NonNull TypeDefinition<?> type) {
+        return BaseUint16Type.INSTANCE.getPath().equals(type.getPath());
+    }
+
+    public static @NonNull Uint32TypeDefinition uint32Type() {
+        return BaseUint32Type.INSTANCE;
+    }
+
+    /**
+     * Check if a particular type is the base type for uint32.
+     *
+     * @param type The type to check
+     * @return If the type corresponds to the base uint32 type.
+     * @throws NullPointerException if type is null
+     */
+    public static boolean isUint32(final @NonNull TypeDefinition<?> type) {
+        return BaseUint32Type.INSTANCE.getPath().equals(type.getPath());
+    }
+
+    public static @NonNull Uint64TypeDefinition uint64Type() {
+        return BaseUint64Type.INSTANCE;
+    }
+
+    /**
+     * Check if a particular type is the base type for uint64.
+     *
+     * @param type The type to check
+     * @return If the type corresponds to the base uint64 type.
+     * @throws NullPointerException if type is null
+     */
+    public static boolean isUint64(final @NonNull TypeDefinition<?> type) {
+        return BaseUint64Type.INSTANCE.getPath().equals(type.getPath());
+    }
+
+    /**
+     * Return the base type of a particular type. This method performs recursive lookup through the type's base type
+     * until it finds the last element and returns it. If the argument is already the base type, it is returned as is.
+     *
+     * @param type Type for which to find the base type
+     * @return Base type of specified type
+     * @throws NullPointerException if type is null
+     */
+    public static @NonNull TypeDefinition<?> baseTypeOf(final @NonNull TypeDefinition<?> type) {
+        TypeDefinition<?> ret = type;
+        while (ret.getBaseType() != null) {
+            ret = ret.getBaseType();
+        }
+        return ret;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUint16Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUint16Type.java
new file mode 100644 (file)
index 0000000..1e95079
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseUint16Type extends AbstractRangeRestrictedBaseType<Uint16TypeDefinition, Uint16>
+        implements Uint16TypeDefinition {
+    static final @NonNull BaseUint16Type INSTANCE = new BaseUint16Type();
+
+    private BaseUint16Type() {
+        super(BaseTypes.UINT16_QNAME, Uint16.valueOf(0), Uint16.valueOf(65535));
+    }
+
+    @Override
+    public int hashCode() {
+        return Uint16TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Uint16TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Uint16TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUint32Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUint32Type.java
new file mode 100644 (file)
index 0000000..bdf3ccd
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseUint32Type extends AbstractRangeRestrictedBaseType<Uint32TypeDefinition, Uint32>
+        implements Uint32TypeDefinition {
+    static final @NonNull BaseUint32Type INSTANCE = new BaseUint32Type();
+
+    private BaseUint32Type() {
+        super(BaseTypes.UINT32_QNAME, Uint32.valueOf(0), Uint32.valueOf(4294967295L));
+    }
+
+    @Override
+    public int hashCode() {
+        return Uint32TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Uint32TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Uint32TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUint64Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUint64Type.java
new file mode 100644 (file)
index 0000000..2b6043f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseUint64Type extends AbstractRangeRestrictedBaseType<Uint64TypeDefinition, Uint64>
+        implements Uint64TypeDefinition {
+    static final @NonNull BaseUint64Type INSTANCE = new BaseUint64Type();
+
+    private BaseUint64Type() {
+        super(BaseTypes.UINT64_QNAME, Uint64.valueOf(0), Uint64.valueOf("18446744073709551615"));
+    }
+
+    @Override
+    public int hashCode() {
+        return Uint64TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Uint64TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Uint64TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUint8Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUint8Type.java
new file mode 100644 (file)
index 0000000..2a3d481
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+final class BaseUint8Type extends AbstractRangeRestrictedBaseType<Uint8TypeDefinition, Uint8>
+        implements Uint8TypeDefinition {
+    static final @NonNull BaseUint8Type INSTANCE = new BaseUint8Type();
+
+    private BaseUint8Type() {
+        super(BaseTypes.UINT8_QNAME, Uint8.valueOf(0), Uint8.valueOf(255));
+    }
+
+    @Override
+    public int hashCode() {
+        return Uint8TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Uint8TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Uint8TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUnionType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BaseUnionType.java
new file mode 100644 (file)
index 0000000..9eb3ec0
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+
+final class BaseUnionType extends AbstractBaseType<UnionTypeDefinition> implements UnionTypeDefinition {
+    private final ImmutableList<TypeDefinition<?>> types;
+
+    BaseUnionType(final SchemaPath path, final List<UnknownSchemaNode> unknownSchemaNodes,
+            final Collection<TypeDefinition<?>> types) {
+        super(path, unknownSchemaNodes);
+        this.types = ImmutableList.copyOf(types);
+    }
+
+    @Override
+    public List<TypeDefinition<?>> getTypes() {
+        return types;
+    }
+
+    @Override
+    public int hashCode() {
+        return UnionTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return UnionTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return UnionTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BitBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BitBuilder.java
new file mode 100644 (file)
index 0000000..b759257
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+
+/**
+ * Utility builder for {@link Bit} instances.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class BitBuilder implements Builder<Bit>, Mutable {
+    private final SchemaPath schemaPath;
+    private final Long position;
+
+    private ImmutableList<UnknownSchemaNode> unknownSchemaNodes = ImmutableList.of();
+    private Status status = Status.CURRENT;
+    private String description;
+    private String reference;
+
+    private BitBuilder(final SchemaPath schemaPath, final Long position) {
+        this.schemaPath = requireNonNull(schemaPath);
+        this.position = requireNonNull(position);
+    }
+
+    public static BitBuilder create(final SchemaPath schemaPath, final Long position) {
+        return new BitBuilder(schemaPath, position);
+    }
+
+    public BitBuilder setDescription(final String description) {
+        this.description = description;
+        return this;
+    }
+
+    public BitBuilder setReference(final String reference) {
+        this.reference = reference;
+        return this;
+    }
+
+    public BitBuilder setStatus(final Status status) {
+        this.status = requireNonNull(status);
+        return this;
+    }
+
+    public BitBuilder setUnknownSchemaNodes(final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        this.unknownSchemaNodes = ImmutableList.copyOf(unknownSchemaNodes);
+        return this;
+    }
+
+    public BitBuilder setUnknownSchemaNodes(final UnknownSchemaNode... unknownSchemaNodes) {
+        this.unknownSchemaNodes = ImmutableList.copyOf(unknownSchemaNodes);
+        return this;
+    }
+
+    @Override
+    public Bit build() {
+        return new BitImpl(schemaPath, position, description, reference, status, unknownSchemaNodes);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BitImpl.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BitImpl.java
new file mode 100644 (file)
index 0000000..5bb12ef
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2016 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.util.type;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+
+final class BitImpl implements Bit, Immutable {
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
+    private final @NonNull SchemaPath schemaPath;
+    private final String description;
+    private final String reference;
+    private final @NonNull Status status;
+    private final long position;
+
+    BitImpl(final SchemaPath schemaPath, final long position, final String description,
+            final String reference, final Status status, final List<UnknownSchemaNode> unknownNodes) {
+        this.schemaPath = requireNonNull(schemaPath, "Schema Path should not be null");
+
+        checkArgument(position >= 0L && position <= 4294967295L, "Invalid position %s", position);
+        this.position = position;
+        this.description = description;
+        this.reference = reference;
+        this.status = requireNonNull(status);
+        this.unknownNodes = ImmutableList.copyOf(unknownNodes);
+    }
+
+    @Override
+    public QName getQName() {
+        return schemaPath.getLastComponent();
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return schemaPath;
+    }
+
+    @Override
+    public Optional<String> getDescription() {
+        return Optional.ofNullable(description);
+    }
+
+    @Override
+    public Optional<String> getReference() {
+        return Optional.ofNullable(reference);
+    }
+
+    @Override
+    public Status getStatus() {
+        return status;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownNodes;
+    }
+
+    @Override
+    public long getPosition() {
+        return position;
+    }
+
+    @Override
+    public String getName() {
+        return getQName().getLocalName();
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + getQName().hashCode();
+        result = prime * result + schemaPath.hashCode();
+        result = prime * result + Long.hashCode(position);
+        result = prime * result + unknownNodes.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Bit other = (Bit) obj;
+        return Objects.equals(schemaPath, other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return Bit.class.getSimpleName() + "[name=" + getQName().getLocalName() + ", position=" + position + "]";
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BitsTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/BitsTypeBuilder.java
new file mode 100644 (file)
index 0000000..11e0bed
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.Map;
+import java.util.TreeMap;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+
+public final class BitsTypeBuilder extends AbstractRestrictedTypeBuilder<BitsTypeDefinition> {
+    private final Builder<String, Bit> builder = ImmutableMap.builder();
+
+    BitsTypeBuilder(final SchemaPath path) {
+        super(null, path);
+    }
+
+    BitsTypeBuilder(final BitsTypeDefinition baseType, final SchemaPath path) {
+        super(baseType, path);
+    }
+
+    public BitsTypeBuilder addBit(final @NonNull Bit item) {
+        // in case we are dealing with a restricted bits type, validate if the bit is a subset of its base type
+        if (getBaseType() != null) {
+            validateRestrictedBit(item);
+        }
+
+        builder.put(item.getName(), item);
+        touch();
+        return this;
+    }
+
+    private void validateRestrictedBit(final @NonNull Bit item) {
+        boolean isASubsetOfBaseBits = false;
+        for (Bit baseTypeBit : getBaseType().getBits()) {
+            if (item.getName().equals(baseTypeBit.getName())) {
+                if (item.getPosition() != baseTypeBit.getPosition()) {
+                    throw new InvalidBitDefinitionException(item, "Position of bit '%s' must be the same as the "
+                            + "position of corresponding bit in the base bits type %s.", item.getName(),
+                            getBaseType().getQName());
+                }
+                isASubsetOfBaseBits = true;
+                break;
+            }
+        }
+
+        if (!isASubsetOfBaseBits) {
+            throw new InvalidBitDefinitionException(item, "Bit '%s' is not a subset of its base bits type %s.",
+                    item.getName(), getBaseType().getQName());
+        }
+    }
+
+    @Override
+    public BitsTypeDefinition buildType() {
+        final Map<String, Bit> map = builder.build();
+        final Map<Long, Bit> positionMap = new TreeMap<>();
+
+        for (Bit b : map.values()) {
+            final Bit conflict = positionMap.put(b.getPosition(), b);
+            if (conflict != null) {
+                throw new InvalidBitDefinitionException(b, "Bit %s conflicts on position with bit ", conflict);
+            }
+        }
+
+        return getBaseType() == null ? new BaseBitsType(getPath(), getUnknownSchemaNodes(), positionMap.values())
+                : new RestrictedBitsType(getBaseType(), getPath(), getUnknownSchemaNodes(), positionMap.values());
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/CompatUtils.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/CompatUtils.java
new file mode 100644 (file)
index 0000000..ca854ff
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2015 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.util.type;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.LengthRestrictedTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+
+/**
+ * Compatibility utilities for dealing with differences between the old parser's ExtendedType-driven type
+ * representation versus the representation this package models.
+ *
+ * @deprecated This class is provided strictly for compatibility only. No new users should be introduced, as this class
+ *             is scheduled for removal when its two OpenDaylight users, Java Binding v1 and YANG JMX Bindings are
+ *             removed.
+ */
+@Deprecated
+public final class CompatUtils {
+    private CompatUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * This package's type hierarchy model generates a type which encapsulates the default value and units for leaves.
+     * Java Binding specification is implemented in a way, where it needs to revert this process if the internal
+     * declaration has not restricted the type further -- which is not something available via
+     * {@link TypeDefinition#getBaseType()}.
+     *
+     * <p>
+     * Here are the possible scenarios:
+     *
+     * <pre>
+     * leaf foo {
+     *     type uint8 {
+     *         range 1..2;
+     *     }
+     * }
+     * </pre>
+     * The leaf type's schema path does not match the schema path of the leaf. We do NOT want to strip it, as
+     * we need to generate an inner class to hold the restrictions.
+     *
+     * <pre>
+     * leaf foo {
+     *     type uint8 {
+     *         range 1..2;
+     *     }
+     *     default 1;
+     * }
+     * </pre>
+     * The leaf type's schema path will match the schema path of the leaf. We do NOT want to strip it, as we need
+     * to generate an inner class to hold the restrictions.
+     *
+     * <pre>
+     * leaf foo {
+     *     type uint8;
+     *     default 1;
+     * }
+     * </pre>
+     * The leaf type's schema path will match the schema path of the leaf. We DO want to strip it, as we will deal
+     * with the default value ourselves.
+     *
+     * <pre>
+     * leaf foo {
+     *     type uint8;
+     * }
+     * </pre>
+     * The leaf type's schema path will not match the schema path of the leaf. We do NOT want to strip it.
+     *
+     * <p>
+     * The situation is different for types which do not have a default instantiation in YANG: leafref, enumeration,
+     * identityref, decimal64, bits and union. If these types are defined within this leaf's statement, a base type
+     * will be instantiated. If the leaf defines a default statement, this base type will be visible via getBaseType().
+     *
+     * <pre>
+     * leaf foo {
+     *     type decimal64 {
+     *         fraction-digits 2;
+     *     }
+     * }
+     * </pre>
+     * The leaf type's schema path will not match the schema path of the leaf, and we do not want to strip it, as it
+     * needs to be generated.
+     *
+     * <pre>
+     * leaf foo {
+     *     type decimal64 {
+     *         fraction-digits 2;
+     *     }
+     *     default 1;
+     * }
+     * </pre>
+     * The leaf type's schema path will match the schema path of the leaf, and we DO want to strip it.
+     *
+     * @param leaf Leaf for which we are acquiring the type
+     * @return Potentially base type of the leaf type.
+     */
+    public static @NonNull TypeDefinition<?> compatType(final @NonNull TypedDataSchemaNode leaf) {
+        final TypeDefinition<?> leafType = requireNonNull(leaf.getType());
+
+        if (!leaf.getPath().equals(leafType.getPath())) {
+            // Old parser semantics, or no new default/units defined for this leaf
+            return leafType;
+        }
+
+        // We are dealing with a type generated for the leaf itself
+        final TypeDefinition<?> baseType = leafType.getBaseType();
+        checkArgument(baseType != null, "Leaf %s has type for leaf, but no base type", leaf);
+
+        if (leaf.getPath().equals(baseType.getPath().getParent())) {
+            // Internal instantiation of a base YANG type (decimal64 and similar)
+            return baseType;
+        }
+
+        // At this point we have dealt with the easy cases. Now we need to perform per-type checking if there are no
+        // new constraints introduced by this type. If there were not, we will return the base type.
+        if (leafType instanceof BinaryTypeDefinition) {
+            return baseTypeIfNotConstrained((BinaryTypeDefinition) leafType);
+        } else if (leafType instanceof DecimalTypeDefinition) {
+            return baseTypeIfNotConstrained((DecimalTypeDefinition) leafType);
+        } else if (leafType instanceof InstanceIdentifierTypeDefinition) {
+            return baseTypeIfNotConstrained((InstanceIdentifierTypeDefinition) leafType);
+        } else if (leafType instanceof Int8TypeDefinition) {
+            return baseTypeIfNotConstrained((Int8TypeDefinition) leafType);
+        } else if (leafType instanceof Int16TypeDefinition) {
+            return baseTypeIfNotConstrained((Int16TypeDefinition) leafType);
+        } else if (leafType instanceof Int32TypeDefinition) {
+            return baseTypeIfNotConstrained((Int32TypeDefinition) leafType);
+        } else if (leafType instanceof Int64TypeDefinition) {
+            return baseTypeIfNotConstrained((Int64TypeDefinition) leafType);
+        } else if (leafType instanceof StringTypeDefinition) {
+            return baseTypeIfNotConstrained((StringTypeDefinition) leafType);
+        } else if (leafType instanceof Uint8TypeDefinition) {
+            return baseTypeIfNotConstrained((Uint8TypeDefinition) leafType);
+        } else if (leafType instanceof Uint16TypeDefinition) {
+            return baseTypeIfNotConstrained((Uint16TypeDefinition) leafType);
+        } else if (leafType instanceof Uint32TypeDefinition) {
+            return baseTypeIfNotConstrained((Uint32TypeDefinition) leafType);
+        } else if (leafType instanceof Uint64TypeDefinition) {
+            return baseTypeIfNotConstrained((Uint64TypeDefinition) leafType);
+        } else {
+            // Other types cannot be constrained, return the base type
+            return baseType;
+        }
+    }
+
+    private static BinaryTypeDefinition baseTypeIfNotConstrained(final @NonNull BinaryTypeDefinition type) {
+        return baseTypeIfNotConstrained(type, type.getBaseType());
+    }
+
+    private static TypeDefinition<?> baseTypeIfNotConstrained(final @NonNull DecimalTypeDefinition type) {
+        return baseTypeIfNotConstrained(type, type.getBaseType());
+    }
+
+    private static TypeDefinition<?> baseTypeIfNotConstrained(final @NonNull InstanceIdentifierTypeDefinition type) {
+        final InstanceIdentifierTypeDefinition base = type.getBaseType();
+        return type.requireInstance() == base.requireInstance() ? base : type;
+    }
+
+    private static TypeDefinition<?> baseTypeIfNotConstrained(final @NonNull StringTypeDefinition type) {
+        final StringTypeDefinition base = type.getBaseType();
+        final List<PatternConstraint> patterns = type.getPatternConstraints();
+        final Optional<LengthConstraint> optLengths = type.getLengthConstraint();
+
+        if ((patterns.isEmpty() || patterns.equals(base.getPatternConstraints()))
+                && (optLengths.isEmpty() || optLengths.equals(base.getLengthConstraint()))) {
+            return base;
+        }
+
+        return type;
+    }
+
+    private static <T extends RangeRestrictedTypeDefinition<T, ?>> T baseTypeIfNotConstrained(final @NonNull T type) {
+        return baseTypeIfNotConstrained(type, type.getBaseType());
+    }
+
+    private static <T extends RangeRestrictedTypeDefinition<T, ?>> T baseTypeIfNotConstrained(final @NonNull T type,
+            final T base) {
+        final Optional<?> optConstraint = type.getRangeConstraint();
+        if (optConstraint.isEmpty()) {
+            return base;
+        }
+        return optConstraint.equals(base.getRangeConstraint()) ? base : type;
+    }
+
+    private static <T extends LengthRestrictedTypeDefinition<T>> T baseTypeIfNotConstrained(final @NonNull T type,
+            final T base) {
+        final Optional<LengthConstraint> optConstraint = type.getLengthConstraint();
+        if (optConstraint.isEmpty()) {
+            return base;
+        }
+        return optConstraint.equals(base.getLengthConstraint()) ? base : type;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/ConcreteTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/ConcreteTypeBuilder.java
new file mode 100644 (file)
index 0000000..4a9d9ac
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Builder of {@link TypeDefinition}s for use in leaf statements. While similar to {@link DerivedTypeBuilder}, this
+ * builder does not support adding of unknown nodes and will return the base type if the type is not modified, hence
+ * not preserving the schema path.
+ *
+ * @param <T> Resulting {@link TypeDefinition}
+ */
+@Beta
+public abstract class ConcreteTypeBuilder<T extends TypeDefinition<T>> extends DerivedTypeBuilder<T> {
+    ConcreteTypeBuilder(final T baseType, final SchemaPath path) {
+        super(baseType, path);
+
+        setStatus(baseType.getStatus());
+        baseType.getDescription().ifPresent(this::setDescription);
+        baseType.getReference().ifPresent(this::setReference);
+    }
+
+    /**
+     * Build the resulting type.
+     *
+     * @return A new type instance
+     */
+    abstract @NonNull T buildType();
+
+    @Override
+    public final T build() {
+        final T base = getBaseType();
+        if (Objects.equals(getDefaultValue(), base.getDefaultValue().orElse(null))
+                && Objects.equals(getUnits(), base.getUnits().orElse(null))) {
+            return base;
+        }
+
+        return verifyNotNull(buildType());
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/ConcreteTypes.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/ConcreteTypes.java
new file mode 100644 (file)
index 0000000..a1fd2cd
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+
+/**
+ * Support for creating {@link TypeDefinition} instances defined by leaf and leaf-list statements.
+ */
+@Beta
+@NonNullByDefault
+public final class ConcreteTypes {
+    private ConcreteTypes() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static ConcreteTypeBuilder<?> concreteTypeBuilder(final TypeDefinition<?> baseType, final SchemaPath path) {
+        if (baseType instanceof BinaryTypeDefinition) {
+            return concreteBinaryBuilder((BinaryTypeDefinition) baseType, path);
+        } else if (baseType instanceof BitsTypeDefinition) {
+            return concreteBitsBuilder((BitsTypeDefinition) baseType, path);
+        } else if (baseType instanceof BooleanTypeDefinition) {
+            return concreteBooleanBuilder((BooleanTypeDefinition) baseType, path);
+        } else if (baseType instanceof DecimalTypeDefinition) {
+            return concreteDecimalBuilder((DecimalTypeDefinition) baseType, path);
+        } else if (baseType instanceof EmptyTypeDefinition) {
+            return concreteEmptyBuilder((EmptyTypeDefinition) baseType, path);
+        } else if (baseType instanceof EnumTypeDefinition) {
+            return concreteEnumerationBuilder((EnumTypeDefinition) baseType, path);
+        } else if (baseType instanceof IdentityrefTypeDefinition) {
+            return concreteIdentityrefBuilder((IdentityrefTypeDefinition) baseType, path);
+        } else if (baseType instanceof InstanceIdentifierTypeDefinition) {
+            return concreteInstanceIdentifierBuilder((InstanceIdentifierTypeDefinition) baseType, path);
+        } else if (baseType instanceof Int8TypeDefinition) {
+            return concreteInt8Builder((Int8TypeDefinition) baseType, path);
+        } else if (baseType instanceof Int16TypeDefinition) {
+            return concreteInt16Builder((Int16TypeDefinition) baseType, path);
+        } else if (baseType instanceof Int32TypeDefinition) {
+            return concreteInt32Builder((Int32TypeDefinition) baseType, path);
+        } else if (baseType instanceof Int64TypeDefinition) {
+            return concreteInt64Builder((Int64TypeDefinition) baseType, path);
+        } else if (baseType instanceof LeafrefTypeDefinition) {
+            return concreteLeafrefBuilder((LeafrefTypeDefinition) baseType, path);
+        } else if (baseType instanceof StringTypeDefinition) {
+            return concreteStringBuilder((StringTypeDefinition) baseType, path);
+        } else if (baseType instanceof UnionTypeDefinition) {
+            return concreteUnionBuilder((UnionTypeDefinition) baseType, path);
+        } else if (baseType instanceof Uint8TypeDefinition) {
+            return concreteUint8Builder((Uint8TypeDefinition) baseType, path);
+        } else if (baseType instanceof Uint16TypeDefinition) {
+            return concreteUint16Builder((Uint16TypeDefinition) baseType, path);
+        } else if (baseType instanceof Uint32TypeDefinition) {
+            return concreteUint32Builder((Uint32TypeDefinition) baseType, path);
+        } else if (baseType instanceof Uint64TypeDefinition) {
+            return concreteUint64Builder((Uint64TypeDefinition) baseType, path);
+        } else {
+            throw new IllegalArgumentException("Unhandled type definition class " + baseType.getClass());
+        }
+    }
+
+    private static ConcreteTypeBuilder<BinaryTypeDefinition> concreteBinaryBuilder(
+            final BinaryTypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<BinaryTypeDefinition>(baseType, path) {
+            @Override
+            public BinaryTypeDefinition buildType() {
+                return new DerivedBinaryType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<BitsTypeDefinition> concreteBitsBuilder(final BitsTypeDefinition baseType,
+            final SchemaPath path) {
+        return new ConcreteTypeBuilder<BitsTypeDefinition>(baseType, path) {
+            @Override
+            public BitsTypeDefinition buildType() {
+                return new DerivedBitsType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<BooleanTypeDefinition> concreteBooleanBuilder(
+            final BooleanTypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<BooleanTypeDefinition>(baseType, path) {
+            @Override
+            public BooleanTypeDefinition buildType() {
+                return new DerivedBooleanType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<DecimalTypeDefinition> concreteDecimalBuilder(
+            final DecimalTypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<DecimalTypeDefinition>(baseType, path) {
+            @Override
+            public DecimalTypeDefinition buildType() {
+                return new DerivedDecimalType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<EmptyTypeDefinition> concreteEmptyBuilder(final EmptyTypeDefinition baseType,
+            final SchemaPath path) {
+        return new ConcreteTypeBuilder<EmptyTypeDefinition>(baseType, path) {
+            @Override
+            public EmptyTypeDefinition buildType() {
+                return new DerivedEmptyType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<EnumTypeDefinition> concreteEnumerationBuilder(
+            final EnumTypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<EnumTypeDefinition>(baseType, path) {
+            @Override
+            public EnumTypeDefinition buildType() {
+                return new DerivedEnumerationType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<IdentityrefTypeDefinition> concreteIdentityrefBuilder(
+            final IdentityrefTypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<IdentityrefTypeDefinition>(baseType, path) {
+            @Override
+            public IdentityrefTypeDefinition buildType() {
+                return new DerivedIdentityrefType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<InstanceIdentifierTypeDefinition> concreteInstanceIdentifierBuilder(
+            final InstanceIdentifierTypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<InstanceIdentifierTypeDefinition>(baseType, path) {
+            @Override
+            public InstanceIdentifierTypeDefinition buildType() {
+                return new DerivedInstanceIdentifierType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes(), baseType.requireInstance());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<Int8TypeDefinition> concreteInt8Builder(
+            final Int8TypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<Int8TypeDefinition>(baseType, path) {
+            @Override
+            public Int8TypeDefinition buildType() {
+                return new DerivedInt8Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<Int16TypeDefinition> concreteInt16Builder(
+            final Int16TypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<Int16TypeDefinition>(baseType, path) {
+            @Override
+            public Int16TypeDefinition buildType() {
+                return new DerivedInt16Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<Int32TypeDefinition> concreteInt32Builder(
+            final Int32TypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<Int32TypeDefinition>(baseType, path) {
+            @Override
+            public Int32TypeDefinition buildType() {
+                return new DerivedInt32Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<Int64TypeDefinition> concreteInt64Builder(
+            final Int64TypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<Int64TypeDefinition>(baseType, path) {
+            @Override
+            public Int64TypeDefinition buildType() {
+                return new DerivedInt64Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<LeafrefTypeDefinition> concreteLeafrefBuilder(
+            final LeafrefTypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<LeafrefTypeDefinition>(baseType, path) {
+            @Override
+            public LeafrefTypeDefinition buildType() {
+                return new DerivedLeafrefType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<StringTypeDefinition> concreteStringBuilder(final StringTypeDefinition baseType,
+            final SchemaPath path) {
+        return new ConcreteTypeBuilder<StringTypeDefinition>(baseType, path) {
+            @Override
+            public StringTypeDefinition buildType() {
+                return new DerivedStringType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<UnionTypeDefinition> concreteUnionBuilder(final UnionTypeDefinition baseType,
+            final SchemaPath path) {
+        return new ConcreteTypeBuilder<UnionTypeDefinition>(baseType, path) {
+            @Override
+            public DerivedUnionType buildType() {
+                return new DerivedUnionType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<Uint8TypeDefinition> concreteUint8Builder(
+            final Uint8TypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<Uint8TypeDefinition>(baseType, path) {
+            @Override
+            public Uint8TypeDefinition buildType() {
+                return new DerivedUint8Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<Uint16TypeDefinition> concreteUint16Builder(
+            final Uint16TypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<Uint16TypeDefinition>(baseType, path) {
+            @Override
+            public Uint16TypeDefinition buildType() {
+                return new DerivedUint16Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<Uint32TypeDefinition> concreteUint32Builder(
+            final Uint32TypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<Uint32TypeDefinition>(baseType, path) {
+            @Override
+            public Uint32TypeDefinition buildType() {
+                return new DerivedUint32Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static ConcreteTypeBuilder<Uint64TypeDefinition> concreteUint64Builder(
+            final Uint64TypeDefinition baseType, final SchemaPath path) {
+        return new ConcreteTypeBuilder<Uint64TypeDefinition>(baseType, path) {
+            @Override
+            public Uint64TypeDefinition buildType() {
+                return new DerivedUint64Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DecimalTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DecimalTypeBuilder.java
new file mode 100644 (file)
index 0000000..fbb7ca6
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.base.Preconditions;
+import java.math.BigDecimal;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+
+public final class DecimalTypeBuilder extends RangeRestrictedTypeBuilder<DecimalTypeDefinition, BigDecimal> {
+    private Integer fractionDigits;
+
+    DecimalTypeBuilder(final SchemaPath path) {
+        super(null, path);
+    }
+
+    public DecimalTypeBuilder setFractionDigits(final int fractionDigits) {
+        Preconditions.checkState(this.fractionDigits == null, "Fraction digits already defined to %s",
+                this.fractionDigits);
+        this.fractionDigits = fractionDigits;
+        return this;
+    }
+
+    @Override
+    DecimalTypeDefinition buildType() {
+        Preconditions.checkState(fractionDigits != null, "Fraction digits not defined");
+
+        return new BaseDecimalType(getPath(), getUnknownSchemaNodes(), fractionDigits,
+            calculateRangeConstraint(BaseDecimalType.constraintsForDigits(fractionDigits)));
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedBinaryType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedBinaryType.java
new file mode 100644 (file)
index 0000000..c16c021
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+
+final class DerivedBinaryType extends AbstractLengthRestrictedDerivedType<BinaryTypeDefinition>
+        implements BinaryTypeDefinition {
+    DerivedBinaryType(final BinaryTypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+            final String description, final String reference, final Status status, final String units,
+            final Collection<UnknownSchemaNode> unknownSchemNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return BinaryTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return BinaryTypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedBitsType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedBitsType.java
new file mode 100644 (file)
index 0000000..6207ae2
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+
+final class DerivedBitsType extends AbstractDerivedType<BitsTypeDefinition> implements BitsTypeDefinition {
+    DerivedBitsType(final BitsTypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+        final String description, final String reference, final Status status, final String units,
+        final Collection<UnknownSchemaNode> unknownSchemNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemNodes);
+    }
+
+    @Override
+    public List<Bit> getBits() {
+        return baseType().getBits();
+    }
+
+    @Override
+    public int hashCode() {
+        return BitsTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return BitsTypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedBooleanType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedBooleanType.java
new file mode 100644 (file)
index 0000000..c018a7a
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+
+final class DerivedBooleanType extends AbstractDerivedType<BooleanTypeDefinition> implements BooleanTypeDefinition {
+    DerivedBooleanType(final BooleanTypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+        final String description, final String reference, final Status status, final String units,
+        final Collection<UnknownSchemaNode> unknownSchemNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return BooleanTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return BooleanTypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedDecimalType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedDecimalType.java
new file mode 100644 (file)
index 0000000..6f52d61
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+
+final class DerivedDecimalType extends AbstractRangeRestrictedDerivedType<DecimalTypeDefinition, BigDecimal>
+        implements DecimalTypeDefinition {
+    DerivedDecimalType(final DecimalTypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+        final String description, final String reference, final Status status, final String units,
+        final Collection<UnknownSchemaNode> unknownSchemNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemNodes);
+    }
+
+    @Override
+    public int getFractionDigits() {
+        return baseType().getFractionDigits();
+    }
+
+    @Override
+    public int hashCode() {
+        return DecimalTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return DecimalTypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedEmptyType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedEmptyType.java
new file mode 100644 (file)
index 0000000..6100823
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+
+final class DerivedEmptyType extends AbstractDerivedType<EmptyTypeDefinition> implements EmptyTypeDefinition {
+    DerivedEmptyType(final EmptyTypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+        final String description, final String reference, final Status status, final String units,
+        final Collection<UnknownSchemaNode> unknownSchemNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return EmptyTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return EmptyTypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedEnumerationType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedEnumerationType.java
new file mode 100644 (file)
index 0000000..d3cf3d0
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+
+final class DerivedEnumerationType extends AbstractDerivedType<EnumTypeDefinition> implements EnumTypeDefinition {
+    DerivedEnumerationType(final EnumTypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+        final String description, final String reference, final Status status, final String units,
+        final Collection<UnknownSchemaNode> unknownSchemNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemNodes);
+    }
+
+    @Override
+    public List<EnumPair> getValues() {
+        return baseType().getValues();
+    }
+
+    @Override
+    public int hashCode() {
+        return EnumTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return EnumTypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedIdentityrefType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedIdentityrefType.java
new file mode 100644 (file)
index 0000000..d1714d8
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+
+final class DerivedIdentityrefType extends AbstractDerivedType<IdentityrefTypeDefinition>
+        implements IdentityrefTypeDefinition {
+    DerivedIdentityrefType(final IdentityrefTypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+        final String description, final String reference, final Status status, final String units,
+        final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public Set<IdentitySchemaNode> getIdentities() {
+        return baseType().getIdentities();
+    }
+
+    @Override
+    public int hashCode() {
+        return IdentityrefTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return IdentityrefTypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInstanceIdentifierType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInstanceIdentifierType.java
new file mode 100644 (file)
index 0000000..3922a7f
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+
+final class DerivedInstanceIdentifierType extends AbstractDerivedType<InstanceIdentifierTypeDefinition>
+        implements InstanceIdentifierTypeDefinition {
+    private final boolean requireInstance;
+
+    DerivedInstanceIdentifierType(final InstanceIdentifierTypeDefinition baseType, final SchemaPath path,
+            final Object defaultValue, final String description, final String reference, final Status status,
+            final String units, final Collection<UnknownSchemaNode> unknownSchemaNodes,
+            final boolean requireInstance) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+        this.requireInstance = requireInstance;
+    }
+
+    @Override
+    public boolean requireInstance() {
+        return requireInstance;
+    }
+
+    @Override
+    public int hashCode() {
+        return InstanceIdentifierTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return InstanceIdentifierTypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInt16Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInt16Type.java
new file mode 100644 (file)
index 0000000..6f798de
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+
+final class DerivedInt16Type extends AbstractRangeRestrictedDerivedType<Int16TypeDefinition, Short>
+        implements Int16TypeDefinition {
+    DerivedInt16Type(final Int16TypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+            final String description, final String reference, final Status status, final String units,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Int16TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Int16TypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInt32Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInt32Type.java
new file mode 100644 (file)
index 0000000..978580d
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+
+final class DerivedInt32Type extends AbstractRangeRestrictedDerivedType<Int32TypeDefinition, Integer>
+        implements Int32TypeDefinition {
+    DerivedInt32Type(final Int32TypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+            final String description, final String reference, final Status status, final String units,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Int32TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Int32TypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInt64Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInt64Type.java
new file mode 100644 (file)
index 0000000..899a978
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+
+final class DerivedInt64Type extends AbstractRangeRestrictedDerivedType<Int64TypeDefinition, Long>
+        implements Int64TypeDefinition {
+    DerivedInt64Type(final Int64TypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+            final String description, final String reference, final Status status, final String units,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Int64TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Int64TypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInt8Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedInt8Type.java
new file mode 100644 (file)
index 0000000..8937ed0
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+
+final class DerivedInt8Type extends AbstractRangeRestrictedDerivedType<Int8TypeDefinition, Byte>
+        implements Int8TypeDefinition {
+    DerivedInt8Type(final Int8TypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+            final String description, final String reference, final Status status, final String units,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Int8TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Int8TypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedLeafrefType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedLeafrefType.java
new file mode 100644 (file)
index 0000000..0a04811
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+
+final class DerivedLeafrefType extends AbstractDerivedType<LeafrefTypeDefinition> implements LeafrefTypeDefinition {
+
+    DerivedLeafrefType(final LeafrefTypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+        final String description, final String reference, final Status status, final String units,
+        final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public PathExpression getPathStatement() {
+        return baseType().getPathStatement();
+    }
+
+    @Override
+    public boolean requireInstance() {
+        return baseType().requireInstance();
+    }
+
+    @Override
+    public int hashCode() {
+        return LeafrefTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return LeafrefTypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedStringType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedStringType.java
new file mode 100644 (file)
index 0000000..165f2b8
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+
+final class DerivedStringType extends AbstractLengthRestrictedDerivedType<StringTypeDefinition>
+        implements StringTypeDefinition {
+    DerivedStringType(final StringTypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+            final String description, final String reference, final Status status, final String units,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public List<PatternConstraint> getPatternConstraints() {
+        return baseType().getPatternConstraints();
+    }
+
+    @Override
+    public int hashCode() {
+        return StringTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return StringTypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedTypeBuilder.java
new file mode 100644 (file)
index 0000000..811c6ae
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Builder of {@link TypeDefinition}s for use in typedef statements.
+ *
+ * @param <T> Resulting {@link TypeDefinition}
+ */
+public abstract class DerivedTypeBuilder<T extends TypeDefinition<T>> extends TypeBuilder<T> {
+    private static final Logger LOG = LoggerFactory.getLogger(DerivedTypeBuilder.class);
+    private Object defaultValue;
+    private String description;
+    private String reference;
+    private Status status = Status.CURRENT;
+    private String units;
+
+    DerivedTypeBuilder(final T baseType, final SchemaPath path) {
+        super(requireNonNull(baseType), path);
+
+        checkArgument(baseType instanceof AbstractBaseType || baseType instanceof AbstractDerivedType
+            || baseType instanceof AbstractRestrictedType,
+            "Derived type can be built only from a base, derived, or restricted type, not %s", baseType);
+
+        // http://tools.ietf.org/html/rfc6020#section-7.3.4
+        defaultValue = baseType.getDefaultValue().orElse(null);
+
+        // In similar vein, it makes sense to propagate units
+        units = baseType.getUnits().orElse(null);
+    }
+
+    public void setDefaultValue(final @NonNull Object defaultValue) {
+        this.defaultValue = requireNonNull(defaultValue);
+    }
+
+    public final void setDescription(final @NonNull String description) {
+        this.description = requireNonNull(description);
+    }
+
+    public final void setReference(final @NonNull String reference) {
+        this.reference = requireNonNull(reference);
+    }
+
+    public final void setStatus(final @NonNull Status status) {
+        this.status = requireNonNull(status);
+    }
+
+    public final void setUnits(final String units) {
+        requireNonNull(units);
+
+        final Optional<String> baseUnits = getBaseType().getUnits();
+        if (baseUnits.isPresent() && !units.equals(baseUnits.get())) {
+            LOG.warn("Type {} uverrides 'units' of type {} to \"{}\"", getPath(), getBaseType(), units);
+        }
+
+        this.units = units;
+    }
+
+    final Object getDefaultValue() {
+        return defaultValue;
+    }
+
+    final String getDescription() {
+        return description;
+    }
+
+    final String getReference() {
+        return reference;
+    }
+
+    final Status getStatus() {
+        return status;
+    }
+
+    final String getUnits() {
+        return units;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedTypes.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedTypes.java
new file mode 100644 (file)
index 0000000..e098c41
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+
+/**
+ * YANG defines 'derived type' as a type created through a 'typedef' statement. These types are exposed in the
+ * hierarchical namespace and can be looked up.
+ *
+ * <p>
+ * A derived type can redefine the default value, description, status and reference of a particular type definition.
+ * It can only refine the units attribute, as that attribute is tied to the semantics of the value. The default value,
+ * and units attributes are inherited from the super (base or restricted) type, others are left undefined if not
+ * explicitly set. Status defaults to current.
+ */
+@Beta
+public final class DerivedTypes {
+    private DerivedTypes() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static @NonNull DerivedTypeBuilder<?> derivedTypeBuilder(final @NonNull TypeDefinition<?> baseType,
+            final @NonNull SchemaPath path) {
+        if (baseType instanceof BinaryTypeDefinition) {
+            return derivedBinaryBuilder((BinaryTypeDefinition) baseType, path);
+        } else if (baseType instanceof BitsTypeDefinition) {
+            return derivedBitsBuilder((BitsTypeDefinition) baseType, path);
+        } else if (baseType instanceof BooleanTypeDefinition) {
+            return derivedBooleanBuilder((BooleanTypeDefinition) baseType, path);
+        } else if (baseType instanceof DecimalTypeDefinition) {
+            return derivedDecimalBuilder((DecimalTypeDefinition) baseType, path);
+        } else if (baseType instanceof EmptyTypeDefinition) {
+            return derivedEmptyBuilder((EmptyTypeDefinition) baseType, path);
+        } else if (baseType instanceof EnumTypeDefinition) {
+            return derivedEnumerationBuilder((EnumTypeDefinition) baseType, path);
+        } else if (baseType instanceof IdentityrefTypeDefinition) {
+            return derivedIdentityrefBuilder((IdentityrefTypeDefinition) baseType, path);
+        } else if (baseType instanceof InstanceIdentifierTypeDefinition) {
+            return derivedInstanceIdentifierBuilder((InstanceIdentifierTypeDefinition) baseType, path);
+        } else if (baseType instanceof Int8TypeDefinition) {
+            return derivedInt8Builder((Int8TypeDefinition) baseType, path);
+        } else if (baseType instanceof Int16TypeDefinition) {
+            return derivedInt16Builder((Int16TypeDefinition) baseType, path);
+        } else if (baseType instanceof Int32TypeDefinition) {
+            return derivedInt32Builder((Int32TypeDefinition) baseType, path);
+        } else if (baseType instanceof Int64TypeDefinition) {
+            return derivedInt64Builder((Int64TypeDefinition) baseType, path);
+        } else if (baseType instanceof LeafrefTypeDefinition) {
+            return derivedLeafrefBuilder((LeafrefTypeDefinition) baseType, path);
+        } else if (baseType instanceof StringTypeDefinition) {
+            return derivedStringBuilder((StringTypeDefinition) baseType, path);
+        } else if (baseType instanceof UnionTypeDefinition) {
+            return derivedUnionBuilder((UnionTypeDefinition) baseType, path);
+        } else if (baseType instanceof Uint8TypeDefinition) {
+            return derivedUint8Builder((Uint8TypeDefinition) baseType, path);
+        } else if (baseType instanceof Uint16TypeDefinition) {
+            return derivedUint16Builder((Uint16TypeDefinition) baseType, path);
+        } else if (baseType instanceof Uint32TypeDefinition) {
+            return derivedUint32Builder((Uint32TypeDefinition) baseType, path);
+        } else if (baseType instanceof Uint64TypeDefinition) {
+            return derivedUint64Builder((Uint64TypeDefinition) baseType, path);
+        } else {
+            throw new IllegalArgumentException("Unhandled type definition class " + baseType.getClass());
+        }
+    }
+
+    public static @NonNull DerivedTypeBuilder<BinaryTypeDefinition> derivedBinaryBuilder(
+            final @NonNull BinaryTypeDefinition baseType, final @NonNull SchemaPath path) {
+        return new DerivedTypeBuilder<BinaryTypeDefinition>(baseType, path) {
+            @Override
+            public BinaryTypeDefinition build() {
+                return new DerivedBinaryType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull DerivedTypeBuilder<BitsTypeDefinition> derivedBitsBuilder(final BitsTypeDefinition baseType,
+            final SchemaPath path) {
+        return new DerivedTypeBuilder<BitsTypeDefinition>(baseType, path) {
+            @Override
+            public BitsTypeDefinition build() {
+                return new DerivedBitsType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull DerivedTypeBuilder<BooleanTypeDefinition> derivedBooleanBuilder(
+            final @NonNull BooleanTypeDefinition baseType, final @NonNull SchemaPath path) {
+        return new DerivedTypeBuilder<BooleanTypeDefinition>(baseType, path) {
+            @Override
+            public BooleanTypeDefinition build() {
+                return new DerivedBooleanType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static @NonNull DerivedTypeBuilder<DecimalTypeDefinition> derivedDecimalBuilder(
+            final DecimalTypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<DecimalTypeDefinition>(baseType, path) {
+            @Override
+            public DecimalTypeDefinition build() {
+                return new DerivedDecimalType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull DerivedTypeBuilder<EmptyTypeDefinition> derivedEmptyBuilder(
+            final EmptyTypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<EmptyTypeDefinition>(baseType, path) {
+            @Override
+            public EmptyTypeDefinition build() {
+                return new DerivedEmptyType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static @NonNull DerivedTypeBuilder<EnumTypeDefinition> derivedEnumerationBuilder(
+            final EnumTypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<EnumTypeDefinition>(baseType, path) {
+            @Override
+            public EnumTypeDefinition build() {
+                return new DerivedEnumerationType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull DerivedTypeBuilder<IdentityrefTypeDefinition> derivedIdentityrefBuilder(
+            final IdentityrefTypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<IdentityrefTypeDefinition>(baseType, path) {
+            @Override
+            public IdentityrefTypeDefinition build() {
+                return new DerivedIdentityrefType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull DerivedTypeBuilder<InstanceIdentifierTypeDefinition> derivedInstanceIdentifierBuilder(
+            final InstanceIdentifierTypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<InstanceIdentifierTypeDefinition>(baseType, path) {
+            @Override
+            public InstanceIdentifierTypeDefinition build() {
+                return new DerivedInstanceIdentifierType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes(), baseType.requireInstance());
+            }
+        };
+    }
+
+    private static @NonNull DerivedTypeBuilder<Int8TypeDefinition> derivedInt8Builder(final Int8TypeDefinition baseType,
+            final SchemaPath path) {
+        return new DerivedTypeBuilder<Int8TypeDefinition>(baseType, path) {
+            @Override
+            public Int8TypeDefinition build() {
+                return new DerivedInt8Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull DerivedTypeBuilder<Int16TypeDefinition> derivedInt16Builder(
+            final Int16TypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<Int16TypeDefinition>(baseType, path) {
+            @Override
+            public Int16TypeDefinition build() {
+                return new DerivedInt16Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull DerivedTypeBuilder<Int32TypeDefinition> derivedInt32Builder(
+            final Int32TypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<Int32TypeDefinition>(baseType, path) {
+            @Override
+            public Int32TypeDefinition build() {
+                return new DerivedInt32Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static @NonNull DerivedTypeBuilder<Int64TypeDefinition> derivedInt64Builder(
+            final Int64TypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<Int64TypeDefinition>(baseType, path) {
+            @Override
+            public Int64TypeDefinition build() {
+                return new DerivedInt64Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull DerivedTypeBuilder<LeafrefTypeDefinition> derivedLeafrefBuilder(
+            final LeafrefTypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<LeafrefTypeDefinition>(baseType, path) {
+            @Override
+            public LeafrefTypeDefinition build() {
+                return new DerivedLeafrefType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static @NonNull DerivedTypeBuilder<StringTypeDefinition> derivedStringBuilder(
+            final StringTypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<StringTypeDefinition>(baseType, path) {
+            @Override
+            public StringTypeDefinition build() {
+                return new DerivedStringType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull DerivedTypeBuilder<UnionTypeDefinition> derivedUnionBuilder(
+            final UnionTypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<UnionTypeDefinition>(baseType, path) {
+            @Override
+            public DerivedUnionType build() {
+                return new DerivedUnionType(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static @NonNull DerivedTypeBuilder<Uint8TypeDefinition> derivedUint8Builder(
+            final Uint8TypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<Uint8TypeDefinition>(baseType, path) {
+            @Override
+            public Uint8TypeDefinition build() {
+                return new DerivedUint8Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    private static @NonNull DerivedTypeBuilder<Uint16TypeDefinition> derivedUint16Builder(
+            final Uint16TypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<Uint16TypeDefinition>(baseType, path) {
+            @Override
+            public Uint16TypeDefinition build() {
+                return new DerivedUint16Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull DerivedTypeBuilder<Uint32TypeDefinition> derivedUint32Builder(
+            final Uint32TypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<Uint32TypeDefinition>(baseType, path) {
+            @Override
+            public Uint32TypeDefinition build() {
+                return new DerivedUint32Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull DerivedTypeBuilder<Uint64TypeDefinition> derivedUint64Builder(
+            final Uint64TypeDefinition baseType, final SchemaPath path) {
+        return new DerivedTypeBuilder<Uint64TypeDefinition>(baseType, path) {
+            @Override
+            public Uint64TypeDefinition build() {
+                return new DerivedUint64Type(getBaseType(), getPath(), getDefaultValue(), getDescription(),
+                        getReference(), getStatus(), getUnits(), getUnknownSchemaNodes());
+            }
+        };
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUint16Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUint16Type.java
new file mode 100644 (file)
index 0000000..94ff32e
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+
+final class DerivedUint16Type extends AbstractRangeRestrictedDerivedType<Uint16TypeDefinition, Uint16>
+        implements Uint16TypeDefinition {
+
+    DerivedUint16Type(final Uint16TypeDefinition baseType, final SchemaPath path,
+            final Object defaultValue, final String description, final String reference, final Status status,
+            final String units, final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Uint16TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Uint16TypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUint32Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUint32Type.java
new file mode 100644 (file)
index 0000000..6e695d7
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+
+final class DerivedUint32Type extends AbstractRangeRestrictedDerivedType<Uint32TypeDefinition, Uint32>
+        implements Uint32TypeDefinition {
+
+    DerivedUint32Type(final Uint32TypeDefinition baseType, final SchemaPath path,
+            final Object defaultValue, final String description, final String reference, final Status status,
+            final String units, final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Uint32TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Uint32TypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUint64Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUint64Type.java
new file mode 100644 (file)
index 0000000..83ea8f3
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+
+final class DerivedUint64Type extends AbstractRangeRestrictedDerivedType<Uint64TypeDefinition, Uint64>
+        implements Uint64TypeDefinition {
+    DerivedUint64Type(final Uint64TypeDefinition baseType, final SchemaPath path,
+            final Object defaultValue, final String description, final String reference, final Status status,
+            final String units, final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Uint64TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Uint64TypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUint8Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUint8Type.java
new file mode 100644 (file)
index 0000000..d120b56
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+
+final class DerivedUint8Type extends AbstractRangeRestrictedDerivedType<Uint8TypeDefinition, Uint8>
+        implements Uint8TypeDefinition {
+    DerivedUint8Type(final Uint8TypeDefinition baseType, final SchemaPath path,
+            final Object defaultValue, final String description, final String reference, final Status status,
+            final String units, final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Uint8TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Uint8TypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUnionType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/DerivedUnionType.java
new file mode 100644 (file)
index 0000000..a2c540f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+
+final class DerivedUnionType extends AbstractDerivedType<UnionTypeDefinition> implements UnionTypeDefinition {
+    DerivedUnionType(final UnionTypeDefinition baseType, final SchemaPath path, final Object defaultValue,
+        final String description, final String reference, final Status status, final String units,
+        final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, defaultValue, description, reference, status, units, unknownSchemaNodes);
+    }
+
+    @Override
+    public List<TypeDefinition<?>> getTypes() {
+        return baseType().getTypes();
+    }
+
+    @Override
+    public int hashCode() {
+        return UnionTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return UnionTypeDefinition.equals(this, obj);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/EnumPairBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/EnumPairBuilder.java
new file mode 100644 (file)
index 0000000..5d21de1
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+
+/**
+ * Utility builder for {@link EnumPair} instances.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class EnumPairBuilder implements Builder<EnumPair>, Mutable {
+    private final String name;
+    private final Integer value;
+
+    private ImmutableList<UnknownSchemaNode> unknownSchemaNodes = ImmutableList.of();
+    private Status status = Status.CURRENT;
+    private String description;
+    private String reference;
+
+    private EnumPairBuilder(final String name, final Integer value) {
+        this.name = requireNonNull(name);
+        this.value = requireNonNull(value);
+    }
+
+    public static EnumPairBuilder create(final String name, final Integer value) {
+        return new EnumPairBuilder(name, value);
+    }
+
+    public EnumPairBuilder setDescription(final String description) {
+        this.description = description;
+        return this;
+    }
+
+    public EnumPairBuilder setReference(final String reference) {
+        this.reference = reference;
+        return this;
+    }
+
+    public EnumPairBuilder setStatus(final Status status) {
+        this.status = requireNonNull(status);
+        return this;
+    }
+
+    public EnumPairBuilder setUnknownSchemaNodes(final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        this.unknownSchemaNodes = ImmutableList.copyOf(unknownSchemaNodes);
+        return this;
+    }
+
+    public EnumPairBuilder setUnknownSchemaNodes(final UnknownSchemaNode... unknownSchemaNodes) {
+        this.unknownSchemaNodes = ImmutableList.copyOf(unknownSchemaNodes);
+        return this;
+    }
+
+    @Override
+    public EnumPair build() {
+        return new EnumPairImpl(name, value, description, reference, status, unknownSchemaNodes);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/EnumPairImpl.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/EnumPairImpl.java
new file mode 100644 (file)
index 0000000..fdf61dd
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+
+final class EnumPairImpl implements EnumPair, Immutable {
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownSchemaNodes;
+    private final String description;
+    private final String reference;
+    private final @NonNull Status status;
+    private final @NonNull String name;
+    private final int value;
+
+    EnumPairImpl(final String name, final int value, final String description, final String reference,
+            final Status status, final ImmutableList<UnknownSchemaNode> unknownSchemaNodes) {
+        this.name = requireNonNull(name);
+        this.value = value;
+        this.description = description;
+        this.reference = reference;
+        this.status = requireNonNull(status);
+        this.unknownSchemaNodes = requireNonNull(unknownSchemaNodes);
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownSchemaNodes;
+    }
+
+    @Override
+    public Optional<String> getDescription() {
+        return Optional.ofNullable(description);
+    }
+
+    @Override
+    public Optional<String> getReference() {
+        return Optional.ofNullable(reference);
+    }
+
+    @Override
+    public Status getStatus() {
+        return status;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public int getValue() {
+        return value;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + unknownSchemaNodes.hashCode();
+        result = prime * result + name.hashCode();
+        result = prime * result + Integer.hashCode(value);
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof EnumPair)) {
+            return false;
+        }
+        EnumPair other = (EnumPair) obj;
+        if (!Objects.equals(name, other.getName())) {
+            return false;
+        }
+
+        return value == other.getValue() && Objects.equals(unknownSchemaNodes, other.getUnknownSchemaNodes());
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("name", name).add("value", value).toString();
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/EnumerationTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/EnumerationTypeBuilder.java
new file mode 100644 (file)
index 0000000..bb95d6e
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.HashMap;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+
+public final class EnumerationTypeBuilder extends AbstractRestrictedTypeBuilder<EnumTypeDefinition> {
+    private final Builder<String, EnumPair> builder = ImmutableMap.builder();
+
+    EnumerationTypeBuilder(final SchemaPath path) {
+        super(null, path);
+    }
+
+    EnumerationTypeBuilder(final EnumTypeDefinition baseType, final SchemaPath path) {
+        super(baseType, path);
+    }
+
+    public EnumerationTypeBuilder addEnum(final @NonNull EnumPair item) {
+        // in case we are dealing with a restricted enumeration type, validate if the enum is a subset of its base type
+        final EnumTypeDefinition base = getBaseType();
+        if (base != null) {
+            validateRestrictedEnum(item, base);
+        }
+
+        builder.put(item.getName(), item);
+        touch();
+        return this;
+    }
+
+    private static void validateRestrictedEnum(final @NonNull EnumPair item, final @NonNull EnumTypeDefinition base) {
+        boolean isASubsetOfBaseEnums = false;
+        for (EnumPair baseTypeEnumPair : base.getValues()) {
+            if (item.getName().equals(baseTypeEnumPair.getName())) {
+                if (item.getValue() != baseTypeEnumPair.getValue()) {
+                    throw new InvalidEnumDefinitionException(item, "Value of enum '%s' must be the same as the value"
+                            + " of corresponding enum in the base enumeration type %s.", item.getName(),
+                            base.getQName());
+                }
+                isASubsetOfBaseEnums = true;
+                break;
+            }
+        }
+
+        if (!isASubsetOfBaseEnums) {
+            throw new InvalidEnumDefinitionException(item, "Enum '%s' is not a subset of its base enumeration type %s.",
+                    item.getName(), base.getQName());
+        }
+    }
+
+    @Override
+    public EnumTypeDefinition buildType() {
+        final Map<String, EnumPair> map = builder.build();
+        final Map<Integer, EnumPair> positionMap = new HashMap<>();
+
+        for (EnumPair p : map.values()) {
+            final EnumPair conflict = positionMap.put(p.getValue(), p);
+            if (conflict != null) {
+                throw new InvalidEnumDefinitionException(p, "Enum '%s' conflicts on value with enum ", conflict);
+            }
+        }
+
+        return getBaseType() == null ? new BaseEnumerationType(getPath(), getUnknownSchemaNodes(), map.values())
+                : new RestrictedEnumerationType(getBaseType(), getPath(), getUnknownSchemaNodes(), map.values());
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/IdentityrefTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/IdentityrefTypeBuilder.java
new file mode 100644 (file)
index 0000000..23d63bd
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+
+public final class IdentityrefTypeBuilder extends TypeBuilder<IdentityrefTypeDefinition> {
+    private final Builder<IdentitySchemaNode> builder = ImmutableSet.builder();
+
+    IdentityrefTypeBuilder(final SchemaPath path) {
+        super(null, path);
+    }
+
+    public IdentityrefTypeBuilder addIdentity(final @NonNull IdentitySchemaNode identity) {
+        builder.add(identity);
+        return this;
+    }
+
+    @Override
+    public IdentityrefTypeDefinition build() {
+        final Set<IdentitySchemaNode> identities = builder.build();
+        checkState(!identities.isEmpty(), "No identities specified in %s, at least one is required", getPath());
+        return new BaseIdentityrefType(getPath(), getUnknownSchemaNodes(), identities);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InstanceIdentifierTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InstanceIdentifierTypeBuilder.java
new file mode 100644 (file)
index 0000000..5c99a06
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+
+public final class InstanceIdentifierTypeBuilder
+        extends RequireInstanceRestrictedTypeBuilder<InstanceIdentifierTypeDefinition> {
+
+    InstanceIdentifierTypeBuilder(final InstanceIdentifierTypeDefinition baseType,
+            final SchemaPath path) {
+        super(requireNonNull(baseType), path);
+    }
+
+    @Override
+    InstanceIdentifierTypeDefinition buildType() {
+        final InstanceIdentifierTypeDefinition base = getBaseType();
+        if (getRequireInstance() == base.requireInstance()) {
+            return base;
+        }
+
+        return new RestrictedInstanceIdentifierType(base, getPath(), getUnknownSchemaNodes(), getRequireInstance());
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InvalidBitDefinitionException.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InvalidBitDefinitionException.java
new file mode 100644 (file)
index 0000000..81ce1b2
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+
+@Beta
+public class InvalidBitDefinitionException extends IllegalArgumentException {
+    private static final long serialVersionUID = 1L;
+
+    @SuppressFBWarnings("SE_BAD_FIELD")
+    private final Bit offendingBit;
+
+    protected InvalidBitDefinitionException(final Bit offendingBit, final String message) {
+        super(message);
+        this.offendingBit = requireNonNull(offendingBit);
+    }
+
+    public InvalidBitDefinitionException(final Bit offendingBit, final String format,
+            final Object... args) {
+        this(offendingBit, String.format(format, args));
+    }
+
+    public Bit getOffendingBit() {
+        return offendingBit;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InvalidEnumDefinitionException.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InvalidEnumDefinitionException.java
new file mode 100644 (file)
index 0000000..59d059d
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+
+@Beta
+public class InvalidEnumDefinitionException extends IllegalArgumentException {
+    private static final long serialVersionUID = 1L;
+
+    @SuppressFBWarnings("SE_BAD_FIELD")
+    private final EnumPair offendingEnum;
+
+    protected InvalidEnumDefinitionException(final EnumPair offendingEnum, final String message) {
+        super(message);
+        this.offendingEnum = requireNonNull(offendingEnum);
+    }
+
+    public InvalidEnumDefinitionException(final EnumPair offendingEnum, final String format,
+            final Object... args) {
+        this(offendingEnum, String.format(format, args));
+    }
+
+    public EnumPair getOffendingEnum() {
+        return offendingEnum;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InvalidLengthConstraintException.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InvalidLengthConstraintException.java
new file mode 100644 (file)
index 0000000..533504a
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+
+@Beta
+public final class InvalidLengthConstraintException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public InvalidLengthConstraintException(final @NonNull String format, final Object... args) {
+        super(String.format(format, args));
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InvalidRangeConstraintException.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/InvalidRangeConstraintException.java
new file mode 100644 (file)
index 0000000..3da55e7
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.RangeSet;
+
+@Beta
+public class InvalidRangeConstraintException extends IllegalArgumentException {
+    private static final long serialVersionUID = 1L;
+
+    private final RangeSet<?> offendingRangeConstraint;
+
+    protected InvalidRangeConstraintException(final RangeSet<?> offendingConstraint, final String message) {
+        super(message);
+        this.offendingRangeConstraint = requireNonNull(offendingConstraint);
+    }
+
+    public InvalidRangeConstraintException(final RangeSet<?> offendingConstraint, final String format,
+            final Object... args) {
+        this(offendingConstraint, String.format(format, args));
+    }
+
+    public RangeSet<?> getOffendingRanges() {
+        return offendingRangeConstraint;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/JavaLengthConstraints.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/JavaLengthConstraints.java
new file mode 100644 (file)
index 0000000..1f9ea4b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeSet;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+
+final class JavaLengthConstraints {
+    private JavaLengthConstraints() {
+        throw new UnsupportedOperationException();
+    }
+
+    private static final RangeSet<Integer> INTEGER_ALLOWED_RANGES =
+            ImmutableRangeSet.of(Range.closed(0, Integer.MAX_VALUE));
+
+    static final LengthConstraint INTEGER_SIZE_CONSTRAINTS = new LengthConstraint() {
+        @Override
+        public Optional<String> getReference() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<String> getErrorMessage() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<String> getErrorAppTag() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<String> getDescription() {
+            return Optional.empty();
+        }
+
+        @Override
+        public RangeSet<Integer> getAllowedRanges() {
+            return INTEGER_ALLOWED_RANGES;
+        }
+    };
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/LeafrefTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/LeafrefTypeBuilder.java
new file mode 100644 (file)
index 0000000..77a3c3a
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+
+public final class LeafrefTypeBuilder extends RequireInstanceRestrictedTypeBuilder<LeafrefTypeDefinition> {
+    private PathExpression pathStatement;
+
+    LeafrefTypeBuilder(final SchemaPath path) {
+        super(null, path);
+    }
+
+    public LeafrefTypeBuilder setPathStatement(final @NonNull PathExpression pathStatement) {
+        checkState(this.pathStatement == null, "Path statement already set to %s", this.pathStatement);
+        this.pathStatement = requireNonNull(pathStatement);
+        return this;
+    }
+
+    @Override
+    LeafrefTypeDefinition buildType() {
+        return new BaseLeafrefType(getPath(), pathStatement, getRequireInstance(), getUnknownSchemaNodes());
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/LengthRestrictedTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/LengthRestrictedTypeBuilder.java
new file mode 100644 (file)
index 0000000..e0b989f
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Verify;
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.ImmutableRangeSet.Builder;
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeSet;
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.LengthRestrictedTypeDefinition;
+
+public abstract class LengthRestrictedTypeBuilder<T extends LengthRestrictedTypeDefinition<T>>
+        extends AbstractRestrictedTypeBuilder<T> {
+    private LengthConstraint lengthConstraint;
+
+    LengthRestrictedTypeBuilder(final T baseType, final SchemaPath path) {
+        super(requireNonNull(baseType), path);
+    }
+
+    /**
+     * Set a new length constraint.
+     *
+     * @param constraint Constraint metadata
+     * @param ranges Allowed ranges
+     * @throws IllegalStateException if the constraint has already been set
+     * @throws InvalidLengthConstraintException if one of the proposed ranges does not overlap with supertype
+     * @throws NullPointerException if any of the arguments is null
+     */
+    public final void setLengthConstraint(final @NonNull ConstraintMetaDefinition constraint,
+            final @NonNull List<ValueRange> ranges) throws InvalidLengthConstraintException {
+        Preconditions.checkState(lengthConstraint == null, "Length constraint already defined as %s", lengthConstraint);
+        final LengthConstraint baseLengths = findLenghts();
+        if (ranges.isEmpty()) {
+            lengthConstraint = baseLengths;
+            return;
+        }
+
+        // Run through alternatives and resolve them against the base type
+        requireNonNull(constraint);
+        final Builder<Integer> builder = ImmutableRangeSet.builder();
+        final Range<Integer> span = baseLengths.getAllowedRanges().span();
+
+        for (ValueRange c : ranges) {
+            builder.add(Range.closed(resolveLength(c.lowerBound(), span), resolveLength(c.upperBound(), span)));
+        }
+
+
+        // Now verify if new ranges are strict subset of base ranges
+        final RangeSet<Integer> allowed = builder.build();
+        final RangeSet<Integer> baseRanges = baseLengths.getAllowedRanges();
+        for (Range<Integer> range : allowed.asRanges()) {
+            if (!baseRanges.encloses(range)) {
+                throw new InvalidLengthConstraintException("Range %s is not a subset of parent constraint %s", range,
+                    baseRanges);
+            }
+        }
+
+        lengthConstraint = new ResolvedLengthConstraint(constraint, allowed);
+        touch();
+    }
+
+    abstract T buildType(LengthConstraint constraint);
+
+    @Override
+    final T buildType() {
+        return buildType(lengthConstraint != null ? lengthConstraint : findLenghts());
+    }
+
+    abstract LengthConstraint typeLengthConstraints();
+
+    private static Integer resolveLength(final Number unresolved, final Range<Integer> span) {
+        if (unresolved instanceof Integer) {
+            return (Integer) unresolved;
+        }
+        if (unresolved instanceof UnresolvedNumber) {
+            return ((UnresolvedNumber)unresolved).resolveLength(span);
+        }
+
+        return Verify.verifyNotNull(NumberUtil.converterTo(Integer.class)).apply(unresolved);
+    }
+
+    private LengthConstraint findLenghts() {
+        Optional<LengthConstraint> ret = Optional.empty();
+        T wlk = getBaseType();
+        while (wlk != null && ret.isEmpty()) {
+            ret = wlk.getLengthConstraint();
+            wlk = wlk.getBaseType();
+        }
+
+        return ret.orElse(typeLengthConstraints());
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/NumberUtil.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/NumberUtil.java
new file mode 100644 (file)
index 0000000..eda61cf
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.ImmutableMap;
+import java.math.BigDecimal;
+import java.util.Comparator;
+import java.util.function.Function;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.common.Uint8;
+
+final class NumberUtil {
+    private static final Comparator<Number> NUMBER_COMPARATOR = (o1, o2) -> {
+        checkArgument(o1.getClass().equals(o2.getClass()), "Incompatible Number classes %s and %s",
+            o1.getClass(), o2.getClass());
+
+        if (o1 instanceof Byte) {
+            return ((Byte)o1).compareTo((Byte) o2);
+        } else if (o1 instanceof Short) {
+            return ((Short)o1).compareTo((Short) o2);
+        } else if (o1 instanceof Integer) {
+            return ((Integer)o1).compareTo((Integer) o2);
+        } else if (o1 instanceof Long) {
+            return ((Long)o1).compareTo((Long) o2);
+        } else if (o1 instanceof Uint8) {
+            return ((Uint8)o1).compareTo((Uint8) o2);
+        } else if (o1 instanceof Uint16) {
+            return ((Uint16)o1).compareTo((Uint16) o2);
+        } else if (o1 instanceof Uint32) {
+            return ((Uint32)o1).compareTo((Uint32) o2);
+        } else if (o1 instanceof Uint64) {
+            return ((Uint64)o1).compareTo((Uint64) o2);
+        } else if (o1 instanceof BigDecimal) {
+            return ((BigDecimal)o1).compareTo((BigDecimal) o2);
+        } else {
+            throw new IllegalArgumentException("Unsupported Number class " + o1.getClass());
+        }
+    };
+
+    private static final ImmutableMap<Class<? extends Number>, Function<Number, Number>> CONVERTERS;
+
+    static {
+        final ImmutableMap.Builder<Class<? extends Number>, Function<Number, Number>> b = ImmutableMap.builder();
+        b.put(Byte.class, input -> {
+            if (input instanceof Byte) {
+                return input;
+            }
+
+            return Byte.valueOf(input.toString());
+        });
+        b.put(Short.class, input -> {
+            if (input instanceof Short) {
+                return input;
+            }
+            if (input instanceof Byte) {
+                return input.shortValue();
+            }
+
+            return Short.valueOf(input.toString());
+        });
+        b.put(Integer.class, input -> {
+            if (input instanceof Integer) {
+                return input;
+            }
+            if (input instanceof Byte || input instanceof Short) {
+                return input.intValue();
+            }
+
+            return Integer.valueOf(input.toString());
+        });
+        b.put(Long.class, input ->  {
+            if (input instanceof Long) {
+                return input;
+            }
+            if (input instanceof Byte || input instanceof Short || input instanceof Integer) {
+                return input.longValue();
+            }
+
+            return Long.valueOf(input.toString());
+        });
+        b.put(BigDecimal.class, input -> {
+            if (input instanceof BigDecimal) {
+                return input;
+            }
+            if (input instanceof Byte || input instanceof Short || input instanceof Integer || input instanceof Long) {
+                return BigDecimal.valueOf(input.longValue());
+            }
+
+            return new BigDecimal(input.toString());
+        });
+        b.put(Uint8.class, input -> {
+            if (input instanceof Uint8) {
+                return input;
+            }
+            // FIXME: revise this
+            if (input instanceof Byte || input instanceof Short || input instanceof Integer || input instanceof Long
+                    || input instanceof Uint16 || input instanceof Uint32 || input instanceof Uint64) {
+                return Uint8.valueOf(input.longValue());
+            }
+
+            return Uint8.valueOf(input.toString());
+        });
+        b.put(Uint16.class, input -> {
+            if (input instanceof Uint16) {
+                return input;
+            }
+            // FIXME: revise this
+            if (input instanceof Byte || input instanceof Short || input instanceof Integer || input instanceof Long
+                    || input instanceof Uint8 || input instanceof Uint32 || input instanceof Uint64) {
+                return Uint16.valueOf(input.longValue());
+            }
+
+            return Uint16.valueOf(input.toString());
+        });
+        b.put(Uint32.class, input -> {
+            if (input instanceof Uint32) {
+                return input;
+            }
+            // FIXME: revise this
+            if (input instanceof Byte || input instanceof Short || input instanceof Integer || input instanceof Long
+                    || input instanceof Uint8 || input instanceof Uint16 || input instanceof Uint64) {
+                return Uint32.valueOf(input.longValue());
+            }
+
+            return Uint32.valueOf(input.toString());
+        });
+        b.put(Uint64.class, input -> {
+            if (input instanceof Uint64) {
+                return input;
+            }
+            // FIXME: revise this
+            if (input instanceof Byte || input instanceof Short || input instanceof Integer || input instanceof Long
+                    || input instanceof Uint8 || input instanceof Uint16 || input instanceof Uint32) {
+                return Uint64.valueOf(input.longValue());
+            }
+
+            return Uint64.valueOf(input.toString());
+        });
+        CONVERTERS = b.build();
+    }
+
+    private NumberUtil() {
+        throw new UnsupportedOperationException();
+    }
+
+    @SuppressWarnings("unchecked")
+    static <T extends Number> Function<Number, T> converterTo(final Class<T> clazz) {
+        return (Function<Number, T>) CONVERTERS.get(clazz);
+    }
+
+    static boolean isRangeCovered(final Number min, final Number max, final Number superMin, final Number superMax) {
+        return NumberUtil.NUMBER_COMPARATOR.compare(min, superMin) >= 0
+            && NumberUtil.NUMBER_COMPARATOR.compare(max, superMax) <= 0;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RangeRestrictedTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RangeRestrictedTypeBuilder.java
new file mode 100644 (file)
index 0000000..b60e5c7
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Verify;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.ImmutableRangeSet.Builder;
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeSet;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
+
+public abstract class RangeRestrictedTypeBuilder<T extends RangeRestrictedTypeDefinition<T, N>,
+        N extends Number & Comparable<N>> extends AbstractRestrictedTypeBuilder<T> {
+    private ConstraintMetaDefinition constraint;
+    private ImmutableList<ValueRange> ranges;
+
+    RangeRestrictedTypeBuilder(final T baseType, final SchemaPath path) {
+        super(baseType, path);
+    }
+
+    @SuppressWarnings("checkstyle:hiddenField")
+    public final void setRangeConstraint(final @NonNull ConstraintMetaDefinition constraint,
+            final @NonNull List<ValueRange> ranges) {
+        checkState(this.ranges == null, "Range constraint already defined as %s %s", this.ranges, this.constraint);
+
+        this.constraint = requireNonNull(constraint);
+        this.ranges = ImmutableList.copyOf(ranges);
+        touch();
+    }
+
+    final RangeConstraint<N> calculateRangeConstraint(final RangeConstraint<N> baseRangeConstraint) {
+        if (ranges == null) {
+            return baseRangeConstraint;
+        }
+
+        // Run through alternatives and resolve them against the base type
+        final RangeSet<N> baseRangeSet = baseRangeConstraint.getAllowedRanges();
+        Verify.verify(!baseRangeSet.isEmpty(), "Base type %s does not define constraints", getBaseType());
+
+        final Range<N> baseRange = baseRangeSet.span();
+        final List<ValueRange> resolvedRanges = ensureResolvedRanges(ranges, baseRange);
+
+        // Next up, ensure the of boundaries match base constraints
+        final RangeSet<N> typedRanges = ensureTypedRanges(resolvedRanges, baseRange.lowerEndpoint().getClass());
+
+        // Now verify if new ranges are strict subset of base ranges
+        if (!baseRangeSet.enclosesAll(typedRanges)) {
+            throw new InvalidRangeConstraintException(typedRanges,
+                "Range constraint %s is not a subset of parent constraint %s", typedRanges, baseRangeSet);
+        }
+
+        return new ResolvedRangeConstraint<>(constraint, typedRanges);
+    }
+
+    private static <C extends Number & Comparable<C>> List<ValueRange> ensureResolvedRanges(
+            final List<ValueRange> unresolved, final Range<C> baseRange) {
+        // First check if we need to resolve anything at all
+        for (ValueRange c : unresolved) {
+            if (c.lowerBound() instanceof UnresolvedNumber || c.upperBound() instanceof UnresolvedNumber) {
+                return resolveRanges(unresolved, baseRange);
+            }
+        }
+
+        // No need, just return the same list
+        return unresolved;
+    }
+
+    private static <T extends Number & Comparable<T>> List<ValueRange> resolveRanges(final List<ValueRange> unresolved,
+            final Range<T> baseRange) {
+        final List<ValueRange> ret = new ArrayList<>(unresolved.size());
+        for (ValueRange range : unresolved) {
+            final Number min = range.lowerBound();
+            final Number max = range.upperBound();
+
+            if (max instanceof UnresolvedNumber || min instanceof UnresolvedNumber) {
+                final @NonNull Number rMin = min instanceof UnresolvedNumber
+                        ?  ((UnresolvedNumber)min).resolveRange(baseRange) : min;
+                final @NonNull Number rMax = max instanceof UnresolvedNumber
+                        ?  ((UnresolvedNumber)max).resolveRange(baseRange) : max;
+                ret.add(ValueRange.of(rMin, rMax));
+            } else {
+                ret.add(range);
+            }
+        }
+
+        return ret;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T extends Number & Comparable<T>> RangeSet<T> ensureTypedRanges(final List<ValueRange> ranges,
+            final Class<? extends Number> clazz) {
+        final Builder<T> builder = ImmutableRangeSet.builder();
+        for (ValueRange range : ranges) {
+            if (!clazz.isInstance(range.lowerBound()) || !clazz.isInstance(range.upperBound())) {
+                return typedRanges(ranges, clazz);
+            }
+
+            builder.add(Range.closed((T) range.lowerBound(), (T)range.upperBound()));
+        }
+
+        return builder.build();
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T extends Number & Comparable<T>> RangeSet<T> typedRanges(final List<ValueRange> ranges,
+            final Class<? extends Number> clazz) {
+        final Function<Number, ? extends Number> function = NumberUtil.converterTo(clazz);
+        Preconditions.checkArgument(function != null, "Unsupported range class %s", clazz);
+
+        final Builder<T> builder = ImmutableRangeSet.builder();
+
+        for (ValueRange range : ranges) {
+            if (!clazz.isInstance(range.lowerBound()) || !clazz.isInstance(range.upperBound())) {
+                final Number min;
+                final Number max;
+
+                try {
+                    min = function.apply(range.lowerBound());
+                    max = function.apply(range.upperBound());
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException(String.format("Constraint %s does not fit into range of %s",
+                        range, clazz.getSimpleName()), e);
+                }
+
+                builder.add(Range.closed((T)min, (T)max));
+            } else {
+                builder.add(Range.closed((T) range.lowerBound(), (T)range.upperBound()));
+            }
+        }
+
+        return builder.build();
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RangeRestrictedTypeBuilderWithBase.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RangeRestrictedTypeBuilderWithBase.java
new file mode 100644 (file)
index 0000000..5f8e1fd
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
+
+abstract class RangeRestrictedTypeBuilderWithBase<T extends RangeRestrictedTypeDefinition<T, N>,
+        N extends Number & Comparable<N>> extends RangeRestrictedTypeBuilder<T, N> {
+
+    RangeRestrictedTypeBuilderWithBase(final T baseType, final SchemaPath path) {
+        super(requireNonNull(baseType), path);
+    }
+
+    @Override
+    final T buildType() {
+        return buildType(calculateRangeConstraint(getBaseType().getRangeConstraint().get()));
+    }
+
+    abstract T buildType(RangeConstraint<N> rangeConstraints);
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RequireInstanceRestrictedTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RequireInstanceRestrictedTypeBuilder.java
new file mode 100644 (file)
index 0000000..c65e4ac
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 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.util.type;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.RequireInstanceRestrictedTypeDefinition;
+
+@Beta
+public abstract class RequireInstanceRestrictedTypeBuilder<T extends RequireInstanceRestrictedTypeDefinition<T>>
+        extends AbstractRestrictedTypeBuilder<T> {
+    private boolean requireInstance;
+
+    RequireInstanceRestrictedTypeBuilder(final T baseType, final SchemaPath path) {
+        super(baseType, path);
+        requireInstance = baseType == null || baseType.requireInstance();
+    }
+
+    public final void setRequireInstance(final boolean requireInstance) {
+        this.requireInstance = requireInstance;
+        touch();
+    }
+
+    final boolean getRequireInstance() {
+        return requireInstance;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/ResolvedLengthConstraint.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/ResolvedLengthConstraint.java
new file mode 100644 (file)
index 0000000..3abae2e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.RangeSet;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+
+final class ResolvedLengthConstraint implements LengthConstraint {
+    private final ConstraintMetaDefinition meta;
+    private final ImmutableRangeSet<Integer> ranges;
+
+    ResolvedLengthConstraint(final ConstraintMetaDefinition meta, final RangeSet<Integer> ranges) {
+        this.meta = requireNonNull(meta);
+        this.ranges = ImmutableRangeSet.copyOf(ranges);
+    }
+
+    @Override
+    public Optional<String> getDescription() {
+        return meta.getDescription();
+    }
+
+    @Override
+    public Optional<String> getErrorAppTag() {
+        return meta.getErrorAppTag();
+    }
+
+    @Override
+    public Optional<String> getErrorMessage() {
+        return meta.getErrorMessage();
+    }
+
+    @Override
+    public Optional<String> getReference() {
+        return meta.getReference();
+    }
+
+    @Override
+    public RangeSet<Integer> getAllowedRanges() {
+        return ranges;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/ResolvedRangeConstraint.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/ResolvedRangeConstraint.java
new file mode 100644 (file)
index 0000000..178ef21
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.RangeSet;
+import java.util.Optional;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+
+final class ResolvedRangeConstraint<T extends Number & Comparable<T>> implements RangeConstraint<T>, Immutable {
+    private final ConstraintMetaDefinition meta;
+    private final ImmutableRangeSet<T> ranges;
+
+    ResolvedRangeConstraint(final ConstraintMetaDefinition meta, final RangeSet<T> ranges) {
+        this.meta = requireNonNull(meta);
+        this.ranges = ImmutableRangeSet.copyOf(ranges);
+    }
+
+    @Override
+    public Optional<String> getDescription() {
+        return meta.getDescription();
+    }
+
+    @Override
+    public Optional<String> getErrorAppTag() {
+        return meta.getErrorAppTag();
+    }
+
+    @Override
+    public Optional<String> getErrorMessage() {
+        return meta.getErrorMessage();
+    }
+
+    @Override
+    public Optional<String> getReference() {
+        return meta.getReference();
+    }
+
+    @Override
+    public RangeSet<T> getAllowedRanges() {
+        return ranges;
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedBinaryType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedBinaryType.java
new file mode 100644 (file)
index 0000000..ee94181
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+
+final class RestrictedBinaryType extends AbstractLengthRestrictedType<BinaryTypeDefinition>
+        implements BinaryTypeDefinition {
+    RestrictedBinaryType(final BinaryTypeDefinition baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes,
+            final @Nullable LengthConstraint lengthConstraint) {
+        super(baseType, path, unknownSchemaNodes, lengthConstraint);
+    }
+
+    @Override
+    public int hashCode() {
+        return BinaryTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return BinaryTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return BinaryTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedBitsType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedBitsType.java
new file mode 100644 (file)
index 0000000..7353d2c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+
+final class RestrictedBitsType extends AbstractRestrictedType<BitsTypeDefinition> implements BitsTypeDefinition {
+    private final @NonNull ImmutableList<Bit> bits;
+
+    RestrictedBitsType(final BitsTypeDefinition baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes, final Collection<Bit> bits) {
+        super(baseType, path, unknownSchemaNodes);
+        this.bits = ImmutableList.copyOf(bits);
+    }
+
+    @Override
+    public List<Bit> getBits() {
+        return bits;
+    }
+
+    @Override
+    public int hashCode() {
+        return BitsTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return BitsTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return BitsTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedBooleanType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedBooleanType.java
new file mode 100644 (file)
index 0000000..91e2a97
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+
+final class RestrictedBooleanType extends AbstractRestrictedType<BooleanTypeDefinition>
+        implements BooleanTypeDefinition {
+    RestrictedBooleanType(final BooleanTypeDefinition baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, unknownSchemaNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return BooleanTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return BooleanTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return BooleanTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedDecimalType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedDecimalType.java
new file mode 100644 (file)
index 0000000..9066aad
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+
+final class RestrictedDecimalType extends AbstractRangeRestrictedType<DecimalTypeDefinition, BigDecimal>
+        implements DecimalTypeDefinition {
+    RestrictedDecimalType(final DecimalTypeDefinition baseType, final SchemaPath path,
+        final Collection<UnknownSchemaNode> unknownSchemaNodes,
+        final @Nullable RangeConstraint<BigDecimal> rangeConstraint) {
+        super(baseType, path, unknownSchemaNodes, rangeConstraint);
+    }
+
+    @Override
+    public int getFractionDigits() {
+        return getBaseType().getFractionDigits();
+    }
+
+    @Override
+    public int hashCode() {
+        return DecimalTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return DecimalTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return DecimalTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedEmptyType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedEmptyType.java
new file mode 100644 (file)
index 0000000..fc36f91
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+
+final class RestrictedEmptyType extends AbstractRestrictedType<EmptyTypeDefinition> implements EmptyTypeDefinition {
+    RestrictedEmptyType(final EmptyTypeDefinition baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, unknownSchemaNodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return EmptyTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return EmptyTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return EmptyTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedEnumerationType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedEnumerationType.java
new file mode 100644 (file)
index 0000000..5fbec43
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+
+final class RestrictedEnumerationType extends AbstractRestrictedType<EnumTypeDefinition> implements EnumTypeDefinition {
+    private final @NonNull ImmutableList<EnumPair> values;
+
+    RestrictedEnumerationType(final EnumTypeDefinition baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes, final Collection<EnumPair> values) {
+        super(baseType, path, unknownSchemaNodes);
+        this.values = ImmutableList.copyOf(values);
+    }
+
+    @Override
+    public List<EnumPair> getValues() {
+        return values;
+    }
+
+    @Override
+    public int hashCode() {
+        return EnumTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return EnumTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return EnumTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedIdentityrefType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedIdentityrefType.java
new file mode 100644 (file)
index 0000000..fae4d20
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+
+final class RestrictedIdentityrefType extends AbstractRestrictedType<IdentityrefTypeDefinition>
+        implements IdentityrefTypeDefinition {
+    RestrictedIdentityrefType(final IdentityrefTypeDefinition baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, unknownSchemaNodes);
+    }
+
+    @Override
+    public Set<IdentitySchemaNode> getIdentities() {
+        return getBaseType().getIdentities();
+    }
+
+    @Override
+    public int hashCode() {
+        return IdentityrefTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return IdentityrefTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return IdentityrefTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInstanceIdentifierType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInstanceIdentifierType.java
new file mode 100644 (file)
index 0000000..ed84cd9
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+
+final class RestrictedInstanceIdentifierType extends AbstractRestrictedType<InstanceIdentifierTypeDefinition>
+        implements InstanceIdentifierTypeDefinition {
+    private final boolean requireInstance;
+
+    RestrictedInstanceIdentifierType(final InstanceIdentifierTypeDefinition baseType, final SchemaPath path,
+        final Collection<UnknownSchemaNode> unknownSchemaNodes, final boolean requireInstance) {
+        super(baseType, path, unknownSchemaNodes);
+        this.requireInstance = requireInstance;
+    }
+
+    @Override
+    public boolean requireInstance() {
+        return requireInstance;
+    }
+
+    @Override
+    public int hashCode() {
+        return InstanceIdentifierTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return InstanceIdentifierTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return InstanceIdentifierTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInt16Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInt16Type.java
new file mode 100644 (file)
index 0000000..68ed9af
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+
+final class RestrictedInt16Type extends AbstractRangeRestrictedType<Int16TypeDefinition, Short>
+        implements Int16TypeDefinition {
+    RestrictedInt16Type(final Int16TypeDefinition baseType, final SchemaPath path,
+        final Collection<UnknownSchemaNode> unknownSchemaNodes,
+        final @Nullable RangeConstraint<Short> rangeConstraint) {
+        super(baseType, path, unknownSchemaNodes, rangeConstraint);
+    }
+
+    @Override
+    public int hashCode() {
+        return Int16TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Int16TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Int16TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInt32Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInt32Type.java
new file mode 100644 (file)
index 0000000..c27e2cd
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+
+final class RestrictedInt32Type extends AbstractRangeRestrictedType<Int32TypeDefinition, Integer>
+        implements Int32TypeDefinition {
+    RestrictedInt32Type(final Int32TypeDefinition baseType, final SchemaPath path,
+        final Collection<UnknownSchemaNode> unknownSchemaNodes,
+        final @Nullable RangeConstraint<Integer> rangeConstraint) {
+        super(baseType, path, unknownSchemaNodes, rangeConstraint);
+    }
+
+    @Override
+    public int hashCode() {
+        return Int32TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Int32TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Int32TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInt64Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInt64Type.java
new file mode 100644 (file)
index 0000000..128fd43
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+
+final class RestrictedInt64Type extends AbstractRangeRestrictedType<Int64TypeDefinition, Long>
+        implements Int64TypeDefinition {
+    RestrictedInt64Type(final Int64TypeDefinition baseType, final SchemaPath path,
+        final Collection<UnknownSchemaNode> unknownSchemaNodes, final @Nullable RangeConstraint<Long> rangeConstraint) {
+        super(baseType, path, unknownSchemaNodes, rangeConstraint);
+    }
+
+    @Override
+    public int hashCode() {
+        return Int64TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Int64TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Int64TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInt8Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedInt8Type.java
new file mode 100644 (file)
index 0000000..dbc749e
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+
+final class RestrictedInt8Type extends AbstractRangeRestrictedType<Int8TypeDefinition, Byte>
+        implements Int8TypeDefinition {
+    RestrictedInt8Type(final Int8TypeDefinition baseType, final SchemaPath path,
+        final Collection<UnknownSchemaNode> unknownSchemaNodes, final @Nullable RangeConstraint<Byte> rangeConstraint) {
+        super(baseType, path, unknownSchemaNodes, rangeConstraint);
+    }
+
+    @Override
+    public int hashCode() {
+        return Int8TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Int8TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Int8TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedLeafrefType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedLeafrefType.java
new file mode 100644 (file)
index 0000000..a09143e
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+
+final class RestrictedLeafrefType extends AbstractRestrictedType<LeafrefTypeDefinition>
+        implements LeafrefTypeDefinition {
+
+    private final boolean requireInstance;
+
+    RestrictedLeafrefType(final LeafrefTypeDefinition baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes, final boolean requireInstance) {
+        super(baseType, path, unknownSchemaNodes);
+
+        this.requireInstance = requireInstance;
+    }
+
+    @Override
+    public PathExpression getPathStatement() {
+        return getBaseType().getPathStatement();
+    }
+
+    @Override
+    public boolean requireInstance() {
+        return requireInstance;
+    }
+
+    @Override
+    public int hashCode() {
+        return LeafrefTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return LeafrefTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return LeafrefTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedStringType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedStringType.java
new file mode 100644 (file)
index 0000000..37b9757
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+
+final class RestrictedStringType extends AbstractLengthRestrictedType<StringTypeDefinition>
+        implements StringTypeDefinition {
+    private final @NonNull ImmutableList<PatternConstraint> patternConstraints;
+
+    RestrictedStringType(final StringTypeDefinition baseType, final SchemaPath path,
+        final Collection<UnknownSchemaNode> unknownSchemaNodes,
+        final @Nullable LengthConstraint lengthConstraints,
+        final List<PatternConstraint> patternConstraints) {
+        super(baseType, path, unknownSchemaNodes, lengthConstraints);
+        this.patternConstraints = ImmutableList.copyOf(patternConstraints);
+    }
+
+    @Override
+    public List<PatternConstraint> getPatternConstraints() {
+        return patternConstraints;
+    }
+
+    @Override
+    public int hashCode() {
+        return StringTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return StringTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return StringTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedTypes.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedTypes.java
new file mode 100644 (file)
index 0000000..b2e40d8
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.annotations.Beta;
+import java.math.BigDecimal;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+
+/**
+ * Restricted types are a refinement of the restrictions applied to a particular type. YANG defines restrictions only
+ * on a subset of the base types, but conceptually any such definition can hold unknown nodes.
+ *
+ * <p>
+ * 1) Restrictable
+ *    binary (length)
+ *    int{8,16,32,64} (range)
+ *    string (length, patterns)
+ *    uint{8,16,32,64} (range)
+ *    decimal64 (range)
+ *    instance-identifier (require-instance)
+ *
+ * <p>
+ * 2) Non-restrictable
+ *    boolean
+ *    bits
+ *    empty (ignores default on derivation)
+ *    enumeration
+ *    identityref
+ *    leafref
+ *    union
+ *
+ * <p>
+ * This class holds methods which allow creation of restricted types using {@link TypeBuilder} and its subclasses. Each
+ * restricted type is logically anchored at a {@link SchemaPath}, but can be substituted by its base type if it does
+ * not contribute any additional restrictions. TypeBuilder instances take this into account, and result in the base type
+ * being returned from the builder when the base type and restricted type are semantically equal.
+ *
+ * <p>
+ * Restricted types inherit the default value, description, reference, status and units from the base type, if that type
+ * defines them.
+ */
+@Beta
+public final class RestrictedTypes {
+    private RestrictedTypes() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static @NonNull LengthRestrictedTypeBuilder<BinaryTypeDefinition> newBinaryBuilder(
+            final @NonNull BinaryTypeDefinition baseType, final @NonNull SchemaPath path) {
+        return new LengthRestrictedTypeBuilder<>(baseType, path) {
+            @Override
+            BinaryTypeDefinition buildType(final @Nullable LengthConstraint constraint) {
+                return new RestrictedBinaryType(getBaseType(), getPath(), getUnknownSchemaNodes(), constraint);
+            }
+
+            @Override
+            LengthConstraint typeLengthConstraints() {
+                /**
+                 * Length constraint imposed on YANG binary type by our implementation. byte[].length is an integer,
+                 * capping our ability to support arbitrary binary data.
+                 */
+                return JavaLengthConstraints.INTEGER_SIZE_CONSTRAINTS;
+            }
+        };
+    }
+
+    public static @NonNull BitsTypeBuilder newBitsBuilder(final BitsTypeDefinition baseType,
+            final SchemaPath path) {
+        return new BitsTypeBuilder(baseType, path);
+    }
+
+    public static @NonNull TypeBuilder<BooleanTypeDefinition> newBooleanBuilder(
+            final @NonNull BooleanTypeDefinition baseType, final @NonNull SchemaPath path) {
+        return new AbstractRestrictedTypeBuilder<>(baseType, path) {
+            @Override
+            BooleanTypeDefinition buildType() {
+                return new RestrictedBooleanType(getBaseType(), getPath(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull RangeRestrictedTypeBuilder<DecimalTypeDefinition, BigDecimal> newDecima64Builder(
+            final DecimalTypeDefinition baseType, final SchemaPath path) {
+        return new RangeRestrictedTypeBuilderWithBase<>(baseType, path) {
+            @Override
+            DecimalTypeDefinition buildType(final RangeConstraint<BigDecimal> rangeConstraint) {
+                return new RestrictedDecimalType(getBaseType(), getPath(), getUnknownSchemaNodes(), rangeConstraint);
+            }
+        };
+    }
+
+    public static @NonNull TypeBuilder<EmptyTypeDefinition> newEmptyBuilder(final EmptyTypeDefinition baseType,
+            final SchemaPath path) {
+        return new AbstractRestrictedTypeBuilder<>(baseType, path) {
+            @Override
+            EmptyTypeDefinition buildType() {
+                return new RestrictedEmptyType(getBaseType(), getPath(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull EnumerationTypeBuilder newEnumerationBuilder(final EnumTypeDefinition baseType,
+            final SchemaPath path) {
+        return new EnumerationTypeBuilder(baseType, path);
+    }
+
+    public static @NonNull TypeBuilder<IdentityrefTypeDefinition> newIdentityrefBuilder(
+            final IdentityrefTypeDefinition baseType, final SchemaPath path) {
+        return new AbstractRestrictedTypeBuilder<>(baseType, path) {
+            @Override
+            IdentityrefTypeDefinition buildType() {
+                return new RestrictedIdentityrefType(getBaseType(), getPath(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull InstanceIdentifierTypeBuilder newInstanceIdentifierBuilder(
+            final InstanceIdentifierTypeDefinition baseType, final SchemaPath path) {
+        return new InstanceIdentifierTypeBuilder(baseType, path);
+    }
+
+    public static @NonNull RequireInstanceRestrictedTypeBuilder<LeafrefTypeDefinition> newLeafrefBuilder(
+            final LeafrefTypeDefinition baseType, final SchemaPath path) {
+        return new RequireInstanceRestrictedTypeBuilder<>(baseType, path) {
+            @Override
+            LeafrefTypeDefinition buildType() {
+                final LeafrefTypeDefinition base = getBaseType();
+                if (getRequireInstance() == base.requireInstance()) {
+                    return base;
+                }
+                return new RestrictedLeafrefType(getBaseType(), getPath(), getUnknownSchemaNodes(),
+                        getRequireInstance());
+            }
+        };
+    }
+
+    public static @NonNull RangeRestrictedTypeBuilder<Int8TypeDefinition, Byte> newInt8Builder(
+            final Int8TypeDefinition baseType, final SchemaPath path) {
+        return new RangeRestrictedTypeBuilderWithBase<>(baseType, path) {
+            @Override
+            Int8TypeDefinition buildType(final RangeConstraint<Byte> rangeConstraint) {
+                return new RestrictedInt8Type(getBaseType(), getPath(), getUnknownSchemaNodes(), rangeConstraint);
+            }
+        };
+    }
+
+    public static @NonNull RangeRestrictedTypeBuilder<Int16TypeDefinition, Short> newInt16Builder(
+            final Int16TypeDefinition baseType, final SchemaPath path) {
+        return new RangeRestrictedTypeBuilderWithBase<>(baseType, path) {
+            @Override
+            Int16TypeDefinition buildType(final RangeConstraint<Short> rangeConstraint) {
+                return new RestrictedInt16Type(getBaseType(), getPath(), getUnknownSchemaNodes(), rangeConstraint);
+            }
+        };
+    }
+
+    public static @NonNull RangeRestrictedTypeBuilder<Int32TypeDefinition, Integer> newInt32Builder(
+            final Int32TypeDefinition baseType, final SchemaPath path) {
+        return new RangeRestrictedTypeBuilderWithBase<>(baseType, path) {
+            @Override
+            Int32TypeDefinition buildType(final RangeConstraint<Integer> rangeConstraint) {
+                return new RestrictedInt32Type(getBaseType(), getPath(), getUnknownSchemaNodes(), rangeConstraint);
+            }
+        };
+    }
+
+    public static @NonNull RangeRestrictedTypeBuilder<Int64TypeDefinition, Long> newInt64Builder(
+            final Int64TypeDefinition baseType, final SchemaPath path) {
+        return new RangeRestrictedTypeBuilderWithBase<>(baseType, path) {
+            @Override
+            Int64TypeDefinition buildType(final RangeConstraint<Long> rangeConstraint) {
+                return new RestrictedInt64Type(getBaseType(), getPath(), getUnknownSchemaNodes(), rangeConstraint);
+            }
+        };
+    }
+
+    public static @NonNull StringTypeBuilder newStringBuilder(final StringTypeDefinition baseType,
+            final SchemaPath path) {
+        return new StringTypeBuilder(baseType, path);
+    }
+
+    public static @NonNull TypeBuilder<UnionTypeDefinition> newUnionBuilder(final UnionTypeDefinition baseType,
+            final SchemaPath path) {
+        return new AbstractRestrictedTypeBuilder<>(baseType, path) {
+            @Override
+            UnionTypeDefinition buildType() {
+                return new RestrictedUnionType(getBaseType(), getPath(), getUnknownSchemaNodes());
+            }
+        };
+    }
+
+    public static @NonNull RangeRestrictedTypeBuilder<Uint8TypeDefinition, Uint8> newUint8Builder(
+            final Uint8TypeDefinition baseType, final SchemaPath path) {
+        return new RangeRestrictedTypeBuilderWithBase<Uint8TypeDefinition, Uint8>(baseType, path) {
+            @Override
+            Uint8TypeDefinition buildType(final RangeConstraint<Uint8> rangeConstraint) {
+                return new RestrictedUint8Type(getBaseType(), getPath(), getUnknownSchemaNodes(), rangeConstraint);
+            }
+        };
+    }
+
+    public static @NonNull RangeRestrictedTypeBuilder<Uint16TypeDefinition, Uint16> newUint16Builder(
+            final Uint16TypeDefinition baseType, final SchemaPath path) {
+        return new RangeRestrictedTypeBuilderWithBase<Uint16TypeDefinition, Uint16>(baseType, path) {
+            @Override
+            Uint16TypeDefinition buildType(final RangeConstraint<Uint16> rangeConstraint) {
+                return new RestrictedUint16Type(getBaseType(), getPath(), getUnknownSchemaNodes(), rangeConstraint);
+            }
+        };
+    }
+
+    public static @NonNull RangeRestrictedTypeBuilder<Uint32TypeDefinition, Uint32> newUint32Builder(
+            final Uint32TypeDefinition baseType, final SchemaPath path) {
+        return new RangeRestrictedTypeBuilderWithBase<Uint32TypeDefinition, Uint32>(baseType, path) {
+            @Override
+            Uint32TypeDefinition buildType(final RangeConstraint<Uint32> rangeConstraint) {
+                return new RestrictedUint32Type(getBaseType(), getPath(), getUnknownSchemaNodes(), rangeConstraint);
+            }
+        };
+    }
+
+    public static @NonNull RangeRestrictedTypeBuilder<Uint64TypeDefinition, Uint64> newUint64Builder(
+            final Uint64TypeDefinition baseType, final SchemaPath path) {
+        return new RangeRestrictedTypeBuilderWithBase<Uint64TypeDefinition, Uint64>(baseType, path) {
+            @Override
+            Uint64TypeDefinition buildType(final RangeConstraint<Uint64> rangeConstraint) {
+                return new RestrictedUint64Type(getBaseType(), getPath(), getUnknownSchemaNodes(), rangeConstraint);
+            }
+        };
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUint16Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUint16Type.java
new file mode 100644 (file)
index 0000000..9078f2c
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+
+final class RestrictedUint16Type extends AbstractRangeRestrictedType<Uint16TypeDefinition, Uint16>
+        implements Uint16TypeDefinition {
+    RestrictedUint16Type(final Uint16TypeDefinition baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes,
+            final @Nullable RangeConstraint<Uint16> rangeConstraint) {
+        super(baseType, path, unknownSchemaNodes, rangeConstraint);
+    }
+
+    @Override
+    public int hashCode() {
+        return Uint16TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Uint16TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Uint16TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUint32Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUint32Type.java
new file mode 100644 (file)
index 0000000..40afffe
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+
+final class RestrictedUint32Type extends AbstractRangeRestrictedType<Uint32TypeDefinition, Uint32>
+        implements Uint32TypeDefinition {
+    RestrictedUint32Type(final Uint32TypeDefinition baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes,
+            final @Nullable RangeConstraint<Uint32> rangeConstraint) {
+        super(baseType, path, unknownSchemaNodes, rangeConstraint);
+    }
+
+    @Override
+    public int hashCode() {
+        return Uint32TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Uint32TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Uint32TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUint64Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUint64Type.java
new file mode 100644 (file)
index 0000000..9265b43
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+
+final class RestrictedUint64Type extends AbstractRangeRestrictedType<Uint64TypeDefinition, Uint64>
+        implements Uint64TypeDefinition {
+    RestrictedUint64Type(final Uint64TypeDefinition baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes,
+            final @Nullable RangeConstraint<Uint64> rangeConstraint) {
+        super(baseType, path, unknownSchemaNodes, rangeConstraint);
+    }
+
+    @Override
+    public int hashCode() {
+        return Uint64TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Uint64TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Uint64TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUint8Type.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUint8Type.java
new file mode 100644 (file)
index 0000000..11e564b
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+
+final class RestrictedUint8Type extends AbstractRangeRestrictedType<Uint8TypeDefinition, Uint8>
+        implements Uint8TypeDefinition {
+    RestrictedUint8Type(final Uint8TypeDefinition baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes,
+            final @Nullable RangeConstraint<Uint8> rangeConstraint) {
+        super(baseType, path, unknownSchemaNodes, rangeConstraint);
+    }
+
+    @Override
+    public int hashCode() {
+        return Uint8TypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return Uint8TypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return Uint8TypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUnionType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/RestrictedUnionType.java
new file mode 100644 (file)
index 0000000..e67dbe9
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import java.util.Collection;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+
+final class RestrictedUnionType extends AbstractRestrictedType<UnionTypeDefinition> implements UnionTypeDefinition {
+    RestrictedUnionType(final UnionTypeDefinition baseType, final SchemaPath path,
+            final Collection<UnknownSchemaNode> unknownSchemaNodes) {
+        super(baseType, path, unknownSchemaNodes);
+    }
+
+    @Override
+    public List<TypeDefinition<?>> getTypes() {
+        return getBaseType().getTypes();
+    }
+
+    @Override
+    public int hashCode() {
+        return UnionTypeDefinition.hashCode(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return UnionTypeDefinition.equals(this, obj);
+    }
+
+    @Override
+    public String toString() {
+        return UnionTypeDefinition.toString(this);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/StringTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/StringTypeBuilder.java
new file mode 100644 (file)
index 0000000..34b524c
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+
+public final class StringTypeBuilder extends LengthRestrictedTypeBuilder<StringTypeDefinition> {
+    private final List<PatternConstraint> patternConstraints = new ArrayList<>(0);
+
+    StringTypeBuilder(final StringTypeDefinition baseType, final SchemaPath path) {
+        super(baseType, path);
+    }
+
+    public StringTypeBuilder addPatternConstraint(final PatternConstraint constraint) {
+        patternConstraints.add(requireNonNull(constraint));
+        touch();
+        return this;
+    }
+
+    @Override
+    LengthConstraint typeLengthConstraints() {
+        /**
+         * Length constraint imposed on YANG string type by our implementation. {@link String#length()} is an integer,
+         * capping our ability to support strings up to 18446744073709551615 as defined in
+         * http://tools.ietf.org/html/rfc6020#section-9.4.4.
+         *
+         * FIXME: We could bump this number up to allow such models, but that could lead to unexpected run-time errors.
+         *        In order to do that, the parser would need another pass on the effective statements, which would cap
+         *        the constraints to the run-time environment.
+         */
+        return JavaLengthConstraints.INTEGER_SIZE_CONSTRAINTS;
+    }
+
+    @Override
+    StringTypeDefinition buildType(final @Nullable LengthConstraint constraint) {
+        return new RestrictedStringType(getBaseType(), getPath(), getUnknownSchemaNodes(), constraint,
+            patternConstraints);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/TypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/TypeBuilder.java
new file mode 100644 (file)
index 0000000..ef54193
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+public abstract class TypeBuilder<T extends TypeDefinition<T>> implements Builder<T> {
+    private final ImmutableList.Builder<UnknownSchemaNode> unknownSchemaNodes = ImmutableList.builder();
+    private final @NonNull SchemaPath path;
+    private final @Nullable T baseType;
+
+    TypeBuilder(final @Nullable T baseType, final SchemaPath path) {
+        this.path = requireNonNull(path);
+        this.baseType = baseType;
+    }
+
+    final @Nullable T getBaseType() {
+        return baseType;
+    }
+
+    final @NonNull SchemaPath getPath() {
+        return path;
+    }
+
+    final @NonNull List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownSchemaNodes.build();
+    }
+
+    public final void addUnknownSchemaNode(final @NonNull UnknownSchemaNode node) {
+        unknownSchemaNodes.add(node);
+    }
+}
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/UnionTypeBuilder.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/type/UnionTypeBuilder.java
new file mode 100644 (file)
index 0000000..18923ae
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.util.type;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+
+public final class UnionTypeBuilder extends TypeBuilder<UnionTypeDefinition> {
+    private final Builder<TypeDefinition<?>> builder = ImmutableList.builder();
+
+    UnionTypeBuilder(final SchemaPath path) {
+        super(null, path);
+    }
+
+    public UnionTypeBuilder addType(final @NonNull TypeDefinition<?> type) {
+        builder.add(type);
+        return this;
+    }
+
+    @Override
+    public UnionTypeDefinition build() {
+        return new BaseUnionType(getPath(), getUnknownSchemaNodes(), builder.build());
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/repo/util/FilesystemSchemaSourceCacheTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/repo/util/FilesystemSchemaSourceCacheTest.java
new file mode 100644 (file)
index 0000000..13b5c27
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * 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 static org.hamcrest.CoreMatchers.both;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.either;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Collections2;
+import com.google.common.io.Files;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+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;
+
+public class FilesystemSchemaSourceCacheTest {
+
+    @Mock
+    private SchemaSourceRegistry registry;
+    @Mock
+    private SchemaSourceRegistration<?> registration;
+    private File storageDir;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        this.storageDir = Files.createTempDir();
+        doReturn(this.registration).when(this.registry).registerSchemaSource(any(SchemaSourceProvider.class),
+            any(PotentialSchemaSource.class));
+    }
+
+    @Test
+    public void testCacheAndRestore() throws Exception {
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> cache
+                = new FilesystemSchemaSourceCache<>(this.registry, YangTextSchemaSource.class, this.storageDir);
+
+        final String content = "content1";
+        final YangTextSchemaSource source = new TestingYangSource("test", "2012-12-12", content);
+        cache.offer(source);
+
+        final String content2 = "content2";
+        final YangTextSchemaSource source2 = new TestingYangSource("test2", null, content);
+        cache.offer(source2);
+
+        final List<File> storedFiles = getFilesFromCache();
+        assertEquals(2, storedFiles.size());
+        final Collection<String> fileNames = filesToFilenamesWithoutRevision(storedFiles);
+
+        assertThat(fileNames, both(hasItem("test2")).and(hasItem("test@2012-12-12")));
+
+        assertThat(Files.asCharSource(storedFiles.get(0), StandardCharsets.UTF_8).read(),
+            either(containsString(content)).or(containsString(content2)));
+        assertThat(Files.asCharSource(storedFiles.get(1), StandardCharsets.UTF_8).read(),
+            either(containsString(content)).or(containsString(content2)));
+
+        verify(this.registry, times(2)).registerSchemaSource(any(SchemaSourceProvider.class),
+            any(PotentialSchemaSource.class));
+
+        // Create new cache from stored sources
+        new FilesystemSchemaSourceCache<>(this.registry, YangTextSchemaSource.class, this.storageDir);
+
+        verify(this.registry, times(4)).registerSchemaSource(any(SchemaSourceProvider.class),
+            any(PotentialSchemaSource.class));
+
+        final List<File> storedFilesAfterNewCache = getFilesFromCache();
+        assertEquals(2, storedFilesAfterNewCache.size());
+    }
+
+    private static Collection<String> filesToFilenamesWithoutRevision(final List<File> storedFiles) {
+        return Collections2.transform(storedFiles, input -> Files.getNameWithoutExtension(input.getName()));
+    }
+
+    @Test
+    public void testCacheDuplicate() throws Exception {
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> cache
+                = new FilesystemSchemaSourceCache<>(this.registry, YangTextSchemaSource.class, this.storageDir);
+
+        final String content = "content1";
+        final YangTextSchemaSource source = new TestingYangSource("test", null, content);
+        // Double offer
+        cache.offer(source);
+        cache.offer(source);
+
+        final List<File> storedFiles = getFilesFromCache();
+        assertEquals(1, storedFiles.size());
+        verify(this.registry).registerSchemaSource(any(SchemaSourceProvider.class), any(PotentialSchemaSource.class));
+    }
+
+    @Test
+    public void testCacheMultipleRevisions() throws Exception {
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> cache
+                = new FilesystemSchemaSourceCache<>(this.registry, YangTextSchemaSource.class, this.storageDir);
+
+        final String content = "content1";
+        final YangTextSchemaSource source = new TestingYangSource("test", null, content);
+        final YangTextSchemaSource source2 = new TestingYangSource("test", "2012-12-12", content);
+        final YangTextSchemaSource source3 = new TestingYangSource("test", "2013-12-12", content);
+        // Double offer
+        cache.offer(source);
+        cache.offer(source2);
+        cache.offer(source3);
+
+        final List<File> storedFiles = getFilesFromCache();
+        assertEquals(3, storedFiles.size());
+
+        assertThat(filesToFilenamesWithoutRevision(storedFiles), both(hasItem("test"))
+            .and(hasItem("test@2012-12-12")).and(hasItem("test@2013-12-12")));
+
+        verify(this.registry, times(3)).registerSchemaSource(any(SchemaSourceProvider.class),
+            any(PotentialSchemaSource.class));
+    }
+
+    @Test
+    public void sourceIdToFileEmptyRevWithEmptyDir() {
+        final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("test");
+        final File sourceIdToFile = FilesystemSchemaSourceCache.sourceIdToFile(sourceIdentifier, this.storageDir);
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(this.registry,
+                YangTextSchemaSource.class, sourceIdToFile);
+        Assert.assertNotNull(cache);
+        final List<File> storedFiles = Arrays.asList(sourceIdToFile.listFiles());
+        assertEquals(0, storedFiles.size());
+    }
+
+    @Test
+    public void sourceIdToFileEmptyRevWithOneItemInDir() {
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(this.registry,
+                YangTextSchemaSource.class, this.storageDir);
+        final String content = "content1";
+        final YangTextSchemaSource source = new TestingYangSource("test", "2013-12-12", content);
+        cache.offer(source);
+
+        final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("test");
+        final File sourceIdToFile = FilesystemSchemaSourceCache.sourceIdToFile(sourceIdentifier,
+                this.storageDir);
+        Assert.assertNotNull(sourceIdToFile);
+        final List<File> storedFiles = Arrays.asList(this.storageDir.listFiles());
+        assertEquals(1, storedFiles.size());
+    }
+
+    @Test
+    public void sourceIdToFileEmptyRevWithMoreItemsInDir() {
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(this.registry,
+                YangTextSchemaSource.class, this.storageDir);
+        final String content = "content1";
+        final YangTextSchemaSource source = new TestingYangSource("test", "2012-12-12", content);
+        final YangTextSchemaSource source2 = new TestingYangSource("test", "2013-12-12", content);
+        cache.offer(source);
+        cache.offer(source2);
+
+        final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("test");
+        final File sourceIdToFile = FilesystemSchemaSourceCache.sourceIdToFile(sourceIdentifier, this.storageDir);
+        Assert.assertNotNull(sourceIdToFile);
+        final List<File> storedFiles = Arrays.asList(this.storageDir.listFiles());
+        assertEquals(2, storedFiles.size());
+    }
+
+    @Test
+    public void test() throws Exception {
+
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(this.registry,
+                YangTextSchemaSource.class, this.storageDir);
+        final String content = "content1";
+        final YangTextSchemaSource source = new TestingYangSource("test", "2013-12-12", content);
+        cache.offer(source);
+        final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("test", Revision.of("2013-12-12"));
+        final ListenableFuture<? extends YangTextSchemaSource> checked = cache.getSource(sourceIdentifier);
+        Assert.assertNotNull(checked);
+        final YangTextSchemaSource checkedGet = checked.get();
+        Assert.assertEquals(sourceIdentifier, checkedGet.getIdentifier());
+        Assert.assertTrue(checked.isDone());
+    }
+
+    @Test(expected = ExecutionException.class)
+    public void test1() throws Exception {
+
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(this.registry,
+                YangTextSchemaSource.class, this.storageDir);
+        final String content = "content1";
+        final YangTextSchemaSource source = new TestingYangSource("test", "2013-12-12", content);
+        cache.offer(source);
+        final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("test1", Revision.of("2012-12-12"));
+        final ListenableFuture<? extends YangTextSchemaSource> checked = cache.getSource(sourceIdentifier);
+        Assert.assertNotNull(checked);
+        checked.get();
+    }
+
+    private List<File> getFilesFromCache() {
+        return Arrays.asList(this.storageDir.listFiles());
+    }
+
+    private class TestingYangSource extends YangTextSchemaSource {
+
+        private final String content;
+
+        TestingYangSource(final String name, final String revision, final String content) {
+            super(RevisionSourceIdentifier.create(name, Revision.ofNullable(revision)));
+            this.content = content;
+        }
+
+        @Override
+        protected MoreObjects.ToStringHelper addToStringAttributes(final MoreObjects.ToStringHelper toStringHelper) {
+            return toStringHelper;
+        }
+
+        @Override
+        public InputStream openStream() throws IOException {
+            return new ByteArrayInputStream(this.content.getBytes(StandardCharsets.UTF_8));
+        }
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCacheTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/repo/util/InMemorySchemaSourceCacheTest.java
new file mode 100644 (file)
index 0000000..e615aa8
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2016 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 static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+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.api.YangSchemaSourceRepresentation;
+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;
+
+@RunWith(MockitoJUnitRunner.class)
+public class InMemorySchemaSourceCacheTest {
+
+    private static final Class<YangSchemaSourceRepresentation> REPRESENTATION = YangSchemaSourceRepresentation.class;
+    private static final long LIFETIME = 1000L;
+    private static final TimeUnit UNITS = TimeUnit.MILLISECONDS;
+
+    @Mock
+    private SchemaSourceRegistry registry;
+    @Mock
+    private SchemaSourceRegistration<?> registration;
+
+    @Before
+    public void setUp() throws Exception {
+        doNothing().when(registration).close();
+        doReturn(registration).when(registry).registerSchemaSource(any(SchemaSourceProvider.class),
+                any(PotentialSchemaSource.class));
+    }
+
+    @Test
+    public void inMemorySchemaSourceCacheTest1() {
+        final InMemorySchemaSourceCache<YangSchemaSourceRepresentation> inMemorySchemaSourceCache =
+            InMemorySchemaSourceCache.createSoftCache(this.registry, REPRESENTATION);
+        Assert.assertNotNull(inMemorySchemaSourceCache);
+        inMemorySchemaSourceCache.close();
+    }
+
+    @Test
+    public void inMemorySchemaSourceCacheTest2() {
+        final InMemorySchemaSourceCache<YangSchemaSourceRepresentation> inMemorySchemaSourceCache =
+            InMemorySchemaSourceCache.createSoftCache(this.registry, REPRESENTATION, LIFETIME, UNITS);
+        Assert.assertNotNull(inMemorySchemaSourceCache);
+        inMemorySchemaSourceCache.close();
+    }
+
+    @Test
+    public void inMemorySchemaSourceCacheOfferAndGetSourcestest() throws Exception {
+        final InMemorySchemaSourceCache<YangSchemaSourceRepresentation> inMemorySchemaSourceCache =
+            InMemorySchemaSourceCache.createSoftCache(this.registry, REPRESENTATION);
+        final String content = "content";
+        final YangTextSchemaSource source = new TestingYangSource("test", "2012-12-12", content);
+        inMemorySchemaSourceCache.offer(source);
+        final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("test", Revision.of("2012-12-12"));
+        final ListenableFuture<? extends YangSchemaSourceRepresentation> checkedSource = inMemorySchemaSourceCache
+                .getSource(sourceIdentifier);
+        Assert.assertNotNull(checkedSource);
+        final YangSchemaSourceRepresentation yangSchemaSourceRepresentation = checkedSource.get();
+        Assert.assertNotNull(yangSchemaSourceRepresentation);
+        Assert.assertEquals(sourceIdentifier, yangSchemaSourceRepresentation.getIdentifier());
+        inMemorySchemaSourceCache.close();
+    }
+
+    @Test(expected = ExecutionException.class)
+    public void inMemorySchemaSourceCacheNullGetSourcestest() throws Exception {
+        final InMemorySchemaSourceCache<YangSchemaSourceRepresentation> inMemorySchemaSourceCache =
+            InMemorySchemaSourceCache.createSoftCache(this.registry, REPRESENTATION);
+        final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("test", Revision.of("2012-12-12"));
+        final ListenableFuture<? extends YangSchemaSourceRepresentation> checkedSource =
+            inMemorySchemaSourceCache.getSource(sourceIdentifier);
+        Assert.assertNotNull(checkedSource);
+        checkedSource.get();
+        inMemorySchemaSourceCache.close();
+    }
+
+    @Test
+    public void inMemorySchemaSourceCache3test() throws InterruptedException, ExecutionException {
+        final InMemorySchemaSourceCache<YangSchemaSourceRepresentation> inMemorySchemaSourceCache =
+            InMemorySchemaSourceCache.createSoftCache(this.registry, REPRESENTATION);
+        final InMemorySchemaSourceCache<YangSchemaSourceRepresentation> inMemorySchemaSourceCache2 =
+            InMemorySchemaSourceCache.createSoftCache(this.registry, REPRESENTATION, LIFETIME, UNITS);
+
+        final String content = "content";
+        final YangTextSchemaSource source = new TestingYangSource("test", "2012-12-12", content);
+        inMemorySchemaSourceCache.offer(source);
+        inMemorySchemaSourceCache2.offer(source);
+
+        final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("test", Revision.of("2012-12-12"));
+        final ListenableFuture<? extends YangSchemaSourceRepresentation> checkedSource =
+            inMemorySchemaSourceCache.getSource(sourceIdentifier);
+        final ListenableFuture<? extends SchemaSourceRepresentation> checkedSource2 =
+            inMemorySchemaSourceCache2.getSource(sourceIdentifier);
+        Assert.assertNotNull(checkedSource);
+        Assert.assertNotNull(checkedSource2);
+
+        Assert.assertEquals(checkedSource.get(), checkedSource2.get());
+        inMemorySchemaSourceCache.close();
+        inMemorySchemaSourceCache2.close();
+    }
+
+    private class TestingYangSource extends YangTextSchemaSource {
+
+        private final String content;
+
+        protected TestingYangSource(final String name, final String revision, final String content) {
+            super(RevisionSourceIdentifier.create(name, Revision.ofNullable(revision)));
+            this.content = content;
+        }
+
+        @Override
+        protected MoreObjects.ToStringHelper addToStringAttributes(final MoreObjects.ToStringHelper toStringHelper) {
+            return toStringHelper;
+        }
+
+        @Override
+        public InputStream openStream() throws IOException {
+            return new ByteArrayInputStream(this.content.getBytes(StandardCharsets.UTF_8));
+        }
+    }
+
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistrationTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/repo/util/RefcountedRegistrationTest.java
new file mode 100644 (file)
index 0000000..df0de0c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016 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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RefcountedRegistrationTest {
+    @Mock
+    private SchemaSourceRegistration<?> reg;
+
+    @Before
+    public void before() {
+        doNothing().when(reg).close();
+    }
+
+    @Test
+    public void refcountDecTrue() {
+        final RefcountedRegistration ref = new RefcountedRegistration(reg);
+        assertTrue(ref.decRef());
+        verify(reg, times(1)).close();
+    }
+
+    @Test
+    public void refcountIncDecFalse() {
+        final RefcountedRegistration ref = new RefcountedRegistration(reg);
+        ref.incRef();
+        assertFalse(ref.decRef());
+        verify(reg, times(0)).close();
+    }
+
+    @Test
+    public void refcountIncDecTrue() {
+        final RefcountedRegistration ref = new RefcountedRegistration(reg);
+        ref.incRef();
+        assertFalse(ref.decRef());
+        assertTrue(ref.decRef());
+        verify(reg, times(1)).close();
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformerTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/repo/util/SchemaSourceTransformerTest.java
new file mode 100644 (file)
index 0000000..1abd85b
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2016 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 static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import java.util.Arrays;
+import java.util.concurrent.Future;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+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.api.YangSchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.YinXmlSchemaSource;
+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.SchemaSourceRegistry;
+
+@RunWith(MockitoJUnitRunner.class)
+public class SchemaSourceTransformerTest {
+
+    private SchemaSourceTransformer<YangSchemaSourceRepresentation, YinXmlSchemaSource> schema;
+    private static final Class<YangSchemaSourceRepresentation> SRC_CLASS = YangSchemaSourceRepresentation.class;
+    private static final Class<YinXmlSchemaSource> DST_CLASS = YinXmlSchemaSource.class;
+
+    @Mock
+    private SchemaRepository provider;
+
+    @Mock
+    private SchemaSourceRegistry consumer;
+
+    @Mock
+    private AsyncFunction<YangSchemaSourceRepresentation, YinXmlSchemaSource> function;
+
+    @Test
+    public void schemaSourceTransformerTest() {
+        this.schema = new SchemaSourceTransformer<>(
+                this.provider, SchemaSourceTransformerTest.SRC_CLASS, this.consumer,
+                SchemaSourceTransformerTest.DST_CLASS, this.function);
+        assertNotNull(this.schema);
+    }
+
+    @Test
+    public void schemaSourceTransformerGetSourceTest() {
+        final Provider p = new Provider();
+        final Registrator reg = new Registrator(p, SchemaSourceTransformerTest.SRC_CLASS,
+                PotentialSchemaSource.Costs.IMMEDIATE);
+        final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("source");
+        reg.register(sourceIdentifier);
+        this.schema = new SchemaSourceTransformer<>(p,
+                SchemaSourceTransformerTest.SRC_CLASS, this.consumer, SchemaSourceTransformerTest.DST_CLASS,
+                this.function);
+        final SchemaSourceProvider<YinXmlSchemaSource> prov = this.schema;
+        final Future<? extends YinXmlSchemaSource> source = prov.getSource(sourceIdentifier);
+        assertNotNull(source);
+        source.cancel(true);
+        assertTrue(source.isDone());
+    }
+
+    @Test
+    public void schemaSourceRegAndUnregSchemaSourceTest() {
+        final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("source");
+        final Foo<YangSchemaSourceRepresentation> foo = new Foo<>(sourceIdentifier,
+                SchemaSourceTransformerTest.SRC_CLASS,
+                PotentialSchemaSource.Costs.COMPUTATION);
+        final Provider p = new Provider();
+
+        final Registrator reg = new Registrator(p, SchemaSourceTransformerTest.SRC_CLASS,
+                PotentialSchemaSource.Costs.IMMEDIATE);
+        reg.register(sourceIdentifier);
+
+        final Consumer c = new Consumer();
+        this.schema = new SchemaSourceTransformer<>(p,
+                SchemaSourceTransformerTest.SRC_CLASS, c, SchemaSourceTransformerTest.DST_CLASS, this.function);
+
+        final SchemaSourceListener listener = this.schema;
+        p.registerSchemaSourceListener(listener);
+
+        final PotentialSchemaSource<?>[] potList = { foo.getPotentialSchemSource() };
+        final Iterable<PotentialSchemaSource<?>> sources = Arrays.asList(potList);
+        listener.schemaSourceRegistered(sources);
+        final ListenableFuture<YinXmlSchemaSource> source = this.schema.getSource(sourceIdentifier);
+        assertNotNull(source);
+
+        listener.schemaSourceUnregistered(foo.getPotentialSchemSource());
+        final ListenableFuture<YinXmlSchemaSource> source2 = this.schema.getSource(sourceIdentifier);
+        assertNotNull(source2);
+    }
+
+    private class Foo<T extends SchemaSourceRepresentation> {
+
+        final PotentialSchemaSource<T> src;
+
+        Foo(final SourceIdentifier sourceIdentifier, final Class<T> representation, final Costs cost) {
+            this.src = PotentialSchemaSource.create(sourceIdentifier, representation,
+                    cost.getValue());
+        }
+
+        public PotentialSchemaSource<T> getPotentialSchemSource() {
+            return this.src;
+        }
+
+    }
+
+    private class Registrator extends AbstractSchemaSourceCache<YangSchemaSourceRepresentation> {
+
+        Registrator(final SchemaSourceRegistry consumer, final Class<YangSchemaSourceRepresentation> srcClass,
+                final Costs cost) {
+            super(consumer, srcClass, cost);
+        }
+
+        @Override
+        protected void offer(final YangSchemaSourceRepresentation source) {
+
+        }
+
+        @Override
+        public ListenableFuture<? extends YangSchemaSourceRepresentation> getSource(
+                final SourceIdentifier sourceIdentifier) {
+            return SettableFuture.create();
+        }
+
+    }
+
+    private class Provider extends AbstractSchemaRepository {
+        @Override
+        public EffectiveModelContextFactory createEffectiveModelContextFactory(
+                final SchemaContextFactoryConfiguration config) {
+            return mock(EffectiveModelContextFactory.class);
+        }
+    }
+
+    private class Consumer extends AbstractSchemaRepository {
+        @Override
+        public EffectiveModelContextFactory createEffectiveModelContextFactory(
+                final SchemaContextFactoryConfiguration config) {
+            return mock(EffectiveModelContextFactory.class);
+        }
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/BaseTypesTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/BaseTypesTest.java
new file mode 100644 (file)
index 0000000..2ed2274
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.model.util;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class BaseTypesTest {
+
+    @Test
+    public void testIsYangBuildInType() {
+        assertFalse("whatever is not build-in type", BaseTypes.isYangBuildInType("whatever"));
+        assertTrue("int8 is build-in type", BaseTypes.isYangBuildInType("int8"));
+    }
+
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/BinaryTypeTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/BinaryTypeTest.java
new file mode 100644 (file)
index 0000000..8cf01ae
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.model.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.model.util.type.BaseTypes.binaryType;
+
+import java.util.Collections;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+
+public class BinaryTypeTest {
+
+    @Test
+    public void canCreateBinaryType() {
+        final BinaryTypeDefinition binType = binaryType();
+        final BinaryTypeDefinition binType1 = binaryType();
+
+        assertFalse(binType.getLengthConstraint().isPresent());
+        assertEquals(Optional.empty(), binType.getDefaultValue());
+        assertEquals("CURRENT", Status.CURRENT, binType.getStatus());
+        assertEquals("Base type is null", null, binType.getBaseType());
+        assertEquals("getQName gives BINARY_QNAME", BaseTypes.BINARY_QNAME, binType.getQName());
+        assertEquals(Optional.empty(), binType.getUnits());
+        assertEquals("getPath gives List of BINARY_QNAME", Collections.singletonList(BaseTypes.BINARY_QNAME),
+            binType.getPath().getPathFromRoot());
+
+        assertTrue("binType1 should equal to binType", binType.equals(binType1) && binType1.equals(binType));
+        assertTrue("Hash code of binType and binType1 should be equal",
+                binType.hashCode() == binType1.hashCode());
+        assertEquals("binType should equals to itself", binType, binType);
+        assertFalse("binType shouldn't equal to null", binType.equals(null));
+        assertFalse("binType shouldn't equal to object of other type", binType.equals("str"));
+    }
+
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/BitsTypeTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/BitsTypeTest.java
new file mode 100644 (file)
index 0000000..f30c3d8
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.doReturn;
+
+import java.util.Collections;
+import java.util.Optional;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+@RunWith(MockitoJUnitRunner.class)
+public class BitsTypeTest {
+    @Mock
+    private BitsTypeDefinition.Bit bit;
+
+    @Test
+    public void canCreateBitsType() {
+        doReturn("test").when(bit).getName();
+        doReturn(0L).when(bit).getPosition();
+        doReturn("toString").when(bit).toString();
+
+        QName qname = QName.create("namespace", "localname");
+        SchemaPath schemaPath = SchemaPath.create(true, qname);
+
+        BitsTypeDefinition bitsType = BaseTypes.bitsTypeBuilder(schemaPath).addBit(bit).build();
+
+        assertFalse(bitsType.getDescription().isPresent());
+        assertEquals("QName", qname, bitsType.getQName());
+        assertEquals(Optional.empty(), bitsType.getUnits());
+        assertNotEquals("Description should not be null", null, bitsType.toString());
+        assertFalse(bitsType.getReference().isPresent());
+        assertNull("BaseType should be null", bitsType.getBaseType());
+        assertEquals(Optional.empty(), bitsType.getDefaultValue());
+        assertEquals("getPath should equal schemaPath", schemaPath, bitsType.getPath());
+        assertEquals("Status should be CURRENT", Status.CURRENT, bitsType.getStatus());
+        assertEquals("Should be empty list", Collections.emptyList(), bitsType.getUnknownSchemaNodes());
+        assertEquals("Values should be [enumPair]", Collections.singletonList(bit), bitsType.getBits());
+
+        assertEquals("Hash code of bitsType should be equal",
+                bitsType.hashCode(), bitsType.hashCode());
+        assertNotEquals("bitsType shouldn't equal to null", null, bitsType);
+        assertEquals("bitsType should equals to itself", bitsType, bitsType);
+        assertNotEquals("bitsType shouldn't equal to object of other type", "str", bitsType);
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/BooleanTypeTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/BooleanTypeTest.java
new file mode 100644 (file)
index 0000000..4df1815
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.model.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.model.util.type.BaseTypes.booleanType;
+
+import java.util.Collections;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+
+public class BooleanTypeTest {
+
+    @Test
+    public void canCreateBooleanType() {
+        final BooleanTypeDefinition boolType = booleanType();
+        final String stringBoolType = boolType.toString();
+
+        assertEquals("getPath gives List of BOOLEAN_QNAME",
+                Collections.singletonList(BaseTypes.BOOLEAN_QNAME), boolType.getPath().getPathFromRoot());
+        assertEquals("getQName gives BOOLEAN_QNAME", BaseTypes.BOOLEAN_QNAME, boolType.getQName());
+        assertFalse(boolType.getDescription().isPresent());
+
+        final String strPath = boolType.getPath().toString();
+        assertTrue("Should contain string of getPath", stringBoolType.contains(strPath));
+        assertEquals(Optional.empty(), boolType.getUnits());
+        assertEquals("Base type is null", null, boolType.getBaseType());
+        assertEquals(Optional.empty(), boolType.getDefaultValue());
+        assertEquals("Status CURRENT", Status.CURRENT, boolType.getStatus());
+        assertEquals("Should contain empty list", Collections.emptyList(), boolType.getUnknownSchemaNodes());
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug4079Test.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug4079Test.java
new file mode 100644 (file)
index 0000000..28db4a5
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2016 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import org.junit.Test;
+
+public class Bug4079Test {
+
+    @Test
+    public void testValidPatternFix() {
+        String fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsArrows})*+");
+        assertEquals("^(?:(\\p{InArrows})*+)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsDingbats})++");
+        assertEquals("^(?:(\\p{InDingbats})++)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsSpecials})?+");
+        assertEquals("^(?:(\\p{InSpecials})?+)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsBatak}){4}+");
+        assertEquals("^(?:(\\p{IsBatak}){4}+)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsLatin}){4,6}+");
+        assertEquals("^(?:(\\p{IsLatin}){4,6}+)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsTibetan}){4,}+");
+        assertEquals("^(?:(\\p{IsTibetan}){4,}+)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsAlphabetic}){4}?");
+        assertEquals("^(?:(\\p{IsAlphabetic}){4}?)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsLowercase}){4,6}?");
+        assertEquals("^(?:(\\p{IsLowercase}){4,6}?)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsUppercase}){4,}?");
+        assertEquals("^(?:(\\p{IsUppercase}){4,}?)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsBasicLatin}|\\p{IsLatin-1Supplement})*");
+        assertEquals("^(?:(\\p{InBasicLatin}|\\p{InLatin-1Supplement})*)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{InBasicLatin}|\\p{InLatin-1Supplement})+");
+        assertEquals("^(?:(\\p{InBasicLatin}|\\p{InLatin-1Supplement})+)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsBasicLatin}|\\p{InLatin-1Supplement})?");
+        assertEquals("^(?:(\\p{InBasicLatin}|\\p{InLatin-1Supplement})?)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{InBasicLatin}|\\p{IsLatin-1Supplement}){4}");
+        assertEquals("^(?:(\\p{InBasicLatin}|\\p{InLatin-1Supplement}){4})$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsLatin}|\\p{IsArmenian}){2,4}");
+        assertEquals("^(?:(\\p{IsLatin}|\\p{IsArmenian}){2,4})$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsLatin}|\\p{IsBasicLatin}){2,}");
+        assertEquals("^(?:(\\p{IsLatin}|\\p{InBasicLatin}){2,})$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\p{IsBasicLatin}|\\p{IsLatin})*?");
+        assertEquals("^(?:(\\p{InBasicLatin}|\\p{IsLatin})*?)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD(
+                "(\\p{IsBasicLatin}|\\p{IsLatin-1Supplement}|\\p{IsArrows})+?");
+        assertEquals("^(?:(\\p{InBasicLatin}|\\p{InLatin-1Supplement}|\\p{InArrows})+?)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD(
+                "(\\p{InBasicLatin}|\\p{IsLatin-1Supplement}|\\p{IsLatin})??");
+        assertEquals("^(?:(\\p{InBasicLatin}|\\p{InLatin-1Supplement}|\\p{IsLatin})??)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\\\\\p{IsBasicLatin})*+");
+        assertEquals("^(?:(\\\\\\p{InBasicLatin})*+)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\\\\\\\\\p{IsBasicLatin})*+");
+        assertEquals("^(?:(\\\\\\\\\\p{InBasicLatin})*+)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+
+        fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\\\\\\\\\\\\\p{IsBasicLatin})*+");
+        assertEquals("^(?:(\\\\\\\\\\\\\\p{InBasicLatin})*+)$", fixedUnicodeScriptPattern);
+        assertNotNull(Pattern.compile(fixedUnicodeScriptPattern));
+    }
+
+    @Test(expected = PatternSyntaxException.class)
+    public void testInvalidPattern() {
+        String fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD("(\\\\p{IsBasicLatin})*+");
+        assertEquals("^(?:(\\\\p{IsBasicLatin})*+)$", fixedUnicodeScriptPattern);
+        // should throw exception
+        Pattern.compile(fixedUnicodeScriptPattern);
+    }
+
+    @Test(expected = PatternSyntaxException.class)
+    public void testInvalidPattern2() {
+        String fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD(
+            "(\\p{IsSpecials}|\\\\\\\\p{IsBasicLatin})*+");
+        assertEquals("^(?:(\\p{InSpecials}|\\\\\\\\p{IsBasicLatin})*+)$", fixedUnicodeScriptPattern);
+        // should throw exception
+        Pattern.compile(fixedUnicodeScriptPattern);
+    }
+
+    @Test(expected = PatternSyntaxException.class)
+    public void testInvalidPattern3() {
+        String fixedUnicodeScriptPattern = RegexUtils.getJavaRegexFromXSD(
+            "(\\\\\\\\\\\\p{IsBasicLatin}|\\p{IsTags})*+");
+        assertEquals("^(?:(\\\\\\\\\\\\p{IsBasicLatin}|\\p{IsTags})*+)$", fixedUnicodeScriptPattern);
+        // should throw exception
+        Pattern.compile(fixedUnicodeScriptPattern);
+    }
+
+    @Test
+    public void testCorrectBranches() {
+        String str = RegexUtils.getJavaRegexFromXSD("a|bb");
+        assertEquals("^(?:a|bb)$", str);
+        Predicate<String> pred = Pattern.compile(str).asPredicate();
+
+        assertTrue(pred.test("a"));
+        assertTrue(pred.test("bb"));
+        assertFalse(pred.test("ab"));
+        assertFalse(pred.test("abb"));
+        assertFalse(pred.test("ac"));
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug4969Test.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug4969Test.java
new file mode 100644 (file)
index 0000000..d7c7fe9
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+
+public class Bug4969Test {
+    @Test
+    public void testRegex() {
+        PathExpression xpath = new PathExpressionImpl(
+                "nd:network[nd:network-id=current()/../network-ref]/nd:node[nd:node-id=current()/../node-ref]"
+                + "/termination-point/tp-id", true);
+        assertEquals("nd:network/nd:node/termination-point/tp-id",
+                SchemaContextUtil.stripConditionsFromXPathString(xpath));
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug5410Test.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug5410Test.java
new file mode 100644 (file)
index 0000000..08aa051
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableList;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import org.junit.Test;
+
+public class Bug5410Test {
+    @Test
+    public void testCaret() {
+        testPattern("^", "\\^");
+    }
+
+    @Test
+    public void testTextCaret() {
+        testPattern("abc^", "abc\\^");
+    }
+
+    @Test
+    public void testTextDollar() {
+        testPattern("abc$", "abc\\$");
+    }
+
+    @Test
+    public void testCaretCaret() {
+        testPattern("^^", "\\^\\^");
+    }
+
+    @Test
+    public void testCaretDollar() {
+        testPattern("^$", "\\^\\$");
+    }
+
+    @Test
+    public void testDot() {
+        testPattern(".", ".");
+    }
+
+    @Test
+    public void testNotColon() {
+        testPattern("[^:]+", "[^:]+");
+    }
+
+    @Test
+    public void testDollar() {
+        testPattern("$", "\\$");
+    }
+
+    @Test
+    public void testDollarOneDollar() {
+        testPattern("$1$", "\\$1\\$");
+    }
+
+    @Test
+    public void testDollarPercentRange() {
+        testPattern("[$-%]+", "[$-%]+");
+    }
+
+    @Test
+    public void testDollarRange() {
+        testPattern("[$$]+", "[$$]+");
+    }
+
+    @Test
+    public void testDollarCaretRange() {
+        testPattern("[$^]+", "[$^]+");
+    }
+
+    @Test
+    public void testSimple() {
+        testPattern("abc", "abc");
+    }
+
+    @Test
+    public void testDotPlus() {
+        testPattern(".+", ".+");
+    }
+
+    @Test
+    public void testDotStar() {
+        testPattern(".*", ".*");
+    }
+
+    @Test
+    public void testSimpleOptional() {
+        testPattern("a?", "a?");
+    }
+
+    @Test
+    public void testRangeOptional() {
+        testPattern("[a-z]?", "[a-z]?");
+    }
+
+    @Test
+    public void testInvalidXSDRegexes() throws UnsupportedEncodingException {
+        testInvalidPattern("$^a^[$^\\]", "Unclosed character class");
+        testInvalidPattern("$(\\)", "Unclosed group");
+    }
+
+    @Test
+    public void testJavaPattern() {
+        testPattern("^[$^]+$", ImmutableList.of("$^", "^", "$"), ImmutableList.of("\\", "a"));
+        testPattern("^[^$-^]$", ImmutableList.of("a", "_", "#"), ImmutableList.of("%", "^", "$", "]", "\\"));
+    }
+
+    @Test
+    public void testJavaRegexFromXSD() {
+        testPattern("^[^:]+$", "^(?:\\^[^:]+\\$)$", ImmutableList.of("^a$", "^abc$"),
+                ImmutableList.of("abc$", "^abc", "^a:bc$"));
+        testPattern("^[$^]$", "^(?:\\^[$^]\\$)$", ImmutableList.of("^^$", "^$$"),
+            ImmutableList.of("^^", "^$", "$^", "$$"));
+        testPattern("[$-%]+", "^(?:[$-%]+)$", ImmutableList.of("$", "%", "%$"),
+            ImmutableList.of("$-", "$-%", "-", "^"));
+        testPattern("[$-&]+", "^(?:[$-&]+)$", ImmutableList.of("$", "%&", "%$", "$%&"),
+            ImmutableList.of("#", "$-&", "'"));
+
+        testPattern("[a-z&&[^m-p]]+", "^(?:[a-z&&[^m-p]]+)$", ImmutableList.of("a", "z", "az"),
+                ImmutableList.of("m", "anz", "o"));
+        testPattern("^[\\[-b&&[^^-a]]+$", "^(?:\\^[\\[-b&&[^^-a]]+\\$)$", ImmutableList.of("^[$", "^\\$", "^]$", "^b$"),
+                ImmutableList.of("^a$", "^^$", "^_$"));
+
+        // FIXME: YANGTOOLS-887: these patterns are not translated correctly, "&&" is a different construct in XSD
+        //        testPattern("[^^-~&&[^$-^]]", "^(?:[^^-~&&[^$-^]])$", ImmutableList.of("!", "\"", "#"),
+        //                ImmutableList.of("a", "A", "z", "Z", "$", "%", "^", "}"));
+        //        testPattern("\\\\\\[^[^^-~&&[^$-^]]", "^(?:\\\\\\[\\^[^^-~&&[^$-^]])$",
+        //                ImmutableList.of("\\[^ ", "\\[^!", "\\[^\"", "\\[^#"),
+        //                ImmutableList.of("\\[^a", "\\[^A", "\\[^z", "\\[^Z", "\\[^$", "\\[^%", "\\[^^", "\\[^}"));
+        //        testPattern("^\\[^\\\\[^^-b&&[^\\[-\\]]]\\]^", "^(?:\\^\\[\\^\\\\[^^-b&&[^\\[-\\]]]\\]\\^)$",
+        //                ImmutableList.of("^[^\\c]^", "^[^\\Z]^"),
+        //                ImmutableList.of("^[^\\[]^", "^[^\\\\]^", "^[^\\]]^", "^[^\\^]^", "^[^\\_]^", "^[^\\b]^"));
+        //        testPattern("[\\^]$", "^(?:[\\^]\\$)$", ImmutableList.of("^$"),
+        //                ImmutableList.of("^", "$", "$^", "\\", "\\^", "\\^\\", "\\^\\$"));
+    }
+
+    @SuppressWarnings("checkstyle:regexpSinglelineJava")
+    private static void testInvalidPattern(final String xsdRegex, final String expectedMessage)
+            throws UnsupportedEncodingException {
+        final PrintStream stdout = System.out;
+        final ByteArrayOutputStream output = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(output, true, "UTF-8"));
+
+        RegexUtils.getJavaRegexFromXSD(xsdRegex);
+
+        final String testLog = output.toString();
+        assertTrue(testLog.contains(expectedMessage));
+        System.setOut(stdout);
+    }
+
+    private static boolean testMatch(final String javaRegex, final String value) {
+        return value.matches(javaRegex);
+    }
+
+    private static void testPattern(final String xsdRegex, final String unanchoredJavaRegex) {
+        testPattern(xsdRegex, "^(?:" + unanchoredJavaRegex + ")$", ImmutableList.of(), ImmutableList.of());
+    }
+
+    private static void testPattern(final String javaRegex, final List<String> positiveMatches,
+            final List<String> negativeMatches) {
+        for (final String value : positiveMatches) {
+            assertTrue("Value '" + value + "' does not match java regex '" + javaRegex + "'",
+                    testMatch(javaRegex, value));
+        }
+        for (final String value : negativeMatches) {
+            assertFalse("Value '" + value + "' matches java regex '" + javaRegex + "'", testMatch(javaRegex, value));
+        }
+    }
+
+    private static void testPattern(final String xsdRegex, final String expectedJavaRegex,
+            final List<String> positiveMatches, final List<String> negativeMatches) {
+        final String javaRegexFromXSD = RegexUtils.getJavaRegexFromXSD(xsdRegex);
+        assertEquals(expectedJavaRegex, javaRegexFromXSD);
+
+        for (final String value : positiveMatches) {
+            assertTrue("Value '" + value + "' does not match java regex '" + javaRegexFromXSD + "'",
+                    testMatch(javaRegexFromXSD, value));
+        }
+        for (final String value : negativeMatches) {
+            assertFalse("Value '" + value + "' matches java regex '" + javaRegexFromXSD + "'",
+                    testMatch(javaRegexFromXSD, value));
+        }
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/DataNodeIteratorTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/DataNodeIteratorTest.java
new file mode 100644 (file)
index 0000000..41eb9b2
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedMap;
+import java.util.Collections;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.SortedMap;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CopyableNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DataNodeIteratorTest {
+    private DataNodeIterator dataNodeIterator;
+
+    @Before
+    public void before() {
+        this.dataNodeIterator = new DataNodeIterator(mockDataNodeContainer(DataNodeContainer.class));
+    }
+
+    private static <T extends DataNodeContainer> T mockDataNodeContainer(final Class<T> clazz) {
+        final T mock = mock(clazz);
+        doReturn(Collections.emptyList()).when(mock).getChildNodes();
+        doReturn(Collections.emptySet()).when(mock).getGroupings();
+        doReturn(Collections.emptySet()).when(mock).getTypeDefinitions();
+        return mock;
+    }
+
+    private static <T extends OperationDefinition> T mockOperationDefinition(final T mock) {
+        doReturn(Collections.emptySet()).when(mock).getTypeDefinitions();
+        doReturn(mockDataNodeContainer(ContainerSchemaNode.class)).when(mock).getOutput();
+        return mock;
+    }
+
+    @Deprecated
+    private static <T extends CopyableNode> T mockCopyableNode(final boolean augmenting, final T node) {
+        doReturn(augmenting).when(node).isAugmenting();
+        return node;
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createDataNodeIteratorWithNullArgument() {
+        new DataNodeIterator(null);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void removeFromEmptyDataNodeContainer() {
+        dataNodeIterator.remove();
+    }
+
+    @Test(expected = NoSuchElementException.class)
+    public void tryNextOnEmptyDataContainer() {
+        dataNodeIterator.next();
+    }
+
+    @Test
+    public void createDataNodeIteratorWith() {
+        assertFalse("Has no next", dataNodeIterator.hasNext());
+        assertEquals("Should be empty list", Collections.emptyList(), dataNodeIterator.allChoices());
+        assertEquals("Should be empty list", Collections.emptyList(), dataNodeIterator.allContainers());
+        assertEquals("Should be empty list", Collections.emptyList(), dataNodeIterator.allTypedefs());
+        assertEquals("Should be empty list", Collections.emptyList(), dataNodeIterator.allGroupings());
+        assertEquals("Should be empty list", Collections.emptyList(), dataNodeIterator.allLists());
+    }
+
+    @Test
+    public void testTraversal() {
+        final Module mockedModule = mockDataNodeContainer(Module.class);
+
+        final ContainerSchemaNode mockedAugmentingContainer = mockCopyableNode(true, mock(ContainerSchemaNode.class));
+        final ContainerSchemaNode mockedContainer = mockCopyableNode(false, mockDataNodeContainer(
+            ContainerSchemaNode.class));
+
+        final ListSchemaNode mockedList = mockCopyableNode(false, mockDataNodeContainer(ListSchemaNode.class));
+
+        final ChoiceSchemaNode mockedChoice = mockCopyableNode(false, mock(ChoiceSchemaNode.class));
+        final CaseSchemaNode mockedCase1 = mockDataNodeContainer(CaseSchemaNode.class);
+        final QName mockedCase1QName = QName.create("", "case1");
+        final CaseSchemaNode mockedCase2 = mockDataNodeContainer(CaseSchemaNode.class);
+        final QName mockedCase2QName = QName.create("", "case2");
+        final SortedMap<QName, CaseSchemaNode> cases = ImmutableSortedMap.of(mockedCase1QName, mockedCase1,
+            mockedCase2QName, mockedCase2);
+        doReturn(cases).when(mockedChoice).getCases();
+
+        final Set<DataSchemaNode> childNodes = ImmutableSet.of(mockedAugmentingContainer, mockedContainer, mockedList,
+                mockedChoice);
+        doReturn(childNodes).when(mockedModule).getChildNodes();
+
+        final NotificationDefinition mockedNotification = mockDataNodeContainer(NotificationDefinition.class);
+        final ContainerSchemaNode mockedContainerInNotification = mockCopyableNode(false,
+            mockDataNodeContainer(ContainerSchemaNode.class));
+        final Set<DataSchemaNode> notificationChildNodes = ImmutableSet.of(mockedContainerInNotification);
+        doReturn(notificationChildNodes).when(mockedNotification).getChildNodes();
+        final Set<NotificationDefinition> notifications = ImmutableSet.of(mockedNotification);
+
+        doReturn(notifications).when(mockedModule).getNotifications();
+
+        final RpcDefinition mockedRpc = mockOperationDefinition(mock(RpcDefinition.class));
+        final ContainerSchemaNode mockedContainerInRpcInput = mockDataNodeContainer(ContainerSchemaNode.class);
+        final ListSchemaNode mockedListInRpcInputContainer = mockCopyableNode(false,
+            mockDataNodeContainer(ListSchemaNode.class));
+        final Set<DataSchemaNode> rpcInputChildNodes = ImmutableSet.of(mockedListInRpcInputContainer);
+        doReturn(rpcInputChildNodes).when(mockedContainerInRpcInput).getChildNodes();
+        doReturn(mockedContainerInRpcInput).when(mockedRpc).getInput();
+        final Set<RpcDefinition> rpcs = ImmutableSet.of(mockedRpc);
+
+        doReturn(rpcs).when(mockedModule).getRpcs();
+
+        final GroupingDefinition mockedGrouping = mockDataNodeContainer(GroupingDefinition.class);
+        final Set<GroupingDefinition> groupings = ImmutableSet.of(mockedGrouping);
+
+        doReturn(groupings).when(mockedModule).getGroupings();
+
+        final DataNodeIterator it = new DataNodeIterator(mockedModule);
+        assertFalse(it.allContainers().contains(mockedAugmentingContainer));
+        assertTrue(it.allContainers().contains(mockedContainer));
+        assertTrue(it.allLists().contains(mockedList));
+        assertTrue(it.allChoices().contains(mockedChoice));
+        assertTrue(it.allChoices().get(0).getCases().values().contains(mockedCase1));
+        assertTrue(it.allChoices().get(0).getCases().values().contains(mockedCase2));
+        assertTrue(it.allContainers().contains(mockedContainerInNotification));
+        assertTrue(it.allLists().contains(mockedListInRpcInputContainer));
+        assertTrue(it.allGroupings().contains(mockedGrouping));
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/EmptyTypeTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/EmptyTypeTest.java
new file mode 100644 (file)
index 0000000..17bc7bf
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.model.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.model.util.type.BaseTypes.emptyType;
+
+import java.util.Collections;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+
+public class EmptyTypeTest {
+
+    @Test
+    public void canCreateEmptyType() {
+        EmptyTypeDefinition emptyType = emptyType();
+
+        assertEquals("QName", BaseTypes.EMPTY_QNAME, emptyType.getQName());
+        assertEquals("Path", Collections.singletonList(BaseTypes.EMPTY_QNAME),
+                emptyType.getPath().getPathFromRoot());
+        assertEquals("BaseType", null, emptyType.getBaseType());
+        assertEquals("DefaultValue", Optional.empty(), emptyType.getDefaultValue());
+        assertEquals("Status", Status.CURRENT, emptyType.getStatus());
+        assertFalse(emptyType.getReference().isPresent());
+        assertEquals("Units", Optional.empty(), emptyType.getUnits());
+        assertFalse(emptyType.getDescription().isPresent());
+        assertEquals("UnknownSchemaNodes", Collections.emptyList(), emptyType.getUnknownSchemaNodes());
+        assertTrue("toString", emptyType.toString().contains("empty"));
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Int8Test.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Int8Test.java
new file mode 100644 (file)
index 0000000..c2990c1
--- /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.model.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+public class Int8Test {
+
+    @Test
+    public void testInt8() {
+        Int8TypeDefinition int8 = BaseTypes.int8Type();
+        Int8TypeDefinition int8Second = BaseTypes.int8Type();
+        assertSame("The method 'getInstance()' has to return the same instance", int8, int8Second);
+        assertNull("The method 'getBaseType()' is returning incorrect value", int8.getBaseType());
+        assertEquals("The method 'getDefaultType()' is returning incorrect value", Optional.empty(),
+            int8.getDefaultValue());
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/LeafrefTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/LeafrefTest.java
new file mode 100644 (file)
index 0000000..e53cd44
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.model.util.type.LeafrefTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.RequireInstanceRestrictedTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+
+public class LeafrefTest {
+
+    @Test
+    public void testMethodsOfLeafrefTest() {
+        final SchemaPath schemaPath = SchemaPath.create(false, QName.create("test", "Cont1"),
+            QName.create("test", "List1"));
+        final PathExpression revision = new PathExpressionImpl("/test:Cont1/test:List1", false);
+        final PathExpression revision2 = new PathExpressionImpl("/test:Cont1/test:List2", false);
+
+        final LeafrefTypeDefinition leafref = BaseTypes.leafrefTypeBuilder(schemaPath).setPathStatement(revision)
+            .build();
+        final LeafrefTypeDefinition leafref2 = BaseTypes.leafrefTypeBuilder(schemaPath).setPathStatement(revision2)
+            .build();
+        final LeafrefTypeDefinition leafref3 = BaseTypes.leafrefTypeBuilder(schemaPath).setPathStatement(revision)
+            .build();
+        final LeafrefTypeDefinition leafref4 = leafref;
+
+        assertNotNull("Object 'leafref' shouldn't be null.", leafref);
+        assertNull("Base type of 'leafref' should be null.", leafref.getBaseType());
+        assertEquals(Optional.empty(), leafref.getUnits());
+        assertEquals(Optional.empty(), leafref.getDefaultValue());
+        assertEquals(QName.create("test", "List1"), leafref.getQName());
+        assertEquals("SchemaPath of 'leafref' is '/Cont1/List1'.", schemaPath, leafref.getPath());
+        assertFalse(leafref.getDescription().isPresent());
+        assertFalse(leafref.getReference().isPresent());
+        assertEquals("Status of 'leafref' is current.", Status.CURRENT, leafref.getStatus());
+        assertTrue("Object 'leafref' shouldn't have any unknown schema nodes.",
+                leafref.getUnknownSchemaNodes().isEmpty());
+        assertEquals("Revision aware XPath of 'leafref' should be '/test:Cont1/test:List1'.", revision,
+                leafref.getPathStatement());
+        assertNotNull("String representation of 'leafref' shouldn't be null.", leafref.toString());
+        assertNotEquals("Hash codes of two different object of type Leafref shouldn't be equal.", leafref.hashCode(),
+                leafref2.hashCode());
+        assertTrue("Objects of type Leafref should be equal.", leafref.equals(leafref3));
+        assertTrue("Objects of type Leafref should be equal.", leafref.equals(leafref4));
+        assertFalse("Objects of type Leafref shouldn't be equal.", leafref.equals(leafref2));
+        assertFalse("Objects shouldn't be equal.", leafref.equals(null));
+        assertFalse("Objects shouldn't be equal.", leafref.equals("test"));
+    }
+
+    @Test
+    public void testRequireInstanceSubstatement() {
+        final SchemaPath schemaPath = SchemaPath.create(true, QName.create("test", "my-cont"),
+            QName.create("test", "my-leafref"));
+        final PathExpression path = new PathExpressionImpl("../my-leaf", false);
+        final LeafrefTypeBuilder leafrefTypeBuilder = BaseTypes.leafrefTypeBuilder(schemaPath).setPathStatement(path);
+
+        assertTrue(leafrefTypeBuilder.build().requireInstance());
+
+        leafrefTypeBuilder.setRequireInstance(false);
+        final LeafrefTypeDefinition falseLeafref = leafrefTypeBuilder.build();
+        assertFalse(falseLeafref.requireInstance());
+
+        leafrefTypeBuilder.setRequireInstance(true);
+        final LeafrefTypeDefinition trueLeafref = leafrefTypeBuilder.build();
+        assertTrue(trueLeafref.requireInstance());
+
+        final RequireInstanceRestrictedTypeBuilder<LeafrefTypeDefinition> falseBuilder =
+                RestrictedTypes.newLeafrefBuilder(falseLeafref, schemaPath);
+        assertFalse(falseBuilder.build().requireInstance());
+
+        final RequireInstanceRestrictedTypeBuilder<LeafrefTypeDefinition> trueBuilder =
+                RestrictedTypes.newLeafrefBuilder(trueLeafref, schemaPath);
+        assertTrue(trueBuilder.build().requireInstance());
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/ModuleDependencySortTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/ModuleDependencySortTest.java
new file mode 100644 (file)
index 0000000..b8b4f60
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.util;
+
+import static com.google.common.collect.ImmutableList.of;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+
+import com.google.common.collect.ImmutableSet;
+import java.net.URI;
+import java.util.List;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ModuleDependencySortTest {
+    private static final QNameModule FOO_MODULE = QNameModule.create(URI.create("foo"));
+    private static final QNameModule BAR_MODULE = QNameModule.create(URI.create("bar"));
+
+    @Mock
+    private Module fooNoRev;
+
+    @Mock
+    private ModuleImport fooNoRevImport;
+
+    @Mock
+    private Module bar;
+
+    @Mock
+    private Module barSubmodule;
+
+    @Before
+    public void before() {
+        doReturn("foo").when(fooNoRev).getName();
+        doReturn(FOO_MODULE.getNamespace()).when(fooNoRev).getNamespace();
+        doReturn(FOO_MODULE.getRevision()).when(fooNoRev).getRevision();
+        doReturn(ImmutableSet.of()).when(fooNoRev).getImports();
+        doReturn(ImmutableSet.of()).when(fooNoRev).getSubmodules();
+
+        doReturn("foo").when(fooNoRevImport).getModuleName();
+        doReturn(Optional.empty()).when(fooNoRevImport).getRevision();
+
+        doReturn("bar").when(bar).getName();
+        doReturn(BAR_MODULE.getNamespace()).when(bar).getNamespace();
+        doReturn(BAR_MODULE.getRevision()).when(bar).getRevision();
+        doReturn(YangVersion.VERSION_1).when(bar).getYangVersion();
+        doReturn(ImmutableSet.of()).when(bar).getImports();
+        doReturn(ImmutableSet.of(barSubmodule)).when(bar).getSubmodules();
+
+        doReturn(ImmutableSet.of(fooNoRevImport)).when(barSubmodule).getImports();
+    }
+
+    @Test
+    public void testSimpleModules() {
+        assertSortedTo(of(fooNoRev), fooNoRev);
+    }
+
+    @Test
+    public void testSubmodules() {
+        assertSortedTo(of(fooNoRev, bar), bar, fooNoRev);
+        assertSortedTo(of(fooNoRev, bar), fooNoRev, bar);
+    }
+
+    private static void assertSortedTo(final List<Module> expected, final Module... modules) {
+        assertEquals(expected, ModuleDependencySort.sort(modules));
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/PatternConstraintImplTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/PatternConstraintImplTest.java
new file mode 100644 (file)
index 0000000..a98fc2a
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Optional;
+import org.junit.Test;
+
+public class PatternConstraintImplTest {
+
+    @Test
+    public void testMethodsOfPatternConstraintImpl() {
+        final String regexExp = "\\D";
+        final Optional<String> description = Optional.of("test description");
+        final Optional<String> reference = Optional.of("RFC 6020");
+        final PatternConstraintImpl patternConstraint = new PatternConstraintImpl(regexExp, description, reference);
+        final String regexExp2 = "\\s";
+        final Optional<String> description2 = Optional.of("test description2");
+        final Optional<String> reference2 = Optional.of("RFC 6020 http://tools.ietf.org/html/rfc6020#page-23");
+        final PatternConstraintImpl patternConstraint2 = new PatternConstraintImpl(regexExp2, description2, reference2);
+        final PatternConstraintImpl patternConstraint3 = patternConstraint;
+        final PatternConstraintImpl patternConstraint4 = new PatternConstraintImpl(regexExp, description2, reference);
+        final PatternConstraintImpl patternConstraint5 = new PatternConstraintImpl(regexExp2, description2, reference2);
+
+        assertNotNull("Object of PatternConstraintImpl shouldn't be null.", patternConstraint);
+        assertEquals("Description should be 'test description'.", Optional.of("test description"),
+                patternConstraint.getDescription());
+        assertEquals(Optional.of("invalid-regular-expression"), patternConstraint.getErrorAppTag());
+        assertFalse(patternConstraint.getErrorMessage().isPresent());
+        assertEquals(Optional.of("RFC 6020"), patternConstraint.getReference());
+        assertEquals("Regular expression should be equls '\\D'.", "\\D", patternConstraint.getJavaPatternString());
+        assertNotEquals("Hash codes shouldn't be equals.", patternConstraint.hashCode(), patternConstraint2.hashCode());
+        assertFalse("String representation shouldn't be empty.", patternConstraint.toString().isEmpty());
+
+        assertTrue("Objects should be equals.", patternConstraint.equals(patternConstraint3));
+        assertFalse("Objects shouldn't be equals.", patternConstraint.equals(patternConstraint2));
+        assertFalse("Objects shouldn't be equals.", patternConstraint4.equals(patternConstraint));
+        assertFalse("Objects shouldn't be equals.", patternConstraint5.equals(patternConstraint));
+        assertFalse("Objects shouldn't be equals.", patternConstraint.equals("test"));
+        assertFalse("Objects shouldn't be equals.", patternConstraint.equals(null));
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextProxyTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextProxyTest.java
new file mode 100644 (file)
index 0000000..9450170
--- /dev/null
@@ -0,0 +1,658 @@
+/*
+ * Copyright (c) 2015 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.ImmutableSet;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.FilteringSchemaContextProxy.ModuleId;
+
+public class SchemaContextProxyTest {
+
+    private static final URI NAMESPACE = URI.create("urn:opendaylight:params:xml:ns:yang:controller:config");
+    private static final Revision REVISION = Revision.of("2015-01-01");
+    private static final Revision REVISION2 = Revision.of("2015-01-15");
+
+    private static final String CONFIG_NAME = "config";
+    private static final String ROOT_NAME = "root";
+    private static final String MODULE2_NAME = "module2";
+    private static final String MODULE3_NAME = "module3";
+    private static final String MODULE4_NAME = "module4";
+    private static final String MODULE41_NAME = "module41";
+    private static final String MODULE5_NAME = "module5";
+
+    private static SchemaContext mockSchema(final Module... modules) {
+        final List<Module> sortedModules = Arrays.asList(modules);
+        sortedModules.sort(AbstractSchemaContext.NAME_REVISION_COMPARATOR);
+        SchemaContext mock = mock(SchemaContext.class);
+        doReturn(ImmutableSet.copyOf(sortedModules)).when(mock).getModules();
+        return mock;
+    }
+
+    /*
+     * CFG(R)
+     *  | \
+     *  |  \
+     * M2 <- M3
+     */
+    @Test
+    public void testBasic() {
+        Module moduleConfig = mockModule(CONFIG_NAME);
+        Module module2 = mockModule(MODULE2_NAME);
+        Module module3 = mockModule(MODULE3_NAME);
+
+        mockModuleImport(module2, moduleConfig);
+        mockModuleImport(module3, module2, moduleConfig);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, module2, module3);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext, null,
+                moduleConfig);
+        assertProxyContext(filteringSchemaContextProxy, moduleConfig, module2, module3);
+    }
+
+    /*
+     * No root or additional modules
+     *  | \
+     *  |  \
+     * M2 <- M3
+     */
+    @Test
+    public void testNull() {
+        Module moduleConfig = mockModule(CONFIG_NAME);
+        Module module2 = mockModule(MODULE2_NAME);
+        Module module3 = mockModule(MODULE3_NAME);
+
+        mockModuleImport(module2, moduleConfig);
+        mockModuleImport(module3, module2, moduleConfig);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, module2, module3);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext, null, null);
+        assertProxyContext(filteringSchemaContextProxy, null);
+    }
+
+    /*
+     *  Config
+     *  | \ (NR)
+     *  |  \
+     * M2 <- M3
+     */
+    @Test
+    public void testConfigDifferentRevisions() {
+        Module moduleConfigNullRevision = mockModule(CONFIG_NAME, null);
+        Module moduleConfig = mockModule(CONFIG_NAME, REVISION);
+        Module moduleConfig2 = mockModule(CONFIG_NAME, REVISION2);
+        Module module2 = mockModule(MODULE2_NAME);
+        Module module3 = mockModule(MODULE3_NAME);
+
+        mockModuleImport(module2, moduleConfig);
+        mockModuleImport(module3, module2, moduleConfigNullRevision);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, moduleConfig2, module2, module3);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext, null,
+                moduleConfig);
+        assertProxyContext(filteringSchemaContextProxy, moduleConfig, moduleConfig2, module2, module3);
+    }
+
+    /*
+     *     CFG(R)
+     *    |      \
+     *   |         \
+     * M2<-(NullRev)M3
+     */
+    @Test
+    public void testBasicNullRevision() throws Exception {
+        final Module moduleConfig = mockModule(CONFIG_NAME, Revision.of("2013-04-05"));
+        final Module module2 = mockModule(MODULE2_NAME, Revision.of("2014-06-17"));
+        final Module module20 = mockModule(MODULE2_NAME, null);
+        final Module module3 = mockModule(MODULE3_NAME, Revision.of("2014-06-12"));
+        final Module module30 = mockModule(MODULE3_NAME, null);
+
+        mockModuleImport(module20, moduleConfig);
+        mockModuleImport(module2, moduleConfig);
+        mockModuleImport(module3, module20, moduleConfig);
+        mockModuleImport(module30, module20, moduleConfig);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, module2, module3);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext, null,
+                moduleConfig);
+
+        assertProxyContext(filteringSchemaContextProxy, moduleConfig, module2, module3);
+    }
+
+    /*
+     * CFG(R)   ROOT(R)
+     *  |         \
+     *  |          \
+     * M2          M3
+     */
+    @Test
+    public void testBasicMoreRootModules() {
+        final Module moduleConfig = mockModule(CONFIG_NAME);
+        final Module moduleRoot = mockModule(ROOT_NAME);
+        final Module module2 = mockModule(MODULE2_NAME);
+        final Module module3 = mockModule(MODULE3_NAME);
+
+        mockModuleImport(module2, moduleConfig);
+        mockModuleImport(module3, moduleRoot);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, moduleRoot, module2, module3);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext, null, moduleRoot,
+                moduleConfig);
+        assertProxyContext(filteringSchemaContextProxy, moduleRoot, module3, moduleConfig, module2);
+    }
+
+    /*
+     * CFG(R)
+     *  |
+     *  |
+     * M2 <- M3
+     */
+    @Test
+    public void testChainNotDepend() {
+        Module moduleConfig = mockModule(CONFIG_NAME);
+        Module module2 = mockModule(MODULE2_NAME);
+        Module module3 = mockModule(MODULE3_NAME);
+
+        mockModuleImport(module2, moduleConfig);
+        mockModuleImport(module3, module2);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, module2, module3);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext, null,
+                moduleConfig);
+        assertProxyContext(filteringSchemaContextProxy, moduleConfig, module2);
+    }
+
+    /*
+     * CFG(R)
+     *  |
+     *  |
+     * M2 -> M3 -> M4 -> M5
+     */
+    @Test
+    public void testChainDependMulti() {
+        Module moduleConfig = mockModule(CONFIG_NAME);
+        Module module2 = mockModule(MODULE2_NAME);
+        Module module3 = mockModule(MODULE3_NAME);
+        Module module4 = mockModule(MODULE4_NAME);
+        Module module5 = mockModule(MODULE5_NAME);
+
+        mockModuleImport(module2, moduleConfig, module3);
+        mockModuleImport(module3, module4);
+        mockModuleImport(module4, module5);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, module2, module3, module4, module5);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext, null,
+                moduleConfig);
+        assertProxyContext(filteringSchemaContextProxy, moduleConfig, module2, module3, module4, module5);
+    }
+
+    /*
+     * CFG(R)
+     *  |
+     *  |
+     * M2 -> M3 <- M4
+     */
+    @Test
+    public void testChainNotDependMulti() {
+        Module moduleConfig = mockModule(CONFIG_NAME);
+        Module module2 = mockModule(MODULE2_NAME);
+        Module module3 = mockModule(MODULE3_NAME);
+        Module module4 = mockModule(MODULE4_NAME);
+
+        mockModuleImport(module2, moduleConfig, module3);
+        mockModuleImport(module4, module3);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, module2, module3, module4);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext, null,
+                moduleConfig);
+        assertProxyContext(filteringSchemaContextProxy, moduleConfig, module2, module3);
+    }
+
+    /*
+     *  CFG(R)
+     *  | \ \ \
+     *  |  \ \ \
+     * M2 M3 M4 M5
+     */
+    @Test
+    public void testChainNotMulti() {
+        final Module moduleConfig = mockModule(CONFIG_NAME);
+        final Module module2 = mockModule(MODULE2_NAME);
+        final Module module3 = mockModule(MODULE3_NAME);
+        final Module module4 = mockModule(MODULE4_NAME);
+        final Module module5 = mockModule(MODULE5_NAME);
+
+        mockModuleImport(module2, moduleConfig);
+        mockModuleImport(module3, moduleConfig);
+        mockModuleImport(module4, moduleConfig);
+        mockModuleImport(module5, moduleConfig);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, module2, module3, module4, module5);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext, null,
+                moduleConfig);
+        assertProxyContext(filteringSchemaContextProxy, moduleConfig, module2, module3, module4, module5);
+    }
+
+    /*
+     * CFG(R)
+     *  | \
+     *  |  \
+     * M2 <- M3 M4=M3(Different revision)
+     */
+    @Test
+    public void testBasicRevisionChange() throws Exception {
+        Module moduleConfig = mockModule(CONFIG_NAME);
+        Module module2 = mockModule(MODULE2_NAME);
+        Module module3 = mockModule(MODULE3_NAME);
+        Module module4 = mockModule(MODULE3_NAME, Revision.of("2015-10-10"));
+
+        mockModuleImport(module2, moduleConfig);
+        mockModuleImport(module3, module2, moduleConfig);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, module2, module3, module4);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext, null,
+                moduleConfig);
+        assertProxyContext(filteringSchemaContextProxy, moduleConfig, module2, module3);
+    }
+
+    /*
+     * CFG(R)
+     * |
+     * M2 -(no revision)-> M3(R2) ... M3(R1)
+     */
+    @Test
+    public void testImportNoRevision() {
+        Module moduleConfig = mockModule(CONFIG_NAME, REVISION);
+        Module module2 = mockModule(MODULE2_NAME, REVISION);
+
+        Module module3  = mockModule(MODULE3_NAME, null);
+        Module module30 = mockModule(MODULE3_NAME, REVISION);
+        Module module31 = mockModule(MODULE3_NAME, REVISION2);
+        mockModuleImport(module2, moduleConfig, module3);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, module2, module30, module31);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext, null,
+                moduleConfig);
+
+        assertProxyContext(filteringSchemaContextProxy, moduleConfig, module2, module31);
+    }
+
+    /*
+     * CFG(R)
+     * |   \
+     * |    \
+     * |    M2 -> M3
+     * |
+     * M41(S) => M4
+     */
+    @Test
+    public void testBasicSubmodule() {
+        Module moduleConfig = mockModule(CONFIG_NAME);
+        Module module2 = mockModule(MODULE2_NAME);
+        Module module3 = mockModule(MODULE3_NAME);
+        Module module4 = mockModule(MODULE4_NAME);
+        Module module41 = mockModule(MODULE41_NAME);
+
+        mockSubmodules(module4, module41);
+        mockModuleImport(module2, moduleConfig, module3);
+        mockModuleImport(module41, moduleConfig);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, module2, module3, module4);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext, null,
+                moduleConfig);
+        assertProxyContext(filteringSchemaContextProxy, moduleConfig, module2, module3, module4);
+    }
+
+    /*
+     * M2 -> M3 -> M4 -> M5
+     */
+    @Test
+    public void testChainAdditionalModules() {
+        Module module2 = mockModule(MODULE2_NAME);
+        Module module3 = mockModule(MODULE3_NAME);
+        Module module4 = mockModule(MODULE4_NAME);
+        Module module5 = mockModule(MODULE5_NAME);
+
+        mockModuleImport(module2, module3);
+        mockModuleImport(module3, module4);
+        mockModuleImport(module4, module5);
+
+        SchemaContext schemaContext = mockSchema(module2, module3, module4, module5);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext,
+                Collections.singleton(module2), null);
+        assertProxyContext(filteringSchemaContextProxy, module2, module3, module4, module5);
+    }
+
+    /*
+     *
+     * CFG(R)
+     *  |
+     *  |       M5
+     * M2
+     *
+     * M3 -> M4
+     */
+    @Test
+    public void testChainAdditionalModulesConfig() {
+        Module moduleConfig = mockModule(CONFIG_NAME);
+        Module module2 = mockModule(MODULE2_NAME);
+
+        Module module3 = mockModule(MODULE3_NAME);
+        Module module4 = mockModule(MODULE4_NAME);
+        Module module5 = mockModule(MODULE5_NAME);
+
+        mockModuleImport(module2, moduleConfig);
+        mockModuleImport(module3, module4);
+
+        SchemaContext schemaContext = mockSchema(moduleConfig, module2, module3, module4, module5);
+
+        FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext,
+            Collections.singleton(module3), moduleConfig);
+        assertProxyContext(filteringSchemaContextProxy, moduleConfig, module2, module3, module4);
+    }
+
+    @Test
+    public void testGetDataDefinitions() {
+        final Module moduleConfig = mockModule(CONFIG_NAME);
+        final SchemaContext schemaContext = mockSchema(moduleConfig);
+        final FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext,
+                new HashSet<>(), moduleConfig);
+
+        final ContainerSchemaNode mockedContainer = mock(ContainerSchemaNode.class);
+        final Set<DataSchemaNode> childNodes = Collections.singleton(mockedContainer);
+        doReturn(childNodes).when(moduleConfig).getChildNodes();
+
+        final Set<DataSchemaNode> dataDefinitions = filteringSchemaContextProxy.getDataDefinitions();
+        assertTrue(dataDefinitions.contains(mockedContainer));
+    }
+
+    @Test
+    public void testGetNotifications() {
+        final Module moduleConfig = mockModule(CONFIG_NAME);
+        final SchemaContext schemaContext = mockSchema(moduleConfig);
+        final FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext,
+                new HashSet<>(), moduleConfig);
+
+        final NotificationDefinition mockedNotification = mock(NotificationDefinition.class);
+        final Set<NotificationDefinition> notifications = Collections.singleton(mockedNotification);
+        doReturn(notifications).when(moduleConfig).getNotifications();
+
+        final Set<NotificationDefinition> schemaContextProxyNotifications =
+            filteringSchemaContextProxy.getNotifications();
+        assertTrue(schemaContextProxyNotifications.contains(mockedNotification));
+    }
+
+    @Test
+    public void testGetOperations() {
+        final Module moduleConfig = mockModule(CONFIG_NAME);
+        final SchemaContext schemaContext = mockSchema(moduleConfig);
+        final FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext,
+                new HashSet<>(), moduleConfig);
+
+        final RpcDefinition mockedRpc = mock(RpcDefinition.class);
+        final Set<RpcDefinition> rpcs = Collections.singleton(mockedRpc);
+        doReturn(rpcs).when(moduleConfig).getRpcs();
+
+        final Set<RpcDefinition> operations = filteringSchemaContextProxy.getOperations();
+        assertTrue(operations.contains(mockedRpc));
+    }
+
+    @Test
+    public void testGetExtensions() {
+        final Module moduleConfig = mockModule(CONFIG_NAME);
+        final SchemaContext schemaContext = mockSchema(moduleConfig);
+        final FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext,
+                new HashSet<>(), moduleConfig);
+
+        final ExtensionDefinition mockedExtension = mock(ExtensionDefinition.class);
+        final List<ExtensionDefinition> extensions = Collections.singletonList(mockedExtension);
+        doReturn(extensions).when(moduleConfig).getExtensionSchemaNodes();
+
+        final Set<ExtensionDefinition> schemaContextProxyExtensions = filteringSchemaContextProxy.getExtensions();
+        assertTrue(schemaContextProxyExtensions.contains(mockedExtension));
+    }
+
+    @Test
+    public void testGetUnknownSchemaNodes() {
+        final Module moduleConfig = mockModule(CONFIG_NAME);
+        final SchemaContext schemaContext = mockSchema(moduleConfig);
+        final FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext,
+                new HashSet<>(), moduleConfig);
+
+        final UnknownSchemaNode mockedUnknownSchemaNode = mock(UnknownSchemaNode.class);
+        final List<UnknownSchemaNode> unknownSchemaNodes = Collections.singletonList(mockedUnknownSchemaNode);
+        doReturn(unknownSchemaNodes).when(moduleConfig).getUnknownSchemaNodes();
+
+        final List<UnknownSchemaNode> schemaContextProxyUnknownSchemaNodes =
+                filteringSchemaContextProxy.getUnknownSchemaNodes();
+        assertTrue(schemaContextProxyUnknownSchemaNodes.contains(mockedUnknownSchemaNode));
+    }
+
+    @Test
+    public void testGetTypeDefinitions() {
+        final Module moduleConfig = mockModule(CONFIG_NAME);
+        final SchemaContext schemaContext = mockSchema(moduleConfig);
+        final FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext,
+                new HashSet<>(), moduleConfig);
+
+        final TypeDefinition<?> mockedTypeDefinition = mock(TypeDefinition.class);
+        final Set<TypeDefinition<?>> typeDefinitions = Collections.singleton(mockedTypeDefinition);
+        doReturn(typeDefinitions).when(moduleConfig).getTypeDefinitions();
+
+        final Set<TypeDefinition<?>> schemaContextProxyTypeDefinitions = filteringSchemaContextProxy
+            .getTypeDefinitions();
+        assertTrue(schemaContextProxyTypeDefinitions.contains(mockedTypeDefinition));
+    }
+
+    @Test
+    public void testGetChildNodes() {
+        final Module moduleConfig = mockModule(CONFIG_NAME);
+        final SchemaContext schemaContext = mockSchema(moduleConfig);
+        final FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext,
+                new HashSet<>(), moduleConfig);
+
+        final ContainerSchemaNode mockedContainer = mock(ContainerSchemaNode.class);
+        final Set<DataSchemaNode> childNodes = Collections.singleton(mockedContainer);
+        doReturn(childNodes).when(moduleConfig).getChildNodes();
+
+        final Set<DataSchemaNode> schemaContextProxyChildNodes = filteringSchemaContextProxy.getChildNodes();
+        assertTrue(schemaContextProxyChildNodes.contains(mockedContainer));
+    }
+
+    @Test
+    public void testGetGroupings() {
+        final Module moduleConfig = mockModule(CONFIG_NAME);
+        final SchemaContext schemaContext = mockSchema(moduleConfig);
+        final FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext,
+                new HashSet<>(), moduleConfig);
+
+        final GroupingDefinition mockedGrouping = mock(GroupingDefinition.class);
+        final Set<GroupingDefinition> groupings = Collections.singleton(mockedGrouping);
+        doReturn(groupings).when(moduleConfig).getGroupings();
+
+        final Set<GroupingDefinition> schemaContextProxyGroupings = filteringSchemaContextProxy.getGroupings();
+        assertTrue(schemaContextProxyGroupings.contains(mockedGrouping));
+    }
+
+    @Test
+    public void testGetDataChildByName() {
+        final Module moduleConfig = mockModule(CONFIG_NAME);
+        final SchemaContext schemaContext = mockSchema(moduleConfig);
+        final FilteringSchemaContextProxy filteringSchemaContextProxy = createProxySchemaCtx(schemaContext,
+                new HashSet<>(), moduleConfig);
+
+        final QName qname = QName.create("config-namespace", "2016-08-11", "cont");
+        final ContainerSchemaNode mockedContainer = mock(ContainerSchemaNode.class);
+        doReturn(Optional.of(mockedContainer)).when(moduleConfig).findDataChildByName(any(QName.class));
+
+        final DataSchemaNode dataSchemaNode = filteringSchemaContextProxy.getDataChildByName(qname);
+        assertTrue(dataSchemaNode instanceof ContainerSchemaNode);
+    }
+
+    private static void assertProxyContext(final FilteringSchemaContextProxy filteringSchemaContextProxy,
+            final Module... expected) {
+
+        final Set<Module> modSet = expected != null ? ImmutableSet.copyOf(expected) : new HashSet<>();
+        Set<Module> modSetFiltering = filteringSchemaContextProxy.getModules();
+
+        assertEquals(modSet, modSetFiltering);
+
+        //asserting collections
+        if (expected != null) {
+            for (final Module module : expected) {
+                assertEquals(module, filteringSchemaContextProxy.findModule(module.getName(), module.getRevision())
+                    .get());
+
+                Set<Module> mod = filteringSchemaContextProxy.findModules(module.getNamespace());
+                assertTrue(mod.contains(module));
+                assertEquals(module, filteringSchemaContextProxy.findModule(module.getNamespace(),
+                    module.getRevision().orElse(null)).get());
+            }
+        }
+    }
+
+    private static FilteringSchemaContextProxy createProxySchemaCtx(final SchemaContext schemaContext,
+            final Set<Module> additionalModules, final Module... modules) {
+        Set<Module> modulesSet = new HashSet<>();
+        if (modules != null) {
+            modulesSet = ImmutableSet.copyOf(modules);
+        }
+
+        return new FilteringSchemaContextProxy(schemaContext, createModuleIds(modulesSet),
+                createModuleIds(additionalModules));
+    }
+
+    private static Set<ModuleId> createModuleIds(final Set<Module> modules) {
+        Set<ModuleId> moduleIds = new HashSet<>();
+        if (modules != null) {
+            for (Module module : modules) {
+                moduleIds.add(new ModuleId(module.getName(), module.getRevision()));
+            }
+        }
+
+        return moduleIds;
+    }
+
+    private static void mockSubmodules(final Module mainModule, final Module... submodules) {
+        Set<Module> submodulesSet = new HashSet<>();
+        submodulesSet.addAll(Arrays.asList(submodules));
+
+        doReturn(submodulesSet).when(mainModule).getSubmodules();
+    }
+
+    private static void mockModuleImport(final Module importer, final Module... imports) {
+        Set<ModuleImport> mockedImports = new HashSet<>();
+        for (final Module module : imports) {
+            mockedImports.add(new ModuleImport() {
+                @Override
+                public String getModuleName() {
+                    return module.getName();
+                }
+
+                @Override
+                public Optional<Revision> getRevision() {
+                    return module.getRevision();
+                }
+
+                @Override
+                public String getPrefix() {
+                    return module.getName();
+                }
+
+                @Override
+                public Optional<SemVer> getSemanticVersion() {
+                    return module.getSemanticVersion();
+                }
+
+                @Override
+                public Optional<String> getDescription() {
+                    return module.getDescription();
+                }
+
+                @Override
+                public Optional<String> getReference() {
+                    return module.getReference();
+                }
+
+                @Override
+                public String toString() {
+                    return String.format("Module: %s, revision:%s", module.getName(), module.getRevision());
+                }
+            });
+        }
+        doReturn(mockedImports).when(importer).getImports();
+    }
+
+    //mock module with revision
+    private static Module mockModule(final String name, final Revision rev) {
+
+        final Module mod = mockModule(name);
+
+        doReturn(QNameModule.create(mod.getNamespace(), rev)).when(mod).getQNameModule();
+        doReturn(Optional.ofNullable(rev)).when(mod).getRevision();
+        doReturn(mod.getQNameModule().toString()).when(mod).toString();
+
+        return mod;
+    }
+
+    //mock module with default revision
+    private static Module mockModule(final String name) {
+
+        Module mockedModule = mock(Module.class);
+        doReturn(name).when(mockedModule).getName();
+        doReturn(Optional.of(REVISION)).when(mockedModule).getRevision();
+        final URI newNamespace = URI.create(NAMESPACE.toString() + ":" + name);
+        doReturn(newNamespace).when(mockedModule).getNamespace();
+        doReturn(QNameModule.create(newNamespace, REVISION)).when(mockedModule).getQNameModule();
+        doReturn(new HashSet<>()).when(mockedModule).getSubmodules();
+        doReturn(mockedModule.getQNameModule().toString()).when(mockedModule).toString();
+        mockModuleImport(mockedModule);
+
+        return mockedModule;
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtilTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtilTest.java
new file mode 100644 (file)
index 0000000..c9c60f6
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class SchemaContextUtilTest {
+    private static final Splitter SPACE_SPLITTER = Splitter.on(' ');
+    private static final URI NAMESPACE = URI.create("abc");
+
+    // The idea is:
+    // container baz {
+    //     leaf xyzzy {
+    //         type leafref;
+    //     }
+    //     leaf foo {
+    //         type string;
+    //     }
+    //     leaf bar {
+    //         type string;
+    //     }
+    // }
+    private static final QName FOO = QName.create(NAMESPACE, "foo");
+    private static final QName BAR = QName.create(NAMESPACE, "bar");
+    private static final QName BAZ = QName.create(NAMESPACE, "baz");
+    private static final QName XYZZY = QName.create(NAMESPACE, "xyzzy");
+
+    @Mock
+    private SchemaContext mockSchemaContext;
+    @Mock
+    private Module mockModule;
+
+    @Mock
+    private SchemaNode schemaNode;
+
+    @Before
+    public void before() {
+        doReturn(Optional.empty()).when(mockSchemaContext).findModule(any(QNameModule.class));
+        doReturn(Optional.empty()).when(mockSchemaContext).findDataTreeChild(any(Iterable.class));
+
+        doReturn("test").when(mockModule).getName();
+        doReturn("test").when(mockModule).getPrefix();
+        doReturn(NAMESPACE).when(mockModule).getNamespace();
+        doReturn(QNameModule.create(NAMESPACE)).when(mockModule).getQNameModule();
+        doReturn(Optional.empty()).when(mockModule).getRevision();
+
+        doReturn(SchemaPath.create(true, BAZ, XYZZY)).when(schemaNode).getPath();
+    }
+
+    @Test
+    public void testFindDummyData() {
+
+        QName qname = QName.create("namespace", "localname");
+        SchemaPath schemaPath = SchemaPath.create(Collections.singletonList(qname), true);
+        assertNull("Should be null. Module TestQName not found",
+                SchemaContextUtil.findDataSchemaNode(mockSchemaContext, schemaPath));
+
+        PathExpression xpath = new PathExpressionImpl("/test:bookstore/test:book/test:title", true);
+        assertNull("Should be null. Module bookstore not found",
+                SchemaContextUtil.findDataSchemaNode(mockSchemaContext, mockModule, xpath));
+
+        SchemaNode int32node = BaseTypes.int32Type();
+        PathExpression xpathRelative = new PathExpressionImpl("../prefix", false);
+        assertNull("Should be null, Module prefix not found",
+                SchemaContextUtil.findDataSchemaNodeForRelativeXPath(
+                        mockSchemaContext, mockModule, int32node, xpathRelative));
+
+        assertNull("Should be null. Module TestQName not found",
+                SchemaContextUtil.findNodeInSchemaContext(mockSchemaContext, Collections.singleton(qname)));
+
+        assertNull("Should be null.", SchemaContextUtil.findParentModule(mockSchemaContext, int32node));
+    }
+
+    @Test
+    public void testDeref() {
+        PathExpression xpath = new PathExpressionImpl("deref(../foo)/../bar", false);
+        assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(mockSchemaContext, mockModule, schemaNode,
+            xpath));
+    }
+
+    @Test
+    public void testNormalizeXPath() {
+        assertNormalizedPath(0, ImmutableList.of(""), "");
+        assertNormalizedPath(0, ImmutableList.of("a"), "a");
+        assertNormalizedPath(0, ImmutableList.of("a", "b"), "a b");
+        assertNormalizedPath(1, ImmutableList.of("..", "b"), ".. b");
+        assertNormalizedPath(0, ImmutableList.of(), "a ..");
+        assertNormalizedPath(0, ImmutableList.of("b"), "a .. b");
+        assertNormalizedPath(2, ImmutableList.of("..", "..", "a", "c"), ".. .. a b .. c");
+        assertNormalizedPath(3, ImmutableList.of("..", "..", "..", "b"), ".. .. a .. .. b");
+    }
+
+    private static void assertNormalizedPath(final int expectedLead, final List<String> expectedList,
+            final String input) {
+        final List<String> list = new ArrayList<>(SPACE_SPLITTER.splitToList(input));
+        assertEquals(expectedLead, SchemaContextUtil.normalizeXPath(list));
+        assertEquals(expectedList, list);
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaNodeUtilsTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaNodeUtilsTest.java
new file mode 100644 (file)
index 0000000..ff23c4b
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.util;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.doReturn;
+
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+public class SchemaNodeUtilsTest {
+
+    @Mock
+    private DerivableSchemaNode derivableNode;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testHandleNullGetOriginalIfPossible() {
+        Optional<SchemaNode> originalIfPossible = SchemaNodeUtils
+                .getOriginalIfPossible(null);
+        assertNotNull(originalIfPossible);
+        assertThat(originalIfPossible, instanceOf(Optional.class));
+    }
+
+    @Test
+    public void testHandleNodeGetOriginalIfPossible() {
+        Optional<DerivableSchemaNode> of = Optional.of(derivableNode);
+        doReturn(of).when(derivableNode).getOriginal();
+        Optional<SchemaNode> originalIfPossible = SchemaNodeUtils
+                .getOriginalIfPossible(derivableNode);
+        assertNotNull(originalIfPossible);
+        assertThat(originalIfPossible, instanceOf(Optional.class));
+    }
+
+    @Test
+    public void testHandleNullGetRootOriginalIfPossible() {
+        SchemaNode rootOriginalIfPossible = SchemaNodeUtils
+                .getRootOriginalIfPossible(null);
+        assertNull(rootOriginalIfPossible);
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SimpleSchemaContextTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SimpleSchemaContextTest.java
new file mode 100644 (file)
index 0000000..411ded5
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.util;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.net.URI;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+public class SimpleSchemaContextTest {
+    @Test
+    public void testGetModulesOrdering() {
+        final Module foo0 = mockModule("foo", Optional.empty());
+        final Module foo1 = mockModule("foo", Revision.ofNullable("2018-01-01"));
+        final Module foo2 = mockModule("foo", Revision.ofNullable("2018-01-16"));
+
+        final List<Module> expected = ImmutableList.of(foo2, foo1, foo0);
+        assertGetModules(expected, foo0, foo1, foo2);
+        assertGetModules(expected, foo0, foo2, foo1);
+        assertGetModules(expected, foo1, foo0, foo2);
+        assertGetModules(expected, foo1, foo2, foo0);
+        assertGetModules(expected, foo2, foo0, foo1);
+        assertGetModules(expected, foo2, foo1, foo0);
+
+        assertFindModules(expected, "foo", foo0, foo1, foo2);
+        assertFindModules(expected, "foo", foo0, foo2, foo1);
+        assertFindModules(expected, "foo", foo1, foo0, foo2);
+        assertFindModules(expected, "foo", foo1, foo2, foo0);
+        assertFindModules(expected, "foo", foo2, foo0, foo1);
+        assertFindModules(expected, "foo", foo2, foo1, foo0);
+
+        final URI uri = URI.create("foo");
+        assertFindModules(expected, uri, foo0, foo1, foo2);
+        assertFindModules(expected, uri, foo0, foo2, foo1);
+        assertFindModules(expected, uri, foo1, foo0, foo2);
+        assertFindModules(expected, uri, foo1, foo2, foo0);
+        assertFindModules(expected, uri, foo2, foo0, foo1);
+        assertFindModules(expected, uri, foo2, foo1, foo0);
+    }
+
+    private static void assertGetModules(final List<Module> expected, final Module... modules) {
+        final Set<Module> actual = SimpleSchemaContext.forModules(ImmutableSet.copyOf(modules)).getModules();
+        assertArrayEquals(expected.toArray(), actual.toArray());
+    }
+
+    private static void assertFindModules(final List<Module> expected, final String name, final Module... modules) {
+        final Set<Module> actual = SimpleSchemaContext.forModules(ImmutableSet.copyOf(modules)).findModules(name);
+        assertArrayEquals(expected.toArray(), actual.toArray());
+    }
+
+    private static void assertFindModules(final List<Module> expected, final URI uri, final Module... modules) {
+        final Set<Module> actual = SimpleSchemaContext.forModules(ImmutableSet.copyOf(modules)).findModules(uri);
+        assertArrayEquals(expected.toArray(), actual.toArray());
+    }
+
+    private static Module mockModule(final String name, final Optional<Revision> revision) {
+        final QNameModule mod = QNameModule.create(URI.create(name), revision);
+        final Module ret = mock(Module.class);
+        doReturn(name).when(ret).getName();
+        doReturn(mod.getNamespace()).when(ret).getNamespace();
+        doReturn(mod.getRevision()).when(ret).getRevision();
+        doReturn(mod).when(ret).getQNameModule();
+        doReturn(mod.toString()).when(ret).toString();
+        doReturn(ImmutableSet.of()).when(ret).getImports();
+        doReturn(ImmutableSet.of()).when(ret).getSubmodules();
+        doReturn(ImmutableList.of()).when(ret).getUnknownSchemaNodes();
+        return ret;
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/type/BitImplTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/type/BitImplTest.java
new file mode 100644 (file)
index 0000000..53e1a15
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.util.type;
+
+import static java.util.Collections.emptyList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+
+public class BitImplTest {
+
+    @Test
+    // We're testing equals()
+    @SuppressWarnings({"ObjectEqualsNull", "EqualsBetweenInconvertibleTypes"})
+    public void test() throws URISyntaxException {
+
+        // hashCode method test
+        final URI uriA1 = new URI("some:uriA1");
+        final URI uriA2 = new URI("some:uriA2");
+        final URI uriB1 = new URI("some:uriB1");
+        final URI uriB2 = new URI("some:uriB2");
+
+        QName qnameA1 = QName.create(uriA1, Revision.of("2000-01-01"), "someNameA1");
+        QName qnameA2 = QName.create(uriA2, Revision.of("2002-01-01"), "someNameA2");
+        SchemaPath schemaPathA = SchemaPath.create(true, qnameA1, qnameA2);
+
+        final QName qnameB1 = QName.create(uriB1, Revision.of("2000-01-01"), "someNameB1");
+        final QName qnameB2 = QName.create(uriB2, Revision.of("2002-01-01"), "someNameB2");
+        final SchemaPath schemaPathB = SchemaPath.create(true, qnameB1, qnameB2);
+
+        BitImpl biA = new BitImpl(schemaPathA, 55L, "description", "reference", Status.CURRENT, emptyList());
+
+        assertEquals("biA should equals to itsefl", biA, biA);
+        assertFalse("biA shouldn't equal to null", biA.equals(null));
+        assertFalse("biA shouldn't equal to object of other type", biA.equals("str"));
+
+         // // test schemaPath
+        biA = new BitImpl(schemaPathA, 55L, "description", "reference", Status.CURRENT, emptyList());
+        BitImpl biB = new BitImpl(schemaPathB, 55L, "description", "reference", Status.CURRENT, emptyList());
+        assertFalse("biA shouldn't equal to biB", biA.equals(biB));
+
+        biA = new BitImpl(schemaPathB, 55L, "description", "reference", Status.CURRENT, emptyList());
+        biB = new BitImpl(schemaPathB, 55L, "description", "reference", Status.CURRENT, emptyList());
+        assertEquals("biA should equal to biB", biA, biB);
+
+        biA = new BitImpl(schemaPathA, 55L, "description", "reference", Status.CURRENT, emptyList());
+        biB = new BitImpl(schemaPathB, 55L, "description", "reference", Status.CURRENT, emptyList());
+        assertFalse("biA shouldn't equal to biB", biA.equals(biB));
+
+        biA = new BitImpl(schemaPathA, 55L, "description", "reference", Status.CURRENT, emptyList());
+        biB = new BitImpl(schemaPathA, 55L, "description", "reference", Status.CURRENT, emptyList());
+        assertEquals("biA should equal to biB", biA, biB);
+
+        biA = new BitImpl(schemaPathA, 55L, "description", "reference", Status.CURRENT, emptyList());
+
+        // test of getter methods
+        assertEquals("Incorrect value for qname.", qnameA2, biA.getQName());
+        assertEquals("Incorrect value for schema path.", schemaPathA, biA.getPath());
+        assertEquals("Incorrect value for description.", Optional.of("description"), biA.getDescription());
+        assertEquals("Incorrect value for reference.", Optional.of("reference"), biA.getReference());
+        assertEquals("Incorrect value for status.", Status.CURRENT, biA.getStatus());
+        assertEquals("Incorrect value for unknown nodes.", emptyList(), biA.getUnknownSchemaNodes());
+
+        // test of toString method
+        assertEquals("toString method doesn't return correct value", "Bit[name=someNameA2, position=55]",
+                biA.toString());
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/type/EnumPairImplTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/type/EnumPairImplTest.java
new file mode 100644 (file)
index 0000000..694e9f2
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016 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.util.type;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+
+public class EnumPairImplTest {
+
+    @Test
+    public void testEnumPairImpl() {
+        final UnknownSchemaNode mockedUnknownSchemaNodeA = mock(UnknownSchemaNode.class);
+        final UnknownSchemaNode mockedUnknownSchemaNodeB = mock(UnknownSchemaNode.class);
+
+        final Collection<UnknownSchemaNode> unknownSchemaNodes =
+                ImmutableList.of(mockedUnknownSchemaNodeA, mockedUnknownSchemaNodeB);
+
+        final EnumPair enumPair = EnumPairBuilder.create("enum-zero", 0).setStatus(Status.DEPRECATED)
+                .setDescription("enum description").setReference("enum reference")
+                .setUnknownSchemaNodes(unknownSchemaNodes).build();
+
+        assertNotNull(enumPair);
+        assertEquals(unknownSchemaNodes, enumPair.getUnknownSchemaNodes());
+        assertEquals(Optional.of("enum description"), enumPair.getDescription());
+        assertEquals(Optional.of("enum reference"), enumPair.getReference());
+        assertEquals(Status.DEPRECATED, enumPair.getStatus());
+
+        final EnumPair enumPair2 = EnumPairBuilder.create("enum-zero", 0).setStatus(Status.DEPRECATED)
+                .setDescription("enum description").setReference("enum reference")
+                .setUnknownSchemaNodes(unknownSchemaNodes).build();
+
+        assertEquals(enumPair.hashCode(), enumPair2.hashCode());
+        assertTrue(enumPair.equals(enumPair2));
+
+        final EnumPair enumPair3 = EnumPairBuilder.create("enum-one", 1).setStatus(Status.DEPRECATED)
+                .setDescription("enum description").setReference("enum reference")
+                .setUnknownSchemaNodes(unknownSchemaNodes).build();
+
+        assertFalse(enumPair2.equals(enumPair3));
+
+        assertFalse(enumPair.equals("unequal"));
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/type/NumberUtilTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/type/NumberUtilTest.java
new file mode 100644 (file)
index 0000000..562dc1b
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2016 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.util.type;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.function.Function;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
+
+public class NumberUtilTest {
+
+    @Test
+    public void testRangeCoveredForShort() {
+        final short min = 100;
+        final short superMin = 50;
+        final short max = 200;
+        final short superMax = 300;
+
+        assertTrue(NumberUtil.isRangeCovered(min, max, superMin, superMax));
+    }
+
+    @Test
+    public void testRangeCoveredForLong() {
+        final long min = 100L;
+        final long superMin = 50L;
+        final long max = 200L;
+        final long superMax = 300L;
+
+        assertTrue(NumberUtil.isRangeCovered(min, max, superMin, superMax));
+    }
+
+    @Test
+    public void testRangeCoveredForBigDecimal() {
+        final BigDecimal min = new BigDecimal(100.0);
+        final BigDecimal superMin = new BigDecimal(50.0);
+        final BigDecimal max = new BigDecimal(200.0);
+        final BigDecimal superMax = new BigDecimal(300.0);
+
+        assertTrue(NumberUtil.isRangeCovered(min, max, superMin, superMax));
+    }
+
+    @Test
+    public void testRangeCoveredForUint8() {
+        final Uint64 min = Uint64.valueOf("100");
+        final Uint64 superMin = Uint64.valueOf("50");
+        final Uint64 max = Uint64.valueOf("200");
+        final Uint64 superMax = Uint64.valueOf("250");
+
+        assertTrue(NumberUtil.isRangeCovered(min, max, superMin, superMax));
+    }
+
+    @Test
+    public void testRangeCoveredForUint16() {
+        final Uint16 min = Uint16.valueOf("100");
+        final Uint16 superMin = Uint16.valueOf("50");
+        final Uint16 max = Uint16.valueOf("200");
+        final Uint16 superMax = Uint16.valueOf("300");
+
+        assertTrue(NumberUtil.isRangeCovered(min, max, superMin, superMax));
+    }
+
+    @Test
+    public void testRangeCoveredForUint32() {
+        final Uint32 min = Uint32.valueOf("100");
+        final Uint32 superMin = Uint32.valueOf("50");
+        final Uint32 max = Uint32.valueOf("200");
+        final Uint32 superMax = Uint32.valueOf("300");
+
+        assertTrue(NumberUtil.isRangeCovered(min, max, superMin, superMax));
+    }
+
+    @Test
+    public void testRangeCoveredForUint64() {
+        final Uint64 min = Uint64.valueOf("100");
+        final Uint64 superMin = Uint64.valueOf("50");
+        final Uint64 max = Uint64.valueOf("200");
+        final Uint64 superMax = Uint64.valueOf("300");
+
+        assertTrue(NumberUtil.isRangeCovered(min, max, superMin, superMax));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testRangeCoveredForUnsupportedNumberType() {
+        final double min = 100.0;
+        final double superMin = 50.0;
+        final double max = 200.0;
+        final double superMax = 300.0;
+
+        NumberUtil.isRangeCovered(min, max, superMin, superMax);
+    }
+
+    @Test
+    public void testConverterToShort() {
+        final Short shortNum = 20;
+        final Function<Number, Short> numberFunction = NumberUtil.converterTo(Short.class);
+        assertEquals(shortNum, numberFunction.apply(shortNum));
+
+        final byte byteNum = 20;
+        assertEquals(shortNum, numberFunction.apply(byteNum));
+
+        final int intNum = 20;
+        assertEquals(shortNum, numberFunction.apply(intNum));
+    }
+
+    @Test
+    public void testConverterToInteger() {
+        final Integer intNum = 20;
+        final byte byteNum = 20;
+        final Function<Number, Integer> numberFunction = NumberUtil.converterTo(Integer.class);
+        assertEquals(intNum, numberFunction.apply(byteNum));
+    }
+
+    @Test
+    public void testConverterToLong() {
+        final Long longNum = 20L;
+        final Function<Number, Long> numberFunction = NumberUtil.converterTo(Long.class);
+        assertEquals(longNum, numberFunction.apply(longNum));
+
+        final byte byteNum = 20;
+        assertEquals(longNum, numberFunction.apply(byteNum));
+
+        final BigInteger bigIntNum = new BigInteger("20");
+        assertEquals(longNum, numberFunction.apply(bigIntNum));
+    }
+
+    @Test
+    public void testConverterToBigDecimal() {
+        BigDecimal bigDecNum = new BigDecimal(20.0);
+        final Function<Number, BigDecimal> numberFunction = NumberUtil.converterTo(BigDecimal.class);
+        assertEquals(bigDecNum, numberFunction.apply(bigDecNum));
+
+        int intNum = 20;
+        assertEquals(bigDecNum, numberFunction.apply(intNum));
+
+        double doubleNum = 20.0;
+        bigDecNum = new BigDecimal("20.0");
+        assertEquals(bigDecNum, numberFunction.apply(doubleNum));
+    }
+
+    @Test
+    public void testConverterToUint64() {
+        final Uint64 bigIntNum = Uint64.valueOf("20");
+        final Function<Number, Uint64> numberFunction = NumberUtil.converterTo(Uint64.class);
+        assertEquals(bigIntNum, numberFunction.apply(bigIntNum));
+
+        final int intNum = 20;
+        assertEquals(bigIntNum, numberFunction.apply(intNum));
+
+        final BigDecimal bigDecNum = new BigDecimal(20.0);
+        assertEquals(bigIntNum, numberFunction.apply(bigDecNum));
+    }
+}
diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/type/TypeTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/type/TypeTest.java
new file mode 100644 (file)
index 0000000..c58aa1f
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 2016 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.util.type;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeSet;
+import java.util.List;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseConstraints;
+import org.opendaylight.yangtools.yang.model.util.PathExpressionImpl;
+
+public class TypeTest {
+    private static final QName Q_NAME = QName.create("test.namespace", "2016-01-01", "test-name");
+    private static final SchemaPath SCHEMA_PATH = SchemaPath.create(true, Q_NAME);
+    private static final PathExpression REVISION_AWARE_XPATH = new PathExpressionImpl("/test", true);
+    private static final Bit BIT_A = BitBuilder.create(SCHEMA_PATH, 55L).setDescription("description")
+            .setReference("reference").build();
+    private static final Optional<String> ABSENT = Optional.empty();
+
+    @Test
+    public void binaryTypeTest() {
+        final BaseBinaryType baseBinaryType1 = BaseBinaryType.INSTANCE;
+        final BaseBinaryType baseBinaryType2 = (BaseBinaryType)BaseTypes.binaryType();
+        hashCodeEqualsToStringTest(baseBinaryType1, baseBinaryType2);
+        assertEquals(baseBinaryType1.getLengthConstraint(), baseBinaryType2.getLengthConstraint());
+
+        final DerivedBinaryType derivedBinaryType1 = (DerivedBinaryType)DerivedTypes.derivedTypeBuilder(baseBinaryType1,
+                SCHEMA_PATH).build();
+        final DerivedBinaryType derivedBinaryType2 = (DerivedBinaryType)DerivedTypes.derivedTypeBuilder(baseBinaryType2,
+                SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedBinaryType1, derivedBinaryType2);
+
+        final RestrictedBinaryType restrictedBinaryType1 = (RestrictedBinaryType)RestrictedTypes.newBinaryBuilder(
+                baseBinaryType1, SCHEMA_PATH).buildType();
+        final RestrictedBinaryType restrictedBinaryType2 = (RestrictedBinaryType)RestrictedTypes.newBinaryBuilder(
+                baseBinaryType2, SCHEMA_PATH).buildType();
+        hashCodeEqualsToStringTest(restrictedBinaryType1, restrictedBinaryType2);
+
+        final LengthRestrictedTypeBuilder<BinaryTypeDefinition> lengthRestrictedTypeBuilder = RestrictedTypes
+                .newBinaryBuilder(baseBinaryType1, SCHEMA_PATH);
+        final BaseBinaryType baseBinaryType = (BaseBinaryType)lengthRestrictedTypeBuilder.build();
+        assertEquals(baseBinaryType, baseBinaryType1);
+        concreteBuilderTest(baseBinaryType1, derivedBinaryType1);
+    }
+
+    @Test
+    public void booleanTypeTest() {
+        final BaseBooleanType baseBooleanType1 = BaseBooleanType.INSTANCE;
+        final BaseBooleanType baseBooleanType2 = (BaseBooleanType)BaseTypes.booleanType();
+        hashCodeEqualsToStringTest(baseBooleanType1, baseBooleanType2);
+
+        final DerivedBooleanType derivedBooleanType1 = (DerivedBooleanType)DerivedTypes.derivedTypeBuilder(
+                baseBooleanType1, SCHEMA_PATH).build();
+        final DerivedBooleanType derivedBooleanType2 = (DerivedBooleanType)DerivedTypes.derivedTypeBuilder(
+                baseBooleanType1, SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedBooleanType1, derivedBooleanType2);
+
+        restrictedBuilderTest(RestrictedTypes.newBooleanBuilder(baseBooleanType1, SCHEMA_PATH), RestrictedTypes
+                .newBooleanBuilder(baseBooleanType2, SCHEMA_PATH));
+        concreteBuilderTest(baseBooleanType1, derivedBooleanType1);
+    }
+
+    @Test
+    public void identityrefTypeTest() {
+        final IdentityrefTypeBuilder identityrefTypeBuilder1 = BaseTypes.identityrefTypeBuilder(SCHEMA_PATH);
+        final IdentitySchemaNode identitySchemaNode = mock(IdentitySchemaNode.class);
+        doReturn("identitySchemaNode").when(identitySchemaNode).toString();
+        identityrefTypeBuilder1.addIdentity(identitySchemaNode);
+        final IdentityrefTypeDefinition identityrefTypeDefinition1 = identityrefTypeBuilder1.build();
+        final IdentityrefTypeBuilder identityrefTypeBuilder2 = BaseTypes.identityrefTypeBuilder(SCHEMA_PATH);
+        identityrefTypeBuilder2.addIdentity(identitySchemaNode);
+        final IdentityrefTypeDefinition identityrefTypeDefinition2 = identityrefTypeBuilder2.build();
+        hashCodeEqualsToStringTest(identityrefTypeDefinition1, identityrefTypeDefinition2);
+
+        final DerivedIdentityrefType derivedIdentityrefType1 = (DerivedIdentityrefType)DerivedTypes.derivedTypeBuilder(
+                identityrefTypeDefinition1, SCHEMA_PATH).build();
+        final DerivedIdentityrefType derivedIdentityrefType2 = (DerivedIdentityrefType)DerivedTypes.derivedTypeBuilder(
+                identityrefTypeDefinition2, SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedIdentityrefType1, derivedIdentityrefType2);
+        concreteBuilderTest(identityrefTypeDefinition1, derivedIdentityrefType1);
+
+        restrictedBuilderTest(RestrictedTypes.newIdentityrefBuilder(derivedIdentityrefType1, SCHEMA_PATH),
+                RestrictedTypes.newIdentityrefBuilder(derivedIdentityrefType2, SCHEMA_PATH));
+    }
+
+    @Test
+    public void decimalTypeTest() {
+        final BaseDecimalType baseDecimalType1 = (BaseDecimalType)BaseTypes.decimalTypeBuilder(SCHEMA_PATH)
+                .setFractionDigits(1)
+                .buildType();
+        final BaseDecimalType baseDecimalType2 = (BaseDecimalType)BaseTypes.decimalTypeBuilder(SCHEMA_PATH)
+                .setFractionDigits(1)
+                .buildType();
+        hashCodeEqualsToStringTest(baseDecimalType1, baseDecimalType2);
+        assertEquals(baseDecimalType1.getFractionDigits(), baseDecimalType2.getFractionDigits());
+
+        final DerivedDecimalType derivedDecimalType1 = (DerivedDecimalType)DerivedTypes
+                .derivedTypeBuilder(baseDecimalType1, SCHEMA_PATH).build();
+        final DerivedDecimalType derivedDecimalType2 = (DerivedDecimalType)DerivedTypes
+                .derivedTypeBuilder(baseDecimalType1, SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedDecimalType1, derivedDecimalType2);
+
+        final RestrictedDecimalType restrictedDecimalType1 = (RestrictedDecimalType)
+                RestrictedTypes.newDecima64Builder(baseDecimalType1, SCHEMA_PATH).buildType();
+        final RestrictedDecimalType restrictedDecimalType2 = (RestrictedDecimalType)
+                RestrictedTypes.newDecima64Builder(baseDecimalType2, SCHEMA_PATH).buildType();
+        hashCodeEqualsToStringTest(restrictedDecimalType1, restrictedDecimalType2);
+        concreteBuilderTest(baseDecimalType1, derivedDecimalType1);
+    }
+
+    @Test
+    public void emptyTypeTest() {
+        final BaseEmptyType baseEmptyType1 = BaseEmptyType.INSTANCE;
+        final BaseEmptyType baseEmptyType2 = (BaseEmptyType)BaseTypes.emptyType();
+        hashCodeEqualsToStringTest(baseEmptyType1, baseEmptyType2);
+
+        final DerivedEmptyType derivedEmptyType1 = (DerivedEmptyType)DerivedTypes.derivedTypeBuilder(
+                baseEmptyType1, SCHEMA_PATH).build();
+        final DerivedEmptyType derivedEmptyType2 = (DerivedEmptyType)DerivedTypes.derivedTypeBuilder(
+                baseEmptyType2, SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedEmptyType1, derivedEmptyType2);
+
+        restrictedBuilderTest(RestrictedTypes.newEmptyBuilder(baseEmptyType1, SCHEMA_PATH),
+                RestrictedTypes.newEmptyBuilder(baseEmptyType2, SCHEMA_PATH));
+        concreteBuilderTest(baseEmptyType1, derivedEmptyType1);
+    }
+
+    @Test
+    public void instanceIdentifierTypeTest() {
+        final BaseInstanceIdentifierType baseInstanceIdentifierType1 = BaseInstanceIdentifierType.INSTANCE;
+        final BaseInstanceIdentifierType baseInstanceIdentifierType2 = (BaseInstanceIdentifierType)BaseTypes
+                .instanceIdentifierType();
+        hashCodeEqualsToStringTest(baseInstanceIdentifierType1, baseInstanceIdentifierType2);
+        assertFalse(baseInstanceIdentifierType1.requireInstance());
+
+        final DerivedInstanceIdentifierType derivedInstanceIdentifierType1 = (DerivedInstanceIdentifierType)
+                DerivedTypes.derivedTypeBuilder(baseInstanceIdentifierType1, SCHEMA_PATH).build();
+        final DerivedInstanceIdentifierType derivedInstanceIdentifierType2 = (DerivedInstanceIdentifierType)
+                DerivedTypes.derivedTypeBuilder(baseInstanceIdentifierType2, SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedInstanceIdentifierType1, derivedInstanceIdentifierType2);
+
+        final InstanceIdentifierTypeBuilder instanceIdentifierBuilder1 = RestrictedTypes
+                .newInstanceIdentifierBuilder(baseInstanceIdentifierType1, SCHEMA_PATH);
+        instanceIdentifierBuilder1.setRequireInstance(true);
+        final InstanceIdentifierTypeDefinition instanceIdentifierTypeDefinition1 = instanceIdentifierBuilder1
+                .buildType();
+        final InstanceIdentifierTypeBuilder instanceIdentifierBuilder2 = RestrictedTypes
+                .newInstanceIdentifierBuilder(baseInstanceIdentifierType1, SCHEMA_PATH);
+        instanceIdentifierBuilder2.setRequireInstance(true);
+        final InstanceIdentifierTypeDefinition instanceIdentifierTypeDefinition2 = instanceIdentifierBuilder2
+                .buildType();
+        hashCodeEqualsToStringTest(instanceIdentifierTypeDefinition2, instanceIdentifierTypeDefinition1);
+        concreteBuilderTest(baseInstanceIdentifierType1, derivedInstanceIdentifierType1);
+    }
+
+    @Test
+    public void integerTypeTest() {
+        final Int8TypeDefinition integerTypeDefinition8 = BaseTypes.int8Type();
+        final Int16TypeDefinition integerTypeDefinition16 = BaseTypes.int16Type();
+        final Int32TypeDefinition integerTypeDefinition32 = BaseTypes.int32Type();
+        final Int64TypeDefinition integerTypeDefinition64 = BaseTypes.int64Type();
+        assertTrue(BaseTypes.isInt8(integerTypeDefinition8));
+        assertTrue(BaseTypes.isInt16(integerTypeDefinition16));
+        assertTrue(BaseTypes.isInt32(integerTypeDefinition32));
+        assertTrue(BaseTypes.isInt64(integerTypeDefinition64));
+        testInstance(BaseInt8Type.INSTANCE, integerTypeDefinition8);
+        testInstance(BaseInt16Type.INSTANCE, integerTypeDefinition16);
+        testInstance(BaseInt32Type.INSTANCE, integerTypeDefinition32);
+        testInstance(BaseInt64Type.INSTANCE, integerTypeDefinition64);
+
+        final RestrictedInt8Type restrictedIntegerType1 = (RestrictedInt8Type)RestrictedTypes.newInt8Builder(
+                integerTypeDefinition8, SCHEMA_PATH).buildType();
+        final RestrictedInt8Type restrictedIntegerType2 = (RestrictedInt8Type)RestrictedTypes.newInt8Builder(
+                BaseInt8Type.INSTANCE, SCHEMA_PATH).buildType();
+        hashCodeEqualsToStringTest(restrictedIntegerType1, restrictedIntegerType2);
+
+        final Uint8TypeDefinition integerTypeDefinitionu8 = BaseTypes.uint8Type();
+        final Uint16TypeDefinition integerTypeDefinitionu16 = BaseTypes.uint16Type();
+        final Uint32TypeDefinition integerTypeDefinitionu32 = BaseTypes.uint32Type();
+        final Uint64TypeDefinition integerTypeDefinitionu64 = BaseTypes.uint64Type();
+        assertTrue(BaseTypes.isUint8(integerTypeDefinitionu8));
+        assertTrue(BaseTypes.isUint16(integerTypeDefinitionu16));
+        assertTrue(BaseTypes.isUint32(integerTypeDefinitionu32));
+        assertTrue(BaseTypes.isUint64(integerTypeDefinitionu64));
+        testInstance(BaseUint8Type.INSTANCE, integerTypeDefinitionu8);
+        testInstance(BaseUint16Type.INSTANCE, integerTypeDefinitionu16);
+        testInstance(BaseUint32Type.INSTANCE, integerTypeDefinitionu32);
+        testInstance(BaseUint64Type.INSTANCE, BaseTypes.baseTypeOf(integerTypeDefinitionu64));
+
+        final DerivedInt8Type derivedIntegerType1 = (DerivedInt8Type)DerivedTypes
+                .derivedTypeBuilder(integerTypeDefinition8, SCHEMA_PATH).build();
+        final DerivedInt8Type derivedIntegerType2 = (DerivedInt8Type)DerivedTypes
+                .derivedTypeBuilder(BaseInt8Type.INSTANCE, SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedIntegerType1, derivedIntegerType2);
+
+        final DerivedUint8Type derivedUnsignedType1 = (DerivedUint8Type)DerivedTypes
+                .derivedTypeBuilder(integerTypeDefinitionu8, SCHEMA_PATH).build();
+        final DerivedUint8Type derivedUnsignedType2 = (DerivedUint8Type)DerivedTypes
+                .derivedTypeBuilder(BaseUint8Type.INSTANCE, SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedUnsignedType1, derivedUnsignedType2);
+
+        final RestrictedUint8Type restrictedUnsignedType1 = (RestrictedUint8Type)RestrictedTypes
+                .newUint8Builder(integerTypeDefinitionu8, SCHEMA_PATH).buildType();
+        final RestrictedUint8Type restrictedUnsignedType2 = (RestrictedUint8Type)RestrictedTypes
+                .newUint8Builder(BaseUint8Type.INSTANCE, SCHEMA_PATH).buildType();
+        hashCodeEqualsToStringTest(restrictedUnsignedType1, restrictedUnsignedType2);
+        concreteBuilderTest(integerTypeDefinition8, derivedIntegerType1);
+        concreteBuilderTest(integerTypeDefinitionu8, derivedUnsignedType2);
+
+        final DerivedTypeBuilder<?> derivedTypeBuilder = DerivedTypes.derivedTypeBuilder(integerTypeDefinition8,
+                SCHEMA_PATH);
+        derivedTypeBuilder.setDefaultValue(1);
+        derivedTypeBuilder.setDescription("test-description");
+        derivedTypeBuilder.setReference("test-reference");
+        derivedTypeBuilder.setUnits("Int");
+        derivedTypeBuilder.setStatus(Status.CURRENT);
+        assertEquals(derivedTypeBuilder.getStatus(), Status.CURRENT);
+        assertEquals(derivedTypeBuilder.getDescription(), "test-description");
+        assertEquals(derivedTypeBuilder.getReference(), "test-reference");
+        assertEquals(derivedTypeBuilder.getUnits(), "Int");
+    }
+
+    @Test
+    public void stringTypeTest() {
+        final BaseStringType baseStringType1 = BaseStringType.INSTANCE;
+        final BaseStringType baseStringType2 = (BaseStringType)BaseTypes.stringType();
+        hashCodeEqualsToStringTest(baseStringType1, baseStringType2);
+        assertEquals(baseStringType1.getLengthConstraint(), baseStringType2.getLengthConstraint());
+        assertEquals(baseStringType1.getPatternConstraints(), baseStringType2.getPatternConstraints());
+
+        final DerivedStringType derivedStringType1 = (DerivedStringType)
+                DerivedTypes.derivedTypeBuilder(baseStringType1, SCHEMA_PATH).build();
+        final DerivedStringType derivedStringType2 = (DerivedStringType)
+                DerivedTypes.derivedTypeBuilder(baseStringType2, SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedStringType1, derivedStringType2);
+
+        final RestrictedStringType restrictedStringType1 = (RestrictedStringType)RestrictedTypes
+                .newStringBuilder(baseStringType1, SCHEMA_PATH).buildType();
+        final RestrictedStringType restrictedStringType2 = (RestrictedStringType)RestrictedTypes
+                .newStringBuilder(baseStringType2, SCHEMA_PATH).buildType();
+        hashCodeEqualsToStringTest(restrictedStringType1, restrictedStringType2);
+        concreteBuilderTest(baseStringType1, derivedStringType1);
+
+        final StringTypeBuilder stringTypeBuilder = new StringTypeBuilder(baseStringType1, SCHEMA_PATH);
+        final PatternConstraint patternConstraint = BaseConstraints.newPatternConstraint("pattern", ABSENT, ABSENT);
+        stringTypeBuilder.addPatternConstraint(patternConstraint);
+        final StringTypeDefinition stringTypeDefinition = stringTypeBuilder.buildType();
+        assertNotNull(stringTypeDefinition);
+    }
+
+    @Test
+    public void bitsTypeTest() {
+        final BitsTypeBuilder bitsTypeBuilder = BaseTypes.bitsTypeBuilder(SCHEMA_PATH);
+        bitsTypeBuilder.addBit(BIT_A);
+        final BitsTypeDefinition bitsTypeDefinition1 = bitsTypeBuilder.build();
+        final BitsTypeDefinition bitsTypeDefinition2 = bitsTypeBuilder.build();
+        hashCodeEqualsToStringTest(bitsTypeDefinition1, bitsTypeDefinition2);
+        assertEquals(bitsTypeDefinition1.getBits(), bitsTypeDefinition1.getBits());
+
+        final DerivedBitsType derivedBitsType1 = (DerivedBitsType)DerivedTypes
+                .derivedTypeBuilder(bitsTypeDefinition1, SCHEMA_PATH).build();
+        final DerivedBitsType derivedBitsType2 = (DerivedBitsType)DerivedTypes
+                .derivedTypeBuilder(bitsTypeDefinition2, SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedBitsType1, derivedBitsType2);
+
+        restrictedBuilderTest(RestrictedTypes.newBitsBuilder(bitsTypeDefinition1, SCHEMA_PATH),
+                RestrictedTypes.newBitsBuilder(bitsTypeDefinition2, SCHEMA_PATH));
+        concreteBuilderTest(bitsTypeDefinition1, derivedBitsType1);
+    }
+
+    @Test
+    public void enumerationTypeTest() {
+        final BaseEnumerationType baseEnumerationType1 = (BaseEnumerationType)BaseTypes.enumerationTypeBuilder(
+                SCHEMA_PATH).build();
+        final BaseEnumerationType baseEnumerationType2 = (BaseEnumerationType)BaseTypes.enumerationTypeBuilder(
+                SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(baseEnumerationType1, baseEnumerationType2);
+        assertEquals(baseEnumerationType1.getValues(), baseEnumerationType2.getValues());
+
+        final DerivedEnumerationType derivedEnumerationType1 = (DerivedEnumerationType)DerivedTypes
+                .derivedTypeBuilder(baseEnumerationType1, SCHEMA_PATH).build();
+        final DerivedEnumerationType derivedEnumerationType2 = (DerivedEnumerationType)DerivedTypes
+                .derivedTypeBuilder(baseEnumerationType2, SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedEnumerationType1, derivedEnumerationType2);
+
+        restrictedBuilderTest(RestrictedTypes.newEnumerationBuilder(baseEnumerationType1, SCHEMA_PATH),
+                RestrictedTypes.newEnumerationBuilder(baseEnumerationType2, SCHEMA_PATH));
+        concreteBuilderTest(baseEnumerationType1, derivedEnumerationType1);
+    }
+
+    @Test
+    public void leafrefTypeTest() {
+        final LeafrefTypeBuilder leafrefTypeBuilder1 = BaseTypes.leafrefTypeBuilder(SCHEMA_PATH);
+        final LeafrefTypeBuilder leafrefTypeBuilder2 = BaseTypes.leafrefTypeBuilder(SCHEMA_PATH);
+        leafrefTypeBuilder1.setPathStatement(REVISION_AWARE_XPATH);
+        leafrefTypeBuilder2.setPathStatement(REVISION_AWARE_XPATH);
+        final BaseLeafrefType baseLeafrefType1 = (BaseLeafrefType)leafrefTypeBuilder1.build();
+        final BaseLeafrefType baseLeafrefType2 = (BaseLeafrefType)leafrefTypeBuilder1.build();
+        hashCodeEqualsToStringTest(baseLeafrefType1, baseLeafrefType2);
+        assertEquals(baseLeafrefType1.getPathStatement(), REVISION_AWARE_XPATH);
+
+        final DerivedLeafrefType derivedLeafrefType1 = (DerivedLeafrefType)DerivedTypes
+                .derivedTypeBuilder(baseLeafrefType1, SCHEMA_PATH).build();
+        final DerivedLeafrefType derivedLeafrefType2 = (DerivedLeafrefType)DerivedTypes
+                .derivedTypeBuilder(baseLeafrefType2, SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedLeafrefType1, derivedLeafrefType2);
+
+        restrictedBuilderTest(RestrictedTypes.newLeafrefBuilder(baseLeafrefType1, SCHEMA_PATH),
+                RestrictedTypes.newLeafrefBuilder(baseLeafrefType2, SCHEMA_PATH));
+        concreteBuilderTest(baseLeafrefType1, derivedLeafrefType1);
+    }
+
+    @Test
+    public void unionTypeTest() throws IllegalAccessException, InstantiationException {
+        final BaseDecimalType baseDecimalType1 = (BaseDecimalType)BaseTypes.decimalTypeBuilder(SCHEMA_PATH)
+                .setFractionDigits(1)
+                .buildType();
+        final BaseDecimalType baseDecimalType2 = (BaseDecimalType)BaseTypes.decimalTypeBuilder(SCHEMA_PATH)
+                .setFractionDigits(1)
+                .buildType();
+        final UnionTypeBuilder unionTypeBuilder1 = BaseTypes.unionTypeBuilder(SCHEMA_PATH);
+        final UnionTypeBuilder unionTypeBuilder2 = BaseTypes.unionTypeBuilder(SCHEMA_PATH);
+        unionTypeBuilder1.addType(baseDecimalType1);
+        unionTypeBuilder2.addType(baseDecimalType2);
+        final BaseUnionType baseUnionType1 = (BaseUnionType)unionTypeBuilder1.build();
+        final BaseUnionType baseUnionType2 = (BaseUnionType)unionTypeBuilder2.build();
+        hashCodeEqualsToStringTest(baseUnionType1, baseUnionType2);
+        assertEquals(baseUnionType1.getTypes(), baseUnionType2.getTypes());
+
+        final DerivedUnionType derivedUnionType1 = (DerivedUnionType)DerivedTypes
+                .derivedTypeBuilder(baseUnionType1, SCHEMA_PATH).build();
+        final DerivedUnionType derivedUnionType2 = (DerivedUnionType)DerivedTypes
+                .derivedTypeBuilder(baseUnionType2, SCHEMA_PATH).build();
+        hashCodeEqualsToStringTest(derivedUnionType1, derivedUnionType2);
+
+        restrictedBuilderTest(RestrictedTypes.newUnionBuilder(baseUnionType1, SCHEMA_PATH),
+                RestrictedTypes.newUnionBuilder(baseUnionType2, SCHEMA_PATH));
+        concreteBuilderTest(baseUnionType1, derivedUnionType1);
+    }
+
+    @Test
+    public void abstractTypeDefinitionQnameTest() {
+        final AbstractTypeDefinition<?> abstractTypeDefinition = (AbstractTypeDefinition<?>)
+            BaseTypes.decimalTypeBuilder(SCHEMA_PATH).setFractionDigits(1).buildType();
+        assertEquals(abstractTypeDefinition.getQName(), Q_NAME);
+    }
+
+    @Test
+    public void abstractDerivedTypeTest() {
+        final BaseBinaryType baseBinaryType1 = BaseBinaryType.INSTANCE;
+        final AbstractDerivedType<?> abstractDerivedType = (AbstractDerivedType<?>)
+            DerivedTypes.derivedTypeBuilder(baseBinaryType1, SCHEMA_PATH).build();
+        assertEquals(Optional.empty(), abstractDerivedType.getDescription());
+        assertEquals(Optional.empty(), abstractDerivedType.getReference());
+        assertEquals(Status.CURRENT, abstractDerivedType.getStatus());
+    }
+
+    @Test
+    public void concreteTypeBuilderBuildTest() {
+        final BaseEnumerationType baseEnumerationType1 = (BaseEnumerationType)
+            BaseTypes.enumerationTypeBuilder(SCHEMA_PATH).build();
+        final ConcreteTypeBuilder<?> concreteTypeBuilder = ConcreteTypes.concreteTypeBuilder(
+                baseEnumerationType1, SCHEMA_PATH);
+        final TypeDefinition<?> typeDefinition = concreteTypeBuilder.build();
+        assertNotNull(typeDefinition);
+    }
+
+    @Test
+    public void constraintTypeBuilderTest() throws InvalidLengthConstraintException {
+        final BaseBinaryType baseBinaryType = (BaseBinaryType)BaseTypes.binaryType();
+        final LengthRestrictedTypeBuilder<?> lengthRestrictedTypeBuilder = RestrictedTypes
+                .newBinaryBuilder(baseBinaryType, SCHEMA_PATH);
+        final Long min = Long.valueOf(0);
+        final UnresolvedNumber max = UnresolvedNumber.max();
+        final List<ValueRange> lengthArrayList = ImmutableList.of(ValueRange.of(min, max));
+        lengthRestrictedTypeBuilder.setLengthConstraint(mock(ConstraintMetaDefinition.class), lengthArrayList);
+        final TypeDefinition<?> typeDefinition = lengthRestrictedTypeBuilder.buildType();
+        assertNotNull(typeDefinition);
+
+        final Int8TypeDefinition integerTypeDefinition8 = BaseTypes.int8Type();
+        final RangeRestrictedTypeBuilder<?, ?> rangeRestrictedTypeBuilder = RestrictedTypes.newInt8Builder(
+            integerTypeDefinition8, SCHEMA_PATH);
+        rangeRestrictedTypeBuilder.setRangeConstraint(mock(ConstraintMetaDefinition.class), lengthArrayList);
+        final TypeDefinition<?> typeDefinition1 = rangeRestrictedTypeBuilder.buildType();
+        assertNotNull(typeDefinition1);
+    }
+
+    @Test
+    public void exceptionTest() {
+        final UnresolvedNumber min = UnresolvedNumber.min();
+        final UnresolvedNumber max = UnresolvedNumber.max();
+        final EnumPair enumPair = EnumPairBuilder.create("enum1", 1).setDescription("description")
+                .setReference("reference").setUnknownSchemaNodes(mock(UnknownSchemaNode.class)).build();
+
+        final RangeSet<Integer> rangeset = ImmutableRangeSet.of(Range.closed(1, 2));
+        final InvalidRangeConstraintException invalidRangeConstraintException = new InvalidRangeConstraintException(
+                rangeset, "error msg", "other important messages");
+        assertSame(rangeset, invalidRangeConstraintException.getOffendingRanges());
+
+        final InvalidBitDefinitionException invalidBitDefinitionException = new InvalidBitDefinitionException(
+                BIT_A, "error msg", "other important messages");
+        assertEquals(invalidBitDefinitionException.getOffendingBit(), BIT_A);
+
+        final InvalidEnumDefinitionException invalidEnumDefinitionException = new InvalidEnumDefinitionException(
+                enumPair, "error msg", "other important messages");
+        assertEquals(invalidEnumDefinitionException.getOffendingEnum(), enumPair);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void identityrefTypeBuilderException() {
+        BaseTypes.identityrefTypeBuilder(SCHEMA_PATH).build();
+    }
+
+    @Test(expected = InvalidBitDefinitionException.class)
+    public void invalidBitDefinitionExceptionTest() {
+        final BitsTypeBuilder bitsTypeBuilder = BaseTypes.bitsTypeBuilder(SCHEMA_PATH);
+        final QName qName = QName.create("test.namespace.1", "2016-01-02", "test-name-1");
+        final SchemaPath schemaPath = SchemaPath.create(true, qName);
+        bitsTypeBuilder.addBit(BIT_A);
+        bitsTypeBuilder.addBit(BitBuilder.create(schemaPath, 55L).build());
+        bitsTypeBuilder.build();
+    }
+
+    @Test(expected = InvalidEnumDefinitionException.class)
+    public void invalidEnumDefinitionExceptionTest() {
+        final UnknownSchemaNode unknown = mock(UnknownSchemaNode.class);
+        final EnumPair enumPair1 = EnumPairBuilder.create("enum1", 1).setDescription("description")
+                .setReference("reference").setUnknownSchemaNodes(unknown).build();
+        final EnumPair enumPair2 = EnumPairBuilder.create("enum", 1).setDescription("description")
+                .setReference("reference").setUnknownSchemaNodes(unknown).build();
+        final EnumerationTypeBuilder enumerationTypeBuilder = BaseTypes.enumerationTypeBuilder(SCHEMA_PATH);
+        enumerationTypeBuilder.addEnum(enumPair1);
+        enumerationTypeBuilder.addEnum(enumPair2);
+        enumerationTypeBuilder.build();
+    }
+
+    private static void hashCodeEqualsToStringTest(final TypeDefinition<?> type1, final TypeDefinition<?> type2) {
+        assertEquals(type1.hashCode(), type2.hashCode());
+        assertEquals(type1.toString(), type2.toString());
+        assertTrue(type1.equals(type2));
+    }
+
+    private static <T> void testInstance(final T type1, final T type2) {
+        assertEquals(type1, type2);
+    }
+
+    private static void restrictedBuilderTest(final Builder<?> typeBuilder1, final Builder<?> typeBuilder2) {
+        final TypeDefinition<?> typeDefinition1 = ((AbstractRestrictedTypeBuilder<?>) typeBuilder1).buildType();
+        final TypeDefinition<?> typeDefinition2 = ((AbstractRestrictedTypeBuilder<?>) typeBuilder2).buildType();
+        hashCodeEqualsToStringTest(typeDefinition1, typeDefinition2);
+    }
+
+    private static void concreteBuilderTest(final TypeDefinition<?> baseTypeDef,
+            final TypeDefinition<?> derivedTypeDef) {
+        final ConcreteTypeBuilder<?> concreteTypeBuilder = ConcreteTypes.concreteTypeBuilder(baseTypeDef, SCHEMA_PATH);
+        final TypeDefinition<?> typeDefinition = concreteTypeBuilder.buildType();
+        assertEquals(typeDefinition.getBaseType(), derivedTypeDef.getBaseType());
+    }
+}
diff --git a/yang/yang-model-validator/pom.xml b/yang/yang-model-validator/pom.xml
new file mode 100644 (file)
index 0000000..f31e80d
--- /dev/null
@@ -0,0 +1,116 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.odlparent</groupId>
+        <artifactId>odlparent</artifactId>
+        <version>6.0.1</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>yang-model-validator</artifactId>
+    <version>4.0.3-SNAPSHOT</version>
+    <name>${project.artifactId}</name>
+    <description>YANG model validator</description>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>4.0.3-SNAPSHOT</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-cli</groupId>
+            <artifactId>commons-cli</artifactId>
+            <version>1.4</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>yang-parser-system-test-java</id>
+                        <goals>
+                            <goal>java</goal>
+                        </goals>
+                        <configuration>
+                            <includeProjectDependencies>true</includeProjectDependencies>
+                            <includePluginDependencies>true</includePluginDependencies>
+                            <mainClass>org.opendaylight.yangtools.yang.validator.Main</mainClass>
+                            <arguments>
+                                <argument>./src/main/yang</argument>
+                            </arguments>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>yang-parser-system-test-exec</id>
+                        <goals>
+                            <goal>exec</goal>
+                        </goals>
+                        <configuration>
+                            <classpathScope>test</classpathScope>
+                            <executable>java</executable>
+                            <arguments>
+                                <argument>-classpath</argument>
+                                <classpath/>
+                                <argument>org.opendaylight.yangtools.yang.validator.Main</argument>
+                                <argument>./src/main/yang</argument>
+                            </arguments>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>attached</goal>
+                        </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <descriptorRefs>
+                                <descriptorRef>jar-with-dependencies</descriptorRef>
+                            </descriptorRefs>
+                            <archive>
+                                <manifest>
+                                    <mainClass>org.opendaylight.yangtools.yang.validator.Main</mainClass>
+                                </manifest>
+                            </archive>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-model-validator/src/main/java/org/opendaylight/yangtools/yang/validator/Main.java b/yang/yang-model-validator/src/main/java/org/opendaylight/yangtools/yang/validator/Main.java
new file mode 100644 (file)
index 0000000..ddf4480
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2016 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.validator;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.FileAppender;
+import com.google.common.base.Stopwatch;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionGroup;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Main class of Yang parser system test.
+ *
+ * <p>
+ * yang-system-test [-f features] [-h help] [-p path] [-v verbose] yangFiles...
+ *  -f,--features &lt;arg&gt;   features is a string in the form
+ *                        [feature(,feature)*] and feature is a string in the form
+ *                        [($namespace?revision=$revision)$local_name].
+ *                        This option is used to prune the data model by removing
+ *                        all nodes that are defined with a "if-feature".
+ *  -h,--help             print help message and exit.
+ *  -p,--path &lt;arg&gt;       path is a colon (:) separated list of directories
+ *                        to search for yang modules.
+ *  -r, --recursive       recursive search of directories specified by -p option
+ *  -v, --verbose         shows details about the results of test running.
+ *  -o, --output          path to output file for logs. Output file will be overwritten.
+ *  -m, --module-name     validate yang by module name.
+ */
+@SuppressWarnings({"checkstyle:LoggerMustBeSlf4j", "checkstyle:LoggerFactoryClassParameter"})
+public final class Main {
+    private static final Logger LOG = LoggerFactory.getLogger(Main.class);
+    private static final ch.qos.logback.classic.Logger LOG_ROOT =
+            (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
+    private static final int MB = 1024 * 1024;
+
+    private static final Option FEATURE = new Option("f", "features", true,
+        "features is a string in the form [feature(,feature)*] and feature is a string in the form "
+                + "[($namespace?revision=$revision)$local_name]. This option is used to prune the data model "
+                + "by removing all nodes that are defined with a \"if-feature\".");
+
+    private static final Option HELP = new Option("h", "help", false, "print help message and exit.");
+    private static final Option MODULE_NAME = new Option("m", "module-name", true,
+            "validate yang model by module name.");
+    private static final Option OUTPUT = new Option("o", "output", true,
+            "path to output file for logs. Output file will be overwritten");
+    private static final Option PATH = new Option("p", "path", true,
+            "path is a colon (:) separated list of directories to search for yang modules.");
+    private static final Option RECURSIVE = new Option("r", "recursive", false,
+            "recursive search of directories specified by -p option.");
+
+    private static final Option DEBUG = new Option("d", "debug", false, "add debug output");
+    private static final Option QUIET = new Option("q", "quiet", false, "completely suppress output.");
+    private static final Option VERBOSE = new Option("v", "verbose", false,
+        "shows details about the results of test running.");
+
+    private Main() {
+        // Hidden on purpose
+    }
+
+    private static Options createOptions() {
+        final Options options = new Options();
+        options.addOption(HELP);
+        options.addOption(PATH);
+        options.addOption(RECURSIVE);
+
+        final OptionGroup verbosity = new OptionGroup();
+        verbosity.addOption(DEBUG);
+        verbosity.addOption(QUIET);
+        verbosity.addOption(VERBOSE);
+        options.addOptionGroup(verbosity);
+
+        options.addOption(OUTPUT);
+        options.addOption(MODULE_NAME);
+        options.addOption(FEATURE);
+        return options;
+    }
+
+    public static void main(final String[] args) {
+        final HelpFormatter formatter = new HelpFormatter();
+        final Options options = createOptions();
+        final CommandLine arguments = parseArguments(args, options, formatter);
+
+        if (arguments.hasOption(HELP.getLongOpt())) {
+            printHelp(options, formatter);
+            return;
+        }
+
+        final String[] outputValues = arguments.getOptionValues(OUTPUT.getLongOpt());
+        if (outputValues != null) {
+            setOutput(outputValues);
+        }
+
+        LOG_ROOT.setLevel(Level.WARN);
+        if (arguments.hasOption(DEBUG.getLongOpt())) {
+            LOG_ROOT.setLevel(Level.DEBUG);
+        } else if (arguments.hasOption(VERBOSE.getLongOpt())) {
+            LOG_ROOT.setLevel(Level.INFO);
+        } else if (arguments.hasOption(QUIET.getLongOpt())) {
+            LOG_ROOT.detachAndStopAllAppenders();
+        }
+
+        final List<String> yangLibDirs = initYangDirsPath(arguments);
+        final List<String> yangFiles = new ArrayList<>();
+        final String[] moduleNameValues = arguments.getOptionValues(MODULE_NAME.getLongOpt());
+        if (moduleNameValues != null) {
+            yangFiles.addAll(Arrays.asList(moduleNameValues));
+        }
+        yangFiles.addAll(Arrays.asList(arguments.getArgs()));
+
+        final Set<QName> supportedFeatures = initSupportedFeatures(arguments);
+
+        runSystemTest(yangLibDirs, yangFiles, supportedFeatures, arguments.hasOption("recursive"));
+
+        LOG_ROOT.getLoggerContext().reset();
+    }
+
+    private static void setOutput(final String... paths) {
+        LOG_ROOT.getLoggerContext().reset();
+
+        final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
+        encoder.setPattern("%date %level [%thread] [%file:%line] %msg%n");
+        encoder.setContext(LOG_ROOT.getLoggerContext());
+        encoder.start();
+
+        for (final String path : paths) {
+            // create FileAppender
+            final FileAppender<ILoggingEvent> logfileOut = new FileAppender<>();
+            logfileOut.setAppend(false);
+            logfileOut.setFile(path);
+            logfileOut.setContext(LOG_ROOT.getLoggerContext());
+            logfileOut.setEncoder(encoder);
+            logfileOut.start();
+
+            // attach the rolling file appender to the root logger
+            LOG_ROOT.addAppender(logfileOut);
+        }
+    }
+
+    @SuppressFBWarnings({ "DM_EXIT", "DM_GC" })
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private static void runSystemTest(final List<String> yangLibDirs, final List<String> yangFiles,
+            final Set<QName> supportedFeatures, final boolean recursiveSearch) {
+        LOG.info("Yang model dirs: {} ", yangLibDirs);
+        LOG.info("Yang model files: {} ", yangFiles);
+        LOG.info("Supported features: {} ", supportedFeatures);
+
+        SchemaContext context = null;
+
+        printMemoryInfo("start");
+        final Stopwatch stopWatch = Stopwatch.createStarted();
+
+        try {
+            context = SystemTestUtils.parseYangSources(yangLibDirs, yangFiles, supportedFeatures, recursiveSearch);
+        } catch (final Exception e) {
+            LOG.error("Failed to create SchemaContext.", e);
+            System.exit(1);
+        }
+
+        stopWatch.stop();
+        LOG.info("Elapsed time: {}", stopWatch);
+        printMemoryInfo("end");
+        LOG.info("SchemaContext resolved Successfully. {}", context);
+        Runtime.getRuntime().gc();
+        printMemoryInfo("after gc");
+    }
+
+    private static List<String> initYangDirsPath(final CommandLine arguments) {
+        final List<String> yangDirs = new ArrayList<>();
+        if (arguments.hasOption("path")) {
+            for (final String pathArg : arguments.getOptionValues("path")) {
+                yangDirs.addAll(Arrays.asList(pathArg.split(":")));
+            }
+        }
+        return yangDirs;
+    }
+
+    private static Set<QName> initSupportedFeatures(final CommandLine arguments) {
+        Set<QName> supportedFeatures = null;
+        if (arguments.hasOption("features")) {
+            supportedFeatures = new HashSet<>();
+            for (final String pathArg : arguments.getOptionValues("features")) {
+                supportedFeatures.addAll(createQNames(pathArg.split(",")));
+            }
+        }
+        return supportedFeatures;
+    }
+
+    private static Collection<? extends QName> createQNames(final String[] featuresArg) {
+        final Set<QName> qnames = new HashSet<>();
+        for (final String featureStr : featuresArg) {
+            qnames.add(QName.create(featureStr));
+        }
+
+        return qnames;
+    }
+
+    @SuppressFBWarnings("DM_EXIT")
+    private static CommandLine parseArguments(final String[] args, final Options options,
+            final HelpFormatter formatter) {
+        final CommandLineParser parser = new DefaultParser();
+
+        CommandLine cmd = null;
+        try {
+            cmd = parser.parse(options, args);
+        } catch (final ParseException e) {
+            LOG.error("Failed to parse command line options.", e);
+            printHelp(options, formatter);
+            System.exit(1);
+        }
+
+        return cmd;
+    }
+
+    private static void printHelp(final Options options, final HelpFormatter formatter) {
+        formatter.printHelp("yang-system-test [OPTION...] YANG-FILE...", options);
+    }
+
+    private static void printMemoryInfo(final String info) {
+        LOG.info("Memory INFO [{}]: free {}MB, used {}MB, total {}MB, max {}MB", info,
+            Runtime.getRuntime().freeMemory() / MB,
+            (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / MB,
+            Runtime.getRuntime().totalMemory() / MB, Runtime.getRuntime().maxMemory() / MB);
+    }
+}
diff --git a/yang/yang-model-validator/src/main/java/org/opendaylight/yangtools/yang/validator/SystemTestUtils.java b/yang/yang-model-validator/src/main/java/org/opendaylight/yangtools/yang/validator/SystemTestUtils.java
new file mode 100644 (file)
index 0000000..be14e7b
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2016 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.validator;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+final class SystemTestUtils {
+
+    private static final Pattern MODULE_PATTERN = Pattern.compile("module(.*?)\\{");
+    private static final Pattern WHITESPACES = Pattern.compile("\\s+");
+    private static final @NonNull YangParserFactory PARSER_FACTORY;
+
+    static {
+        final Iterator<@NonNull YangParserFactory> it = ServiceLoader.load(YangParserFactory.class).iterator();
+        if (!it.hasNext()) {
+            throw new IllegalStateException("No YangParserFactory found");
+        }
+        PARSER_FACTORY = it.next();
+    }
+
+    private SystemTestUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    static final FileFilter YANG_FILE_FILTER = file -> {
+        final String name = file.getName().toLowerCase(Locale.ENGLISH);
+        return name.endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION) && file.isFile();
+    };
+
+    static SchemaContext parseYangSources(final List<String> yangLibDirs, final List<String> yangTestFiles,
+            final Set<QName> supportedFeatures, final boolean recursiveSearch) throws IOException, YangParserException {
+        /*
+         * Current dir "." should be always present implicitly in the list of
+         * directories where dependencies are searched for
+         */
+        if (!yangLibDirs.contains(".")) {
+            yangLibDirs.add(".");
+        }
+
+        final List<File> libFiles = new ArrayList<>();
+        for (final String yangLibDir : yangLibDirs) {
+            libFiles.addAll(getYangFiles(yangLibDir, recursiveSearch));
+        }
+
+        final List<File> testFiles = new ArrayList<>();
+        for (final String yangTestFile : yangTestFiles) {
+            if (!yangTestFile.endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION)) {
+                testFiles.add(findInFiles(libFiles, yangTestFile));
+            } else {
+                testFiles.add(new File(yangTestFile));
+            }
+        }
+
+        return parseYangSources(supportedFeatures, testFiles, libFiles);
+    }
+
+    static SchemaContext parseYangSources(final Set<QName> supportedFeatures, final List<File> testFiles,
+            final List<File> libFiles) throws IOException, YangParserException {
+        checkArgument(!testFiles.isEmpty(), "No yang sources");
+
+        final YangParser parser = PARSER_FACTORY.createParser();
+        if (supportedFeatures != null) {
+            parser.setSupportedFeatures(supportedFeatures);
+        }
+
+        for (File file : testFiles) {
+            parser.addSource(YangTextSchemaSource.forFile(file));
+        }
+        for (File file : libFiles) {
+            parser.addLibSource(YangTextSchemaSource.forFile(file));
+        }
+
+        return parser.buildSchemaContext();
+    }
+
+    private static File findInFiles(final List<File> libFiles, final String yangTestFile) throws IOException {
+        for (final File file : libFiles) {
+            if (WHITESPACES.matcher(getModelNameFromFile(file)).replaceAll("").equals(yangTestFile)) {
+                return file;
+            }
+        }
+        throw new FileNotFoundException("Model with specific module-name does not exist : " + yangTestFile);
+    }
+
+    private static String getModelNameFromFile(final File file) throws IOException {
+        final String fileAsString = readFile(file.getAbsolutePath());
+        final Matcher matcher = MODULE_PATTERN.matcher(fileAsString);
+        return matcher.find() ? matcher.group(1) : "";
+    }
+
+    private static String readFile(final String path) throws IOException {
+        return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
+    }
+
+    @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
+    private static Collection<File> getYangFiles(final String yangSourcesDirectoryPath, final boolean recursiveSearch)
+            throws FileNotFoundException {
+        final File testSourcesDir = new File(yangSourcesDirectoryPath);
+        if (!testSourcesDir.isDirectory()) {
+            throw new FileNotFoundException(String.format("%s no such directory", yangSourcesDirectoryPath));
+        }
+
+        return recursiveSearch ? searchYangFiles(testSourcesDir)
+            : Arrays.asList(testSourcesDir.listFiles(YANG_FILE_FILTER));
+    }
+
+    @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
+    private static List<File> searchYangFiles(final File dir) {
+        requireNonNull(dir);
+        checkArgument(dir.isDirectory(), "File %s is not a directory", dir.getPath());
+
+        final List<File> yangFiles = new ArrayList<>();
+        for (final File file : dir.listFiles()) {
+            if (file.isDirectory()) {
+                yangFiles.addAll(searchYangFiles(file));
+            } else if (file.isFile()
+                    && file.getName().toLowerCase(Locale.ENGLISH).endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION)) {
+                yangFiles.add(file);
+            }
+        }
+
+        return yangFiles;
+    }
+}
diff --git a/yang/yang-model-validator/src/main/yang/example.yang b/yang/yang-model-validator/src/main/yang/example.yang
new file mode 100644 (file)
index 0000000..27b1f0e
--- /dev/null
@@ -0,0 +1,10 @@
+module example {
+    namespace "example";
+    prefix example;
+    yang-version 1;
+
+    revision 2016-10-24;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-api/pom.xml b/yang/yang-parser-api/pom.xml
new file mode 100644 (file)
index 0000000..49204e9
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-parser-api</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>YANG parser API</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.parser.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParser.java b/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParser.java
new file mode 100644 (file)
index 0000000..bbfb37e
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.parser.api;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.SetMultimap;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+
+/**
+ * Configurable single-use YANG parser. Each instance can be configured to use a different set of models after
+ * which it is built. Models once added cannot be removed. Implementations are expected to be NOT thread-safe.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface YangParser {
+    /**
+     * Return enumeration of concrete types of {@link SchemaSourceRepresentation} parsers created from this factory
+     * support. Users can use this information prepare the source they have to a representation which will be accepted
+     * by this parser.
+     *
+     * @return Enumeration of supported schema source representations.
+     */
+    @NonNull Collection<Class<? extends SchemaSourceRepresentation>> supportedSourceRepresentations();
+
+    /**
+     * Return the set of all YANG statements semantically supported by this parser instance.
+     *
+     * @return Set of all YANG statements semantically supported by this parser instance.
+     */
+    @NonNull Set<QName> supportedStatements();
+
+    /**
+     * Add main source. All main sources are present in resulting SchemaContext.
+     *
+     * @param source which should be added into main sources
+     * @throws YangSyntaxErrorException when one of the sources fails syntactic analysis
+     * @throws IOException when an IO error occurs
+     * @throws IllegalArgumentException if the representation is not supported
+     */
+    @NonNull YangParser addSource(SchemaSourceRepresentation source) throws IOException, YangSyntaxErrorException;
+
+    /**
+     * Add main sources. All main sources are present in resulting SchemaContext.
+     *
+     * @param sources which should be added into main sources
+     * @throws YangSyntaxErrorException when one of the sources fails syntactic analysis
+     * @throws IOException when an IO error occurs
+     * @throws IllegalArgumentException if the representation is not supported
+     */
+    default @NonNull YangParser addSources(final SchemaSourceRepresentation... sources) throws IOException,
+        YangSyntaxErrorException {
+        for (SchemaSourceRepresentation source : sources) {
+            addSource(source);
+        }
+        return this;
+    }
+
+    default @NonNull YangParser addSources(final Collection<? extends SchemaSourceRepresentation> sources)
+            throws IOException, YangSyntaxErrorException {
+        for (SchemaSourceRepresentation source : sources) {
+            addSource(source);
+        }
+        return this;
+    }
+
+    YangParser addLibSource(SchemaSourceRepresentation source) throws IOException, YangSyntaxErrorException;
+
+    /**
+     * Add library sources. Only library sources required by main sources are present in resulting SchemaContext.
+     * Any other library sources are ignored and this also applies to error reporting.
+     *
+     * <p>
+     * Note: Library sources are not supported in semantic version mode currently.
+     *
+     * @param sources YANG sources which should be added into library sources
+     * @throws YangSyntaxErrorException when one of the sources fails syntactic analysis
+     * @throws IOException when an IO error occurs
+     * @throws IllegalArgumentException if the representation is not supported
+     */
+    default @NonNull YangParser addLibSources(final SchemaSourceRepresentation... sources) throws IOException,
+            YangSyntaxErrorException {
+        for (SchemaSourceRepresentation source : sources) {
+            addLibSource(source);
+        }
+        return this;
+    }
+
+    default @NonNull YangParser addLibSources(final Collection<SchemaSourceRepresentation> sources) throws IOException,
+            YangSyntaxErrorException {
+        for (SchemaSourceRepresentation source : sources) {
+            addLibSource(source);
+        }
+        return this;
+    }
+
+    /**
+     * Set supported features based on which all if-feature statements in the parsed YANG modules will be resolved. If
+     * this method is not invoked, all features will be supported.
+     *
+     * @param supportedFeatures Set of supported features in the final SchemaContext. If the set is empty, no features
+     *                          encountered will be supported.
+     */
+    @NonNull YangParser setSupportedFeatures(@NonNull Set<QName> supportedFeatures);
+
+    /**
+     * Set YANG modules which can be deviated by specified modules during the parsing process. Map key (QNameModule)
+     * denotes a module which can be deviated by the modules in the Map value.
+     *
+     * @param modulesDeviatedByModules Map of YANG modules (Map key) which can be deviated by specified modules (Map
+     *                                 value) in the final SchemaContext. If the map is empty, no deviations encountered
+     *                                 will be supported.
+     */
+    @NonNull YangParser setModulesWithSupportedDeviations(
+            @NonNull SetMultimap<QNameModule, QNameModule> modulesDeviatedByModules);
+
+    /**
+     * Build the declared view of a combined view of declared statements.
+     *
+     * @return Ordered collection of declared statements from requested sources.
+     * @throws YangSyntaxErrorException When a syntactic error is encountered.
+     */
+    @NonNull List<DeclaredStatement<?>> buildDeclaredModel() throws YangParserException;
+
+    /**
+     * Build the effective view of a combined view of effective statements. Note that this representation, unlike
+     * {@link #buildDeclaredModel()} does not expose submodules as top-level contracts. These are available from their
+     * respective parent modules.
+     *
+     * @return Effective module statements indexed by their QNameModule.
+     * @throws YangSyntaxErrorException When a syntactic error is encountered.
+     */
+    @NonNull EffectiveModelContext buildEffectiveModel() throws YangParserException;
+
+    /**
+     * Build effective {@link SchemaContext}.
+     *
+     * @return An effective schema context comprised of configured models.
+     * @throws YangSyntaxErrorException When a syntactic error is encountered.
+     */
+    default @NonNull SchemaContext buildSchemaContext() throws YangParserException {
+        return buildEffectiveModel();
+    }
+}
diff --git a/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParserException.java b/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParserException.java
new file mode 100644 (file)
index 0000000..96e4793
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.parser.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Base exception reported by {@link YangParser}. This is used as an exception of last resort, semantic reporting
+ * of individual errors is performed via subclasses, like {@link YangSyntaxErrorException}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public class YangParserException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public YangParserException(final String message) {
+        super(requireNonNull(message));
+    }
+
+    public YangParserException(final String message, final Throwable cause) {
+        super(requireNonNull(message), cause);
+    }
+}
diff --git a/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParserFactory.java b/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangParserFactory.java
new file mode 100644 (file)
index 0000000..f97e40e
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.parser.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+
+/**
+ * Basic entry point into a YANG parser implementation. Implementations of this interface are expected to be
+ * thread-safe.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface YangParserFactory {
+    /**
+     * Return enumeration of {@link StatementParserMode}s supported by this factory.
+     *
+     * @return Enumeration of supported schema source representations.
+     */
+    Collection<StatementParserMode> supportedParserModes();
+
+    /**
+     * Create a {@link YangParser} instance operating in default import resolution mode.
+     *
+     * @return A new {@link YangParser} instance
+     */
+    default YangParser createParser() {
+        return createParser(StatementParserMode.DEFAULT_MODE);
+    }
+
+    /**
+     * Create a {@link YangParser} instance operating in specified import resolution mode.
+     *
+     * @param parserMode Requested parser mode, may not be null.
+     * @return A new {@link YangParser} instance
+     * @throws NullPointerException if parser mode is null
+     * @throws IllegalArgumentException if specified parser mode is not supported
+     */
+    YangParser createParser(StatementParserMode parserMode);
+}
diff --git a/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangSyntaxErrorException.java b/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/YangSyntaxErrorException.java
new file mode 100644 (file)
index 0000000..ce2e920
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.model.parser.api;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+public class YangSyntaxErrorException extends YangParserException {
+    private static final long serialVersionUID = 2L;
+
+    private final SourceIdentifier source;
+    private final int line;
+    private final int charPositionInLine;
+
+    public YangSyntaxErrorException(final @Nullable SourceIdentifier source, final int line,
+            final int charPositionInLine, final String message) {
+        this(source, line, charPositionInLine, message, null);
+    }
+
+    public YangSyntaxErrorException(final @Nullable SourceIdentifier source, final int line,
+            final int charPositionInLine, final String message, final @Nullable Throwable cause) {
+        super(message, cause);
+        this.source = source;
+        this.line = line;
+        this.charPositionInLine = charPositionInLine;
+    }
+
+    public final Optional<SourceIdentifier> getSource() {
+        return Optional.ofNullable(source);
+    }
+
+    public final int getLine() {
+        return line;
+    }
+
+    public final int getCharPositionInLine() {
+        return charPositionInLine;
+    }
+
+    public @NonNull String getFormattedMessage() {
+        final StringBuilder sb = new StringBuilder(getMessage());
+        if (source != null) {
+            sb.append(" in source ");
+            sb.append(source);
+        }
+        if (line != 0) {
+            sb.append(" on line ");
+            sb.append(line);
+            if (charPositionInLine != 0) {
+                sb.append(" character ");
+                sb.append(charPositionInLine);
+            }
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getName() + ": " + getFormattedMessage();
+    }
+}
diff --git a/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/package-info.java b/yang/yang-parser-api/src/main/java/org/opendaylight/yangtools/yang/model/parser/api/package-info.java
new file mode 100644 (file)
index 0000000..f9f08cc
--- /dev/null
@@ -0,0 +1,8 @@
+/*
+ * 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.model.parser.api;
\ No newline at end of file
diff --git a/yang/yang-parser-impl/pom.xml b/yang/yang-parser-impl/pom.xml
new file mode 100644 (file)
index 0000000..d52934d
--- /dev/null
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-parser-impl</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>YANG parser</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-reactor</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-util</artifactId>
+        </dependency>
+
+        <!-- Base RFC6020/RFC7950 reactor support -->
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-rfc7950</artifactId>
+        </dependency>
+
+        <!-- Various extension reactor supports -->
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odlext-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odlext-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>openconfig-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>openconfig-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc6241-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc6241-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc6536-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc6536-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc7952-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8040-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8040-parser-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>rfc8528-parser-support</artifactId>
+        </dependency>
+
+        <!-- XPath reference implementation -->
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-xpath-impl</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kohsuke.metainf-services</groupId>
+            <artifactId>metainf-services</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.parser.impl</Automatic-Module-Name>
+                        <Bundle-Activator>org.opendaylight.yangtools.yang.parser.impl.osgi.Activator</Bundle-Activator>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/DefaultReactors.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/DefaultReactors.java
new file mode 100644 (file)
index 0000000..008336d
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.impl;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.odlext.parser.AnyxmlSchemaLocationNamespace;
+import org.opendaylight.yangtools.odlext.parser.AnyxmlSchemaLocationStatementSupport;
+import org.opendaylight.yangtools.odlext.parser.AnyxmlStatementSupportOverride;
+import org.opendaylight.yangtools.openconfig.parser.EncryptedValueStatementSupport;
+import org.opendaylight.yangtools.openconfig.parser.HashedValueStatementSupport;
+import org.opendaylight.yangtools.rfc6241.parser.GetFilterElementAttributesStatementSupport;
+import org.opendaylight.yangtools.rfc6536.parser.DefaultDenyAllStatementSupport;
+import org.opendaylight.yangtools.rfc6536.parser.DefaultDenyWriteStatementSupport;
+import org.opendaylight.yangtools.rfc7952.parser.AnnotationStatementSupport;
+import org.opendaylight.yangtools.rfc8040.parser.YangDataStatementSupport;
+import org.opendaylight.yangtools.rfc8528.parser.MountPointStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.CustomCrossSourceStatementReactorBuilder;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.Builder;
+
+/**
+ * Utility class for instantiating default-configured {@link CrossSourceStatementReactor}s.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class DefaultReactors {
+    private static final @NonNull CrossSourceStatementReactor DEFAULT_REACTOR = defaultReactorBuilder().build();
+
+    private DefaultReactors() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Get a shared default-configured reactor instance. This instance is configured to handle both RFC6020 and RFC7950,
+     * as well as
+     * <ul>
+     * <li>RFC6536's default-deny-{all,write} extensions</li>
+     * <li>RFC7952's annotation extension</li>
+     * <li>RFC8040's yang-data extension</li>
+     * <li>OpenConfig extensions</li>
+     * <li>OpenDaylight extensions</li>
+     * </ul>
+     *
+     * @return a shared default-configured reactor instance.
+     */
+    public static @NonNull CrossSourceStatementReactor defaultReactor() {
+        return DEFAULT_REACTOR;
+    }
+
+    /**
+     * Return a baseline CrossSourceStatementReactor {@link Builder}. The builder is initialized to the equivalent
+     * of the reactor returned via {@link #defaultReactor()}, but can be further customized before use.
+     *
+     * @return A populated CrossSourceStatementReactor builder.
+     */
+    public static CustomCrossSourceStatementReactorBuilder defaultReactorBuilder() {
+        return RFC7950Reactors.defaultReactorBuilder()
+                // AnyxmlSchemaLocation support
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
+                    AnyxmlSchemaLocationStatementSupport.getInstance())
+                .addNamespaceSupport(ModelProcessingPhase.FULL_DECLARATION, AnyxmlSchemaLocationNamespace.BEHAVIOUR)
+                .overrideStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
+                    AnyxmlStatementSupportOverride.getInstance())
+
+                // RFC6241 get-filter-element-attributes support
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
+                    GetFilterElementAttributesStatementSupport.getInstance())
+
+                // RFC6536 default-deny-{all,write} support
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
+                    DefaultDenyAllStatementSupport.getInstance())
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
+                    DefaultDenyWriteStatementSupport.getInstance())
+
+                // RFC7952 annotation support
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, AnnotationStatementSupport.getInstance())
+
+                // RFC8040 yang-data support
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, YangDataStatementSupport.getInstance())
+
+                // RFC8528 yang-data support
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, MountPointStatementSupport.getInstance())
+
+                // OpenConfig extensions support (except openconfig-version)
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
+                    EncryptedValueStatementSupport.getInstance())
+                .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION,
+                    HashedValueStatementSupport.getInstance());
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserFactoryImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserFactoryImpl.java
new file mode 100644 (file)
index 0000000..f64fb23
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.kohsuke.MetaInfServices;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+
+/**
+ * Reference {@link YangParserFactory} implementation.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@MetaInfServices
+public final class YangParserFactoryImpl implements YangParserFactory {
+    private static final ImmutableList<StatementParserMode> SUPPORTED_MODES = ImmutableList.of(
+        StatementParserMode.DEFAULT_MODE, StatementParserMode.SEMVER_MODE);
+
+    private final CrossSourceStatementReactor reactor;
+
+    /**
+     * Construct a new {@link YangParserFactory} backed by {@link DefaultReactors#defaultReactor()}.
+     */
+    public YangParserFactoryImpl() {
+        this(DefaultReactors.defaultReactor());
+    }
+
+    /**
+     * Construct a new {@link YangParserFactory} backed by specified reactor.
+     *
+     * @param reactor Backing reactor
+     */
+    public YangParserFactoryImpl(@NonNull final CrossSourceStatementReactor reactor) {
+        this.reactor = requireNonNull(reactor);
+    }
+
+    @Override
+    public Collection<StatementParserMode> supportedParserModes() {
+        return SUPPORTED_MODES;
+    }
+
+    @Override
+    public YangParser createParser(final StatementParserMode parserMode) {
+        return new YangParserImpl(reactor.newBuild(parserMode));
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java
new file mode 100644 (file)
index 0000000..6da2bdd
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.SetMultimap;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import javax.xml.transform.TransformerException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
+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.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinDomSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinXmlSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YinStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YinTextToDomTransformer;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+import org.xml.sax.SAXException;
+
+final class YangParserImpl implements YangParser {
+    private static final @NonNull Collection<Class<? extends SchemaSourceRepresentation>> REPRESENTATIONS =
+            ImmutableList.of(ASTSchemaSource.class, YangTextSchemaSource.class,
+                YinDomSchemaSource.class, YinXmlSchemaSource.class, YinTextSchemaSource.class);
+
+    private final BuildAction buildAction;
+
+    YangParserImpl(final BuildAction buildAction) {
+        this.buildAction = requireNonNull(buildAction);
+    }
+
+    @Override
+    public @NonNull Collection<Class<? extends SchemaSourceRepresentation>> supportedSourceRepresentations() {
+        return REPRESENTATIONS;
+    }
+
+    @Override
+    public @NonNull Set<QName> supportedStatements() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public @NonNull YangParser addSource(final SchemaSourceRepresentation source) throws IOException,
+            YangSyntaxErrorException {
+        buildAction.addSources(sourceToStatementStream(source));
+        return this;
+    }
+
+    @Override
+    public @NonNull YangParser addLibSource(final SchemaSourceRepresentation source) throws IOException,
+            YangSyntaxErrorException {
+        buildAction.addLibSources(sourceToStatementStream(source));
+        return this;
+    }
+
+    @Override
+    public @NonNull YangParser setSupportedFeatures(final Set<QName> supportedFeatures) {
+        buildAction.setSupportedFeatures(supportedFeatures);
+        return this;
+    }
+
+    @Override
+    public @NonNull YangParser setModulesWithSupportedDeviations(
+            final SetMultimap<QNameModule, QNameModule> modulesDeviatedByModules) {
+        buildAction.setModulesWithSupportedDeviations(modulesDeviatedByModules);
+        return this;
+    }
+
+    @Override
+    public @NonNull List<DeclaredStatement<?>> buildDeclaredModel() throws YangParserException {
+        try {
+            return buildAction.build().getRootStatements();
+        } catch (ReactorException e) {
+            throw decodeReactorException(e);
+        }
+    }
+
+    @Override
+    public @NonNull EffectiveModelContext buildEffectiveModel() throws YangParserException {
+        try {
+            return buildAction.buildEffective();
+        } catch (ReactorException e) {
+            throw decodeReactorException(e);
+        }
+    }
+
+    private static YangParserException decodeReactorException(final ReactorException reported) {
+        // FIXME: map exception in some reasonable manner
+        return new YangParserException("Failed to assemble sources", reported);
+    }
+
+    private static StatementStreamSource sourceToStatementStream(final SchemaSourceRepresentation source)
+            throws IOException, YangSyntaxErrorException {
+        requireNonNull(source);
+        if (source instanceof ASTSchemaSource) {
+            return YangStatementStreamSource.create((ASTSchemaSource) source);
+        } else if (source instanceof YangTextSchemaSource) {
+            return YangStatementStreamSource.create((YangTextSchemaSource) source);
+        } else if (source instanceof YinDomSchemaSource) {
+            return YinStatementStreamSource.create((YinDomSchemaSource) source);
+        } else if (source instanceof YinTextSchemaSource) {
+            try {
+                return YinStatementStreamSource.create(YinTextToDomTransformer.transformSource(
+                    (YinTextSchemaSource) source));
+            } catch (SAXException e) {
+                throw new YangSyntaxErrorException(source.getIdentifier(), 0, 0, "Failed to parse XML text", e);
+            }
+        } else if (source instanceof YinXmlSchemaSource) {
+            try {
+                return YinStatementStreamSource.create((YinXmlSchemaSource) source);
+            } catch (TransformerException e) {
+                throw new YangSyntaxErrorException(source.getIdentifier(), 0, 0,
+                    "Failed to assemble in-memory representation", e);
+            }
+        } else {
+            throw new IllegalArgumentException("Unsupported source " + source);
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/osgi/Activator.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/osgi/Activator.java
new file mode 100644 (file)
index 0000000..c42d0b3
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.impl.osgi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserFactoryImpl;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * YANG parser implementation activator. Publishes a {@link YangParserFactory} implementation on bundle start.
+ *
+ * @author Robert Varga
+ */
+public final class Activator implements BundleActivator {
+    private ServiceRegistration<@NonNull YangParserFactory> registration;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        registration = context.registerService(YangParserFactory.class, new YangParserFactoryImpl(), null);
+    }
+
+    @Override
+    public void stop(final BundleContext context) throws Exception {
+        if (registration != null) {
+            registration.unregister();
+            registration = null;
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/osgi/package-info.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/osgi/package-info.java
new file mode 100644 (file)
index 0000000..f052302
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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
+ */
+/**
+ * OSGi-specific YANG parser code. Contains activators and similar.
+ */
+package org.opendaylight.yangtools.yang.parser.impl.osgi;
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/package-info.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/package-info.java
new file mode 100644 (file)
index 0000000..7e4a928
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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
+ */
+/**
+ * This package holds the 'parser implementation', which really is just glue code holding together the statement
+ * reactor, base RFC6020/RFC7950 support bundles and supported semantic extensions.
+ */
+package org.opendaylight.yangtools.yang.parser.impl;
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractYangTextSchemaSourceRegistration.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/AbstractYangTextSchemaSourceRegistration.java
new file mode 100644 (file)
index 0000000..bbab150
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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 AbstractYangTextSchemaSourceRegistration extends AbstractObjectRegistration<YangTextSchemaSource>
+        implements YangTextSchemaSourceRegistration {
+    protected AbstractYangTextSchemaSourceRegistration(final YangTextSchemaSource instance) {
+        super(instance);
+    }
+}
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..582ee08
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * 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.MoreObjects;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.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.
+abstract class DependencyResolver {
+    private static final Logger LOG = LoggerFactory.getLogger(DependencyResolver.class);
+    private final ImmutableList<SourceIdentifier> resolvedSources;
+    private final ImmutableList<SourceIdentifier> unresolvedSources;
+    private final ImmutableMultimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
+
+    protected DependencyResolver(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
+        final Collection<SourceIdentifier> resolved = new ArrayList<>(depInfo.size());
+        final Collection<SourceIdentifier> pending = new ArrayList<>(depInfo.keySet());
+        final Map<SourceIdentifier, BelongsToDependency> submodules = new HashMap<>();
+
+        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;
+
+                final Set<ModuleImport> dependencies = dep.getDependencies();
+
+                // in case of submodule, remember belongs to
+                if (dep instanceof YangModelDependencyInfo.SubmoduleDependencyInfo) {
+                    final String parent = ((YangModelDependencyInfo.SubmoduleDependencyInfo) dep).getParentModule();
+                    submodules.put(id, new BelongsToDependency(parent));
+                }
+
+                for (final ModuleImport mi : dependencies) {
+                    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);
+
+        /// Additional check only for belongs-to statement
+        for (final Entry<SourceIdentifier, BelongsToDependency> submodule : submodules.entrySet()) {
+            final BelongsToDependency belongs = submodule.getValue();
+            final SourceIdentifier sourceIdentifier = submodule.getKey();
+            if (!isKnown(resolved, belongs)) {
+                LOG.debug("Source {} is missing parent {}", sourceIdentifier, belongs);
+                pending.add(sourceIdentifier);
+                resolved.remove(sourceIdentifier);
+            }
+        }
+
+        final Multimap<SourceIdentifier, ModuleImport> imports = ArrayListMultimap.create();
+        for (final SourceIdentifier id : pending) {
+            final YangModelDependencyInfo dep = depInfo.get(id);
+            for (final ModuleImport mi : dep.getDependencies()) {
+                if (!isKnown(pending, mi) && !isKnown(resolved, mi)) {
+                    imports.put(id, mi);
+                }
+            }
+        }
+
+        this.resolvedSources = ImmutableList.copyOf(resolved);
+        this.unresolvedSources = ImmutableList.copyOf(pending);
+        this.unsatisfiedImports = ImmutableMultimap.copyOf(imports);
+    }
+
+    protected abstract boolean isKnown(Collection<SourceIdentifier> haystack, ModuleImport mi);
+
+    /**
+     * Collection of sources which have been resolved.
+     */
+    Collection<SourceIdentifier> getResolvedSources() {
+        return resolvedSources;
+    }
+
+    /**
+     * Collection of sources which have not been resolved due to missing dependencies.
+     */
+    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.
+     *
+     * <p>
+     * Note that this map contains only imports which are missing from the reactor,
+     * not transitive failures.
+     *
+     * <p>
+     * Examples:
+     * <ul><li>
+     * If A imports B, B imports C, and both A and B are in the reactor, only B->C
+     * will be reported.
+     * </li><li>
+     * 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.
+     * </li></ul>
+     */
+    Multimap<SourceIdentifier, ModuleImport> getUnsatisfiedImports() {
+        return unsatisfiedImports;
+    }
+
+    private static class BelongsToDependency implements ModuleImport {
+        private final String parent;
+
+        BelongsToDependency(final String parent) {
+            this.parent = parent;
+        }
+
+        @Override
+        public String getModuleName() {
+            return parent;
+        }
+
+        @Override
+        public Optional<Revision> getRevision() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<String> getDescription() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<String> getReference() {
+            return Optional.empty();
+        }
+
+        @Override
+        public String getPrefix() {
+            return null;
+        }
+
+        @Override
+        public Optional<SemVer> getSemanticVersion() {
+            return Optional.empty();
+        }
+
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this).add("parent", parent).toString();
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/RevisionDependencyResolver.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/RevisionDependencyResolver.java
new file mode 100644 (file)
index 0000000..5d905d6
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2016 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 java.util.Collection;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangModelDependencyInfo;
+
+final class RevisionDependencyResolver extends DependencyResolver {
+
+    protected RevisionDependencyResolver(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
+        super(depInfo);
+    }
+
+    protected static SourceIdentifier findWildcard(final Iterable<SourceIdentifier> haystack, final String needle) {
+        for (final SourceIdentifier r : haystack) {
+            if (needle.equals(r.getName())) {
+                return r;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    protected boolean isKnown(final Collection<SourceIdentifier> haystack, final ModuleImport mi) {
+        final SourceIdentifier msi = RevisionSourceIdentifier.create(mi.getModuleName(), mi.getRevision());
+
+        // Quick lookup
+        if (haystack.contains(msi)) {
+            return true;
+        }
+
+        // Slow revision-less walk
+        return mi.getRevision().isEmpty() && findWildcard(haystack, mi.getModuleName()) != null;
+    }
+
+    public static RevisionDependencyResolver create(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
+        return new RevisionDependencyResolver(depInfo);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SemVerDependencyResolver.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SemVerDependencyResolver.java
new file mode 100644 (file)
index 0000000..198da2f
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2016 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 java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangModelDependencyInfo;
+
+final class SemVerDependencyResolver extends DependencyResolver {
+
+    protected SemVerDependencyResolver(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
+        super(depInfo);
+    }
+
+    protected static SourceIdentifier findCompatibleVersion(final Iterable<SourceIdentifier> haystack,
+            final ModuleImport mi) {
+        final String requestedModuleName = mi.getModuleName();
+        for (SourceIdentifier r : haystack) {
+            if (requestedModuleName.equals(r.getName())
+                    && isCompatible(((SemVerSourceIdentifier) r).getSemanticVersion(), mi.getSemanticVersion())) {
+                return r;
+            }
+        }
+
+        return null;
+    }
+
+    private static boolean isCompatible(final Optional<SemVer> moduleSemVer, final Optional<SemVer> importSemVer) {
+        if (importSemVer.isEmpty()) {
+            // Import does not care about the version
+            return true;
+        }
+        if (moduleSemVer.isEmpty()) {
+            // Modules which do not declare a semantic version are incompatible with imports which do
+            return false;
+        }
+
+        final SemVer modVer = moduleSemVer.get();
+        final SemVer impVer = importSemVer.get();
+        return modVer.getMajor() == impVer.getMajor() && modVer.compareTo(impVer) >= 0;
+    }
+
+    @Override
+    protected boolean isKnown(final Collection<SourceIdentifier> haystack, final ModuleImport mi) {
+        final SemVerSourceIdentifier msi = SemVerSourceIdentifier.create(mi.getModuleName(), mi.getRevision(),
+            mi.getSemanticVersion().orElse(null));
+
+        // Quick lookup
+        if (haystack.contains(msi)) {
+            return true;
+        }
+
+        // Slow revision-less walk
+        return findCompatibleVersion(haystack, mi) != null;
+    }
+
+    public static SemVerDependencyResolver create(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
+        return new SemVerDependencyResolver(depInfo);
+    }
+}
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..95405b1
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * 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 static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
+
+import com.google.common.base.Function;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FluentFuture;
+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.MoreExecutors;
+import com.google.common.util.concurrent.SettableFuture;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.eclipse.jdt.annotation.NonNull;
+import org.gaul.modernizer_maven_annotations.SuppressModernizer;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
+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.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.parser.impl.DefaultReactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangModelDependencyInfo;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class SharedSchemaContextFactory implements EffectiveModelContextFactory {
+    private static final Logger LOG = LoggerFactory.getLogger(SharedSchemaContextFactory.class);
+
+    private final Cache<Collection<SourceIdentifier>, EffectiveModelContext> revisionCache = CacheBuilder.newBuilder()
+            .weakValues().build();
+    private final Cache<Collection<SourceIdentifier>, EffectiveModelContext> semVerCache = CacheBuilder.newBuilder()
+            .weakValues().build();
+    private final @NonNull SchemaRepository repository;
+    private final @NonNull SchemaContextFactoryConfiguration config;
+
+    SharedSchemaContextFactory(final @NonNull SchemaRepository repository,
+        final @NonNull SchemaContextFactoryConfiguration config) {
+        this.repository = requireNonNull(repository);
+        this.config = requireNonNull(config);
+    }
+
+    @Override
+    public @NonNull ListenableFuture<EffectiveModelContext> createEffectiveModelContext(
+            final @NonNull Collection<SourceIdentifier> requiredSources) {
+        return createSchemaContext(requiredSources,
+                config.getStatementParserMode() == StatementParserMode.SEMVER_MODE ? semVerCache : revisionCache,
+                new AssembleSources(config));
+    }
+
+    private @NonNull ListenableFuture<EffectiveModelContext> createSchemaContext(
+            final Collection<SourceIdentifier> requiredSources,
+            final Cache<Collection<SourceIdentifier>, EffectiveModelContext> cache,
+            final AsyncFunction<List<ASTSchemaSource>, EffectiveModelContext> assembleSources) {
+        // Make sources unique
+        final List<SourceIdentifier> uniqueSourceIdentifiers = deDuplicateSources(requiredSources);
+
+        final EffectiveModelContext existing = cache.getIfPresent(uniqueSourceIdentifiers);
+        if (existing != null) {
+            LOG.debug("Returning cached context {}", existing);
+            return immediateFluentFuture(existing);
+        }
+
+        // Request all sources be loaded
+        ListenableFuture<List<ASTSchemaSource>> sf = Futures.allAsList(Collections2.transform(uniqueSourceIdentifiers,
+            this::requestSource));
+
+        // Detect mismatch between requested Source IDs and IDs that are extracted from parsed source
+        // Also remove duplicates if present
+        // We are relying on preserved order of uniqueSourceIdentifiers as well as sf
+        sf = Futures.transform(sf, new SourceIdMismatchDetector(uniqueSourceIdentifiers),
+            MoreExecutors.directExecutor());
+
+        // Assemble sources into a schema context
+        final ListenableFuture<EffectiveModelContext> cf = Futures.transformAsync(sf, assembleSources,
+            MoreExecutors.directExecutor());
+
+        final SettableFuture<EffectiveModelContext> rf = SettableFuture.create();
+        Futures.addCallback(cf, new FutureCallback<EffectiveModelContext>() {
+            @Override
+            public void onSuccess(final EffectiveModelContext result) {
+                // Deduplicate concurrent loads
+                final EffectiveModelContext existing;
+                try {
+                    existing = cache.get(uniqueSourceIdentifiers, () -> result);
+                } catch (ExecutionException e) {
+                    LOG.warn("Failed to recheck result with cache, will use computed value", e);
+                    rf.set(result);
+                    return;
+                }
+
+                rf.set(existing);
+            }
+
+            @Override
+            public void onFailure(final Throwable cause) {
+                LOG.debug("Failed to assemble sources", cause);
+                rf.setException(cause);
+            }
+        }, MoreExecutors.directExecutor());
+
+        return rf;
+    }
+
+    private ListenableFuture<ASTSchemaSource> requestSource(final @NonNull SourceIdentifier identifier) {
+        return repository.getSchemaSource(identifier, ASTSchemaSource.class);
+    }
+
+    /**
+     * Return a set of de-duplicated inputs.
+     *
+     * @return set (preserving ordering) from the input collection
+     */
+    private static List<SourceIdentifier> deDuplicateSources(final Collection<SourceIdentifier> requiredSources) {
+        final Set<SourceIdentifier> uniqueSourceIdentifiers = new LinkedHashSet<>(requiredSources);
+        if (uniqueSourceIdentifiers.size() == requiredSources.size()) {
+            // Can potentially reuse input
+            return ImmutableList.copyOf(requiredSources);
+        }
+
+        LOG.warn("Duplicate sources requested for schema context, removed duplicate sources: {}",
+            Collections2.filter(uniqueSourceIdentifiers, input -> Iterables.frequency(requiredSources, input) > 1));
+        return ImmutableList.copyOf(uniqueSourceIdentifiers);
+    }
+
+    @SuppressModernizer
+    private static final class SourceIdMismatchDetector implements Function<List<ASTSchemaSource>,
+            List<ASTSchemaSource>> {
+        private final List<SourceIdentifier> sourceIdentifiers;
+
+        SourceIdMismatchDetector(final List<SourceIdentifier> sourceIdentifiers) {
+            this.sourceIdentifiers = requireNonNull(sourceIdentifiers);
+        }
+
+        @Override
+        public List<ASTSchemaSource> apply(final List<ASTSchemaSource> input) {
+            final Map<SourceIdentifier, ASTSchemaSource> filtered = new LinkedHashMap<>();
+
+            for (int i = 0; i < input.size(); i++) {
+
+                final SourceIdentifier expectedSId = sourceIdentifiers.get(i);
+                final ASTSchemaSource astSchemaSource = input.get(i);
+                final SourceIdentifier realSId = astSchemaSource.getIdentifier();
+
+                if (!expectedSId.equals(realSId)) {
+                    LOG.warn("Source identifier mismatch for module \"{}\", requested as {} but actually is {}. "
+                        + "Using actual id", expectedSId.getName(), expectedSId, realSId);
+                }
+
+                if (filtered.containsKey(realSId)) {
+                    LOG.warn("Duplicate source for module {} detected in reactor", realSId);
+                }
+
+                filtered.put(realSId, astSchemaSource);
+
+            }
+            return ImmutableList.copyOf(filtered.values());
+        }
+    }
+
+    private static final class AssembleSources implements AsyncFunction<List<ASTSchemaSource>, EffectiveModelContext> {
+        private final @NonNull SchemaContextFactoryConfiguration config;
+        private final @NonNull Function<ASTSchemaSource, SourceIdentifier> getIdentifier;
+
+        private AssembleSources(final @NonNull SchemaContextFactoryConfiguration config) {
+            this.config = config;
+            switch (config.getStatementParserMode()) {
+                case SEMVER_MODE:
+                    this.getIdentifier = ASTSchemaSource::getSemVerIdentifier;
+                    break;
+                default:
+                    this.getIdentifier = ASTSchemaSource::getIdentifier;
+            }
+        }
+
+        @Override
+        public FluentFuture<EffectiveModelContext> apply(final List<ASTSchemaSource> sources)
+                throws SchemaResolutionException, ReactorException {
+            final Map<SourceIdentifier, ASTSchemaSource> srcs = Maps.uniqueIndex(sources, getIdentifier);
+            final Map<SourceIdentifier, YangModelDependencyInfo> deps =
+                    Maps.transformValues(srcs, ASTSchemaSource::getDependencyInformation);
+
+            LOG.debug("Resolving dependency reactor {}", deps);
+
+            final StatementParserMode statementParserMode = config.getStatementParserMode();
+            final DependencyResolver res = statementParserMode == StatementParserMode.SEMVER_MODE
+                    ? SemVerDependencyResolver.create(deps) : RevisionDependencyResolver.create(deps);
+            if (!res.getUnresolvedSources().isEmpty()) {
+                LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(),
+                    res.getUnsatisfiedImports());
+                throw new SchemaResolutionException("Failed to resolve required models",
+                        res.getResolvedSources(), res.getUnsatisfiedImports());
+            }
+
+            final BuildAction reactor = DefaultReactors.defaultReactor().newBuild(statementParserMode);
+            config.getSupportedFeatures().ifPresent(reactor::setSupportedFeatures);
+            config.getModulesDeviatedByModules().ifPresent(reactor::setModulesWithSupportedDeviations);
+
+            for (final Entry<SourceIdentifier, ASTSchemaSource> e : srcs.entrySet()) {
+                final ASTSchemaSource ast = e.getValue();
+                final ParserRuleContext parserRuleCtx = ast.getAST();
+                checkArgument(parserRuleCtx instanceof StatementContext, "Unsupported context class %s for source %s",
+                    parserRuleCtx.getClass(), e.getKey());
+
+                reactor.addSource(YangStatementStreamSource.create(e.getKey(), (StatementContext) parserRuleCtx,
+                    ast.getSymbolicName().orElse(null)));
+            }
+
+            final EffectiveModelContext schemaContext;
+            try {
+                schemaContext = reactor.buildEffective();
+            } catch (final ReactorException ex) {
+                throw new SchemaResolutionException("Failed to resolve required models", ex.getSourceIdentifier(), ex);
+            }
+
+            return immediateFluentFuture(schemaContext);
+        }
+    }
+}
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..b25cbee
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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 java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import org.eclipse.jdt.annotation.NonNull;
+import org.kohsuke.MetaInfServices;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+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.
+ *
+ * <p>
+ * Note: for current implementation, "same" means the same filter and the same set of {@link SourceIdentifier}s.
+ */
+@Beta
+@MetaInfServices(value = SchemaRepository.class)
+public final class SharedSchemaRepository extends AbstractSchemaRepository implements Identifiable<String> {
+    private final LoadingCache<SchemaContextFactoryConfiguration, EffectiveModelContextFactory> cacheByConfig =
+            CacheBuilder.newBuilder().softValues()
+            .build(new CacheLoader<SchemaContextFactoryConfiguration, EffectiveModelContextFactory>() {
+                @Override
+                public EffectiveModelContextFactory load(final SchemaContextFactoryConfiguration key) {
+                    return new SharedSchemaContextFactory(SharedSchemaRepository.this, key);
+                }
+            });
+
+    private final @NonNull String id;
+
+    public SharedSchemaRepository(final String id) {
+        this.id = requireNonNull(id);
+    }
+
+    @Override
+    public @NonNull String getIdentifier() {
+        return id;
+    }
+
+    @Override
+    public @NonNull EffectiveModelContextFactory createEffectiveModelContextFactory(
+            final @NonNull SchemaContextFactoryConfiguration config) {
+        return cacheByConfig.getUnchecked(config);
+    }
+
+    @Override
+    public String toString() {
+        return "SchemaRepository: " + id;
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/YangTextSchemaContextResolver.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/YangTextSchemaContextResolver.java
new file mode 100644 (file)
index 0000000..b89bf6f
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2015 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 static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Verify;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+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.EffectiveModelContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
+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.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+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.PotentialSchemaSource.Costs;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaListenerRegistration;
+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.model.repo.util.InMemorySchemaSourceCache;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class YangTextSchemaContextResolver implements AutoCloseable, SchemaSourceProvider<YangTextSchemaSource> {
+    private static final Logger LOG = LoggerFactory.getLogger(YangTextSchemaContextResolver.class);
+    private static final long SOURCE_LIFETIME_SECONDS = 60;
+
+    private final Collection<SourceIdentifier> requiredSources = new ConcurrentLinkedDeque<>();
+    private final Multimap<SourceIdentifier, YangTextSchemaSource> texts = ArrayListMultimap.create();
+    private final AtomicReference<Optional<EffectiveModelContext>> currentSchemaContext =
+            new AtomicReference<>(Optional.empty());
+    private final InMemorySchemaSourceCache<ASTSchemaSource> cache;
+    private final SchemaListenerRegistration transReg;
+    private final SchemaSourceRegistry registry;
+    private final SchemaRepository repository;
+    private volatile Object version = new Object();
+    private volatile Object contextVersion = version;
+
+    private YangTextSchemaContextResolver(final SchemaRepository repository, final SchemaSourceRegistry registry) {
+        this.repository = requireNonNull(repository);
+        this.registry = requireNonNull(registry);
+
+        final TextToASTTransformer t = TextToASTTransformer.create(repository, registry);
+        transReg = registry.registerSchemaSourceListener(t);
+
+        cache = InMemorySchemaSourceCache.createSoftCache(registry, ASTSchemaSource.class, SOURCE_LIFETIME_SECONDS,
+            TimeUnit.SECONDS);
+    }
+
+    public static YangTextSchemaContextResolver create(final String name) {
+        final SharedSchemaRepository sharedRepo = new SharedSchemaRepository(name);
+        return new YangTextSchemaContextResolver(sharedRepo, sharedRepo);
+    }
+
+    /**
+     * Register a {@link YangTextSchemaSource}.
+     *
+     * @param source YANG text source
+     * @return a YangTextSchemaSourceRegistration
+     * @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 @NonNull YangTextSchemaSourceRegistration registerSource(final @NonNull YangTextSchemaSource source)
+            throws SchemaSourceException, IOException, YangSyntaxErrorException {
+        checkArgument(source != null);
+
+        final ASTSchemaSource ast = TextToASTTransformer.transformText(source);
+        LOG.trace("Resolved source {} to source {}", source, ast);
+
+        // AST carries an accurate identifier, check if it matches the one supplied by the source. If it
+        // does not, check how much it differs and emit a warning.
+        final SourceIdentifier providedId = source.getIdentifier();
+        final SourceIdentifier parsedId = ast.getIdentifier();
+        final YangTextSchemaSource text;
+        if (!parsedId.equals(providedId)) {
+            if (!parsedId.getName().equals(providedId.getName())) {
+                LOG.info("Provided module name {} does not match actual text {}, corrected",
+                    providedId.toYangFilename(), parsedId.toYangFilename());
+            } else {
+                final Optional<Revision> sourceRev = providedId.getRevision();
+                final Optional<Revision> astRev = parsedId.getRevision();
+                if (sourceRev.isPresent()) {
+                    if (!sourceRev.equals(astRev)) {
+                        LOG.info("Provided module revision {} does not match actual text {}, corrected",
+                            providedId.toYangFilename(), parsedId.toYangFilename());
+                    }
+                } else {
+                    LOG.debug("Expanded module {} to {}", providedId.toYangFilename(), parsedId.toYangFilename());
+                }
+            }
+
+            text = YangTextSchemaSource.delegateForByteSource(parsedId, source);
+        } else {
+            text = source;
+        }
+
+        synchronized (this) {
+            texts.put(parsedId, text);
+            LOG.debug("Populated {} with text", parsedId);
+
+            final SchemaSourceRegistration<YangTextSchemaSource> reg = registry.registerSchemaSource(this,
+                PotentialSchemaSource.create(parsedId, YangTextSchemaSource.class, Costs.IMMEDIATE.getValue()));
+            requiredSources.add(parsedId);
+            cache.schemaSourceEncountered(ast);
+            LOG.debug("Added source {} to schema context requirements", parsedId);
+            version = new Object();
+
+            return new AbstractYangTextSchemaSourceRegistration(text) {
+                @Override
+                protected void removeRegistration() {
+                    synchronized (YangTextSchemaContextResolver.this) {
+                        requiredSources.remove(parsedId);
+                        LOG.trace("Removed source {} from schema context requirements", parsedId);
+                        version = new Object();
+                        reg.close();
+                        texts.remove(parsedId, text);
+                    }
+                }
+            };
+        }
+    }
+
+    /**
+     * Register a URL containing a YANG text.
+     *
+     * @param url YANG text source URL
+     * @return a YangTextSchemaSourceRegistration for this 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 @NonNull YangTextSchemaSourceRegistration registerSource(final @NonNull URL url)
+            throws SchemaSourceException, IOException, YangSyntaxErrorException {
+        checkArgument(url != null, "Supplied URL must not be null");
+
+        final String path = url.getPath();
+        final String fileName = path.substring(path.lastIndexOf('/') + 1);
+        final SourceIdentifier guessedId = guessSourceIdentifier(fileName);
+        return registerSource(new YangTextSchemaSource(guessedId) {
+            @Override
+            public InputStream openStream() throws IOException {
+                return url.openStream();
+            }
+
+            @Override
+            protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+                return toStringHelper.add("url", url);
+            }
+        });
+    }
+
+    private static SourceIdentifier guessSourceIdentifier(final @NonNull String fileName) {
+        try {
+            return YangTextSchemaSource.identifierFromFilename(fileName);
+        } catch (final IllegalArgumentException e) {
+            LOG.warn("Invalid file name format in '{}'", fileName, e);
+            return RevisionSourceIdentifier.create(fileName);
+        }
+    }
+
+    /**
+     * 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<? extends EffectiveModelContext> getEffectiveModelContext() {
+        return getEffectiveModelContext(StatementParserMode.DEFAULT_MODE);
+    }
+
+    /**
+     * Try to parse all currently available yang files and build new schema context depending on specified parsing mode.
+     *
+     * @param statementParserMode mode of statement parser
+     * @return new schema context iif there is at least 1 yang file registered and
+     *         new schema context was successfully built.
+     */
+    public Optional<? extends EffectiveModelContext> getEffectiveModelContext(
+            final StatementParserMode statementParserMode) {
+        final EffectiveModelContextFactory factory = repository.createEffectiveModelContextFactory(
+            config(statementParserMode));
+        Optional<EffectiveModelContext> sc;
+        Object ver;
+        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 {
+                ver = version;
+                sources = ImmutableSet.copyOf(requiredSources);
+            } while (ver != version);
+
+            while (true) {
+                final ListenableFuture<EffectiveModelContext> f = factory.createEffectiveModelContext(sources);
+                try {
+                    sc = Optional.of(f.get());
+                    break;
+                } catch (final InterruptedException e) {
+                    throw new IllegalStateException("Interrupted while assembling schema context", e);
+                } catch (final ExecutionException e) {
+                    LOG.info("Failed to fully assemble schema context for {}", sources, e);
+                    final Throwable cause = e.getCause();
+                    Verify.verify(cause instanceof SchemaResolutionException);
+                    sources = ((SchemaResolutionException) cause).getResolvedSources();
+                }
+            }
+
+            LOG.debug("Resolved schema context for {}", sources);
+
+            synchronized (this) {
+                if (contextVersion == cv) {
+                    currentSchemaContext.set(sc);
+                    contextVersion = ver;
+                }
+            }
+        } while (version == ver);
+
+        return sc;
+    }
+
+    /**
+     * 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.
+     * @deprecated Use {@link #getEffectiveModelContext()} instead.
+     */
+    @Deprecated(forRemoval = true)
+    public Optional<? extends SchemaContext> getSchemaContext() {
+        return getEffectiveModelContext();
+    }
+
+    /**
+     * Try to parse all currently available yang files and build new schema context depending on specified parsing mode.
+     *
+     * @param statementParserMode mode of statement parser
+     * @return new schema context iif there is at least 1 yang file registered and
+     *         new schema context was successfully built.
+     * @deprecated Use {@link #getEffectiveModelContext(StatementParserMode)} instead.
+     */
+    @Deprecated(forRemoval = true)
+    public Optional<? extends SchemaContext> getSchemaContext(final StatementParserMode statementParserMode) {
+        return getEffectiveModelContext(statementParserMode);
+    }
+
+    @Override
+    public synchronized FluentFuture<YangTextSchemaSource> getSource(
+            final SourceIdentifier sourceIdentifier) {
+        final Collection<YangTextSchemaSource> ret = texts.get(sourceIdentifier);
+
+        LOG.debug("Lookup {} result {}", sourceIdentifier, ret);
+        if (ret.isEmpty()) {
+            return immediateFailedFluentFuture(new MissingSchemaSourceException("URL for " + sourceIdentifier
+                + " not registered", sourceIdentifier));
+        }
+
+        return immediateFluentFuture(ret.iterator().next());
+    }
+
+    /**
+     * Return the set of sources currently available in this resolved.
+     *
+     * @return An immutable point-in-time view of available sources.
+     */
+    public synchronized Set<SourceIdentifier> getAvailableSources() {
+        return ImmutableSet.copyOf(texts.keySet());
+    }
+
+    @Beta
+    public synchronized Collection<YangTextSchemaSource> getSourceTexts(final SourceIdentifier sourceIdentifier) {
+        return ImmutableSet.copyOf(texts.get(sourceIdentifier));
+    }
+
+    @Beta
+    public EffectiveModelContext trySchemaContext() throws SchemaResolutionException {
+        return trySchemaContext(StatementParserMode.DEFAULT_MODE);
+    }
+
+    @Beta
+    @SuppressWarnings("checkstyle:avoidHidingCauseException")
+    public EffectiveModelContext trySchemaContext(final StatementParserMode statementParserMode)
+            throws SchemaResolutionException {
+        final ListenableFuture<EffectiveModelContext> future = repository
+                .createEffectiveModelContextFactory(config(statementParserMode))
+                .createEffectiveModelContext(ImmutableSet.copyOf(requiredSources));
+
+        try {
+            return future.get();
+        } catch (final InterruptedException e) {
+            throw new IllegalStateException("Interrupted while waiting for SchemaContext assembly", e);
+        } catch (final ExecutionException e) {
+            final Throwable cause = e.getCause();
+            if (cause instanceof SchemaResolutionException) {
+                throw (SchemaResolutionException) cause;
+            }
+
+            throw new SchemaResolutionException("Failed to assemble SchemaContext", e);
+        }
+    }
+
+    @Override
+    public void close() {
+        transReg.close();
+    }
+
+    private static SchemaContextFactoryConfiguration config(final StatementParserMode statementParserMode) {
+        return SchemaContextFactoryConfiguration.builder().setStatementParserMode(statementParserMode).build();
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/YangTextSchemaSourceRegistration.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/YangTextSchemaSourceRegistration.java
new file mode 100644 (file)
index 0000000..361750b
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2015 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 YangTextSchemaSourceRegistration extends ObjectRegistration<YangTextSchemaSource> {
+    @Override
+    void close();
+}
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolverTest.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/DependencyResolverTest.java
new file mode 100644 (file)
index 0000000..88a1189
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangModelDependencyInfo;
+
+@Deprecated
+public class DependencyResolverTest {
+
+    @Test
+    public void testModulesWithoutRevisionAndImport() throws Exception {
+        final Map<SourceIdentifier, YangModelDependencyInfo> map = new HashMap<>();
+
+        addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.forResource(getClass(),
+            "/no-revision/imported.yang"));
+        addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.forResource(getClass(),
+            "/no-revision/imported@2012-12-12.yang"));
+        addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.forResource(getClass(),
+            "/no-revision/top@2012-10-10.yang"));
+
+        final DependencyResolver resolved = RevisionDependencyResolver.create(map);
+
+        assertEquals(0, resolved.getUnresolvedSources().size());
+        assertEquals(0, resolved.getUnsatisfiedImports().size());
+    }
+
+    @Test
+    public void testSubmoduleNoModule() throws Exception {
+        final Map<SourceIdentifier, YangModelDependencyInfo> map = new HashMap<>();
+
+        // Subfoo does not have parent in reactor
+        addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.forResource(getClass(), "/model/subfoo.yang"));
+        addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.forResource(getClass(), "/model/bar.yang"));
+        addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.forResource(getClass(), "/model/baz.yang"));
+
+        final DependencyResolver resolved = RevisionDependencyResolver.create(map);
+
+        assertEquals(2, resolved.getResolvedSources().size());
+        assertEquals(1, resolved.getUnresolvedSources().size());
+        assertEquals(0, resolved.getUnsatisfiedImports().size());
+    }
+
+    @Test
+    public void testSubmodule() throws Exception {
+        final Map<SourceIdentifier, YangModelDependencyInfo> map = new HashMap<>();
+
+        addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.forResource(getClass(), "/model/subfoo.yang"));
+        addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.forResource(getClass(), "/model/foo.yang"));
+        addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.forResource(getClass(), "/model/bar.yang"));
+        addToMap(map, YangModelDependencyInfo.ModuleDependencyInfo.forResource(getClass(), "/model/baz.yang"));
+
+        final DependencyResolver resolved = RevisionDependencyResolver.create(map);
+        assertEquals(0, resolved.getUnresolvedSources().size());
+        assertEquals(0, resolved.getUnsatisfiedImports().size());
+        assertEquals(4, resolved.getResolvedSources().size());
+    }
+
+    private static void addToMap(final Map<SourceIdentifier, YangModelDependencyInfo> map,
+            final YangModelDependencyInfo yangModelDependencyInfo) {
+        map.put(getSourceId(yangModelDependencyInfo), yangModelDependencyInfo);
+    }
+
+    private static SourceIdentifier getSourceId(final YangModelDependencyInfo depInfo) {
+        final String name = depInfo.getName();
+        return RevisionSourceIdentifier.create(name, depInfo.getRevision());
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/MultipleRevImportBug6875Test.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/MultipleRevImportBug6875Test.java
new file mode 100644 (file)
index 0000000..68108c8
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
+
+public class MultipleRevImportBug6875Test {
+    private static final String BAR_NS = "bar";
+    private static final String BAR_REV_1 = "2017-02-06";
+    private static final String BAR_REV_2 = "1999-01-01";
+    private static final String BAR_REV_3 = "1970-01-01";
+    private static final String FOO_NS = "foo";
+
+    @Test
+    public void testYang11() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository(
+                "shared-schema-repo-multiple-rev-import-test");
+
+        final SettableSchemaProvider<ASTSchemaSource> foo = getSourceProvider(
+            "/rfc7950/bug6875/yang1-1/foo.yang");
+        final SettableSchemaProvider<ASTSchemaSource> bar1 = getSourceProvider(
+            "/rfc7950/bug6875/yang1-1/bar@1999-01-01.yang");
+        final SettableSchemaProvider<ASTSchemaSource> bar2 = getSourceProvider(
+            "/rfc7950/bug6875/yang1-1/bar@2017-02-06.yang");
+        final SettableSchemaProvider<ASTSchemaSource> bar3 = getSourceProvider(
+            "/rfc7950/bug6875/yang1-1/bar@1970-01-01.yang");
+
+        setAndRegister(sharedSchemaRepository, foo);
+        setAndRegister(sharedSchemaRepository, bar1);
+        setAndRegister(sharedSchemaRepository, bar2);
+        setAndRegister(sharedSchemaRepository, bar3);
+
+        final ListenableFuture<EffectiveModelContext> schemaContextFuture =
+                sharedSchemaRepository.createEffectiveModelContextFactory().createEffectiveModelContext(
+                    foo.getId(), bar1.getId(), bar2.getId(), bar3.getId());
+        assertTrue(schemaContextFuture.isDone());
+
+        final SchemaContext context = schemaContextFuture.get();
+        assertEquals(context.getModules().size(), 4);
+
+        assertTrue(findNode(context, ImmutableList.of(foo("root"), foo("my-container-1")))
+            instanceof ContainerSchemaNode);
+        assertTrue(findNode(context, ImmutableList.of(foo("root"), foo("my-container-2")))
+            instanceof ContainerSchemaNode);
+
+        assertTrue(findNode(context, ImmutableList.of(bar3("root"), foo("my-container-1")))
+            instanceof ContainerSchemaNode);
+        assertTrue(findNode(context, ImmutableList.of(bar3("root"), foo("my-container-2")))
+            instanceof ContainerSchemaNode);
+
+        assertNull(findNode(context, ImmutableList.of(bar2("root"), foo("my-container-1"))));
+        assertNull(findNode(context, ImmutableList.of(bar2("root"), foo("my-container-2"))));
+
+        assertNull(findNode(context, ImmutableList.of(bar1("root"), foo("my-container-1"))));
+        assertNull(findNode(context, ImmutableList.of(bar1("root"), foo("my-container-2"))));
+    }
+
+    @Test
+    public void testYang10() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository(
+                "shared-schema-repo-multiple-rev-import-test");
+
+        final SettableSchemaProvider<ASTSchemaSource> foo = getSourceProvider(
+            "/rfc7950/bug6875/yang1-0/foo.yang");
+        final SettableSchemaProvider<ASTSchemaSource> bar1 = getSourceProvider(
+            "/rfc7950/bug6875/yang1-0/bar@1999-01-01.yang");
+        final SettableSchemaProvider<ASTSchemaSource> bar2 = getSourceProvider(
+            "/rfc7950/bug6875/yang1-0/bar@2017-02-06.yang");
+
+        setAndRegister(sharedSchemaRepository, foo);
+        setAndRegister(sharedSchemaRepository, bar1);
+        setAndRegister(sharedSchemaRepository, bar2);
+
+        final ListenableFuture<EffectiveModelContext> schemaContextFuture =
+                sharedSchemaRepository.createEffectiveModelContextFactory().createEffectiveModelContext(
+                    foo.getId(), bar1.getId(), bar2.getId());
+        assertTrue(schemaContextFuture.isDone());
+
+        try {
+            schemaContextFuture.get();
+            fail("Test should fail due to invalid imports of yang source.");
+        } catch (final ExecutionException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                "Module:bar imported twice with different revisions"));
+        }
+    }
+
+    private static void setAndRegister(final SharedSchemaRepository sharedSchemaRepository,
+            final SettableSchemaProvider<ASTSchemaSource> source) {
+        source.register(sharedSchemaRepository);
+        source.setResult();
+    }
+
+    private static SettableSchemaProvider<ASTSchemaSource> getSourceProvider(final String resourceName)
+            throws Exception {
+        final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName);
+        return SettableSchemaProvider.createImmediate(TextToASTTransformer.transformText(yangSource),
+            ASTSchemaSource.class);
+    }
+
+    private static SchemaNode findNode(final SchemaContext context, final Iterable<QName> qnames) {
+        return SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(qnames, true));
+    }
+
+    private static QName foo(final String localName) {
+        return QName.create(FOO_NS, localName);
+    }
+
+    private static QName bar1(final String localName) {
+        return QName.create(BAR_NS, BAR_REV_1, localName);
+    }
+
+    private static QName bar2(final String localName) {
+        return QName.create(BAR_NS, BAR_REV_2, localName);
+    }
+
+    private static QName bar3(final String localName) {
+        return QName.create(BAR_NS, BAR_REV_3, localName);
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/OpenconfigVerSharedSchemaRepositoryTest.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/OpenconfigVerSharedSchemaRepositoryTest.java
new file mode 100644 (file)
index 0000000..4fa3f12
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2016 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
+
+public class OpenconfigVerSharedSchemaRepositoryTest {
+
+    @Test
+    public void testSemVerSharedSchemaRepository() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository(
+                "openconfig-ver-shared-schema-repo-test");
+
+        final SettableSchemaProvider<ASTSchemaSource> bar = getImmediateYangSourceProviderFromResource(
+                "/openconfig-version/openconfigver-shared-schema-repository/bar@2016-01-01.yang");
+        bar.register(sharedSchemaRepository);
+        bar.setResult();
+        final SettableSchemaProvider<ASTSchemaSource> foo = getImmediateYangSourceProviderFromResource(
+                "/openconfig-version/openconfigver-shared-schema-repository/foo.yang");
+        foo.register(sharedSchemaRepository);
+        foo.setResult();
+        final SettableSchemaProvider<ASTSchemaSource> semVer = getImmediateYangSourceProviderFromResource(
+                "/openconfig-version/openconfigver-shared-schema-repository/openconfig-extensions.yang");
+        semVer.register(sharedSchemaRepository);
+        semVer.setResult();
+
+        final EffectiveModelContextFactory fact = sharedSchemaRepository.createEffectiveModelContextFactory(
+            SchemaContextFactoryConfiguration.builder().setStatementParserMode(StatementParserMode.SEMVER_MODE)
+            .build());
+
+        final ListenableFuture<EffectiveModelContext> inetAndTopologySchemaContextFuture =
+                fact.createEffectiveModelContext(bar.getId(), foo.getId(), semVer.getId());
+        assertTrue(inetAndTopologySchemaContextFuture.isDone());
+        assertSchemaContext(inetAndTopologySchemaContextFuture.get(), 3);
+
+        final ListenableFuture<EffectiveModelContext> barSchemaContextFuture = fact.createEffectiveModelContext(
+            bar.getId(), semVer.getId());
+        assertTrue(barSchemaContextFuture.isDone());
+        assertSchemaContext(barSchemaContextFuture.get(), 2);
+    }
+
+    @Test
+    public void testSharedSchemaRepository() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("shared-schema-repo-test");
+
+        final SettableSchemaProvider<ASTSchemaSource> bar = getImmediateYangSourceProviderFromResource(
+                "/openconfig-version/shared-schema-repository/bar@2016-01-01.yang");
+        bar.register(sharedSchemaRepository);
+        bar.setResult();
+        final SettableSchemaProvider<ASTSchemaSource> foo = getImmediateYangSourceProviderFromResource(
+                "/openconfig-version/shared-schema-repository/foo.yang");
+        foo.register(sharedSchemaRepository);
+        foo.setResult();
+        final SettableSchemaProvider<ASTSchemaSource> semVer = getImmediateYangSourceProviderFromResource(
+                "/openconfig-version/shared-schema-repository/openconfig-extensions.yang");
+        semVer.register(sharedSchemaRepository);
+        semVer.setResult();
+
+        final EffectiveModelContextFactory fact = sharedSchemaRepository.createEffectiveModelContextFactory();
+        final ListenableFuture<EffectiveModelContext> inetAndTopologySchemaContextFuture =
+                fact.createEffectiveModelContext(bar.getId(), foo.getId(), semVer.getId());
+        assertTrue(inetAndTopologySchemaContextFuture.isDone());
+        assertSchemaContext(inetAndTopologySchemaContextFuture.get(), 3);
+
+        final ListenableFuture<EffectiveModelContext> barSchemaContextFuture =
+                fact.createEffectiveModelContext(bar.getId(), semVer.getId());
+        assertTrue(barSchemaContextFuture.isDone());
+        assertSchemaContext(barSchemaContextFuture.get(), 2);
+    }
+
+    private static void assertSchemaContext(final SchemaContext schemaContext, final int moduleSize) {
+        assertNotNull(schemaContext);
+        assertEquals(moduleSize, schemaContext.getModules().size());
+    }
+
+    static SettableSchemaProvider<ASTSchemaSource> getImmediateYangSourceProviderFromResource(final String resourceName)
+            throws Exception {
+        final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName);
+        return SettableSchemaProvider.createImmediate(TextToASTTransformer.transformText(yangSource),
+            ASTSchemaSource.class);
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SchemaContextFactoryDeviationsTest.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SchemaContextFactoryDeviationsTest.java
new file mode 100644 (file)
index 0000000..36ed59a
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2017 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 org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.SetMultimap;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+
+public class SchemaContextFactoryDeviationsTest {
+    private static final String FOO = "/bug9195/foo.yang";
+    private static final String BAR = "/bug9195/bar.yang";
+    private static final String BAZ = "/bug9195/baz.yang";
+    private static final String FOOBAR = "/bug9195/foobar.yang";
+    private static final String BAR_INVALID = "/bug9195/bar-invalid.yang";
+    private static final String BAZ_INVALID = "/bug9195/baz-invalid.yang";
+    private static final URI FOO_NS = URI.create("foo-ns");
+    private static final URI BAR_NS = URI.create("bar-ns");
+    private static final URI BAZ_NS = URI.create("baz-ns");
+    private static final Revision REVISION = Revision.of("2017-05-16");
+    private static final QNameModule FOO_MODULE = QNameModule.create(FOO_NS, REVISION);
+    private static final QName MY_FOO_CONT_A = QName.create(FOO_MODULE, "my-foo-cont-a");
+    private static final QName MY_FOO_CONT_B = QName.create(FOO_MODULE, "my-foo-cont-b");
+    private static final QName MY_FOO_CONT_C = QName.create(FOO_MODULE, "my-foo-cont-c");
+    private static final QNameModule BAR_MODULE = QNameModule.create(BAR_NS, REVISION);
+    private static final QName MY_BAR_CONT_A = QName.create(BAR_MODULE, "my-bar-cont-a");
+    private static final QName MY_BAR_CONT_B = QName.create(BAR_MODULE, "my-bar-cont-b");
+    private static final QNameModule BAZ_MODULE = QNameModule.create(BAZ_NS, REVISION);
+
+    @Test
+    public void testDeviationsSupportedInSomeModules() throws Exception {
+        final SetMultimap<QNameModule, QNameModule> modulesWithSupportedDeviations =
+                ImmutableSetMultimap.<QNameModule, QNameModule>builder()
+                .put(FOO_MODULE, BAR_MODULE)
+                .put(FOO_MODULE, BAZ_MODULE)
+                .put(BAR_MODULE, BAZ_MODULE)
+                .build();
+
+        final ListenableFuture<EffectiveModelContext> lf = createSchemaContext(modulesWithSupportedDeviations, FOO, BAR,
+            BAZ, FOOBAR);
+        assertTrue(lf.isDone());
+        final SchemaContext schemaContext = lf.get();
+        assertNotNull(schemaContext);
+
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_A)));
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_B)));
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_C)));
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_BAR_CONT_A)));
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_BAR_CONT_B)));
+    }
+
+    @Test
+    public void testDeviationsSupportedInAllModules() throws Exception {
+        final ListenableFuture<EffectiveModelContext> lf = createSchemaContext(null, FOO, BAR, BAZ, FOOBAR);
+        assertTrue(lf.isDone());
+        final SchemaContext schemaContext = lf.get();
+        assertNotNull(schemaContext);
+
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_A)));
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_B)));
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_C)));
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_BAR_CONT_A)));
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_BAR_CONT_B)));
+    }
+
+    @Test
+    public void testDeviationsSupportedInNoModule() throws Exception {
+        final ListenableFuture<EffectiveModelContext> lf = createSchemaContext(ImmutableSetMultimap.of(), FOO, BAR, BAZ,
+            FOOBAR);
+        assertTrue(lf.isDone());
+        final SchemaContext schemaContext = lf.get();
+        assertNotNull(schemaContext);
+
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_A)));
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_B)));
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_C)));
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_BAR_CONT_A)));
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_BAR_CONT_B)));
+    }
+
+    @Test
+    public void shouldFailOnAttemptToDeviateTheSameModule2() throws Exception {
+        final ListenableFuture<EffectiveModelContext> lf = createSchemaContext(null, BAR_INVALID, BAZ_INVALID);
+        assertTrue(lf.isDone());
+        try {
+            lf.get();
+            fail("Deviation that targets the same module as the one it is defined is forbidden.");
+        } catch (final ExecutionException ex) {
+            final Throwable cause = ex.getCause().getCause().getCause();
+            assertTrue(cause instanceof InferenceException);
+            assertTrue(cause.getMessage()
+                    .startsWith("Deviation must not target the same module as the one it is defined in"));
+        }
+    }
+
+    private static SettableSchemaProvider<ASTSchemaSource> getImmediateYangSourceProviderFromResource(
+            final String resourceName) throws Exception {
+        final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName);
+        return SettableSchemaProvider.createImmediate(TextToASTTransformer.transformText(yangSource),
+                ASTSchemaSource.class);
+    }
+
+    private static ListenableFuture<EffectiveModelContext> createSchemaContext(
+            final SetMultimap<QNameModule, QNameModule> modulesWithSupportedDeviations, final String... resources)
+            throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository(
+                "shared-schema-repo-with-deviations-test");
+
+        final Collection<SourceIdentifier> requiredSources = new ArrayList<>();
+        for (final String resource : resources) {
+            final SettableSchemaProvider<ASTSchemaSource> yangSource = getImmediateYangSourceProviderFromResource(
+                    resource);
+            yangSource.register(sharedSchemaRepository);
+            yangSource.setResult();
+            requiredSources.add(yangSource.getId());
+        }
+
+        final SchemaContextFactoryConfiguration config = SchemaContextFactoryConfiguration.builder()
+                .setModulesDeviatedByModules(modulesWithSupportedDeviations).build();
+        return sharedSchemaRepository.createEffectiveModelContextFactory(config).createEffectiveModelContext(
+            requiredSources);
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SettableSchemaProvider.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SettableSchemaProvider.java
new file mode 100644 (file)
index 0000000..9f48a18
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+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.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+
+class SettableSchemaProvider<T extends SchemaSourceRepresentation> implements SchemaSourceProvider<T> {
+
+    private final SettableFuture<T> future = SettableFuture.create();
+    private final T schemaSourceRepresentation;
+    private final PotentialSchemaSource<T> potentialSchemaSource;
+
+    SettableSchemaProvider(final T schemaSourceRepresentation, final SourceIdentifier sourceIdentifier,
+            final Class<T> representation, final int cost) {
+        this.schemaSourceRepresentation = schemaSourceRepresentation;
+        this.potentialSchemaSource = PotentialSchemaSource.create(sourceIdentifier, representation, cost);
+    }
+
+    public static <T extends SchemaSourceRepresentation> SettableSchemaProvider<T> createRemote(
+            final T schemaSourceRepresentation, final Class<T> representation) {
+        return new SettableSchemaProvider<>(schemaSourceRepresentation, schemaSourceRepresentation.getIdentifier(),
+                representation, PotentialSchemaSource.Costs.REMOTE_IO.getValue());
+    }
+
+    public static <T extends SchemaSourceRepresentation> SettableSchemaProvider<T> createImmediate(
+            final T schemaSourceRepresentation, final Class<T> representation) {
+        return new SettableSchemaProvider<>(schemaSourceRepresentation, schemaSourceRepresentation.getIdentifier(),
+                representation, PotentialSchemaSource.Costs.IMMEDIATE.getValue());
+    }
+
+    @Override
+    public ListenableFuture<T> getSource(final SourceIdentifier sourceIdentifier) {
+        return future;
+    }
+
+    public T getSchemaSourceRepresentation() {
+        return schemaSourceRepresentation;
+    }
+
+    public SourceIdentifier getId() {
+        return schemaSourceRepresentation.getIdentifier();
+    }
+
+    public void setResult() {
+        future.set(schemaSourceRepresentation);
+    }
+
+    public void setException(final Throwable ex) {
+        future.setException(ex);
+    }
+
+    public void register(final SchemaSourceRegistry repo) {
+        repo.registerSchemaSource(this, potentialSchemaSource);
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactoryTest.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactoryTest.java
new file mode 100644 (file)
index 0000000..b29e6f8
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016 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 org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
+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.parser.rfc7950.repo.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
+
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class SharedSchemaContextFactoryTest {
+
+    private final SharedSchemaRepository repository = new SharedSchemaRepository("test");
+
+    private final SchemaContextFactoryConfiguration config = SchemaContextFactoryConfiguration.getDefault();
+    private SourceIdentifier s1;
+    private SourceIdentifier s2;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        final YangTextSchemaSource source1 = YangTextSchemaSource.forResource("/ietf/ietf-inet-types@2010-09-24.yang");
+        final YangTextSchemaSource source2 = YangTextSchemaSource.forResource("/ietf/iana-timezones@2012-07-09.yang");
+        s1 = RevisionSourceIdentifier.create("ietf-inet-types", Revision.of("2010-09-24"));
+        s2 = RevisionSourceIdentifier.create("iana-timezones", Revision.of("2012-07-09"));
+
+        final TextToASTTransformer transformer = TextToASTTransformer.create(repository, repository);
+        repository.registerSchemaSourceListener(transformer);
+
+        repository.registerSchemaSource(sourceIdentifier -> immediateFluentFuture(source1),
+            PotentialSchemaSource.create(s1, YangTextSchemaSource.class, 1));
+
+        repository.registerSchemaSource(sourceIdentifier -> immediateFluentFuture(source2),
+            PotentialSchemaSource.create(s2, YangTextSchemaSource.class, 1));
+    }
+
+    @Test
+    public void testCreateSchemaContextWithDuplicateRequiredSources() throws InterruptedException, ExecutionException {
+        final SharedSchemaContextFactory sharedSchemaContextFactory = new SharedSchemaContextFactory(repository,
+            config);
+        final ListenableFuture<EffectiveModelContext> schemaContext =
+                sharedSchemaContextFactory.createEffectiveModelContext(s1, s1, s2);
+        assertNotNull(schemaContext.get());
+    }
+
+    @Test
+    public void testSourceRegisteredWithDifferentSI() throws Exception {
+        final YangTextSchemaSource source1 = YangTextSchemaSource.forResource("/ietf/ietf-inet-types@2010-09-24.yang");
+        final YangTextSchemaSource source2 = YangTextSchemaSource.forResource("/ietf/iana-timezones@2012-07-09.yang");
+        s1 = source1.getIdentifier();
+        s2 = source2.getIdentifier();
+
+        final SettableSchemaProvider<ASTSchemaSource> provider =
+                SharedSchemaRepositoryTest.getImmediateYangSourceProviderFromResource(
+                    "/no-revision/imported@2012-12-12.yang");
+        provider.setResult();
+        provider.register(repository);
+
+        // Register the same provider under source id without revision
+        final SourceIdentifier sIdWithoutRevision = RevisionSourceIdentifier.create(provider.getId().getName());
+        repository.registerSchemaSource(provider, PotentialSchemaSource.create(
+                sIdWithoutRevision, ASTSchemaSource.class, PotentialSchemaSource.Costs.IMMEDIATE.getValue()));
+
+        final SharedSchemaContextFactory sharedSchemaContextFactory = new SharedSchemaContextFactory(repository,
+            config);
+        final ListenableFuture<EffectiveModelContext> schemaContext =
+                sharedSchemaContextFactory.createEffectiveModelContext(sIdWithoutRevision, provider.getId());
+        assertNotNull(schemaContext.get());
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepositoryTest.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepositoryTest.java
new file mode 100644 (file)
index 0000000..d4717a5
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * 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 org.hamcrest.CoreMatchers.both;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+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.MoreExecutors;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+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.api.YangTextSchemaSource;
+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.util.FilesystemSchemaSourceCache;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
+
+public class SharedSchemaRepositoryTest {
+
+    @Test
+    public void testSourceWithAndWithoutRevision() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
+
+        final SourceIdentifier idNoRevision = loadAndRegisterSource(sharedSchemaRepository,
+            "/no-revision/imported.yang");
+        final SourceIdentifier id2 = loadAndRegisterSource(sharedSchemaRepository,
+            "/no-revision/imported@2012-12-12.yang");
+
+        ListenableFuture<ASTSchemaSource> source = sharedSchemaRepository.getSchemaSource(idNoRevision,
+            ASTSchemaSource.class);
+        assertEquals(idNoRevision, source.get().getIdentifier());
+        source = sharedSchemaRepository.getSchemaSource(id2, ASTSchemaSource.class);
+        assertEquals(id2, source.get().getIdentifier());
+    }
+
+    private static SourceIdentifier loadAndRegisterSource(final SharedSchemaRepository sharedSchemaRepository,
+            final String resourceName) throws Exception {
+        final SettableSchemaProvider<ASTSchemaSource> sourceProvider = getImmediateYangSourceProviderFromResource(
+            resourceName);
+        sourceProvider.setResult();
+        final SourceIdentifier idNoRevision = sourceProvider.getId();
+        sourceProvider.register(sharedSchemaRepository);
+        return idNoRevision;
+    }
+
+    @Test
+    public void testSimpleSchemaContext() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
+
+        final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource(
+            "/ietf/ietf-inet-types@2010-09-24.yang");
+        remoteInetTypesYang.register(sharedSchemaRepository);
+        final ListenableFuture<ASTSchemaSource> registeredSourceFuture = sharedSchemaRepository.getSchemaSource(
+            remoteInetTypesYang.getId(), ASTSchemaSource.class);
+        assertFalse(registeredSourceFuture.isDone());
+
+        final EffectiveModelContextFactory fact = sharedSchemaRepository.createEffectiveModelContextFactory();
+        final ListenableFuture<EffectiveModelContext> schemaContextFuture =
+                fact.createEffectiveModelContext(remoteInetTypesYang.getId());
+
+        assertFalse(schemaContextFuture.isDone());
+
+        // Make source appear
+        remoteInetTypesYang.setResult();
+        assertEquals(remoteInetTypesYang.getSchemaSourceRepresentation(), registeredSourceFuture.get());
+
+        // Verify schema created successfully
+        assertTrue(schemaContextFuture.isDone());
+        final SchemaContext firstSchemaContext = schemaContextFuture.get();
+        assertSchemaContext(firstSchemaContext, 1);
+
+        // Try same schema second time
+        final ListenableFuture<EffectiveModelContext> secondSchemaFuture =
+                sharedSchemaRepository.createEffectiveModelContextFactory().createEffectiveModelContext(
+                    remoteInetTypesYang.getId());
+
+        // Verify second schema created successfully immediately
+        assertTrue(secondSchemaFuture.isDone());
+        // Assert same context instance is returned from first and second attempt
+        assertSame(firstSchemaContext, secondSchemaFuture.get());
+    }
+
+    @Test
+    public void testTwoSchemaContextsSharingSource() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
+
+        final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource(
+            "/ietf/ietf-inet-types@2010-09-24.yang");
+        remoteInetTypesYang.register(sharedSchemaRepository);
+        remoteInetTypesYang.setResult();
+        final SettableSchemaProvider<ASTSchemaSource> remoteTopologyYang = getImmediateYangSourceProviderFromResource(
+            "/ietf/network-topology@2013-10-21.yang");
+        remoteTopologyYang.register(sharedSchemaRepository);
+        remoteTopologyYang.setResult();
+        final SettableSchemaProvider<ASTSchemaSource> remoteModuleNoRevYang =
+                getImmediateYangSourceProviderFromResource("/no-revision/module-without-revision.yang");
+        remoteModuleNoRevYang.register(sharedSchemaRepository);
+
+        final EffectiveModelContextFactory fact = sharedSchemaRepository.createEffectiveModelContextFactory();
+        final ListenableFuture<EffectiveModelContext> inetAndTopologySchemaContextFuture = fact
+                .createEffectiveModelContext(remoteInetTypesYang.getId(), remoteTopologyYang.getId());
+        assertTrue(inetAndTopologySchemaContextFuture.isDone());
+        assertSchemaContext(inetAndTopologySchemaContextFuture.get(), 2);
+
+        final ListenableFuture<EffectiveModelContext> inetAndNoRevSchemaContextFuture =
+                fact.createEffectiveModelContext(remoteInetTypesYang.getId(), remoteModuleNoRevYang.getId());
+        assertFalse(inetAndNoRevSchemaContextFuture.isDone());
+
+        remoteModuleNoRevYang.setResult();
+        assertTrue(inetAndNoRevSchemaContextFuture.isDone());
+        assertSchemaContext(inetAndNoRevSchemaContextFuture.get(), 2);
+    }
+
+    @Test
+    public void testFailedSchemaContext() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
+
+        final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = getImmediateYangSourceProviderFromResource(
+            "/ietf/ietf-inet-types@2010-09-24.yang");
+        remoteInetTypesYang.register(sharedSchemaRepository);
+
+        final EffectiveModelContextFactory fact = sharedSchemaRepository.createEffectiveModelContextFactory();
+
+        // Make source appear
+        final Throwable ex = new IllegalStateException("failed schema");
+        remoteInetTypesYang.setException(ex);
+
+        final ListenableFuture<EffectiveModelContext> schemaContextFuture = fact.createEffectiveModelContext(
+            remoteInetTypesYang.getId());
+
+        try {
+            schemaContextFuture.get();
+        } catch (final ExecutionException e) {
+            assertNotNull(e.getCause());
+            assertNotNull(e.getCause().getCause());
+            assertSame(ex, e.getCause().getCause());
+            return;
+        }
+
+        fail("Schema context creation should have failed");
+    }
+
+    @Test
+    public void testDifferentCosts() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
+
+        final SettableSchemaProvider<ASTSchemaSource> immediateInetTypesYang = spy(
+            getImmediateYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
+        immediateInetTypesYang.register(sharedSchemaRepository);
+        immediateInetTypesYang.setResult();
+
+        final SettableSchemaProvider<ASTSchemaSource> remoteInetTypesYang = spy(
+            getRemoteYangSourceProviderFromResource("/ietf/ietf-inet-types@2010-09-24.yang"));
+        remoteInetTypesYang.register(sharedSchemaRepository);
+        remoteInetTypesYang.setResult();
+
+        final EffectiveModelContextFactory fact = sharedSchemaRepository.createEffectiveModelContextFactory();
+        final ListenableFuture<EffectiveModelContext> schemaContextFuture =
+                fact.createEffectiveModelContext(remoteInetTypesYang.getId());
+
+        assertSchemaContext(schemaContextFuture.get(), 1);
+
+        final SourceIdentifier id = immediateInetTypesYang.getId();
+        verify(remoteInetTypesYang, times(0)).getSource(id);
+        verify(immediateInetTypesYang).getSource(id);
+    }
+
+    @Test
+    public void testWithCacheStartup() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
+
+        class CountingSchemaListener implements SchemaSourceListener {
+            List<PotentialSchemaSource<?>> registeredSources = new ArrayList<>();
+
+            @Override
+            public void schemaSourceEncountered(final SchemaSourceRepresentation source) {
+            }
+
+            @Override
+            public void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> sources) {
+                for (final PotentialSchemaSource<?> source : sources) {
+                    registeredSources.add(source);
+                }
+            }
+
+            @Override
+            public void schemaSourceUnregistered(final PotentialSchemaSource<?> source) {
+            }
+        }
+
+        final File storageDir = Files.createTempDir();
+
+        final CountingSchemaListener listener = new CountingSchemaListener();
+        sharedSchemaRepository.registerSchemaSourceListener(listener);
+
+        final File test = new File(storageDir, "test.yang");
+        Files.asCharSink(test, StandardCharsets.UTF_8).write("content-test");
+
+        final File test2 = new File(storageDir, "test@2012-12-12.yang");
+        Files.asCharSink(test2, StandardCharsets.UTF_8).write("content-test-2012");
+
+        final File test3 = new File(storageDir, "test@2013-12-12.yang");
+        Files.asCharSink(test3, StandardCharsets.UTF_8).write("content-test-2013");
+
+        final File test4 = new File(storageDir, "module@2010-12-12.yang");
+        Files.asCharSink(test4, StandardCharsets.UTF_8).write("content-module-2010");
+
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(
+                sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
+        sharedSchemaRepository.registerSchemaSourceListener(cache);
+
+        assertEquals(4, listener.registeredSources.size());
+
+        assertThat(Lists.transform(listener.registeredSources, PotentialSchemaSource::getSourceIdentifier),
+                both(hasItem(RevisionSourceIdentifier.create("test", Optional.empty())))
+                        .and(hasItem(RevisionSourceIdentifier.create("test", Revision.of("2012-12-12"))))
+                        .and(hasItem(RevisionSourceIdentifier.create("test", Revision.of("2013-12-12"))))
+                        .and(hasItem(RevisionSourceIdentifier.create("module", Revision.of("2010-12-12"))))
+        );
+    }
+
+    @Test
+    public void testWithCacheRunning() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository("netconf-mounts");
+
+        final File storageDir = Files.createTempDir();
+
+        final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(
+                sharedSchemaRepository, YangTextSchemaSource.class, storageDir);
+        sharedSchemaRepository.registerSchemaSourceListener(cache);
+
+        final SourceIdentifier runningId = RevisionSourceIdentifier.create("running", Revision.of("2012-12-12"));
+
+        sharedSchemaRepository.registerSchemaSource(sourceIdentifier -> immediateFluentFuture(
+            new YangTextSchemaSource(runningId) {
+                @Override
+                protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+                    return toStringHelper;
+                }
+
+                @Override
+                public InputStream openStream() throws IOException {
+                    return new ByteArrayInputStream("running".getBytes(StandardCharsets.UTF_8));
+                }
+            }), PotentialSchemaSource.create(runningId, YangTextSchemaSource.class,
+                PotentialSchemaSource.Costs.REMOTE_IO.getValue()));
+
+        final TextToASTTransformer transformer = TextToASTTransformer.create(sharedSchemaRepository,
+            sharedSchemaRepository);
+        sharedSchemaRepository.registerSchemaSourceListener(transformer);
+
+        // Request schema to make repository notify the cache
+        final ListenableFuture<EffectiveModelContext> schemaFuture = sharedSchemaRepository
+                .createEffectiveModelContextFactory()
+                .createEffectiveModelContext(runningId);
+        Futures.addCallback(schemaFuture, new FutureCallback<SchemaContext>() {
+            @Override
+            public void onSuccess(final SchemaContext result) {
+                fail("Creation of schema context should fail from non-regular sources");
+            }
+
+            @Override
+            public void onFailure(final Throwable cause) {
+                // Creation of schema context fails, since we do not provide regular sources, but we just want
+                // to check cache
+                final List<File> cachedSchemas = Arrays.asList(storageDir.listFiles());
+                assertEquals(1, cachedSchemas.size());
+                assertEquals(Files.getNameWithoutExtension(cachedSchemas.get(0).getName()), "running@2012-12-12");
+            }
+        }, MoreExecutors.directExecutor());
+
+        try {
+            schemaFuture.get();
+        } catch (final ExecutionException e) {
+            assertNotNull(e.getCause());
+            assertEquals(MissingSchemaSourceException.class, e.getCause().getClass());
+            return;
+        }
+
+        fail("Creation of schema context should fail from non-regular sources");
+    }
+
+    private static void assertSchemaContext(final SchemaContext schemaContext, final int moduleSize) {
+        assertNotNull(schemaContext);
+        assertEquals(moduleSize, schemaContext.getModules().size());
+    }
+
+    static SettableSchemaProvider<ASTSchemaSource> getRemoteYangSourceProviderFromResource(final String resourceName)
+            throws Exception {
+        final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName);
+        return SettableSchemaProvider.createRemote(TextToASTTransformer.transformText(yangSource),
+            ASTSchemaSource.class);
+    }
+
+    static SettableSchemaProvider<ASTSchemaSource> getImmediateYangSourceProviderFromResource(final String resourceName)
+            throws Exception {
+        final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName);
+        return SettableSchemaProvider.createImmediate(TextToASTTransformer.transformText(yangSource),
+            ASTSchemaSource.class);
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepositoryWithFeaturesTest.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaRepositoryWithFeaturesTest.java
new file mode 100644 (file)
index 0000000..df0c096
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2016 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
+
+public class SharedSchemaRepositoryWithFeaturesTest {
+
+    @Test
+    public void testSharedSchemaRepositoryWithSomeFeaturesSupported() throws Exception {
+        final Set<QName> supportedFeatures = ImmutableSet.of(QName.create("foobar-namespace", "test-feature-1"));
+
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository(
+                "shared-schema-repo-with-features-test");
+
+        final SettableSchemaProvider<ASTSchemaSource> foobar = getImmediateYangSourceProviderFromResource(
+            "/if-feature-resolution-test/shared-schema-repository/foobar.yang");
+        foobar.register(sharedSchemaRepository);
+        foobar.setResult();
+
+        final ListenableFuture<EffectiveModelContext> testSchemaContextFuture =
+                sharedSchemaRepository.createEffectiveModelContextFactory(
+                SchemaContextFactoryConfiguration.builder().setSupportedFeatures(supportedFeatures).build())
+                .createEffectiveModelContext(foobar.getId());
+        assertTrue(testSchemaContextFuture.isDone());
+        assertSchemaContext(testSchemaContextFuture.get(), 1);
+
+        final Module module = testSchemaContextFuture.get().findModules("foobar").iterator().next();
+        assertNotNull(module);
+        assertEquals(2, module.getChildNodes().size());
+
+        final ContainerSchemaNode testContainerA = (ContainerSchemaNode) module.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-container-a"));
+        assertNotNull(testContainerA);
+        final LeafSchemaNode testLeafA = (LeafSchemaNode) testContainerA.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-leaf-a"));
+        assertNotNull(testLeafA);
+
+        final ContainerSchemaNode testContainerB = (ContainerSchemaNode) module.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-container-b"));
+        assertNull(testContainerB);
+
+        final ContainerSchemaNode testContainerC = (ContainerSchemaNode) module.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-container-c"));
+        assertNotNull(testContainerC);
+        final LeafSchemaNode testLeafC = (LeafSchemaNode) testContainerC.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-leaf-c"));
+        assertNotNull(testLeafC);
+    }
+
+    @Test
+    public void testSharedSchemaRepositoryWithAllFeaturesSupported() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository(
+                "shared-schema-repo-with-features-test");
+
+        final SettableSchemaProvider<ASTSchemaSource> foobar = getImmediateYangSourceProviderFromResource(
+            "/if-feature-resolution-test/shared-schema-repository/foobar.yang");
+        foobar.register(sharedSchemaRepository);
+        foobar.setResult();
+
+        final EffectiveModelContextFactory fact = sharedSchemaRepository.createEffectiveModelContextFactory();
+        final ListenableFuture<EffectiveModelContext> testSchemaContextFuture =
+                fact.createEffectiveModelContext(foobar.getId());
+        assertTrue(testSchemaContextFuture.isDone());
+        assertSchemaContext(testSchemaContextFuture.get(), 1);
+
+        final Module module = testSchemaContextFuture.get().findModules("foobar").iterator().next();
+        assertNotNull(module);
+        assertEquals(3, module.getChildNodes().size());
+
+        final ContainerSchemaNode testContainerA = (ContainerSchemaNode) module.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-container-a"));
+        assertNotNull(testContainerA);
+        final LeafSchemaNode testLeafA = (LeafSchemaNode) testContainerA.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-leaf-a"));
+        assertNotNull(testLeafA);
+
+        final ContainerSchemaNode testContainerB = (ContainerSchemaNode) module.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-container-b"));
+        assertNotNull(testContainerB);
+        final LeafSchemaNode testLeafB = (LeafSchemaNode) testContainerB.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-leaf-b"));
+        assertNotNull(testLeafB);
+
+        final ContainerSchemaNode testContainerC = (ContainerSchemaNode) module.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-container-c"));
+        assertNotNull(testContainerC);
+        final LeafSchemaNode testLeafC = (LeafSchemaNode) testContainerC.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-leaf-c"));
+        assertNotNull(testLeafC);
+    }
+
+    @Test
+    public void testSharedSchemaRepositoryWithNoFeaturesSupported() throws Exception {
+        final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository(
+                "shared-schema-repo-with-features-test");
+
+        final SettableSchemaProvider<ASTSchemaSource> foobar = getImmediateYangSourceProviderFromResource(
+            "/if-feature-resolution-test/shared-schema-repository/foobar.yang");
+        foobar.register(sharedSchemaRepository);
+        foobar.setResult();
+
+        final ListenableFuture<EffectiveModelContext> testSchemaContextFuture =
+                sharedSchemaRepository.createEffectiveModelContextFactory(
+                    SchemaContextFactoryConfiguration.builder().setSupportedFeatures(ImmutableSet.of()).build())
+                .createEffectiveModelContext(foobar.getId());
+        assertTrue(testSchemaContextFuture.isDone());
+        assertSchemaContext(testSchemaContextFuture.get(), 1);
+
+        final Module module = testSchemaContextFuture.get().findModules("foobar").iterator().next();
+        assertNotNull(module);
+        assertEquals(1, module.getChildNodes().size());
+
+        final ContainerSchemaNode testContainerC = (ContainerSchemaNode) module.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-container-c"));
+        assertNotNull(testContainerC);
+        final LeafSchemaNode testLeafC = (LeafSchemaNode) testContainerC.getDataChildByName(
+                QName.create(module.getQNameModule(), "test-leaf-c"));
+        assertNotNull(testLeafC);
+    }
+
+    private static SettableSchemaProvider<ASTSchemaSource> getImmediateYangSourceProviderFromResource(
+            final String resourceName) throws Exception {
+        final YangTextSchemaSource yangSource = YangTextSchemaSource.forResource(resourceName);
+        return SettableSchemaProvider.createImmediate(TextToASTTransformer.transformText(yangSource),
+            ASTSchemaSource.class);
+    }
+
+    private static void assertSchemaContext(final SchemaContext schemaContext, final int moduleSize) {
+        assertNotNull(schemaContext);
+        assertEquals(moduleSize, schemaContext.getModules().size());
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/YangTextSchemaContextResolverTest.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/YangTextSchemaContextResolverTest.java
new file mode 100644 (file)
index 0000000..53c8ad3
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2016 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Revision;
+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.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+public class YangTextSchemaContextResolverTest {
+
+    @Test
+    public void testYangTextSchemaContextResolver() throws SchemaSourceException, IOException, YangSyntaxErrorException,
+            InterruptedException, ExecutionException {
+        final YangTextSchemaContextResolver yangTextSchemaContextResolver =
+                YangTextSchemaContextResolver.create("test-bundle");
+        assertNotNull(yangTextSchemaContextResolver);
+
+        final URL yangFile1 = getClass().getResource("/yang-text-schema-context-resolver-test/foo.yang");
+        assertNotNull(yangFile1);
+        final URL yangFile2 = getClass().getResource("/yang-text-schema-context-resolver-test/bar.yang");
+        assertNotNull(yangFile2);
+        final URL yangFile3 = getClass().getResource("/yang-text-schema-context-resolver-test/baz.yang");
+        assertNotNull(yangFile2);
+
+        final YangTextSchemaSourceRegistration registration1 =
+                yangTextSchemaContextResolver.registerSource(yangFile1);
+        assertNotNull(registration1);
+        final YangTextSchemaSourceRegistration registration2 =
+                yangTextSchemaContextResolver.registerSource(yangFile2);
+        assertNotNull(registration2);
+        final YangTextSchemaSourceRegistration registration3 =
+                yangTextSchemaContextResolver.registerSource(yangFile3);
+        assertNotNull(registration3);
+
+        assertEquals(3, yangTextSchemaContextResolver.getAvailableSources().size());
+
+        final SourceIdentifier fooModuleId = RevisionSourceIdentifier.create("foo", Revision.of("2016-09-26"));
+        final ListenableFuture<YangTextSchemaSource> foo = yangTextSchemaContextResolver.getSource(fooModuleId);
+        assertTrue(foo.isDone());
+        assertEquals(fooModuleId, foo.get().getIdentifier());
+
+        final SourceIdentifier barModuleId = RevisionSourceIdentifier.create("bar", Revision.of("2016-09-26"));
+        final ListenableFuture<YangTextSchemaSource> bar = yangTextSchemaContextResolver.getSource(barModuleId);
+        assertTrue(bar.isDone());
+        assertEquals(barModuleId, bar.get().getIdentifier());
+
+        final SourceIdentifier bazModuleId = RevisionSourceIdentifier.create("baz", Revision.of("2016-09-26"));
+        final ListenableFuture<YangTextSchemaSource> baz =
+                yangTextSchemaContextResolver.getSource(bazModuleId);
+        assertTrue(baz.isDone());
+        assertEquals(bazModuleId, baz.get().getIdentifier());
+
+        final SourceIdentifier foobarModuleId = RevisionSourceIdentifier.create("foobar", Revision.of("2016-09-26"));
+        final ListenableFuture<YangTextSchemaSource> foobar = yangTextSchemaContextResolver.getSource(foobarModuleId);
+        assertTrue(foobar.isDone());
+        try {
+            foobar.get();
+            fail("A MissingSchemaSourceException should have been thrown.");
+        } catch (ExecutionException e) {
+            assertEquals("URL for RevisionSourceIdentifier [name=foobar@2016-09-26] not registered",
+                e.getCause().getMessage());
+        }
+
+        Optional<? extends SchemaContext> schemaContextOptional = yangTextSchemaContextResolver.getSchemaContext();
+        assertTrue(schemaContextOptional.isPresent());
+        SchemaContext schemaContext = schemaContextOptional.get();
+        assertEquals(3, schemaContext.getModules().size());
+
+        registration1.close();
+        registration2.close();
+        registration3.close();
+
+        assertEquals(0, yangTextSchemaContextResolver.getAvailableSources().size());
+
+        schemaContextOptional = yangTextSchemaContextResolver.getSchemaContext();
+        assertTrue(schemaContextOptional.isPresent());
+        schemaContext = schemaContextOptional.get();
+        assertEquals(0, schemaContext.getModules().size());
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/bug9195/bar-invalid.yang b/yang/yang-parser-impl/src/test/resources/bug9195/bar-invalid.yang
new file mode 100644 (file)
index 0000000..d49ab16
--- /dev/null
@@ -0,0 +1,19 @@
+module bar {
+    namespace bar-ns;
+    prefix bar;
+
+    import baz {
+        prefix baz;
+        revision-date 2017-05-16;
+    }
+
+    revision 2017-05-16;
+
+    deviation "/baz:my-baz-cont/bar:my-aug-cont" {
+        deviate not-supported;
+    }
+
+    augment "/baz:my-baz-cont" {
+        container my-aug-cont {}
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/resources/bug9195/bar.yang b/yang/yang-parser-impl/src/test/resources/bug9195/bar.yang
new file mode 100644 (file)
index 0000000..46e15df
--- /dev/null
@@ -0,0 +1,19 @@
+module bar {
+    namespace bar-ns;
+    prefix bar-prefix;
+
+    import foo {
+        prefix foo;
+        revision-date 2017-05-16;
+    }
+
+    revision 2017-05-16;
+
+    deviation "/foo:my-foo-cont-a" {
+        deviate not-supported;
+    }
+
+    container my-bar-cont-a {}
+
+    container my-bar-cont-b {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/resources/bug9195/baz-invalid.yang b/yang/yang-parser-impl/src/test/resources/bug9195/baz-invalid.yang
new file mode 100644 (file)
index 0000000..39d1cfe
--- /dev/null
@@ -0,0 +1,8 @@
+module baz {
+    namespace baz-ns;
+    prefix baz;
+
+    revision 2017-05-16;
+
+    container my-baz-cont {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/resources/bug9195/baz.yang b/yang/yang-parser-impl/src/test/resources/bug9195/baz.yang
new file mode 100644 (file)
index 0000000..05df3ad
--- /dev/null
@@ -0,0 +1,26 @@
+module baz {
+    namespace baz-ns;
+    prefix baz-prefix;
+
+    import foo {
+        prefix foo;
+        revision-date 2017-05-16;
+    }
+
+    import bar {
+        prefix bar;
+        revision-date 2017-05-16;
+    }
+
+    revision 2017-05-16;
+
+    deviation "/foo:my-foo-cont-b" {
+        deviate not-supported;
+    }
+
+    deviation "/bar:my-bar-cont-a" {
+        deviate not-supported;
+    }
+
+    container my-baz-cont {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/resources/bug9195/foo-invalid.yang b/yang/yang-parser-impl/src/test/resources/bug9195/foo-invalid.yang
new file mode 100644 (file)
index 0000000..b1cad54
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace foo-ns;
+    prefix foo;
+
+    revision 2017-05-16;
+
+    deviation "/my-foo-cont" {
+        deviate not-supported;
+    }
+
+    container my-foo-cont {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/resources/bug9195/foo.yang b/yang/yang-parser-impl/src/test/resources/bug9195/foo.yang
new file mode 100644 (file)
index 0000000..4027fee
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prefix;
+
+    revision 2017-05-16;
+
+    container my-foo-cont-a {}
+
+    container my-foo-cont-b {}
+
+    container my-foo-cont-c {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/resources/bug9195/foobar.yang b/yang/yang-parser-impl/src/test/resources/bug9195/foobar.yang
new file mode 100644 (file)
index 0000000..97d4478
--- /dev/null
@@ -0,0 +1,24 @@
+module foobar {
+    namespace foobar-ns;
+    prefix foobar-prefix;
+
+    import foo {
+        prefix foo;
+        revision-date 2017-05-16;
+    }
+
+    import bar {
+        prefix bar;
+        revision-date 2017-05-16;
+    }
+
+    revision 2017-05-16;
+
+    deviation "/foo:my-foo-cont-c" {
+        deviate not-supported;
+    }
+
+    deviation "/bar:my-bar-cont-b" {
+        deviate not-supported;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/resources/ietf/iana-timezones@2012-07-09.yang b/yang/yang-parser-impl/src/test/resources/ietf/iana-timezones@2012-07-09.yang
new file mode 100644 (file)
index 0000000..7fc1577
--- /dev/null
@@ -0,0 +1,1701 @@
+module iana-timezones {
+  namespace "urn:ietf:params:xml:ns:yang:iana-timezones";
+  prefix ianatz;
+
+  organization "IANA";
+  contact
+    "        Internet Assigned Numbers Authority
+
+     Postal: ICANN
+             4676 Admiralty Way, Suite 330
+             Marina del Rey, CA 90292
+
+     Tel:    +1 310 823 9358
+     E-Mail: iana&iana.org";
+  description
+    "This YANG module defines the iana-timezone typedef, which
+     contains YANG definitions for IANA-registered timezones.
+
+     This YANG module is maintained by IANA, and reflects the
+     IANA Time Zone Database.
+     (http://www.iana.org/time-zones)
+
+     The latest revision of this YANG module can be obtained from
+     the IANA web site.
+
+     Copyright (c) 2011 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD License
+     set forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC XXXX; see
+     the RFC itself for full legal notices.";
+
+  revision 2012-07-09 {
+    description
+      "Initial revision. Using IANA Time Zone Data v. 2012c
+       (Released 2012-03-27)";
+    reference "RFC XXXX: TITLE";
+  }
+  typedef iana-timezone {
+    description
+      "A timezone location as defined by the IANA timezone
+       database (http://www.iana.org/time-zones)";
+    type enumeration {
+      enum "Europe/Andorra" {
+        value 0;
+      }
+      enum "Asia/Dubai" {
+        value 1;
+      }
+      enum "Asia/Kabul" {
+        value 2;
+      }
+      enum "America/Antigua" {
+        value 3;
+      }
+      enum "America/Anguilla" {
+        value 4;
+      }
+      enum "Europe/Tirane" {
+        value 5;
+      }
+      enum "Asia/Yerevan" {
+        value 6;
+      }
+      enum "Africa/Luanda" {
+        value 7;
+      }
+      enum "Antarctica/McMurdo" {
+        value 8;
+        description
+          "McMurdo Station, Ross Island";
+      }
+      enum "Antarctica/South_Pole" {
+        value 9;
+        description
+          "Amundsen-Scott Station, South Pole";
+      }
+      enum "Antarctica/Rothera" {
+        value 10;
+        description
+          "Rothera Station, Adelaide Island";
+      }
+      enum "Antarctica/Palmer" {
+        value 11;
+        description
+          "Palmer Station, Anvers Island";
+      }
+      enum "Antarctica/Mawson" {
+        value 12;
+        description
+          "Mawson Station, Holme Bay";
+      }
+      enum "Antarctica/Davis" {
+        value 13;
+        description
+          "Davis Station, Vestfold Hills";
+      }
+      enum "Antarctica/Casey" {
+        value 14;
+        description
+          "Casey Station, Bailey Peninsula";
+      }
+      enum "Antarctica/Vostok" {
+        value 15;
+        description
+          "Vostok Station, Lake Vostok";
+      }
+      enum "Antarctica/DumontDUrville" {
+        value 16;
+        description
+          "Dumont-d'Urville Station, Terre Adelie";
+      }
+      enum "Antarctica/Syowa" {
+        value 17;
+        description
+          "Syowa Station, E Ongul I";
+      }
+      enum "Antarctica/Macquarie" {
+        value 18;
+        description
+          "Macquarie Island Station, Macquarie Island";
+      }
+      enum "America/Argentina/Buenos_Aires" {
+        value 19;
+        description
+          "Buenos Aires (BA, CF)";
+      }
+      enum "America/Argentina/Cordoba" {
+        value 20;
+        description
+          "most locations (CB, CC, CN, ER, FM, MN, SE, SF)";
+      }
+      enum "America/Argentina/Salta" {
+        value 21;
+        description
+          "(SA, LP, NQ, RN)";
+      }
+      enum "America/Argentina/Jujuy" {
+        value 22;
+        description
+          "Jujuy (JY)";
+      }
+      enum "America/Argentina/Tucuman" {
+        value 23;
+        description
+          "Tucuman (TM)";
+      }
+      enum "America/Argentina/Catamarca" {
+        value 24;
+        description
+          "Catamarca (CT), Chubut (CH)";
+      }
+      enum "America/Argentina/La_Rioja" {
+        value 25;
+        description
+          "La Rioja (LR)";
+      }
+      enum "America/Argentina/San_Juan" {
+        value 26;
+        description
+          "San Juan (SJ)";
+      }
+      enum "America/Argentina/Mendoza" {
+        value 27;
+        description
+          "Mendoza (MZ)";
+      }
+      enum "America/Argentina/San_Luis" {
+        value 28;
+        description
+          "San Luis (SL)";
+      }
+      enum "America/Argentina/Rio_Gallegos" {
+        value 29;
+        description
+          "Santa Cruz (SC)";
+      }
+      enum "America/Argentina/Ushuaia" {
+        value 30;
+        description
+          "Tierra del Fuego (TF)";
+      }
+      enum "Pacific/Pago_Pago" {
+        value 31;
+      }
+      enum "Europe/Vienna" {
+        value 32;
+      }
+      enum "Australia/Lord_Howe" {
+        value 33;
+        description
+          "Lord Howe Island";
+      }
+      enum "Australia/Hobart" {
+        value 34;
+        description
+          "Tasmania - most locations";
+      }
+      enum "Australia/Currie" {
+        value 35;
+        description
+          "Tasmania - King Island";
+      }
+      enum "Australia/Melbourne" {
+        value 36;
+        description
+          "Victoria";
+      }
+      enum "Australia/Sydney" {
+        value 37;
+        description
+          "New South Wales - most locations";
+      }
+      enum "Australia/Broken_Hill" {
+        value 38;
+        description
+          "New South Wales - Yancowinna";
+      }
+      enum "Australia/Brisbane" {
+        value 39;
+        description
+          "Queensland - most locations";
+      }
+      enum "Australia/Lindeman" {
+        value 40;
+        description
+          "Queensland - Holiday Islands";
+      }
+      enum "Australia/Adelaide" {
+        value 41;
+        description
+          "South Australia";
+      }
+      enum "Australia/Darwin" {
+        value 42;
+        description
+          "Northern Territory";
+      }
+      enum "Australia/Perth" {
+        value 43;
+        description
+          "Western Australia - most locations";
+      }
+      enum "Australia/Eucla" {
+        value 44;
+        description
+          "Western Australia - Eucla area";
+      }
+      enum "America/Aruba" {
+        value 45;
+      }
+      enum "Europe/Mariehamn" {
+        value 46;
+      }
+      enum "Asia/Baku" {
+        value 47;
+      }
+      enum "Europe/Sarajevo" {
+        value 48;
+      }
+      enum "America/Barbados" {
+        value 49;
+      }
+      enum "Asia/Dhaka" {
+        value 50;
+      }
+      enum "Europe/Brussels" {
+        value 51;
+      }
+      enum "Africa/Ouagadougou" {
+        value 52;
+      }
+      enum "Europe/Sofia" {
+        value 53;
+      }
+      enum "Asia/Bahrain" {
+        value 54;
+      }
+      enum "Africa/Bujumbura" {
+        value 55;
+      }
+      enum "Africa/Porto-Novo" {
+        value 56;
+      }
+      enum "America/St_Barthelemy" {
+        value 57;
+      }
+      enum "Atlantic/Bermuda" {
+        value 58;
+      }
+      enum "Asia/Brunei" {
+        value 59;
+      }
+      enum "America/La_Paz" {
+        value 60;
+      }
+      enum "America/Kralendijk" {
+        value 61;
+      }
+      enum "America/Noronha" {
+        value 62;
+        description
+          "Atlantic islands";
+      }
+      enum "America/Belem" {
+        value 63;
+        description
+          "Amapa, E Para";
+      }
+      enum "America/Fortaleza" {
+        value 64;
+        description
+          "NE Brazil (MA, PI, CE, RN, PB)";
+      }
+      enum "America/Recife" {
+        value 65;
+        description
+          "Pernambuco";
+      }
+      enum "America/Araguaina" {
+        value 66;
+        description
+          "Tocantins";
+      }
+      enum "America/Maceio" {
+        value 67;
+        description
+          "Alagoas, Sergipe";
+      }
+      enum "America/Bahia" {
+        value 68;
+        description
+          "Bahia";
+      }
+      enum "America/Sao_Paulo" {
+        value 69;
+        description
+          "S & SE Brazil (GO, DF, MG, ES, RJ, SP, PR, SC, RS)";
+      }
+      enum "America/Campo_Grande" {
+        value 70;
+        description
+          "Mato Grosso do Sul";
+      }
+      enum "America/Cuiaba" {
+        value 71;
+        description
+          "Mato Grosso";
+      }
+      enum "America/Santarem" {
+        value 72;
+        description
+          "W Para";
+      }
+      enum "America/Porto_Velho" {
+        value 73;
+        description
+          "Rondonia";
+      }
+      enum "America/Boa_Vista" {
+        value 74;
+        description
+          "Roraima";
+      }
+      enum "America/Manaus" {
+        value 75;
+        description
+          "E Amazonas";
+      }
+      enum "America/Eirunepe" {
+        value 76;
+        description
+          "W Amazonas";
+      }
+      enum "America/Rio_Branco" {
+        value 77;
+        description
+          "Acre";
+      }
+      enum "America/Nassau" {
+        value 78;
+      }
+      enum "Asia/Thimphu" {
+        value 79;
+      }
+      enum "Africa/Gaborone" {
+        value 80;
+      }
+      enum "Europe/Minsk" {
+        value 81;
+      }
+      enum "America/Belize" {
+        value 82;
+      }
+      enum "America/St_Johns" {
+        value 83;
+        description
+          "Newfoundland Time, including SE Labrador";
+      }
+      enum "America/Halifax" {
+        value 84;
+        description
+          "Atlantic Time - Nova Scotia (most places), PEI";
+      }
+      enum "America/Glace_Bay" {
+        value 85;
+        description
+          "Atlantic Time - Nova Scotia - places that did not observe
+           DST 1966-1971";
+      }
+      enum "America/Moncton" {
+        value 86;
+        description
+          "Atlantic Time - New Brunswick";
+      }
+      enum "America/Goose_Bay" {
+        value 87;
+        description
+          "Atlantic Time - Labrador - most locations";
+      }
+      enum "America/Blanc-Sablon" {
+        value 88;
+        description
+          "Atlantic Standard Time - Quebec - Lower North Shore";
+      }
+      enum "America/Montreal" {
+        value 89;
+        description
+          "Eastern Time - Quebec - most locations";
+      }
+      enum "America/Toronto" {
+        value 90;
+        description
+          "Eastern Time - Ontario - most locations";
+      }
+      enum "America/Nipigon" {
+        value 91;
+        description
+          "Eastern Time - Ontario & Quebec - places that did not
+           observe DST 1967-1973";
+      }
+      enum "America/Thunder_Bay" {
+        value 92;
+        description
+          "Eastern Time - Thunder Bay, Ontario";
+      }
+      enum "America/Iqaluit" {
+        value 93;
+        description
+          "Eastern Time - east Nunavut - most locations";
+      }
+      enum "America/Pangnirtung" {
+        value 94;
+        description
+          "Eastern Time - Pangnirtung, Nunavut";
+      }
+      enum "America/Resolute" {
+        value 95;
+        description
+          "Central Standard Time - Resolute, Nunavut";
+      }
+      enum "America/Atikokan" {
+        value 96;
+        description
+          "Eastern Standard Time - Atikokan, Ontario and Southampton I,
+           Nunavut";
+      }
+      enum "America/Rankin_Inlet" {
+        value 97;
+        description
+          "Central Time - central Nunavut";
+      }
+      enum "America/Winnipeg" {
+        value 98;
+        description
+          "Central Time - Manitoba & west Ontario";
+      }
+      enum "America/Rainy_River" {
+        value 99;
+        description
+          "Central Time - Rainy River & Fort Frances, Ontario";
+      }
+      enum "America/Regina" {
+        value 100;
+        description
+          "Central Standard Time - Saskatchewan - most locations";
+      }
+      enum "America/Swift_Current" {
+        value 101;
+        description
+          "Central Standard Time - Saskatchewan - midwest";
+      }
+      enum "America/Edmonton" {
+        value 102;
+        description
+          "Mountain Time - Alberta, east British Columbia & west
+           Saskatchewan";
+      }
+      enum "America/Cambridge_Bay" {
+        value 103;
+        description
+          "Mountain Time - west Nunavut";
+      }
+      enum "America/Yellowknife" {
+        value 104;
+        description
+          "Mountain Time - central Northwest Territories";
+      }
+      enum "America/Inuvik" {
+        value 105;
+        description
+          "Mountain Time - west Northwest Territories";
+      }
+      enum "America/Creston" {
+        value 106;
+        description
+          "Mountain Standard Time - Creston, British Columbia";
+      }
+      enum "America/Dawson_Creek" {
+        value 107;
+        description
+          "Mountain Standard Time - Dawson Creek & Fort Saint John,
+           British Columbia";
+      }
+      enum "America/Vancouver" {
+        value 108;
+        description
+          "Pacific Time - west British Columbia";
+      }
+      enum "America/Whitehorse" {
+        value 109;
+        description
+          "Pacific Time - south Yukon";
+      }
+      enum "America/Dawson" {
+        value 110;
+        description
+          "Pacific Time - north Yukon";
+      }
+      enum "Indian/Cocos" {
+        value 111;
+      }
+      enum "Africa/Kinshasa" {
+        value 112;
+        description
+          "west Dem. Rep. of Congo";
+      }
+      enum "Africa/Lubumbashi" {
+        value 113;
+        description
+          "east Dem. Rep. of Congo";
+      }
+      enum "Africa/Bangui" {
+        value 114;
+      }
+      enum "Africa/Brazzaville" {
+        value 115;
+      }
+      enum "Europe/Zurich" {
+        value 116;
+      }
+      enum "Africa/Abidjan" {
+        value 117;
+      }
+      enum "Pacific/Rarotonga" {
+        value 118;
+      }
+      enum "America/Santiago" {
+        value 119;
+        description
+          "most locations";
+      }
+      enum "Pacific/Easter" {
+        value 120;
+        description
+          "Easter Island & Sala y Gomez";
+      }
+      enum "Africa/Douala" {
+        value 121;
+      }
+      enum "Asia/Shanghai" {
+        value 122;
+        description
+          "east China - Beijing, Guangdong, Shanghai, etc.";
+      }
+      enum "Asia/Harbin" {
+        value 123;
+        description
+          "Heilongjiang (except Mohe), Jilin";
+      }
+      enum "Asia/Chongqing" {
+        value 124;
+        description
+          "central China - Sichuan, Yunnan, Guangxi, Shaanxi, Guizhou,
+           etc.";
+      }
+      enum "Asia/Urumqi" {
+        value 125;
+        description
+          "most of Tibet & Xinjiang";
+      }
+      enum "Asia/Kashgar" {
+        value 126;
+        description
+          "west Tibet & Xinjiang";
+      }
+      enum "America/Bogota" {
+        value 127;
+      }
+      enum "America/Costa_Rica" {
+        value 128;
+      }
+      enum "America/Havana" {
+        value 129;
+      }
+      enum "Atlantic/Cape_Verde" {
+        value 130;
+      }
+      enum "America/Curacao" {
+        value 131;
+      }
+      enum "Indian/Christmas" {
+        value 132;
+      }
+      enum "Asia/Nicosia" {
+        value 133;
+      }
+      enum "Europe/Prague" {
+        value 134;
+      }
+      enum "Europe/Berlin" {
+        value 135;
+      }
+      enum "Africa/Djibouti" {
+        value 136;
+      }
+      enum "Europe/Copenhagen" {
+        value 137;
+      }
+      enum "America/Dominica" {
+        value 138;
+      }
+      enum "America/Santo_Domingo" {
+        value 139;
+      }
+      enum "Africa/Algiers" {
+        value 140;
+      }
+      enum "America/Guayaquil" {
+        value 141;
+        description
+          "mainland";
+      }
+      enum "Pacific/Galapagos" {
+        value 142;
+        description
+          "Galapagos Islands";
+      }
+      enum "Europe/Tallinn" {
+        value 143;
+      }
+      enum "Africa/Cairo" {
+        value 144;
+      }
+      enum "Africa/El_Aaiun" {
+        value 145;
+      }
+      enum "Africa/Asmara" {
+        value 146;
+      }
+      enum "Europe/Madrid" {
+        value 147;
+        description
+          "mainland";
+      }
+      enum "Africa/Ceuta" {
+        value 148;
+        description
+          "Ceuta & Melilla";
+      }
+      enum "Atlantic/Canary" {
+        value 149;
+        description
+          "Canary Islands";
+      }
+      enum "Africa/Addis_Ababa" {
+        value 150;
+      }
+      enum "Europe/Helsinki" {
+        value 151;
+      }
+      enum "Pacific/Fiji" {
+        value 152;
+      }
+      enum "Atlantic/Stanley" {
+        value 153;
+      }
+      enum "Pacific/Chuuk" {
+        value 154;
+        description
+          "Chuuk (Truk) and Yap";
+      }
+      enum "Pacific/Pohnpei" {
+        value 155;
+        description
+          "Pohnpei (Ponape)";
+      }
+      enum "Pacific/Kosrae" {
+        value 156;
+        description
+          "Kosrae";
+      }
+      enum "Atlantic/Faroe" {
+        value 157;
+      }
+      enum "Europe/Paris" {
+        value 158;
+      }
+      enum "Africa/Libreville" {
+        value 159;
+      }
+      enum "Europe/London" {
+        value 160;
+      }
+      enum "America/Grenada" {
+        value 161;
+      }
+      enum "Asia/Tbilisi" {
+        value 162;
+      }
+      enum "America/Cayenne" {
+        value 163;
+      }
+      enum "Europe/Guernsey" {
+        value 164;
+      }
+      enum "Africa/Accra" {
+        value 165;
+      }
+      enum "Europe/Gibraltar" {
+        value 166;
+      }
+      enum "America/Godthab" {
+        value 167;
+        description
+          "most locations";
+      }
+      enum "America/Danmarkshavn" {
+        value 168;
+        description
+          "east coast, north of Scoresbysund";
+      }
+      enum "America/Scoresbysund" {
+        value 169;
+        description
+          "Scoresbysund / Ittoqqortoormiit";
+      }
+      enum "America/Thule" {
+        value 170;
+        description
+          "Thule / Pituffik";
+      }
+      enum "Africa/Banjul" {
+        value 171;
+      }
+      enum "Africa/Conakry" {
+        value 172;
+      }
+      enum "America/Guadeloupe" {
+        value 173;
+      }
+      enum "Africa/Malabo" {
+        value 174;
+      }
+      enum "Europe/Athens" {
+        value 175;
+      }
+      enum "Atlantic/South_Georgia" {
+        value 176;
+      }
+      enum "America/Guatemala" {
+        value 177;
+      }
+      enum "Pacific/Guam" {
+        value 178;
+      }
+      enum "Africa/Bissau" {
+        value 179;
+      }
+      enum "America/Guyana" {
+        value 180;
+      }
+      enum "Asia/Hong_Kong" {
+        value 181;
+      }
+      enum "America/Tegucigalpa" {
+        value 182;
+      }
+      enum "Europe/Zagreb" {
+        value 183;
+      }
+      enum "America/Port-au-Prince" {
+        value 184;
+      }
+      enum "Europe/Budapest" {
+        value 185;
+      }
+      enum "Asia/Jakarta" {
+        value 186;
+        description
+          "Java & Sumatra";
+      }
+      enum "Asia/Pontianak" {
+        value 187;
+        description
+          "west & central Borneo";
+      }
+      enum "Asia/Makassar" {
+        value 188;
+        description
+          "east & south Borneo, Sulawesi (Celebes), Bali, Nusa
+           Tengarra, west Timor";
+      }
+      enum "Asia/Jayapura" {
+        value 189;
+        description
+          "west New Guinea (Irian Jaya) & Malukus (Moluccas)";
+      }
+      enum "Europe/Dublin" {
+        value 190;
+      }
+      enum "Asia/Jerusalem" {
+        value 191;
+      }
+      enum "Europe/Isle_of_Man" {
+        value 192;
+      }
+      enum "Asia/Kolkata" {
+        value 193;
+      }
+      enum "Indian/Chagos" {
+        value 194;
+      }
+      enum "Asia/Baghdad" {
+        value 195;
+      }
+      enum "Asia/Tehran" {
+        value 196;
+      }
+      enum "Atlantic/Reykjavik" {
+        value 197;
+      }
+      enum "Europe/Rome" {
+        value 198;
+      }
+      enum "Europe/Jersey" {
+        value 199;
+      }
+      enum "America/Jamaica" {
+        value 200;
+      }
+      enum "Asia/Amman" {
+        value 201;
+      }
+      enum "Asia/Tokyo" {
+        value 202;
+      }
+      enum "Africa/Nairobi" {
+        value 203;
+      }
+      enum "Asia/Bishkek" {
+        value 204;
+      }
+      enum "Asia/Phnom_Penh" {
+        value 205;
+      }
+      enum "Pacific/Tarawa" {
+        value 206;
+        description
+          "Gilbert Islands";
+      }
+      enum "Pacific/Enderbury" {
+        value 207;
+        description
+          "Phoenix Islands";
+      }
+      enum "Pacific/Kiritimati" {
+        value 208;
+        description
+          "Line Islands";
+      }
+      enum "Indian/Comoro" {
+        value 209;
+      }
+      enum "America/St_Kitts" {
+        value 210;
+      }
+      enum "Asia/Pyongyang" {
+        value 211;
+      }
+      enum "Asia/Seoul" {
+        value 212;
+      }
+      enum "Asia/Kuwait" {
+        value 213;
+      }
+      enum "America/Cayman" {
+        value 214;
+      }
+      enum "Asia/Almaty" {
+        value 215;
+        description
+          "most locations";
+      }
+      enum "Asia/Qyzylorda" {
+        value 216;
+        description
+          "Qyzylorda (Kyzylorda, Kzyl-Orda)";
+      }
+      enum "Asia/Aqtobe" {
+        value 217;
+        description
+          "Aqtobe (Aktobe)";
+      }
+      enum "Asia/Aqtau" {
+        value 218;
+        description
+          "Atyrau (Atirau, Gur'yev), Mangghystau (Mankistau)";
+      }
+      enum "Asia/Oral" {
+        value 219;
+        description
+          "West Kazakhstan";
+      }
+      enum "Asia/Vientiane" {
+        value 220;
+      }
+      enum "Asia/Beirut" {
+        value 221;
+      }
+      enum "America/St_Lucia" {
+        value 222;
+      }
+      enum "Europe/Vaduz" {
+        value 223;
+      }
+      enum "Asia/Colombo" {
+        value 224;
+      }
+      enum "Africa/Monrovia" {
+        value 225;
+      }
+      enum "Africa/Maseru" {
+        value 226;
+      }
+      enum "Europe/Vilnius" {
+        value 227;
+      }
+      enum "Europe/Luxembourg" {
+        value 228;
+      }
+      enum "Europe/Riga" {
+        value 229;
+      }
+      enum "Africa/Tripoli" {
+        value 230;
+      }
+      enum "Africa/Casablanca" {
+        value 231;
+      }
+      enum "Europe/Monaco" {
+        value 232;
+      }
+      enum "Europe/Chisinau" {
+        value 233;
+      }
+      enum "Europe/Podgorica" {
+        value 234;
+      }
+      enum "America/Marigot" {
+        value 235;
+      }
+      enum "Indian/Antananarivo" {
+        value 236;
+      }
+      enum "Pacific/Majuro" {
+        value 237;
+        description
+          "most locations";
+      }
+      enum "Pacific/Kwajalein" {
+        value 238;
+        description
+          "Kwajalein";
+      }
+      enum "Europe/Skopje" {
+        value 239;
+      }
+      enum "Africa/Bamako" {
+        value 240;
+      }
+      enum "Asia/Rangoon" {
+        value 241;
+      }
+      enum "Asia/Ulaanbaatar" {
+        value 242;
+        description
+          "most locations";
+      }
+      enum "Asia/Hovd" {
+        value 243;
+        description
+          "Bayan-Olgiy, Govi-Altai, Hovd, Uvs, Zavkhan";
+      }
+      enum "Asia/Choibalsan" {
+        value 244;
+        description
+          "Dornod, Sukhbaatar";
+      }
+      enum "Asia/Macau" {
+        value 245;
+      }
+      enum "Pacific/Saipan" {
+        value 246;
+      }
+      enum "America/Martinique" {
+        value 247;
+      }
+      enum "Africa/Nouakchott" {
+        value 248;
+      }
+      enum "America/Montserrat" {
+        value 249;
+      }
+      enum "Europe/Malta" {
+        value 250;
+      }
+      enum "Indian/Mauritius" {
+        value 251;
+      }
+      enum "Indian/Maldives" {
+        value 252;
+      }
+      enum "Africa/Blantyre" {
+        value 253;
+      }
+      enum "America/Mexico_City" {
+        value 254;
+        description
+          "Central Time - most locations";
+      }
+      enum "America/Cancun" {
+        value 255;
+        description
+          "Central Time - Quintana Roo";
+      }
+      enum "America/Merida" {
+        value 256;
+        description
+          "Central Time - Campeche, Yucatan";
+      }
+      enum "America/Monterrey" {
+        value 257;
+        description
+          "Mexican Central Time - Coahuila, Durango, Nuevo Leon,
+           Tamaulipas away from US border";
+      }
+      enum "America/Matamoros" {
+        value 258;
+        description
+          "US Central Time - Coahuila, Durango, Nuevo Leon, Tamaulipas
+           near US border";
+      }
+      enum "America/Mazatlan" {
+        value 259;
+        description
+          "Mountain Time - S Baja, Nayarit, Sinaloa";
+      }
+      enum "America/Chihuahua" {
+        value 260;
+        description
+          "Mexican Mountain Time - Chihuahua away from US border";
+      }
+      enum "America/Ojinaga" {
+        value 261;
+        description
+          "US Mountain Time - Chihuahua near US border";
+      }
+      enum "America/Hermosillo" {
+        value 262;
+        description
+          "Mountain Standard Time - Sonora";
+      }
+      enum "America/Tijuana" {
+        value 263;
+        description
+          "US Pacific Time - Baja California near US border";
+      }
+      enum "America/Santa_Isabel" {
+        value 264;
+        description
+          "Mexican Pacific Time - Baja California away from US border";
+      }
+      enum "America/Bahia_Banderas" {
+        value 265;
+        description
+          "Mexican Central Time - Bahia de Banderas";
+      }
+      enum "Asia/Kuala_Lumpur" {
+        value 266;
+        description
+          "peninsular Malaysia";
+      }
+      enum "Asia/Kuching" {
+        value 267;
+        description
+          "Sabah & Sarawak";
+      }
+      enum "Africa/Maputo" {
+        value 268;
+      }
+      enum "Africa/Windhoek" {
+        value 269;
+      }
+      enum "Pacific/Noumea" {
+        value 270;
+      }
+      enum "Africa/Niamey" {
+        value 271;
+      }
+      enum "Pacific/Norfolk" {
+        value 272;
+      }
+      enum "Africa/Lagos" {
+        value 273;
+      }
+      enum "America/Managua" {
+        value 274;
+      }
+      enum "Europe/Amsterdam" {
+        value 275;
+      }
+      enum "Europe/Oslo" {
+        value 276;
+      }
+      enum "Asia/Kathmandu" {
+        value 277;
+      }
+      enum "Pacific/Nauru" {
+        value 278;
+      }
+      enum "Pacific/Niue" {
+        value 279;
+      }
+      enum "Pacific/Auckland" {
+        value 280;
+        description
+          "most locations";
+      }
+      enum "Pacific/Chatham" {
+        value 281;
+        description
+          "Chatham Islands";
+      }
+      enum "Asia/Muscat" {
+        value 282;
+      }
+      enum "America/Panama" {
+        value 283;
+      }
+      enum "America/Lima" {
+        value 284;
+      }
+      enum "Pacific/Tahiti" {
+        value 285;
+        description
+          "Society Islands";
+      }
+      enum "Pacific/Marquesas" {
+        value 286;
+        description
+          "Marquesas Islands";
+      }
+      enum "Pacific/Gambier" {
+        value 287;
+        description
+          "Gambier Islands";
+      }
+      enum "Pacific/Port_Moresby" {
+        value 288;
+      }
+      enum "Asia/Manila" {
+        value 289;
+      }
+      enum "Asia/Karachi" {
+        value 290;
+      }
+      enum "Europe/Warsaw" {
+        value 291;
+      }
+      enum "America/Miquelon" {
+        value 292;
+      }
+      enum "Pacific/Pitcairn" {
+        value 293;
+      }
+      enum "America/Puerto_Rico" {
+        value 294;
+      }
+      enum "Asia/Gaza" {
+        value 295;
+        description
+          "Gaza Strip";
+      }
+      enum "Asia/Hebron" {
+        value 296;
+        description
+          "West Bank";
+      }
+      enum "Europe/Lisbon" {
+        value 297;
+        description
+          "mainland";
+      }
+      enum "Atlantic/Madeira" {
+        value 298;
+        description
+          "Madeira Islands";
+      }
+      enum "Atlantic/Azores" {
+        value 299;
+        description
+          "Azores";
+      }
+      enum "Pacific/Palau" {
+        value 300;
+      }
+      enum "America/Asuncion" {
+        value 301;
+      }
+      enum "Asia/Qatar" {
+        value 302;
+      }
+      enum "Indian/Reunion" {
+        value 303;
+      }
+      enum "Europe/Bucharest" {
+        value 304;
+      }
+      enum "Europe/Belgrade" {
+        value 305;
+      }
+      enum "Europe/Kaliningrad" {
+        value 306;
+        description
+          "Moscow-01 - Kaliningrad";
+      }
+      enum "Europe/Moscow" {
+        value 307;
+        description
+          "Moscow+00 - west Russia";
+      }
+      enum "Europe/Volgograd" {
+        value 308;
+        description
+          "Moscow+00 - Caspian Sea";
+      }
+      enum "Europe/Samara" {
+        value 309;
+        description
+          "Moscow+00 - Samara, Udmurtia";
+      }
+      enum "Asia/Yekaterinburg" {
+        value 310;
+        description
+          "Moscow+02 - Urals";
+      }
+      enum "Asia/Omsk" {
+        value 311;
+        description
+          "Moscow+03 - west Siberia";
+      }
+      enum "Asia/Novosibirsk" {
+        value 312;
+        description
+          "Moscow+03 - Novosibirsk";
+      }
+      enum "Asia/Novokuznetsk" {
+        value 313;
+        description
+          "Moscow+03 - Novokuznetsk";
+      }
+      enum "Asia/Krasnoyarsk" {
+        value 314;
+        description
+          "Moscow+04 - Yenisei River";
+      }
+      enum "Asia/Irkutsk" {
+        value 315;
+        description
+          "Moscow+05 - Lake Baikal";
+      }
+      enum "Asia/Yakutsk" {
+        value 316;
+        description
+          "Moscow+06 - Lena River";
+      }
+      enum "Asia/Vladivostok" {
+        value 317;
+        description
+          "Moscow+07 - Amur River";
+      }
+      enum "Asia/Sakhalin" {
+        value 318;
+        description
+          "Moscow+07 - Sakhalin Island";
+      }
+      enum "Asia/Magadan" {
+        value 319;
+        description
+          "Moscow+08 - Magadan";
+      }
+      enum "Asia/Kamchatka" {
+        value 320;
+        description
+          "Moscow+08 - Kamchatka";
+      }
+      enum "Asia/Anadyr" {
+        value 321;
+        description
+          "Moscow+08 - Bering Sea";
+      }
+      enum "Africa/Kigali" {
+        value 322;
+      }
+      enum "Asia/Riyadh" {
+        value 323;
+      }
+      enum "Pacific/Guadalcanal" {
+        value 324;
+      }
+      enum "Indian/Mahe" {
+        value 325;
+      }
+      enum "Africa/Khartoum" {
+        value 326;
+      }
+      enum "Europe/Stockholm" {
+        value 327;
+      }
+      enum "Asia/Singapore" {
+        value 328;
+      }
+      enum "Atlantic/St_Helena" {
+        value 329;
+      }
+      enum "Europe/Ljubljana" {
+        value 330;
+      }
+      enum "Arctic/Longyearbyen" {
+        value 331;
+      }
+      enum "Europe/Bratislava" {
+        value 332;
+      }
+      enum "Africa/Freetown" {
+        value 333;
+      }
+      enum "Europe/San_Marino" {
+        value 334;
+      }
+      enum "Africa/Dakar" {
+        value 335;
+      }
+      enum "Africa/Mogadishu" {
+        value 336;
+      }
+      enum "America/Paramaribo" {
+        value 337;
+      }
+      enum "Africa/Juba" {
+        value 338;
+      }
+      enum "Africa/Sao_Tome" {
+        value 339;
+      }
+      enum "America/El_Salvador" {
+        value 340;
+      }
+      enum "America/Lower_Princes" {
+        value 341;
+      }
+      enum "Asia/Damascus" {
+        value 342;
+      }
+      enum "Africa/Mbabane" {
+        value 343;
+      }
+      enum "America/Grand_Turk" {
+        value 344;
+      }
+      enum "Africa/Ndjamena" {
+        value 345;
+      }
+      enum "Indian/Kerguelen" {
+        value 346;
+      }
+      enum "Africa/Lome" {
+        value 347;
+      }
+      enum "Asia/Bangkok" {
+        value 348;
+      }
+      enum "Asia/Dushanbe" {
+        value 349;
+      }
+      enum "Pacific/Fakaofo" {
+        value 350;
+      }
+      enum "Asia/Dili" {
+        value 351;
+      }
+      enum "Asia/Ashgabat" {
+        value 352;
+      }
+      enum "Africa/Tunis" {
+        value 353;
+      }
+      enum "Pacific/Tongatapu" {
+        value 354;
+      }
+      enum "Europe/Istanbul" {
+        value 355;
+      }
+      enum "America/Port_of_Spain" {
+        value 356;
+      }
+      enum "Pacific/Funafuti" {
+        value 357;
+      }
+      enum "Asia/Taipei" {
+        value 358;
+      }
+      enum "Africa/Dar_es_Salaam" {
+        value 359;
+      }
+      enum "Europe/Kiev" {
+        value 360;
+        description
+          "most locations";
+      }
+      enum "Europe/Uzhgorod" {
+        value 361;
+        description
+          "Ruthenia";
+      }
+      enum "Europe/Zaporozhye" {
+        value 362;
+        description
+          "Zaporozh'ye, E Lugansk / Zaporizhia, E Luhansk";
+      }
+      enum "Europe/Simferopol" {
+        value 363;
+        description
+          "central Crimea";
+      }
+      enum "Africa/Kampala" {
+        value 364;
+      }
+      enum "Pacific/Johnston" {
+        value 365;
+        description
+          "Johnston Atoll";
+      }
+      enum "Pacific/Midway" {
+        value 366;
+        description
+          "Midway Islands";
+      }
+      enum "Pacific/Wake" {
+        value 367;
+        description
+          "Wake Island";
+      }
+      enum "America/New_York" {
+        value 368;
+        description
+          "Eastern Time";
+      }
+      enum "America/Detroit" {
+        value 369;
+        description
+          "Eastern Time - Michigan - most locations";
+      }
+      enum "America/Kentucky/Louisville" {
+        value 370;
+        description
+          "Eastern Time - Kentucky - Louisville area";
+      }
+      enum "America/Kentucky/Monticello" {
+        value 371;
+        description
+          "Eastern Time - Kentucky - Wayne County";
+      }
+      enum "America/Indiana/Indianapolis" {
+        value 372;
+        description
+          "Eastern Time - Indiana - most locations";
+      }
+      enum "America/Indiana/Vincennes" {
+        value 373;
+        description
+          "Eastern Time - Indiana - Daviess, Dubois, Knox & Martin
+           Counties";
+      }
+      enum "America/Indiana/Winamac" {
+        value 374;
+        description
+          "Eastern Time - Indiana - Pulaski County";
+      }
+      enum "America/Indiana/Marengo" {
+        value 375;
+        description
+          "Eastern Time - Indiana - Crawford County";
+      }
+      enum "America/Indiana/Petersburg" {
+        value 376;
+        description
+          "Eastern Time - Indiana - Pike County";
+      }
+      enum "America/Indiana/Vevay" {
+        value 377;
+        description
+          "Eastern Time - Indiana - Switzerland County";
+      }
+      enum "America/Chicago" {
+        value 378;
+        description
+          "Central Time";
+      }
+      enum "America/Indiana/Tell_City" {
+        value 379;
+        description
+          "Central Time - Indiana - Perry County";
+      }
+      enum "America/Indiana/Knox" {
+        value 380;
+        description
+          "Central Time - Indiana - Starke County";
+      }
+      enum "America/Menominee" {
+        value 381;
+        description
+          "Central Time - Michigan - Dickinson, Gogebic, Iron &
+           Menominee Counties";
+      }
+      enum "America/North_Dakota/Center" {
+        value 382;
+        description
+          "Central Time - North Dakota - Oliver County";
+      }
+      enum "America/North_Dakota/New_Salem" {
+        value 383;
+        description
+          "Central Time - North Dakota - Morton County (except Mandan
+           area)";
+      }
+      enum "America/North_Dakota/Beulah" {
+        value 384;
+        description
+          "Central Time - North Dakota - Mercer County";
+      }
+      enum "America/Denver" {
+        value 385;
+        description
+          "Mountain Time";
+      }
+      enum "America/Boise" {
+        value 386;
+        description
+          "Mountain Time - south Idaho & east Oregon";
+      }
+      enum "America/Shiprock" {
+        value 387;
+        description
+          "Mountain Time - Navajo";
+      }
+      enum "America/Phoenix" {
+        value 388;
+        description
+          "Mountain Standard Time - Arizona";
+      }
+      enum "America/Los_Angeles" {
+        value 389;
+        description
+          "Pacific Time";
+      }
+      enum "America/Anchorage" {
+        value 390;
+        description
+          "Alaska Time";
+      }
+      enum "America/Juneau" {
+        value 391;
+        description
+          "Alaska Time - Alaska panhandle";
+      }
+      enum "America/Sitka" {
+        value 392;
+        description
+          "Alaska Time - southeast Alaska panhandle";
+      }
+      enum "America/Yakutat" {
+        value 393;
+        description
+          "Alaska Time - Alaska panhandle neck";
+      }
+      enum "America/Nome" {
+        value 394;
+        description
+          "Alaska Time - west Alaska";
+      }
+      enum "America/Adak" {
+        value 395;
+        description
+          "Aleutian Islands";
+      }
+      enum "America/Metlakatla" {
+        value 396;
+        description
+          "Metlakatla Time - Annette Island";
+      }
+      enum "Pacific/Honolulu" {
+        value 397;
+        description
+          "Hawaii";
+      }
+      enum "America/Montevideo" {
+        value 398;
+      }
+      enum "Asia/Samarkand" {
+        value 399;
+        description
+          "west Uzbekistan";
+      }
+      enum "Asia/Tashkent" {
+        value 400;
+        description
+          "east Uzbekistan";
+      }
+      enum "Europe/Vatican" {
+        value 401;
+      }
+      enum "America/St_Vincent" {
+        value 402;
+      }
+      enum "America/Caracas" {
+        value 403;
+      }
+      enum "America/Tortola" {
+        value 404;
+      }
+      enum "America/St_Thomas" {
+        value 405;
+      }
+      enum "Asia/Ho_Chi_Minh" {
+        value 406;
+      }
+      enum "Pacific/Efate" {
+        value 407;
+      }
+      enum "Pacific/Wallis" {
+        value 408;
+      }
+      enum "Pacific/Apia" {
+        value 409;
+      }
+      enum "Asia/Aden" {
+        value 410;
+      }
+      enum "Indian/Mayotte" {
+        value 411;
+      }
+      enum "Africa/Johannesburg" {
+        value 412;
+      }
+      enum "Africa/Lusaka" {
+        value 413;
+      }
+      enum "Africa/Harare" {
+        value 414;
+      }
+    }
+  }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/ietf/ietf-inet-types@2010-09-24.yang b/yang/yang-parser-impl/src/test/resources/ietf/ietf-inet-types@2010-09-24.yang
new file mode 100644 (file)
index 0000000..de20feb
--- /dev/null
@@ -0,0 +1,418 @@
+ module ietf-inet-types {
+
+   namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
+   prefix "inet";
+
+   organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+   contact
+    "WG Web:   <http://tools.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+
+     WG Chair: David Partain
+               <mailto:david.partain@ericsson.com>
+
+     WG Chair: David Kessens
+               <mailto:david.kessens@nsn.com>
+
+     Editor:   Juergen Schoenwaelder
+               <mailto:j.schoenwaelder@jacobs-university.de>";
+
+   description
+    "This module contains a collection of generally useful derived
+     YANG data types for Internet addresses and related things.
+
+     Copyright (c) 2010 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or without
+     modification, is permitted pursuant to, and subject to the license
+     terms contained in, the Simplified BSD License set forth in Section
+     4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6021; see
+     the RFC itself for full legal notices.";
+
+   revision 2010-09-24 {
+     description
+      "Initial revision.";
+     reference
+      "RFC 6021: Common YANG Data Types";
+   }
+
+   /*** collection of protocol field related types ***/
+
+   typedef ip-version {
+     type enumeration {
+       enum unknown {
+         value "0";
+         description
+          "An unknown or unspecified version of the Internet protocol.";
+       }
+       enum ipv4 {
+         value "1";
+         description
+          "The IPv4 protocol as defined in RFC 791.";
+       }
+       enum ipv6 {
+         value "2";
+         description
+          "The IPv6 protocol as defined in RFC 2460.";
+       }
+     }
+     description
+      "This value represents the version of the IP protocol.
+
+       In the value set and its semantics, this type is equivalent
+       to the InetVersion textual convention of the SMIv2.";
+     reference
+      "RFC  791: Internet Protocol
+       RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+       RFC 4001: Textual Conventions for Internet Network Addresses";
+   }
+
+   typedef dscp {
+     type uint8 {
+       range "0..63";
+     }
+     description
+      "The dscp type represents a Differentiated Services Code-Point
+       that may be used for marking packets in a traffic stream.
+
+       In the value set and its semantics, this type is equivalent
+       to the Dscp textual convention of the SMIv2.";
+     reference
+      "RFC 3289: Management Information Base for the Differentiated
+                 Services Architecture
+       RFC 2474: Definition of the Differentiated Services Field
+                 (DS Field) in the IPv4 and IPv6 Headers
+       RFC 2780: IANA Allocation Guidelines For Values In
+                 the Internet Protocol and Related Headers";
+   }
+
+   typedef ipv6-flow-label {
+     type uint32 {
+       range "0..1048575";
+     }
+     description
+      "The flow-label type represents flow identifier or Flow Label
+       in an IPv6 packet header that may be used to discriminate
+       traffic flows.
+
+       In the value set and its semantics, this type is equivalent
+       to the IPv6FlowLabel textual convention of the SMIv2.";
+     reference
+      "RFC 3595: Textual Conventions for IPv6 Flow Label
+       RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
+   }
+
+   typedef port-number {
+     type uint16 {
+       range "0..65535";
+     }
+     description
+      "The port-number type represents a 16-bit port number of an
+       Internet transport layer protocol such as UDP, TCP, DCCP, or
+       SCTP.  Port numbers are assigned by IANA.  A current list of
+       all assignments is available from <http://www.iana.org/>.
+
+       Note that the port number value zero is reserved by IANA.  In
+       situations where the value zero does not make sense, it can
+       be excluded by subtyping the port-number type.
+
+       In the value set and its semantics, this type is equivalent
+       to the InetPortNumber textual convention of the SMIv2.";
+     reference
+      "RFC  768: User Datagram Protocol
+       RFC  793: Transmission Control Protocol
+       RFC 4960: Stream Control Transmission Protocol
+       RFC 4340: Datagram Congestion Control Protocol (DCCP)
+       RFC 4001: Textual Conventions for Internet Network Addresses";
+   }
+
+   /*** collection of autonomous system related types ***/
+
+   typedef as-number {
+     type uint32;
+     description
+      "The as-number type represents autonomous system numbers
+       which identify an Autonomous System (AS).  An AS is a set
+       of routers under a single technical administration, using
+       an interior gateway protocol and common metrics to route
+       packets within the AS, and using an exterior gateway
+       protocol to route packets to other ASs'.  IANA maintains
+       the AS number space and has delegated large parts to the
+       regional registries.
+
+       Autonomous system numbers were originally limited to 16
+       bits.  BGP extensions have enlarged the autonomous system
+       number space to 32 bits.  This type therefore uses an uint32
+       base type without a range restriction in order to support
+       a larger autonomous system number space.
+
+       In the value set and its semantics, this type is equivalent
+       to the InetAutonomousSystemNumber textual convention of
+       the SMIv2.";
+     reference
+      "RFC 1930: Guidelines for creation, selection, and registration
+                 of an Autonomous System (AS)
+       RFC 4271: A Border Gateway Protocol 4 (BGP-4)
+       RFC 4893: BGP Support for Four-octet AS Number Space
+       RFC 4001: Textual Conventions for Internet Network Addresses";
+   }
+
+   /*** collection of IP address and hostname related types ***/
+
+   typedef ip-address {
+     type union {
+       type inet:ipv4-address;
+       type inet:ipv6-address;
+     }
+     description
+      "The ip-address type represents an IP address and is IP
+       version neutral.  The format of the textual representations
+       implies the IP version.";
+   }
+
+   typedef ipv4-address {
+     type string {
+       pattern
+         '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+       +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+       + '(%[\p{N}\p{L}]+)?';
+     }
+     description
+       "The ipv4-address type represents an IPv4 address in
+        dotted-quad notation.  The IPv4 address may include a zone
+        index, separated by a % sign.
+
+        The zone index is used to disambiguate identical address
+        values.  For link-local addresses, the zone index will
+        typically be the interface index number or the name of an
+        interface.  If the zone index is not present, the default
+        zone of the device will be used.
+
+        The canonical format for the zone index is the numerical
+        format";
+   }
+
+   typedef ipv6-address {
+     type string {
+       pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+             + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+             + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+             + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+             + '(%[\p{N}\p{L}]+)?';
+       pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+             + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+             + '(%.+)?';
+     }
+     description
+      "The ipv6-address type represents an IPv6 address in full,
+       mixed, shortened, and shortened-mixed notation.  The IPv6
+       address may include a zone index, separated by a % sign.
+
+       The zone index is used to disambiguate identical address
+       values.  For link-local addresses, the zone index will
+       typically be the interface index number or the name of an
+       interface.  If the zone index is not present, the default
+       zone of the device will be used.
+
+       The canonical format of IPv6 addresses uses the compressed
+       format described in RFC 4291, Section 2.2, item 2 with the
+       following additional rules: the :: substitution must be
+       applied to the longest sequence of all-zero 16-bit chunks
+       in an IPv6 address.  If there is a tie, the first sequence
+       of all-zero 16-bit chunks is replaced by ::.  Single
+       all-zero 16-bit chunks are not compressed.  The canonical
+       format uses lowercase characters and leading zeros are
+       not allowed.  The canonical format for the zone index is
+       the numerical format as described in RFC 4007, Section
+       11.2.";
+     reference
+      "RFC 4291: IP Version 6 Addressing Architecture
+       RFC 4007: IPv6 Scoped Address Architecture
+       RFC 5952: A Recommendation for IPv6 Address Text Representation";
+   }
+
+   typedef ip-prefix {
+     type union {
+       type inet:ipv4-prefix;
+       type inet:ipv6-prefix;
+     }
+     description
+      "The ip-prefix type represents an IP prefix and is IP
+       version neutral.  The format of the textual representations
+       implies the IP version.";
+   }
+
+   typedef ipv4-prefix {
+     type string {
+       pattern
+          '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+        +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+        + '/(([0-9])|([1-2][0-9])|(3[0-2]))';
+     }
+     description
+      "The ipv4-prefix type represents an IPv4 address prefix.
+       The prefix length is given by the number following the
+       slash character and must be less than or equal to 32.
+
+       A prefix length value of n corresponds to an IP address
+       mask that has n contiguous 1-bits from the most
+       significant bit (MSB) and all other bits set to 0.
+
+       The canonical format of an IPv4 prefix has all bits of
+       the IPv4 address set to zero that are not part of the
+       IPv4 prefix.";
+   }
+
+   typedef ipv6-prefix {
+     type string {
+       pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+             + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+             + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+             + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+             + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
+       pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+             + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+             + '(/.+)';
+     }
+     description
+      "The ipv6-prefix type represents an IPv6 address prefix.
+       The prefix length is given by the number following the
+       slash character and must be less than or equal 128.
+
+       A prefix length value of n corresponds to an IP address
+       mask that has n contiguous 1-bits from the most
+       significant bit (MSB) and all other bits set to 0.
+
+       The IPv6 address should have all bits that do not belong
+       to the prefix set to zero.
+
+       The canonical format of an IPv6 prefix has all bits of
+       the IPv6 address set to zero that are not part of the
+       IPv6 prefix.  Furthermore, IPv6 address is represented
+       in the compressed format described in RFC 4291, Section
+       2.2, item 2 with the following additional rules: the ::
+       substitution must be applied to the longest sequence of
+       all-zero 16-bit chunks in an IPv6 address.  If there is
+       a tie, the first sequence of all-zero 16-bit chunks is
+       replaced by ::.  Single all-zero 16-bit chunks are not
+       compressed.  The canonical format uses lowercase
+       characters and leading zeros are not allowed.";
+     reference
+      "RFC 4291: IP Version 6 Addressing Architecture";
+   }
+
+   /*** collection of domain name and URI types ***/
+
+   typedef domain-name {
+     type string {
+       pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+            +  '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+            +  '|\.';
+       length "1..253";
+     }
+     description
+      "The domain-name type represents a DNS domain name.  The
+       name SHOULD be fully qualified whenever possible.
+
+       Internet domain names are only loosely specified.  Section
+       3.5 of RFC 1034 recommends a syntax (modified in Section
+       2.1 of RFC 1123).  The pattern above is intended to allow
+       for current practice in domain name use, and some possible
+       future expansion.  It is designed to hold various types of
+       domain names, including names used for A or AAAA records
+       (host names) and other records, such as SRV records.  Note
+       that Internet host names have a stricter syntax (described
+       in RFC 952) than the DNS recommendations in RFCs 1034 and
+       1123, and that systems that want to store host names in
+       schema nodes using the domain-name type are recommended to
+       adhere to this stricter standard to ensure interoperability.
+
+       The encoding of DNS names in the DNS protocol is limited
+       to 255 characters.  Since the encoding consists of labels
+       prefixed by a length bytes and there is a trailing NULL
+       byte, only 253 characters can appear in the textual dotted
+       notation.
+
+       The description clause of schema nodes using the domain-name
+       type MUST describe when and how these names are resolved to
+       IP addresses.  Note that the resolution of a domain-name value
+       may require to query multiple DNS records (e.g., A for IPv4
+       and AAAA for IPv6).  The order of the resolution process and
+       which DNS record takes precedence can either be defined
+       explicitely or it may depend on the configuration of the
+       resolver.
+
+       Domain-name values use the US-ASCII encoding.  Their canonical
+       format uses lowercase US-ASCII characters.  Internationalized
+       domain names MUST be encoded in punycode as described in RFC
+       3492";
+     reference
+      "RFC  952: DoD Internet Host Table Specification
+       RFC 1034: Domain Names - Concepts and Facilities
+       RFC 1123: Requirements for Internet Hosts -- Application
+                 and Support
+       RFC 2782: A DNS RR for specifying the location of services
+                 (DNS SRV)
+       RFC 3492: Punycode: A Bootstring encoding of Unicode for
+                 Internationalized Domain Names in Applications
+                 (IDNA)
+       RFC 5891: Internationalizing Domain Names in Applications
+                 (IDNA): Protocol";
+   }
+
+   typedef host {
+     type union {
+       type inet:ip-address;
+       type inet:domain-name;
+     }
+     description
+      "The host type represents either an IP address or a DNS
+       domain name.";
+   }
+
+   typedef uri {
+     type string;
+     description
+      "The uri type represents a Uniform Resource Identifier
+       (URI) as defined by STD 66.
+
+       Objects using the uri type MUST be in US-ASCII encoding,
+       and MUST be normalized as described by RFC 3986 Sections
+       6.2.1, 6.2.2.1, and 6.2.2.2.  All unnecessary
+       percent-encoding is removed, and all case-insensitive
+       characters are set to lowercase except for hexadecimal
+       digits, which are normalized to uppercase as described in
+       Section 6.2.2.1.
+
+       The purpose of this normalization is to help provide
+       unique URIs.  Note that this normalization is not
+       sufficient to provide uniqueness.  Two URIs that are
+       textually distinct after this normalization may still be
+       equivalent.
+
+       Objects using the uri type may restrict the schemes that
+       they permit.  For example, 'data:' and 'urn:' schemes
+       might not be appropriate.
+
+       A zero-length URI is not a valid URI.  This can be used to
+       express 'URI absent' where required.
+
+       In the value set and its semantics, this type is equivalent
+       to the Uri SMIv2 textual convention defined in RFC 5017.";
+     reference
+      "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
+       RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
+                 Group: Uniform Resource Identifiers (URIs), URLs,
+                 and Uniform Resource Names (URNs): Clarifications
+                 and Recommendations
+       RFC 5017: MIB Textual Conventions for Uniform Resource
+                 Identifiers (URIs)";
+   }
+
+ }
diff --git a/yang/yang-parser-impl/src/test/resources/ietf/network-topology@2013-10-21.yang b/yang/yang-parser-impl/src/test/resources/ietf/network-topology@2013-10-21.yang
new file mode 100644 (file)
index 0000000..c114334
--- /dev/null
@@ -0,0 +1,334 @@
+module network-topology  {
+    yang-version 1;
+    namespace "urn:TBD:params:xml:ns:yang:network-topology";
+    // replace with IANA namespace when assigned
+    prefix "nt";
+
+    import ietf-inet-types { prefix "inet"; revision-date 2010-09-24; }
+
+    organization "TBD";
+
+    contact "WILL-BE-DEFINED-LATER";
+
+    description
+        "This module defines a model for the topology of a network.
+        Key design decisions are as follows:
+        A topology consists of a set of nodes and links.
+        Links are point-to-point and unidirectional.
+        Bidirectional connections need to be represented through
+        two separate links.
+        Multipoint connections, broadcast domains etc can be represented
+        through a hierarchy of nodes, then connecting nodes at
+        upper layers of the hierarchy.";
+
+    revision 2013-10-21 {
+        description
+            "Initial revision.";
+    }
+
+    typedef topology-id {
+        type inet:uri;
+        description
+            "An identifier for a topology.";
+    }
+
+    typedef node-id {
+        type inet:uri;
+        description
+            "An identifier for a node in a topology.
+            The identifier may be opaque.
+            The identifier SHOULD be chosen such that the same node in a
+            real network topology will always be identified through the
+            same identifier, even if the model is instantiated in separate
+            datastores. An implementation MAY choose to capture semantics
+            in the identifier, for example to indicate the type of node
+            and/or the type of topology that the node is a part of.";
+    }
+
+
+    typedef link-id {
+        type inet:uri;
+        description
+            "An identifier for a link in a topology.
+            The identifier may be opaque.
+            The identifier SHOULD be chosen such that the same link in a
+            real network topology will always be identified through the
+            same identifier, even if the model is instantiated in separate
+            datastores. An implementation MAY choose to capture semantics
+            in the identifier, for example to indicate the type of link
+            and/or the type of topology that the link is a part of.";
+    }
+
+    typedef tp-id {
+        type inet:uri;
+        description
+            "An identifier for termination points on a node.
+            The identifier may be opaque.
+            The identifier SHOULD be chosen such that the same TP in a
+            real network topology will always be identified through the
+            same identifier, even if the model is instantiated in separate
+            datastores. An implementation MAY choose to capture semantics
+            in the identifier, for example to indicate the type of TP
+            and/or the type of node and topology that the TP is a part of.";
+    }
+
+    typedef tp-ref {
+        type leafref {
+            path "/network-topology/topology/node/termination-point/tp-id";
+        }
+        description
+            "A type for an absolute reference to a termination point.
+            (This type should not be used for relative references.
+            In such a case, a relative path should be used instead.)";
+    }
+    typedef topology-ref {
+        type leafref {
+            path "/network-topology/topology/topology-id";
+        }
+        description
+            "A type for an absolute reference a topology instance.";
+    }
+
+    typedef node-ref {
+        type leafref {
+            path "/network-topology/topology/node/node-id";
+        }
+        description
+
+            "A type for an absolute reference to a node instance.
+            (This type should not be used for relative references.
+            In such a case, a relative path should be used instead.)";
+    }
+
+    typedef link-ref {
+        type leafref {
+            path "/network-topology/topology/link/link-id";
+        }
+        description
+            "A type for an absolute reference a link instance.
+            (This type should not be used for relative references.
+            In such a case, a relative path should be used instead.)";
+    }
+
+    grouping tp-attributes {
+        description
+            "The data objects needed to define a termination point.
+            (This only includes a single leaf at this point, used
+            to identify the termination point.)
+            Provided in a grouping so that in addition to the datastore,
+            the data can also be included in notifications.";
+        leaf tp-id {
+            type tp-id;
+        }
+        leaf-list tp-ref {
+            type tp-ref;
+            config false;
+            description
+                "The leaf list identifies any termination points that the
+                termination point is dependent on, or maps onto.
+                Those termination points will themselves be contained
+                in a supporting node.
+                This dependency information can be inferred from
+                the dependencies between links.  For this reason,
+                this item is not separately configurable.  Hence no
+                corresponding constraint needs to be articulated.
+                The corresponding information is simply provided by the
+                implementing system.";
+        }
+    }
+
+    grouping node-attributes {
+        description
+            "The data objects needed to define a node.
+            The objects are provided in a grouping so that in addition to
+            the datastore, the data can also be included in notifications
+            as needed.";
+
+        leaf node-id {
+            type node-id;
+            description
+                "The identifier of a node in the topology.
+                A node is specific to a topology to which it belongs.";
+        }
+        list supporting-node {
+            description
+                "This list defines vertical layering information for nodes.
+                It allows to capture for any given node, which node (or nodes)
+                in the corresponding underlay topology it maps onto.
+                A node can map to zero, one, or more nodes below it;
+                accordingly there can be zero, one, or more elements in the list.
+                If there are specific layering requirements, for example
+                specific to a particular type of topology that only allows
+                for certain layering relationships, the choice
+                below can be augmented with additional cases.
+                A list has been chosen rather than a leaf-list in order
+                to provide room for augmentations, e.g. for
+                statistics or priorization information associated with
+                supporting nodes.";
+            key "node-ref";
+            leaf node-ref {
+                type node-ref;
+            }
+        }
+    }
+
+    grouping link-attributes {
+        // This is a grouping, not defined inline with the link definition itself,
+        // so it can be included in a notification, if needed
+        leaf link-id {
+            type link-id;
+            description
+                "The identifier of a link in the topology.
+                A link is specific to a topology to which it belongs.";
+        }
+        container source {
+            leaf source-node {
+                mandatory true;
+                type node-ref;
+                description
+                    "Source node identifier, must be in same topology.";
+            }
+            leaf source-tp {
+                type tp-ref;
+                description
+                    "Termination point within source node that terminates the link.";
+
+            }
+        }
+        container destination {
+            leaf dest-node {
+                mandatory true;
+                type node-ref;
+                description
+                    "Destination node identifier, must be in same topology.";
+            }
+            leaf dest-tp {
+                type tp-ref;
+                description
+                    "Termination point within destination node that terminates the link.";
+            }
+        }
+        list supporting-link {
+            key "link-ref";
+            leaf link-ref {
+                type link-ref;
+            }
+        }
+    }
+
+
+    container network-topology {
+        list topology {
+            description "
+                This is the model of an abstract topology.
+                A topology contains nodes and links.
+                Each topology MUST be identified by
+                unique topology-id for reason that a network could contain many
+                topologies.
+            ";
+            key "topology-id";
+            leaf topology-id {
+                type topology-id;
+                description "
+                    It is presumed that a datastore will contain many topologies. To
+                    distinguish between topologies it is vital to have UNIQUE
+                    topology identifiers.
+                ";
+            }
+            leaf server-provided {
+                type boolean;
+                config false;
+                description "
+                    Indicates whether the topology is configurable by clients,
+                    or whether it is provided by the server.  This leaf is
+
+                    populated by the server implementing the model.
+                    It is set to false for topologies that are created by a client;
+                    it is set to true otherwise.  If it is set to true, any
+                    attempt to edit the topology MUST be rejected.
+                ";
+            }
+            container topology-types {
+                description
+                    "This container is used to identify the type, or types
+                    (as a topology can support several types simultaneously),
+                    of the topology.
+                    Topology types are the subject of several integrity constraints
+                    that an implementing server can validate in order to
+                    maintain integrity of the datastore.
+                    Topology types are indicated through separate data nodes;
+                    the set of topology types is expected to increase over time.
+                    To add support for a new topology, an augmenting module
+                    needs to augment this container with a new empty optional
+                    container to indicate the new topology type.
+                    The use of a container allows to indicate a subcategorization
+                    of topology types.
+                    The container SHALL NOT be augmented with any data nodes
+                    that serve a purpose other than identifying a particular
+                    topology type.
+                ";
+            }
+            list underlay-topology {
+                key "topology-ref";
+                leaf topology-ref {
+                    type topology-ref;
+                }
+                // a list, not a leaf-list, to allow for potential augmentation
+                // with properties specific to the underlay topology,
+                // such as statistics, preferences, or cost.
+                description
+                    "Identifies the topology, or topologies, that this topology
+                    is dependent on.";
+            }
+
+            list node {
+                description "The list of network nodes defined for the topology.";
+                key "node-id";
+                uses node-attributes;
+                must "boolean(../underlay-topology[*]/node[./supporting-nodes/node-ref])";
+                    // This constraint is meant to ensure that a referenced node is in fact
+                    // a node in an underlay topology.
+                list termination-point {
+                    description
+
+                        "A termination point can terminate a link.
+                        Depending on the type of topology, a termination point could,
+                        for example, refer to a port or an interface.";
+                    key "tp-id";
+                    uses tp-attributes;
+                }
+            }
+
+            list link {
+                description "
+                    A Network Link connects a by Local (Source) node and
+                    a Remote (Destination) Network Nodes via a set of the
+                    nodes' termination points.
+                    As it is possible to have several links between the same
+                    source and destination nodes, and as a link could potentially
+                    be re-homed between termination points, to ensure that we
+                    would always know to distinguish between links, every link
+                    is identified by a dedicated link identifier.
+                    Note that a link models a point-to-point link, not a multipoint
+                    link.
+                    Layering dependencies on links in underlay topologies are
+                    not represented as the layering information of nodes and of
+                    termination points is sufficient.
+                ";
+                key "link-id";
+                uses link-attributes;
+                must "boolean(../underlay-topology/link[./supporting-link])";
+                    // Constraint: any supporting link must be part of an underlay topology
+                must "boolean(../node[./source/source-node])";
+                    // Constraint: A link must have as source a node of the same topology
+                must "boolean(../node[./destination/dest-node])";
+                    // Constraint: A link must have as source a destination of the same topology
+                must "boolean(../node/termination-point[./source/source-tp])";
+                    // Constraint: The source termination point must be contained in the source node
+                must "boolean(../node/termination-point[./destination/dest-tp])";
+                    // Constraint: The destination termination point must be contained
+                    // in the destination node
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/if-feature-resolution-test/shared-schema-repository/foobar.yang b/yang/yang-parser-impl/src/test/resources/if-feature-resolution-test/shared-schema-repository/foobar.yang
new file mode 100644 (file)
index 0000000..ddc7854
--- /dev/null
@@ -0,0 +1,29 @@
+module foobar {
+    namespace "foobar-namespace";
+    prefix "foobar-prefix";
+
+    feature test-feature-1;
+    feature foobar-feature;
+
+    container test-container-a {
+        if-feature test-feature-1;
+
+        leaf test-leaf-a {
+            type string;
+        }
+    }
+
+    container test-container-b {
+        if-feature foobar-feature;
+
+        leaf test-leaf-b {
+            type string;
+        }
+    }
+
+    container test-container-c {
+        leaf test-leaf-c {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/model/bar.yang b/yang/yang-parser-impl/src/test/resources/model/bar.yang
new file mode 100644 (file)
index 0000000..3501ff4
--- /dev/null
@@ -0,0 +1,119 @@
+module bar {
+    yang-version 1;
+    namespace "urn:opendaylight.bar";
+    prefix "bar";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+    description "This model define custom type definitions";
+
+    revision "2013-07-03" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef int32-ext1 {
+        type int32 {
+            range "2..20";
+        }
+    }
+
+    typedef int32-ext2 {
+        type int32-ext1 {
+            range "3..9|11..max";
+        }
+        units "mile";
+        default "11";
+    }
+
+    typedef string-ext1 {
+        type string {
+            pattern "[a-k]*";
+            length "5..11";
+        }
+    }
+
+    typedef string-ext2 {
+        type string-ext1 {
+            length "6..10";
+        }
+    }
+
+    typedef string-ext3 {
+        type string-ext2 {
+            pattern "[b-u]*";
+        }
+    }
+
+    typedef string-ext4 {
+        type string-ext3 {
+            pattern "[e-z]*";
+        }
+    }
+
+    typedef invalid-string-pattern {
+        type string {
+            pattern "[[A-1*-%22!^^}";
+        }
+    }
+
+    typedef multiple-pattern-string {
+        type string {
+            pattern "[[A-1*-%22!^^}";
+            pattern "[e-z]*";
+        }
+    }
+
+    typedef my-decimal-type {
+        type decimal64 {
+            fraction-digits 6;
+        }
+    }
+
+    typedef my-union {
+        type union {
+            type int16 {
+                range "1..100";
+            }
+            type int32;
+        }
+    }
+
+    typedef my-union-ext {
+        type my-union;
+    }
+
+    typedef nested-union2 {
+        type union {
+            type my-union-ext;
+            type string;
+        }
+    }
+
+    container interfaces {
+        grouping ifEntry {
+            container augment-holder;
+        }
+        list ifEntry {
+            key "ifIndex";
+
+            leaf ifIndex {
+                type uint32;
+                units minutes;
+            }
+
+            leaf ifMtu {
+                type int32;
+            }
+
+            min-elements 1;
+            max-elements 11;
+        }
+    }
+
+    extension opendaylight {
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/test/resources/model/baz.yang b/yang/yang-parser-impl/src/test/resources/model/baz.yang
new file mode 100644 (file)
index 0000000..8b9ded0
--- /dev/null
@@ -0,0 +1,194 @@
+module baz {
+    yang-version 1;
+    namespace "urn:opendaylight.baz";
+    prefix "baz";
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef union1 {
+        type union2;
+    }
+
+    typedef union2 {
+        type union {
+            type int32;
+            type br:nested-union2;
+        }
+    }
+
+    augment "/br:interfaces/br:ifEntry" {
+        when "if:ifType='ds0'";
+        container augment-holder {
+            description "Description for augment holder";
+        }
+    }
+
+    augment "/br:interfaces/br:ifEntry" {
+        when "if:ifType='ds2'";
+        container augment-holder2 {
+            description "Description for augment holder";
+        }
+    }
+
+    container network {
+        baz:c-define point {
+        }
+
+        description "network-description";
+        reference "network-reference";
+        status obsolete;
+        config true;
+        presence "some presence text";
+    }
+
+    feature local-storage {
+        description
+            "This feature means the device supports local
+             storage (memory, flash or disk) that can be used to
+             store syslog messages.";
+    }
+
+    extension c-define {
+        description "Takes as argument a name string. Makes the code generator use the given name in the #define.";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+    notification event {
+        leaf event-class {
+            type string;
+        }
+        anyxml reporting-entity;
+        leaf severity {
+            type string;
+        }
+    }
+
+    rpc get-config {
+        description "Retrieve all or part of a specified configuration.";
+        reference "RFC 6241, Section 7.1";
+
+        input {
+            container source {
+                description
+                  "Particular configuration to retrieve.";
+
+                choice config-source {
+                    mandatory true;
+                    description
+                      "The configuration to retrieve.";
+                    case a {
+                        leaf candidate {
+                            if-feature candidate;
+                            type empty;
+                            description
+                              "The candidate configuration is the config source.";
+                        }
+                    }
+                    case b {
+                        leaf running {
+                            type empty;
+                            description
+                              "The running configuration is the config source.";
+                        }
+                    }
+                    case c {
+                        leaf startup {
+                            if-feature startup;
+                            type empty;
+                            description
+                              "The startup configuration is the config source.
+                               This is optional-to-implement on the server because
+                               not all servers will support filtering for this
+                               datastore.";
+                        }
+                    }
+                }
+            }
+
+            anyxml filter {
+                description "Subtree or XPath filter to use.";
+                baz:c-define element-attributes;
+            }
+        }
+
+        output {
+            anyxml data {
+                description
+                  "Copy of the source datastore subset that matched
+                   the filter criteria (if any).  An empty data container
+                   indicates that the request did not produce any results.";
+            }
+        }
+    }
+
+    grouping target {
+        anyxml data {
+            config true;
+            description "Copy of the source datastore subset.";
+            mandatory false;
+            must "test-condition-text";
+            reference "test-no-reference";
+            status "obsolete";
+            when "test-when-text";
+        }
+        choice how {
+            description "test choice description";
+            default interval;
+            case interval {
+                leaf interval {
+                    type uint16;
+                    default 30;
+                    units minutes;
+                }
+            }
+            case daily {
+                leaf daily {
+                    type empty;
+                }
+                leaf time-of-day {
+                    type string;
+                    units 24-hour-clock;
+                    default 1am;
+                }
+            }
+        }
+        leaf address {
+            type string;
+            description "Target IP address";
+            mandatory true;
+        }
+        container port {
+            description "Target port container";
+        }
+        list addresses {
+            key "id";
+            leaf id {
+                type int8;
+            }
+        }
+        grouping target-inner {
+            description "target-inner default description";
+            leaf inner-grouping-id {
+                type int8;
+            }
+        }
+        typedef group-type {
+            type br:my-decimal-type;
+        }
+        
+        br:opendaylight awesome;
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/test/resources/model/foo.yang b/yang/yang-parser-impl/src/test/resources/model/foo.yang
new file mode 100644 (file)
index 0000000..014cfa6
--- /dev/null
@@ -0,0 +1,234 @@
+module foo {
+    yang-version 1;
+    namespace "urn:opendaylight.foo";
+    prefix "foo";
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    import baz {
+        prefix "bz";
+        revision-date 2013-02-27;
+    }
+
+    include subfoo {
+        revision-date "2013-02-27";
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef identifier {
+        type int32;
+    }
+
+    leaf int32-leaf {
+        type br:int32-ext2 {
+            range "12..max";
+        }
+    }
+
+    leaf string-leaf {
+        type br:string-ext4;
+    }
+
+    leaf invalid-pattern-string-leaf {
+        type br:invalid-string-pattern;
+    }
+
+    leaf invalid-direct-string-pattern-def-leaf {
+        type string {
+            pattern "[[A-1*-%22!^^}";
+        }
+    }
+
+    leaf multiple-pattern-string-leaf {
+        type br:multiple-pattern-string;
+    }
+
+    leaf multiple-pattern-direct-string-def-leaf {
+        type string {
+            pattern "[e-z]*";
+            pattern "[[A-1*-%22!^^}";
+            pattern "[a-d]*";
+        }
+    }
+
+    leaf length-leaf {
+        type br:string-ext2 {
+            length "7..max";
+        }
+    }
+
+    leaf decimal-leaf {
+        type br:my-decimal-type {
+            fraction-digits 6;
+        }
+    }
+
+    leaf decimal-leaf2 {
+        type br:my-decimal-type;
+    }
+
+    container ext {
+        bz:c-define "MY_INTERFACES";
+    }
+
+    leaf union-leaf {
+        type br:my-union-ext;
+    }
+
+    deviation /br:interfaces/br:ifEntry {
+        deviate add {
+            config "true";
+        }
+        reference "system/user ref";
+    }
+
+    leaf custom-union-leaf {
+        type bz:union1;
+    }
+
+    container transfer {
+        choice how {
+            default interval;
+            container input {
+            }
+            list output {
+                leaf id {
+                    type string;
+                }
+            }
+            case interval {
+                leaf interval {
+                    type uint16;
+                    default 30;
+                    units minutes;
+                }
+            }
+            case daily {
+                leaf daily {
+                    type empty;
+                }
+                leaf time-of-day {
+                    type string;
+                    units 24-hour-clock;
+                    default 1am;
+                }
+            }
+            case manual {
+                leaf manual {
+                    type empty;
+                }
+            }
+        }
+    }
+
+    anyxml datas {
+        description
+          "Copy of the source typesstore subset that matched
+           the filter criteria (if any).  An empty types container
+           indicates that the request did not produce any results.";
+        status obsolete;
+    }
+
+    augment "/br:interfaces/br:ifEntry/bz:augment-holder" {
+        when "if:ifType='ds0'";
+        leaf ds0ChannelNumber {
+            type string;
+        }
+        leaf interface-id {
+            type leafref {
+                path "/if:interfaces/if:interface/if:name";
+            }
+        }
+        leaf my-type {
+            type br:int32-ext2;
+        }
+        container schemas {
+        }
+        choice odl {
+            leaf id {
+                type int8;
+            }
+            case node1 {
+                description "node1";
+            }
+            case node2 {
+                description "node2";
+            }
+            container node3 {
+                description "node3";
+            }
+        }
+    }
+
+    container mycont {
+        container innercont {
+            typedef mytype {
+                type string;
+            }
+            leaf myleaf {
+                type mytype;
+            }
+        }
+    }
+
+    uses bz:target {
+        augment "how/interval" {
+            description "inner augment";
+            leaf name {
+                type string;
+            }
+        }
+    }
+
+    container peer {
+        container destination {
+            uses bz:target {
+                refine address {
+                    default "1.2.3.4";
+                    description "IP address of target node";
+                    reference "address reference added by refine";
+                    config false;
+                    mandatory false;
+                    must "ifType != 'ethernet' or " +
+                            "(ifType = 'ethernet' and ifMTU = 1500)" {
+                        error-message "An ethernet MTU must be 1500";
+                    }
+                    foo:mountpoint "mnt-extension";
+                }
+                refine port {
+                    description "description of port defined by refine";
+                    reference "port reference added by refine";
+                    config false;
+                    presence "presence is required";
+                }
+                refine "addresses" {
+                    description "description of addresses defined by refine";
+                    reference "addresses reference added by refine";
+                    config false;
+                    min-elements 2;
+                    max-elements unbounded;
+                }
+                refine addresses/id {
+                    description "id of address";
+                }
+            }
+        }
+    }
+
+    extension mountpoint {
+        description "enter point";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/test/resources/model/subfoo.yang b/yang/yang-parser-impl/src/test/resources/model/subfoo.yang
new file mode 100644 (file)
index 0000000..5338ec3
--- /dev/null
@@ -0,0 +1,70 @@
+submodule subfoo {
+    yang-version 1;
+
+    belongs-to foo {
+        prefix f;
+    }
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    import baz {
+        prefix "bz";
+        revision-date 2013-02-27;
+    }
+
+    revision "2013-02-27" {
+    }
+
+    leaf id {
+        type br:int32-ext2 {
+            range "12..max";
+        }
+    }
+
+    leaf foo-id {
+        type f:identifier;
+    }
+
+    container sub-ext {
+        bz:c-define "MY_INTERFACES";
+    }
+
+    container sub-transfer {
+        choice how {
+            default input;
+            container input {
+            }
+            list output {
+                leaf id {
+                    type string;
+                }
+            }
+            case manual {
+                leaf manual {
+                    type empty;
+                }
+            }
+        }
+    }
+
+    anyxml sub-datas {
+        status obsolete;
+    }
+
+    augment "/br:interfaces/br:ifEntry/bz:augment-holder" {
+        when "if:ifType='ds0'";
+        leaf subleaf {
+            type string;
+        }
+    }
+
+    extension sub-mountpoint {
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/test/resources/no-revision/imported.yang b/yang/yang-parser-impl/src/test/resources/no-revision/imported.yang
new file mode 100644 (file)
index 0000000..d36c996
--- /dev/null
@@ -0,0 +1,10 @@
+module imported {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test1";
+    prefix "imp";
+
+    //organization "opendaylight";
+    //contact "WILL-BE-DEFINED-LATER";
+
+}
diff --git a/yang/yang-parser-impl/src/test/resources/no-revision/imported@2012-12-12.yang b/yang/yang-parser-impl/src/test/resources/no-revision/imported@2012-12-12.yang
new file mode 100644 (file)
index 0000000..68004c8
--- /dev/null
@@ -0,0 +1,12 @@
+module imported {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test1";
+    prefix "imp";
+
+    revision 2012-12-12 {}
+
+    //organization "opendaylight";
+    //contact "WILL-BE-DEFINED-LATER";
+
+}
diff --git a/yang/yang-parser-impl/src/test/resources/no-revision/module-without-revision.yang b/yang/yang-parser-impl/src/test/resources/no-revision/module-without-revision.yang
new file mode 100644 (file)
index 0000000..d4046d2
--- /dev/null
@@ -0,0 +1,27 @@
+module module-without-revision {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test1";
+    prefix "mwr";
+
+    import ietf-inet-types {
+        prefix "inet";
+        revision-date 2010-09-24;
+    }
+
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+
+    typedef dscp-ext {
+        type inet:dscp {
+            range "min..max";
+        }
+    }
+
+    leaf id {
+        type inet:port-number {
+            range "0..65535";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/test/resources/no-revision/top@2012-10-10.yang b/yang/yang-parser-impl/src/test/resources/no-revision/top@2012-10-10.yang
new file mode 100644 (file)
index 0000000..2e200bd
--- /dev/null
@@ -0,0 +1,16 @@
+module top {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test1";
+    prefix "top";
+
+    import imported {
+        prefix "imp";
+    }
+
+    revision 2012-10-10 {}
+
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+
+}
diff --git a/yang/yang-parser-impl/src/test/resources/openconfig-version/openconfigver-shared-schema-repository/bar@2016-01-01.yang b/yang/yang-parser-impl/src/test/resources/openconfig-version/openconfigver-shared-schema-repository/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..2a435be
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/openconfig-version/openconfigver-shared-schema-repository/foo.yang b/yang/yang-parser-impl/src/test/resources/openconfig-version/openconfigver-shared-schema-repository/foo.yang
new file mode 100644 (file)
index 0000000..2b37c24
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; revision-date 2016-01-31; oc-ext:openconfig-version "0.1.2";}
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/openconfig-version/openconfigver-shared-schema-repository/openconfig-extensions.yang b/yang/yang-parser-impl/src/test/resources/openconfig-version/openconfigver-shared-schema-repository/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/resources/openconfig-version/shared-schema-repository/bar@2016-01-01.yang b/yang/yang-parser-impl/src/test/resources/openconfig-version/shared-schema-repository/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..2a435be
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/openconfig-version/shared-schema-repository/foo.yang b/yang/yang-parser-impl/src/test/resources/openconfig-version/shared-schema-repository/foo.yang
new file mode 100644 (file)
index 0000000..67ef067
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; revision-date 2016-01-01; oc-ext:openconfig-version "0.1.2";}
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/openconfig-version/shared-schema-repository/openconfig-extensions.yang b/yang/yang-parser-impl/src/test/resources/openconfig-version/shared-schema-repository/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/resources/parse-methods/dependencies/m2@2013-09-30.yang b/yang/yang-parser-impl/src/test/resources/parse-methods/dependencies/m2@2013-09-30.yang
new file mode 100644 (file)
index 0000000..d541875
--- /dev/null
@@ -0,0 +1,17 @@
+module m2 {
+
+    yang-version 1;
+    namespace "urn:test.m2";
+    prefix "m2";
+
+    import m4 {
+        prefix "m4";
+    }
+    import m5 {
+        prefix "m5";
+    }
+
+    revision 2013-09-30 {
+    }
+
+}
diff --git a/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-0/bar@1999-01-01.yang b/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-0/bar@1999-01-01.yang
new file mode 100644 (file)
index 0000000..d254cad
--- /dev/null
@@ -0,0 +1,11 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 1999-01-01;
+
+    grouping grp {
+        container my-container-1 {
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-0/bar@2017-02-06.yang b/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-0/bar@2017-02-06.yang
new file mode 100644 (file)
index 0000000..247854e
--- /dev/null
@@ -0,0 +1,11 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-02-06;
+
+    grouping grp {
+        container my-container-2 {
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-0/foo.yang b/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-0/foo.yang
new file mode 100644 (file)
index 0000000..555b765
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar { prefix bar-1; revision-date 1999-01-01; }
+    import bar { prefix bar-2; revision-date 2017-02-06; }
+
+    container root {
+        uses bar-1:grp;
+        uses bar-2:grp;
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-1/bar@1970-01-01.yang b/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-1/bar@1970-01-01.yang
new file mode 100644 (file)
index 0000000..ad77e4c
--- /dev/null
@@ -0,0 +1,9 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 1970-01-01;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-1/bar@1999-01-01.yang b/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-1/bar@1999-01-01.yang
new file mode 100644 (file)
index 0000000..03681fa
--- /dev/null
@@ -0,0 +1,14 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 1999-01-01;
+
+    container root {
+    }
+
+    grouping grp {
+        container my-container-1 {
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-1/bar@2017-02-06.yang b/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-1/bar@2017-02-06.yang
new file mode 100644 (file)
index 0000000..32661d0
--- /dev/null
@@ -0,0 +1,14 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-02-06;
+
+    container root {
+    }
+
+    grouping grp {
+        container my-container-2 {
+        }
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-1/foo.yang b/yang/yang-parser-impl/src/test/resources/rfc7950/bug6875/yang1-1/foo.yang
new file mode 100644 (file)
index 0000000..8bb4299
--- /dev/null
@@ -0,0 +1,22 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    import bar { prefix bar-1; revision-date 1999-01-01; }
+    import bar { prefix bar-2; revision-date 2017-02-06; }
+    import bar { prefix bar-3; revision-date 1970-01-01; }
+
+    grouping grp {
+        uses bar-1:grp;
+        uses bar-2:grp;
+    }
+
+    container root {
+        uses grp;
+    }
+
+    augment "/bar-3:root" {
+        uses foo:grp;
+    }
+}
diff --git a/yang/yang-parser-impl/src/test/resources/yang-text-schema-context-resolver-test/bar.yang b/yang/yang-parser-impl/src/test/resources/yang-text-schema-context-resolver-test/bar.yang
new file mode 100644 (file)
index 0000000..bac9f5a
--- /dev/null
@@ -0,0 +1,12 @@
+module bar {
+    namespace bar-namespace;
+    prefix bar-prefix;
+
+    revision 2016-09-26;
+
+    container bar-container {
+        leaf bar-leaf {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/resources/yang-text-schema-context-resolver-test/baz.yang b/yang/yang-parser-impl/src/test/resources/yang-text-schema-context-resolver-test/baz.yang
new file mode 100644 (file)
index 0000000..2ec2df7
--- /dev/null
@@ -0,0 +1,12 @@
+module baz {
+    namespace baz-namespace;
+    prefix baz-prefix;
+
+    revision 2016-09-26;
+
+    container baz-container {
+        leaf baz-leaf {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-impl/src/test/resources/yang-text-schema-context-resolver-test/foo.yang b/yang/yang-parser-impl/src/test/resources/yang-text-schema-context-resolver-test/foo.yang
new file mode 100644 (file)
index 0000000..7ec075b
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace foo-namespace;
+    prefix foo-prefix;
+
+    revision 2016-09-26;
+
+    container foo-container {
+        leaf foo-leaf {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-reactor/pom.xml b/yang/yang-parser-reactor/pom.xml
new file mode 100644 (file)
index 0000000..bf4b08a
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-parser-reactor</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>YANG parser</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-util</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.parser.reactor</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/BuildGlobalContext.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/BuildGlobalContext.java
new file mode 100644 (file)
index 0000000..2d4dcf9
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Verify;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Table;
+import com.google.common.collect.TreeBasedTable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.SortedMap;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.RecursiveObjectLeaker;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.DerivedNamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModulesDeviatedByModules;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModulesDeviatedByModules.SupportedModules;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace.SupportedFeatures;
+import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.SourceSpecificContext.PhaseCompletionProgress;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class BuildGlobalContext extends NamespaceStorageSupport implements Registry {
+    private static final Logger LOG = LoggerFactory.getLogger(BuildGlobalContext.class);
+
+    private static final List<ModelProcessingPhase> PHASE_EXECUTION_ORDER =
+            ImmutableList.<ModelProcessingPhase>builder().add(ModelProcessingPhase.SOURCE_PRE_LINKAGE)
+            .add(ModelProcessingPhase.SOURCE_LINKAGE).add(ModelProcessingPhase.STATEMENT_DEFINITION)
+            .add(ModelProcessingPhase.FULL_DECLARATION).add(ModelProcessingPhase.EFFECTIVE_MODEL).build();
+
+    private final Table<YangVersion, QName, StatementDefinitionContext<?, ?, ?>> definitions = HashBasedTable.create();
+    private final Map<QName, StatementDefinitionContext<?, ?, ?>> modelDefinedStmtDefs = new HashMap<>();
+    private final Map<Class<?>, NamespaceBehaviourWithListeners<?, ?, ?>> supportedNamespaces = new HashMap<>();
+    private final List<MutableStatement> mutableStatementsToSeal = new ArrayList<>();
+    private final ImmutableMap<ModelProcessingPhase, StatementSupportBundle> supports;
+    private final Set<SourceSpecificContext> sources = new HashSet<>();
+    private final ImmutableSet<YangVersion> supportedVersions;
+    private final boolean enabledSemanticVersions;
+
+    private Set<SourceSpecificContext> libSources = new HashSet<>();
+    private ModelProcessingPhase currentPhase = ModelProcessingPhase.INIT;
+    private ModelProcessingPhase finishedPhase = ModelProcessingPhase.INIT;
+
+    BuildGlobalContext(final ImmutableMap<ModelProcessingPhase, StatementSupportBundle> supports,
+            final ImmutableMap<ValidationBundleType, Collection<?>> supportedValidation,
+            final StatementParserMode statementParserMode) {
+        this.supports = requireNonNull(supports, "BuildGlobalContext#supports cannot be null");
+
+        switch (statementParserMode) {
+            case DEFAULT_MODE:
+                enabledSemanticVersions = false;
+                break;
+            case SEMVER_MODE:
+                enabledSemanticVersions = true;
+                break;
+            default:
+                throw new IllegalArgumentException("Unhandled parser mode " + statementParserMode);
+        }
+
+        for (final Entry<ValidationBundleType, Collection<?>> validationBundle : supportedValidation.entrySet()) {
+            addToNs(ValidationBundlesNamespace.class, validationBundle.getKey(), validationBundle.getValue());
+        }
+
+        this.supportedVersions = ImmutableSet.copyOf(supports.get(ModelProcessingPhase.INIT).getSupportedVersions());
+    }
+
+    boolean isEnabledSemanticVersioning() {
+        return enabledSemanticVersions;
+    }
+
+    StatementSupportBundle getSupportsForPhase(final ModelProcessingPhase phase) {
+        return supports.get(phase);
+    }
+
+    void addSource(final @NonNull StatementStreamSource source) {
+        sources.add(new SourceSpecificContext(this, source));
+    }
+
+    void addLibSource(final @NonNull StatementStreamSource libSource) {
+        checkState(!isEnabledSemanticVersioning(),
+            "Library sources are not supported in semantic version mode currently.");
+        checkState(currentPhase == ModelProcessingPhase.INIT,
+                "Add library source is allowed in ModelProcessingPhase.INIT only");
+        libSources.add(new SourceSpecificContext(this, libSource));
+    }
+
+    void setSupportedFeatures(final Set<QName> supportedFeatures) {
+        addToNs(SupportedFeaturesNamespace.class, SupportedFeatures.SUPPORTED_FEATURES,
+                    ImmutableSet.copyOf(supportedFeatures));
+    }
+
+    void setModulesDeviatedByModules(final SetMultimap<QNameModule, QNameModule> modulesDeviatedByModules) {
+        addToNs(ModulesDeviatedByModules.class, SupportedModules.SUPPORTED_MODULES,
+                    ImmutableSetMultimap.copyOf(modulesDeviatedByModules));
+    }
+
+    @Override
+    public StorageNodeType getStorageNodeType() {
+        return StorageNodeType.GLOBAL;
+    }
+
+    @Override
+    public NamespaceStorageNode getParentNamespaceStorage() {
+        return null;
+    }
+
+    @Override
+    public NamespaceBehaviour.Registry getBehaviourRegistry() {
+        return this;
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> getNamespaceBehaviour(
+            final Class<N> type) {
+        NamespaceBehaviourWithListeners<?, ?, ?> potential = supportedNamespaces.get(type);
+        if (potential == null) {
+            final NamespaceBehaviour<K, V, N> potentialRaw = supports.get(currentPhase).getNamespaceBehaviour(type);
+            if (potentialRaw != null) {
+                potential = createNamespaceContext(potentialRaw);
+                supportedNamespaces.put(type, potential);
+            } else {
+                throw new NamespaceNotAvailableException("Namespace " + type + " is not available in phase "
+                        + currentPhase);
+            }
+        }
+
+        Verify.verify(type.equals(potential.getIdentifier()));
+        /*
+         * Safe cast, previous checkState checks equivalence of key from which
+         * type argument are derived
+         */
+        return (NamespaceBehaviourWithListeners<K, V, N>) potential;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> createNamespaceContext(
+            final NamespaceBehaviour<K, V, N> potentialRaw) {
+        if (potentialRaw instanceof DerivedNamespaceBehaviour) {
+            final VirtualNamespaceContext derivedContext = new VirtualNamespaceContext(
+                    (DerivedNamespaceBehaviour) potentialRaw);
+            getNamespaceBehaviour(((DerivedNamespaceBehaviour) potentialRaw).getDerivedFrom()).addDerivedNamespace(
+                    derivedContext);
+            return derivedContext;
+        }
+        return new SimpleNamespaceContext<>(potentialRaw);
+    }
+
+    StatementDefinitionContext<?, ?, ?> getStatementDefinition(final YangVersion version, final QName name) {
+        StatementDefinitionContext<?, ?, ?> potential = definitions.get(version, name);
+        if (potential == null) {
+            final StatementSupport<?, ?, ?> potentialRaw = supports.get(currentPhase).getStatementDefinition(version,
+                    name);
+            if (potentialRaw != null) {
+                potential = new StatementDefinitionContext<>(potentialRaw);
+                definitions.put(version, name, potential);
+            }
+        }
+        return potential;
+    }
+
+    StatementDefinitionContext<?, ?, ?> getModelDefinedStatementDefinition(final QName name) {
+        return modelDefinedStmtDefs.get(name);
+    }
+
+    void putModelDefinedStatementDefinition(final QName name, final StatementDefinitionContext<?, ?, ?> def) {
+        modelDefinedStmtDefs.put(name, def);
+    }
+
+    private void executePhases() throws ReactorException {
+        for (final ModelProcessingPhase phase : PHASE_EXECUTION_ORDER) {
+            startPhase(phase);
+            loadPhaseStatements();
+            completePhaseActions();
+            endPhase(phase);
+        }
+    }
+
+    ReactorDeclaredModel build() throws ReactorException {
+        executePhases();
+        return transform();
+    }
+
+    EffectiveSchemaContext buildEffective() throws ReactorException {
+        executePhases();
+        return transformEffective();
+    }
+
+    private ReactorDeclaredModel transform() {
+        checkState(finishedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
+        final List<DeclaredStatement<?>> rootStatements = new ArrayList<>(sources.size());
+        for (final SourceSpecificContext source : sources) {
+            rootStatements.add(source.getRoot().buildDeclared());
+        }
+        return new ReactorDeclaredModel(rootStatements);
+    }
+
+    private SomeModifiersUnresolvedException propagateException(final SourceSpecificContext source,
+            final RuntimeException cause) throws SomeModifiersUnresolvedException {
+        final SourceIdentifier sourceId = StmtContextUtils.createSourceIdentifier(source.getRoot());
+        if (!(cause instanceof SourceException)) {
+            /*
+             * This should not be happening as all our processing should provide SourceExceptions.
+             * We will wrap the exception to provide enough information to identify the problematic model,
+             * but also emit a warning so the offending codepath will get fixed.
+             */
+            LOG.warn("Unexpected error processing source {}. Please file an issue with this model attached.",
+                sourceId, cause);
+        }
+
+        throw new SomeModifiersUnresolvedException(currentPhase, sourceId, cause);
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private EffectiveSchemaContext transformEffective() throws ReactorException {
+        checkState(finishedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
+        final List<DeclaredStatement<?>> rootStatements = new ArrayList<>(sources.size());
+        final List<EffectiveStatement<?, ?>> rootEffectiveStatements = new ArrayList<>(sources.size());
+
+        try {
+            for (final SourceSpecificContext source : sources) {
+                final RootStatementContext<?, ?, ?> root = source.getRoot();
+                try {
+                    rootStatements.add(root.buildDeclared());
+                    rootEffectiveStatements.add(root.buildEffective());
+                } catch (final RuntimeException ex) {
+                    throw propagateException(source, ex);
+                }
+            }
+        } finally {
+            RecursiveObjectLeaker.cleanup();
+        }
+
+        sealMutableStatements();
+        return EffectiveSchemaContext.create(rootStatements, rootEffectiveStatements);
+    }
+
+    private void startPhase(final ModelProcessingPhase phase) {
+        checkState(Objects.equals(finishedPhase, phase.getPreviousPhase()));
+        startPhaseFor(phase, sources);
+        startPhaseFor(phase, libSources);
+
+        currentPhase = phase;
+        LOG.debug("Global phase {} started", phase);
+    }
+
+    private static void startPhaseFor(final ModelProcessingPhase phase, final Set<SourceSpecificContext> sources) {
+        for (final SourceSpecificContext source : sources) {
+            source.startPhase(phase);
+        }
+    }
+
+    private void loadPhaseStatements() throws ReactorException {
+        checkState(currentPhase != null);
+        loadPhaseStatementsFor(sources);
+        loadPhaseStatementsFor(libSources);
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private void loadPhaseStatementsFor(final Set<SourceSpecificContext> srcs) throws ReactorException {
+        for (final SourceSpecificContext source : srcs) {
+            try {
+                source.loadStatements();
+            } catch (final RuntimeException ex) {
+                throw propagateException(source, ex);
+            }
+        }
+    }
+
+    private SomeModifiersUnresolvedException addSourceExceptions(final List<SourceSpecificContext> sourcesToProgress) {
+        boolean addedCause = false;
+        SomeModifiersUnresolvedException buildFailure = null;
+        for (final SourceSpecificContext failedSource : sourcesToProgress) {
+            final Optional<SourceException> optSourceEx = failedSource.failModifiers(currentPhase);
+            if (optSourceEx.isEmpty()) {
+                continue;
+            }
+
+            final SourceException sourceEx = optSourceEx.get();
+            // Workaround for broken logging implementations which ignore
+            // suppressed exceptions
+            final Throwable cause = sourceEx.getCause() != null ? sourceEx.getCause() : sourceEx;
+            if (LOG.isDebugEnabled()) {
+                LOG.error("Failed to parse YANG from source {}", failedSource, sourceEx);
+            } else {
+                LOG.error("Failed to parse YANG from source {}: {}", failedSource, cause.getMessage());
+            }
+
+            final Throwable[] suppressed = sourceEx.getSuppressed();
+            if (suppressed.length > 0) {
+                LOG.error("{} additional errors reported:", suppressed.length);
+
+                int count = 1;
+                for (final Throwable t : suppressed) {
+                    LOG.error("Error {}: {}", count, t.getMessage());
+                    count++;
+                }
+            }
+
+            if (!addedCause) {
+                addedCause = true;
+                final SourceIdentifier sourceId = StmtContextUtils.createSourceIdentifier(failedSource.getRoot());
+                buildFailure = new SomeModifiersUnresolvedException(currentPhase, sourceId, sourceEx);
+            } else {
+                buildFailure.addSuppressed(sourceEx);
+            }
+        }
+        return buildFailure;
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private void completePhaseActions() throws ReactorException {
+        checkState(currentPhase != null);
+        final List<SourceSpecificContext> sourcesToProgress = new ArrayList<>(sources);
+        if (!libSources.isEmpty()) {
+            checkState(currentPhase == ModelProcessingPhase.SOURCE_PRE_LINKAGE,
+                    "Yang library sources should be empty after ModelProcessingPhase.SOURCE_PRE_LINKAGE, "
+                            + "but current phase was %s", currentPhase);
+            sourcesToProgress.addAll(libSources);
+        }
+
+        boolean progressing = true;
+        while (progressing) {
+            // We reset progressing to false.
+            progressing = false;
+            final Iterator<SourceSpecificContext> currentSource = sourcesToProgress.iterator();
+            while (currentSource.hasNext()) {
+                final SourceSpecificContext nextSourceCtx = currentSource.next();
+                try {
+                    final PhaseCompletionProgress sourceProgress = nextSourceCtx.tryToCompletePhase(currentPhase);
+                    switch (sourceProgress) {
+                        case FINISHED:
+                            currentSource.remove();
+                            // we were able to make progress in computation
+                            progressing = true;
+                            break;
+                        case PROGRESS:
+                            progressing = true;
+                            break;
+                        case NO_PROGRESS:
+                            // Noop
+                            break;
+                        default:
+                            throw new IllegalStateException("Unsupported phase progress " + sourceProgress);
+                    }
+                } catch (final RuntimeException ex) {
+                    throw propagateException(nextSourceCtx, ex);
+                }
+            }
+        }
+
+        if (!libSources.isEmpty()) {
+            final Set<SourceSpecificContext> requiredLibs = getRequiredSourcesFromLib();
+            sources.addAll(requiredLibs);
+            libSources = ImmutableSet.of();
+            /*
+             * We want to report errors of relevant sources only, so any others can
+             * be removed.
+             */
+            sourcesToProgress.retainAll(sources);
+        }
+
+        if (!sourcesToProgress.isEmpty()) {
+            final SomeModifiersUnresolvedException buildFailure = addSourceExceptions(sourcesToProgress);
+            if (buildFailure != null) {
+                throw buildFailure;
+            }
+        }
+    }
+
+    private Set<SourceSpecificContext> getRequiredSourcesFromLib() {
+        checkState(currentPhase == ModelProcessingPhase.SOURCE_PRE_LINKAGE,
+                "Required library sources can be collected only in ModelProcessingPhase.SOURCE_PRE_LINKAGE phase,"
+                        + " but current phase was %s", currentPhase);
+        final TreeBasedTable<String, Optional<Revision>, SourceSpecificContext> libSourcesTable = TreeBasedTable.create(
+            String::compareTo, Revision::compare);
+        for (final SourceSpecificContext libSource : libSources) {
+            final SourceIdentifier libSourceIdentifier = requireNonNull(libSource.getRootIdentifier());
+            libSourcesTable.put(libSourceIdentifier.getName(), libSourceIdentifier.getRevision(), libSource);
+        }
+
+        final Set<SourceSpecificContext> requiredLibs = new HashSet<>();
+        for (final SourceSpecificContext source : sources) {
+            collectRequiredSourcesFromLib(libSourcesTable, requiredLibs, source);
+            removeConflictingLibSources(source, requiredLibs);
+        }
+        return requiredLibs;
+    }
+
+    private void collectRequiredSourcesFromLib(
+            final TreeBasedTable<String, Optional<Revision>, SourceSpecificContext> libSourcesTable,
+            final Set<SourceSpecificContext> requiredLibs, final SourceSpecificContext source) {
+        for (final SourceIdentifier requiredSource : source.getRequiredSources()) {
+            final SourceSpecificContext libSource = getRequiredLibSource(requiredSource, libSourcesTable);
+            if (libSource != null && requiredLibs.add(libSource)) {
+                collectRequiredSourcesFromLib(libSourcesTable, requiredLibs, libSource);
+            }
+        }
+    }
+
+    private static SourceSpecificContext getRequiredLibSource(final SourceIdentifier requiredSource,
+            final TreeBasedTable<String, Optional<Revision>, SourceSpecificContext> libSourcesTable) {
+        return requiredSource.getRevision().isPresent()
+                ? libSourcesTable.get(requiredSource.getName(), requiredSource.getRevision())
+                        : getLatestRevision(libSourcesTable.row(requiredSource.getName()));
+    }
+
+    private static SourceSpecificContext getLatestRevision(final SortedMap<Optional<Revision>,
+            SourceSpecificContext> sourceMap) {
+        return sourceMap != null && !sourceMap.isEmpty() ? sourceMap.get(sourceMap.lastKey()) : null;
+    }
+
+    // removes required library sources which would cause namespace/name conflict with one of the main sources
+    // later in the parsing process. this can happen if we add a parent module or a submodule as a main source
+    // and the same parent module or submodule is added as one of the library sources.
+    // such situation may occur when using the yang-system-test artifact - if a parent module/submodule is specified
+    // as its argument and the same dir is specified as one of the library dirs through -p option).
+    private static void removeConflictingLibSources(final SourceSpecificContext source,
+            final Set<SourceSpecificContext> requiredLibs) {
+        final Iterator<SourceSpecificContext> requiredLibsIter = requiredLibs.iterator();
+        while (requiredLibsIter.hasNext()) {
+            final SourceSpecificContext currentReqSource = requiredLibsIter.next();
+            if (source.getRootIdentifier().equals(currentReqSource.getRootIdentifier())) {
+                requiredLibsIter.remove();
+            }
+        }
+    }
+
+    private void endPhase(final ModelProcessingPhase phase) {
+        checkState(currentPhase == phase);
+        finishedPhase = currentPhase;
+        LOG.debug("Global phase {} finished", phase);
+    }
+
+    Set<SourceSpecificContext> getSources() {
+        return sources;
+    }
+
+    public Set<YangVersion> getSupportedVersions() {
+        return supportedVersions;
+    }
+
+    void addMutableStmtToSeal(final MutableStatement mutableStatement) {
+        mutableStatementsToSeal.add(mutableStatement);
+    }
+
+    void sealMutableStatements() {
+        for (final MutableStatement mutableStatement : mutableStatementsToSeal) {
+            mutableStatement.seal();
+        }
+        mutableStatementsToSeal.clear();
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/CrossSourceStatementReactor.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/CrossSourceStatementReactor.java
new file mode 100644 (file)
index 0000000..dca20a7
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.SetMultimap;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
+
+public final class CrossSourceStatementReactor {
+    private final ImmutableMap<ModelProcessingPhase, StatementSupportBundle> supportedTerminology;
+    private final ImmutableMap<ValidationBundleType, Collection<?>> supportedValidation;
+
+    CrossSourceStatementReactor(final Map<ModelProcessingPhase, StatementSupportBundle> supportedTerminology,
+            final Map<ValidationBundleType, Collection<?>> supportedValidation) {
+        this.supportedTerminology = ImmutableMap.copyOf(supportedTerminology);
+        this.supportedValidation = ImmutableMap.copyOf(supportedValidation);
+    }
+
+    /**
+     * Create a new {@link Builder}.
+     *
+     * @return A new builder.
+     */
+    public static @NonNull Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Start a new reactor build using the default statement parser mode with all features and deviations enabled.
+     *
+     * @return A new {@link BuildAction}.
+     */
+    public @NonNull BuildAction newBuild() {
+        return newBuild(StatementParserMode.DEFAULT_MODE);
+    }
+
+    /**
+     * Start a new reactor build using the specified statement parser mode and enabling all features and deviations.
+     *
+     * @param statementParserMode Parser mode to use
+     * @return A new {@link BuildAction}.
+     * @throws NullPointerException if statementParserMode is null
+     */
+    public @NonNull BuildAction newBuild(final StatementParserMode statementParserMode) {
+        return new BuildAction(supportedTerminology, supportedValidation, requireNonNull(statementParserMode));
+    }
+
+    public static class Builder implements org.opendaylight.yangtools.concepts.Builder<CrossSourceStatementReactor> {
+        private final Map<ValidationBundleType, Collection<?>> validationBundles =
+                new EnumMap<>(ValidationBundleType.class);
+        private final Map<ModelProcessingPhase, StatementSupportBundle> bundles =
+                new EnumMap<>(ModelProcessingPhase.class);
+
+        public @NonNull Builder setBundle(final ModelProcessingPhase phase, final StatementSupportBundle bundle) {
+            bundles.put(phase, bundle);
+            return this;
+        }
+
+        public @NonNull Builder setValidationBundle(final ValidationBundleType type,
+                final Collection<?> validationBundle) {
+            validationBundles.put(type, validationBundle);
+            return this;
+        }
+
+        @Override
+        public CrossSourceStatementReactor build() {
+            return new CrossSourceStatementReactor(bundles, validationBundles);
+        }
+    }
+
+    public static class BuildAction {
+        private final BuildGlobalContext context;
+        private boolean supportedFeaturesSet = false;
+        private boolean modulesDeviatedByModulesSet = false;
+
+        BuildAction(final ImmutableMap<ModelProcessingPhase, StatementSupportBundle> supportedTerminology,
+                final ImmutableMap<ValidationBundleType, Collection<?>> supportedValidation,
+                final StatementParserMode statementParserMode) {
+            this.context = new BuildGlobalContext(supportedTerminology, supportedValidation, statementParserMode);
+        }
+
+        /**
+         * Add main source. All main sources are present in resulting SchemaContext.
+         *
+         * @param source
+         *            which should be added into main sources
+         * @return This build action, for fluent use.
+         */
+        public @NonNull BuildAction addSource(final StatementStreamSource source) {
+            context.addSource(source);
+            return this;
+        }
+
+        /**
+         * Add main sources. All main sources are present in resulting SchemaContext.
+         *
+         * @param sources
+         *            which should be added into main sources
+         * @return This build action, for fluent use.
+         */
+        public @NonNull BuildAction addSources(final StatementStreamSource... sources) {
+            addSources(Arrays.asList(sources));
+            return this;
+        }
+
+        public @NonNull BuildAction addSources(final @NonNull Collection<? extends StatementStreamSource> sources) {
+            for (final StatementStreamSource source : sources) {
+                context.addSource(requireNonNull(source));
+            }
+            return this;
+        }
+
+        /**
+         * Add library sources. Only library sources required by main sources are present in resulting SchemaContext.
+         * Any other library sources are ignored and this also applies to error reporting.
+         *
+         * <p>
+         * Library sources are not supported in semantic version mode currently.
+         *
+         * @param libSources yang sources which should be added into library sources
+         * @return This build action, for fluent use.
+         */
+        public @NonNull BuildAction addLibSources(final StatementStreamSource... libSources) {
+            addLibSources(Arrays.asList(libSources));
+            return this;
+        }
+
+        public @NonNull BuildAction addLibSources(final Collection<StatementStreamSource> libSources) {
+            for (final StatementStreamSource libSource : libSources) {
+                context.addLibSource(libSource);
+            }
+            return this;
+        }
+
+        /**
+         * Set supported features based on which all if-feature statements in the
+         * parsed YANG modules will be resolved.
+         *
+         * @param supportedFeatures
+         *            Set of supported features in the final SchemaContext.
+         *            If the set is empty, no features encountered will be supported.
+         * @return This build action, for fluent use.
+         */
+        public @NonNull BuildAction setSupportedFeatures(final @NonNull Set<QName> supportedFeatures) {
+            checkState(!supportedFeaturesSet, "Supported features should be set only once.");
+            context.setSupportedFeatures(requireNonNull(supportedFeatures));
+            supportedFeaturesSet = true;
+            return this;
+        }
+
+        /**
+         * Set YANG modules which can be deviated by specified modules during the parsing process.
+         * Map key (QNameModule) denotes a module which can be deviated by the modules in the Map value.
+         *
+         * @param modulesDeviatedByModules
+         *            Map of YANG modules (Map key) which can be deviated by specified modules (Map value) in the final
+         *            SchemaContext. If the map is empty, no deviations encountered will be supported.
+         * @return This build action, for fluent use.
+         */
+        public @NonNull BuildAction setModulesWithSupportedDeviations(
+                final @NonNull SetMultimap<QNameModule, QNameModule> modulesDeviatedByModules) {
+            checkState(!modulesDeviatedByModulesSet, "Modules with supported deviations should be set only once.");
+            context.setModulesDeviatedByModules(requireNonNull(modulesDeviatedByModules));
+            modulesDeviatedByModulesSet = true;
+            return this;
+        }
+
+        /**
+         * Build the effective model context.
+         */
+        public ReactorDeclaredModel build() throws ReactorException {
+            return context.build();
+        }
+
+        public EffectiveSchemaContext buildEffective() throws ReactorException {
+            return context.buildEffective();
+        }
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/EffectiveSchemaContext.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/EffectiveSchemaContext.java
new file mode 100644 (file)
index 0000000..79e81fc
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Verify;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.util.SimpleSchemaContext;
+
+@VisibleForTesting
+public final class EffectiveSchemaContext extends SimpleSchemaContext implements EffectiveModelContext {
+    private final ImmutableList<DeclaredStatement<?>> rootDeclaredStatements;
+    private final ImmutableMap<QNameModule, ModuleEffectiveStatement> rootEffectiveStatements;
+
+    private EffectiveSchemaContext(final Set<Module> modules, final List<DeclaredStatement<?>> rootDeclaredStatements,
+            final List<EffectiveStatement<?, ?>> rootEffectiveStatements) {
+        super(modules);
+        this.rootDeclaredStatements = ImmutableList.copyOf(rootDeclaredStatements);
+        this.rootEffectiveStatements = rootEffectiveStatements.stream()
+                .filter(ModuleEffectiveStatement.class::isInstance).map(ModuleEffectiveStatement.class::cast)
+                .collect(ImmutableMap.toImmutableMap(ModuleEffectiveStatement::localQNameModule, Function.identity()));
+    }
+
+    static EffectiveSchemaContext create(final List<DeclaredStatement<?>> rootDeclaredStatements,
+            final List<EffectiveStatement<?, ?>> rootEffectiveStatements) {
+        final Set<Module> modules = new HashSet<>();
+        for (EffectiveStatement<?, ?> stmt : rootEffectiveStatements) {
+            if (stmt.getDeclared() instanceof ModuleStatement) {
+                Verify.verify(stmt instanceof Module);
+                modules.add((Module) stmt);
+            }
+        }
+
+        return new EffectiveSchemaContext(modules, rootDeclaredStatements, rootEffectiveStatements);
+    }
+
+    @VisibleForTesting
+    public List<DeclaredStatement<?>> getRootDeclaredStatements() {
+        return rootDeclaredStatements;
+    }
+
+    @Beta
+    public ImmutableMap<QNameModule, ModuleEffectiveStatement> getModuleStatements() {
+        return rootEffectiveStatements;
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ModifierImpl.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ModifierImpl.java
new file mode 100644 (file)
index 0000000..664ad74
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2015, 2016 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.stmt.reactor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.EFFECTIVE_MODEL;
+import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.FULL_DECLARATION;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.function.Function;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase.ContextMutation;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase.OnNamespaceItemAdded;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase.OnPhaseFinished;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ModifierImpl implements ModelActionBuilder {
+    private static final Logger LOG = LoggerFactory.getLogger(ModifierImpl.class);
+
+    private final InferenceContext ctx = new InferenceContext() { };
+
+    private final Set<AbstractPrerequisite<?>> unsatisfied = new HashSet<>(1);
+    private final Set<AbstractPrerequisite<?>> mutations = new HashSet<>(1);
+
+    private InferenceAction action;
+    private boolean actionApplied = false;
+
+    private <D> AbstractPrerequisite<D> addReq(final AbstractPrerequisite<D> prereq) {
+        LOG.trace("Modifier {} adding prerequisite {}", this, prereq);
+        unsatisfied.add(prereq);
+        return prereq;
+    }
+
+    private <T> @NonNull AbstractPrerequisite<T> addMutation(final @NonNull AbstractPrerequisite<T> mutation) {
+        LOG.trace("Modifier {} adding mutation {}", this, mutation);
+        mutations.add(mutation);
+        return mutation;
+    }
+
+    private void checkNotRegistered() {
+        checkState(action == null, "Action was already registered.");
+    }
+
+    private boolean removeSatisfied() {
+        final Iterator<AbstractPrerequisite<?>> it = unsatisfied.iterator();
+        while (it.hasNext()) {
+            final AbstractPrerequisite<?> prereq = it.next();
+            if (prereq.isDone()) {
+                // We are removing current prerequisite from list.
+                LOG.trace("Modifier {} prerequisite {} satisfied", this, prereq);
+                it.remove();
+            }
+        }
+        return unsatisfied.isEmpty();
+    }
+
+    boolean isApplied() {
+        return actionApplied;
+    }
+
+    void failModifier() {
+        removeSatisfied();
+        action.prerequisiteFailed(unsatisfied);
+        action = null;
+    }
+
+    private void applyAction() {
+        checkState(!actionApplied);
+        action.apply(ctx);
+        actionApplied = true;
+    }
+
+    private <K, C extends StmtContext<?, ?, ?>, N extends StatementNamespace<K, ?, ?>> @NonNull AbstractPrerequisite<C>
+            requiresCtxImpl(final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key,
+                    final ModelProcessingPhase phase)  {
+        checkNotRegistered();
+
+        AddedToNamespace<C> addedToNs = new AddedToNamespace<>(phase);
+        addReq(addedToNs);
+        contextImpl(context).onNamespaceItemAddedAction(namespace, key, addedToNs);
+        return addedToNs;
+    }
+
+    private <K, C extends StmtContext<?, ?, ?>, N extends StatementNamespace<K, ?, ?>> @NonNull AbstractPrerequisite<C>
+            requiresCtxImpl(final StmtContext<?, ?, ?> context, final Class<N> namespace,
+                    final NamespaceKeyCriterion<K> criterion, final ModelProcessingPhase phase)  {
+        checkNotRegistered();
+
+        AddedToNamespace<C> addedToNs = new AddedToNamespace<>(phase);
+        addReq(addedToNs);
+        contextImpl(context).onNamespaceItemAddedAction(namespace, phase, criterion, addedToNs);
+        return addedToNs;
+    }
+
+    private <C extends StmtContext<?, ?, ?>> @NonNull AbstractPrerequisite<C> requiresCtxImpl(final C context,
+            final ModelProcessingPhase phase) {
+        checkNotRegistered();
+
+        PhaseFinished<C> phaseFin = new PhaseFinished<>();
+        addReq(phaseFin);
+        contextImpl(context).addPhaseCompletedListener(phase, phaseFin);
+        return phaseFin;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private <K, C extends Mutable<?, ?, ?>, N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>>
+            AbstractPrerequisite<C> mutatesCtxImpl(final StmtContext<?, ?, ?> context, final Class<N> namespace,
+                    final K key, final ModelProcessingPhase phase) {
+        checkNotRegistered();
+
+        final PhaseModificationInNamespace<C> mod = new PhaseModificationInNamespace<>(EFFECTIVE_MODEL);
+        addReq(mod);
+        addMutation(mod);
+        contextImpl(context).onNamespaceItemAddedAction((Class) namespace, key, mod);
+        return mod;
+    }
+
+    private static StatementContextBase<?, ?, ?> contextImpl(final Object value) {
+        checkArgument(value instanceof StatementContextBase, "Supplied context %s is not provided by this reactor.",
+            value);
+        return StatementContextBase.class.cast(value);
+    }
+
+    boolean tryApply() {
+        checkState(action != null, "Action was not defined yet.");
+
+        if (removeSatisfied()) {
+            applyAction();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public <C extends Mutable<?, ?, ?>, T extends C> Prerequisite<C> mutatesCtx(final T context,
+            final ModelProcessingPhase phase) {
+        return addMutation(new PhaseMutation<>(contextImpl(context), phase));
+    }
+
+    @Override
+    public <A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+            AbstractPrerequisite<StmtContext<A, D, E>> requiresCtx(final StmtContext<A, D, E> context,
+                    final ModelProcessingPhase phase) {
+        return requiresCtxImpl(context, phase);
+    }
+
+
+    @Override
+    public <K, N extends StatementNamespace<K, ?, ?>> Prerequisite<StmtContext<?, ?, ?>> requiresCtx(
+            final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key,
+            final ModelProcessingPhase phase) {
+        return requiresCtxImpl(context, namespace, key, phase);
+    }
+
+    @Override
+    public <K, N extends StatementNamespace<K, ?, ?>> Prerequisite<StmtContext<?, ?, ?>> requiresCtx(
+            final StmtContext<?, ?, ?> context, final Class<N> namespace, final NamespaceKeyCriterion<K> criterion,
+            final ModelProcessingPhase phase) {
+        return requiresCtxImpl(context, namespace, criterion, phase);
+    }
+
+    @Override
+    public <D extends DeclaredStatement<?>> Prerequisite<D> requiresDeclared(
+            final StmtContext<?, ? extends D, ?> context) {
+        return requiresCtxImpl(context, FULL_DECLARATION).transform(StmtContext::buildDeclared);
+    }
+
+    @Override
+    public <K, D extends DeclaredStatement<?>, N extends StatementNamespace<K, ? extends D, ?>> Prerequisite<D>
+            requiresDeclared(final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key) {
+        final AbstractPrerequisite<StmtContext<?, D, ?>> rawContext = requiresCtxImpl(context, namespace, key,
+            FULL_DECLARATION);
+        return rawContext.transform(StmtContext::buildDeclared);
+    }
+
+    @Override
+    public <K, D extends DeclaredStatement<?>, N extends StatementNamespace<K, ? extends D, ?>>
+            AbstractPrerequisite<StmtContext<?, D, ?>> requiresDeclaredCtx(final StmtContext<?, ?, ?> context,
+                    final Class<N> namespace, final K key) {
+        return requiresCtxImpl(context, namespace, key, FULL_DECLARATION);
+    }
+
+    @Override
+    public <E extends EffectiveStatement<?, ?>> Prerequisite<E> requiresEffective(
+            final StmtContext<?, ?, ? extends E> stmt) {
+        return requiresCtxImpl(stmt, EFFECTIVE_MODEL).transform(StmtContext::buildEffective);
+    }
+
+    @Override
+    public <K, E extends EffectiveStatement<?, ?>, N extends StatementNamespace<K, ?, ? extends E>> Prerequisite<E>
+            requiresEffective(final StmtContext<?, ?, ?> context, final Class<N> namespace, final K key) {
+        final AbstractPrerequisite<StmtContext<?, ?, E>> rawContext = requiresCtxImpl(context, namespace, key,
+            EFFECTIVE_MODEL);
+        return rawContext.transform(StmtContext::buildEffective);
+    }
+
+    @Override
+    public <K, E extends EffectiveStatement<?, ?>, N extends StatementNamespace<K, ?, ? extends E>>
+            AbstractPrerequisite<StmtContext<?, ?, E>> requiresEffectiveCtx(final StmtContext<?, ?, ?> context,
+                    final Class<N> namespace, final K key) {
+        return requiresCtxImpl(contextImpl(context), namespace, key, EFFECTIVE_MODEL);
+    }
+
+    @Override
+    public <N extends IdentifierNamespace<?, ?>> Prerequisite<Mutable<?, ?, ?>> mutatesNs(
+            final Mutable<?, ?, ?> context, final Class<N> namespace) {
+        return addMutation(new NamespaceMutation<>(contextImpl(context), namespace));
+    }
+
+    @Override
+    public <K, E extends EffectiveStatement<?, ?>, N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>>
+            AbstractPrerequisite<Mutable<?, ?, E>> mutatesEffectiveCtx(final StmtContext<?, ?, ?> context,
+                    final Class<N> namespace, final K key) {
+        return mutatesCtxImpl(context, namespace, key, EFFECTIVE_MODEL);
+    }
+
+    @Override
+    public <K, E extends EffectiveStatement<?, ?>, N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>>
+            AbstractPrerequisite<Mutable<?, ?, E>> mutatesEffectiveCtxPath(final StmtContext<?, ?, ?> context,
+                    final Class<N> namespace, final Iterable<K> keys) {
+        checkNotRegistered();
+
+        final PhaseModificationInNamespacePath<Mutable<?, ?, E>, K, N> ret = new PhaseModificationInNamespacePath<>(
+                EFFECTIVE_MODEL, keys);
+        addReq(ret);
+        addMutation(ret);
+
+        ret.hookOnto(context, namespace);
+        return ret;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:hiddenField")
+    public void apply(final InferenceAction action) {
+        checkState(this.action == null, "Action already defined to %s", this.action);
+        this.action = requireNonNull(action);
+    }
+
+    private abstract class AbstractPrerequisite<T> implements Prerequisite<T> {
+        private boolean done = false;
+        private T value;
+
+        @Override
+        @SuppressWarnings("checkstyle:hiddenField")
+        public final T resolve(final InferenceContext ctx) {
+            checkState(done);
+            checkArgument(ctx == ModifierImpl.this.ctx);
+            return verifyNotNull(value, "Attempted to access unavailable prerequisite %s", this);
+        }
+
+        final boolean isDone() {
+            return done;
+        }
+
+        @SuppressWarnings("checkstyle:hiddenField")
+        final boolean resolvePrereq(final T value) {
+            this.value = value;
+            this.done = true;
+            return isApplied();
+        }
+
+        final <O> @NonNull Prerequisite<O> transform(final Function<? super T, O> transformation) {
+            return context -> transformation.apply(resolve(context));
+        }
+
+        @Override
+        public final String toString() {
+            return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+        }
+
+        ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+            return toStringHelper.add("value", value);
+        }
+    }
+
+    private final class PhaseMutation<C> extends AbstractPrerequisite<C> implements ContextMutation {
+        @SuppressWarnings("unchecked")
+        PhaseMutation(final StatementContextBase<?, ?, ?> context, final ModelProcessingPhase phase) {
+            context.addMutation(phase, this);
+            resolvePrereq((C) context);
+        }
+
+        @Override
+        public boolean isFinished() {
+            return isApplied();
+        }
+    }
+
+    private final class PhaseFinished<C extends StmtContext<?, ?, ?>> extends AbstractPrerequisite<C>
+            implements OnPhaseFinished {
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean phaseFinished(final StatementContextBase<?, ?, ?> context,
+                final ModelProcessingPhase finishedPhase) {
+            return resolvePrereq((C) context);
+        }
+    }
+
+    private final class NamespaceMutation<N extends IdentifierNamespace<?, ?>>
+            extends AbstractPrerequisite<Mutable<?, ?, ?>>  {
+        NamespaceMutation(final StatementContextBase<?, ?, ?> ctx, final Class<N> namespace) {
+            resolvePrereq(ctx);
+        }
+    }
+
+    private final class AddedToNamespace<C extends StmtContext<?, ?, ?>> extends AbstractPrerequisite<C>
+            implements OnNamespaceItemAdded, OnPhaseFinished {
+        private final ModelProcessingPhase phase;
+
+        AddedToNamespace(final ModelProcessingPhase phase) {
+            this.phase = requireNonNull(phase);
+        }
+
+        @Override
+        public void namespaceItemAdded(final StatementContextBase<?, ?, ?> context, final Class<?> namespace,
+                final Object key, final Object value) {
+            ((StatementContextBase<?, ?, ?>) value).addPhaseCompletedListener(phase, this);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean phaseFinished(final StatementContextBase<?, ?, ?> context,
+                final ModelProcessingPhase finishedPhase) {
+            return resolvePrereq((C) context);
+        }
+
+        @Override
+        ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+            return super.addToStringAttributes(toStringHelper).add("phase", phase);
+        }
+    }
+
+    private final class PhaseModificationInNamespace<C extends Mutable<?, ?, ?>> extends AbstractPrerequisite<C>
+            implements OnNamespaceItemAdded, ContextMutation {
+        private final ModelProcessingPhase modPhase;
+
+        PhaseModificationInNamespace(final ModelProcessingPhase phase) {
+            checkArgument(phase != null, "Model processing phase must not be null");
+            this.modPhase = phase;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void namespaceItemAdded(final StatementContextBase<?, ?, ?> context, final Class<?> namespace,
+                final Object key, final Object value) {
+            StatementContextBase<?, ?, ?> targetCtx = contextImpl(value);
+            targetCtx.addMutation(modPhase, this);
+            resolvePrereq((C) targetCtx);
+        }
+
+        @Override
+        public boolean isFinished() {
+            return isApplied();
+        }
+    }
+
+    private final class PhaseModificationInNamespacePath<C extends Mutable<?, ?, ?>, K,
+            N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>> extends AbstractPrerequisite<C>
+            implements OnNamespaceItemAdded, ContextMutation {
+        private final ModelProcessingPhase modPhase;
+        private final Iterable<K> keys;
+        private final Iterator<K> it;
+
+        PhaseModificationInNamespacePath(final ModelProcessingPhase phase, final Iterable<K> keys) {
+            this.modPhase = requireNonNull(phase);
+            this.keys = requireNonNull(keys);
+            it = keys.iterator();
+        }
+
+        @Override
+        public boolean isFinished() {
+            return isApplied();
+        }
+
+        @Override
+        public void namespaceItemAdded(final StatementContextBase<?, ?, ?> context, final Class<?> namespace,
+                final Object key, final Object value) {
+            LOG.debug("Action for {} got key {}", keys, key);
+
+            final StatementContextBase<?, ?, ?> target = contextImpl(value);
+            if (!target.isSupportedByFeatures()) {
+                LOG.debug("Key {} in {} is not supported", key, keys);
+                resolvePrereq(null);
+                action.prerequisiteUnavailable(this);
+                return;
+            }
+
+            if (!it.hasNext()) {
+                target.addMutation(modPhase, this);
+                resolvePrereq((C) value);
+                return;
+            }
+
+            hookOnto(target, namespace, it.next());
+        }
+
+        void hookOnto(final StmtContext<?, ?, ?> context, final Class<?> namespace) {
+            checkArgument(it.hasNext(), "Namespace %s keys may not be empty", namespace);
+            hookOnto(contextImpl(context), namespace, it.next());
+        }
+
+        @SuppressWarnings("unchecked")
+        private void hookOnto(final StatementContextBase<?, ?, ?> context, final Class<?> namespace, final K key) {
+            context.onNamespaceItemAddedAction((Class) namespace, requireNonNull(key), this);
+        }
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/NamespaceBehaviourWithListeners.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/NamespaceBehaviourWithListeners.java
new file mode 100644 (file)
index 0000000..1ac8f66
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+abstract class NamespaceBehaviourWithListeners<K, V, N extends IdentifierNamespace<K, V>>
+        extends NamespaceBehaviour<K, V, N> {
+
+    abstract static class ValueAddedListener<K> {
+        private final NamespaceStorageNode ctxNode;
+
+        ValueAddedListener(final NamespaceStorageNode contextNode) {
+            this.ctxNode = requireNonNull(contextNode);
+        }
+
+        final NamespaceStorageNode getCtxNode() {
+            return ctxNode;
+        }
+    }
+
+    abstract static class KeyedValueAddedListener<K> extends ValueAddedListener<K> {
+        private final K key;
+
+        KeyedValueAddedListener(final NamespaceStorageNode contextNode, final K key) {
+            super(contextNode);
+            this.key = requireNonNull(key);
+        }
+
+        final K getKey() {
+            return key;
+        }
+
+        final <V> boolean isRequestedValue(final NamespaceBehaviour<K, ? , ?> behavior,
+                final NamespaceStorageNode storage, final V value) {
+            return value == behavior.getFrom(getCtxNode(), key);
+        }
+
+        abstract void onValueAdded(Object value);
+    }
+
+    abstract static class PredicateValueAddedListener<K, V> extends ValueAddedListener<K> {
+        PredicateValueAddedListener(final NamespaceStorageNode contextNode) {
+            super(contextNode);
+        }
+
+        abstract boolean onValueAdded(@NonNull K key, @NonNull V value);
+    }
+
+    protected final NamespaceBehaviour<K, V, N> delegate;
+
+    private List<VirtualNamespaceContext<?, V, ?, K>> derivedNamespaces;
+
+    protected NamespaceBehaviourWithListeners(final NamespaceBehaviour<K, V, N> delegate) {
+        super(delegate.getIdentifier());
+        this.delegate = delegate;
+    }
+
+    abstract void addListener(KeyedValueAddedListener<K> listener);
+
+    abstract void addListener(PredicateValueAddedListener<K, V> listener);
+
+    @Override
+    public abstract void addTo(NamespaceStorageNode storage, K key, V value);
+
+    protected void notifyListeners(final NamespaceStorageNode storage,
+            final Iterator<? extends KeyedValueAddedListener<K>> keyListeners, final V value) {
+        List<KeyedValueAddedListener<K>> toNotify = new ArrayList<>();
+        while (keyListeners.hasNext()) {
+            final KeyedValueAddedListener<K> listener = keyListeners.next();
+            if (listener.isRequestedValue(this, storage, value)) {
+                keyListeners.remove();
+                toNotify.add(listener);
+            }
+        }
+        for (KeyedValueAddedListener<K> listener : toNotify) {
+            listener.onValueAdded(value);
+        }
+    }
+
+    protected void notifyDerivedNamespaces(final NamespaceStorageNode storage, final K key, final V value) {
+        if (derivedNamespaces != null) {
+            for (VirtualNamespaceContext<?, V, ?, K> derived : derivedNamespaces) {
+                derived.addedToSourceNamespace(storage, key, value);
+            }
+        }
+    }
+
+    final void addDerivedNamespace(final VirtualNamespaceContext<?, V, ?, K> namespace) {
+        if (derivedNamespaces == null) {
+            derivedNamespaces = new ArrayList<>();
+        }
+        derivedNamespaces.add(namespace);
+    }
+
+    @Override
+    public V getFrom(final NamespaceStorageNode storage, final K key) {
+        return delegate.getFrom(storage, key);
+    }
+
+    @Override
+    public Map<K, V> getAllFrom(final NamespaceStorageNode storage) {
+        return delegate.getAllFrom(storage);
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper).add("delegate", delegate);
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/NamespaceStorageSupport.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/NamespaceStorageSupport.java
new file mode 100644 (file)
index 0000000..7f15c06
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+abstract class NamespaceStorageSupport implements NamespaceStorageNode {
+
+    private Map<Class<?>, Map<?,?>> namespaces = ImmutableMap.of();
+
+    @Override
+    public abstract NamespaceStorageNode getParentNamespaceStorage();
+
+    /**
+     * Return the registry of a source context.
+     *
+     * @return registry of source context
+     */
+    public abstract Registry getBehaviourRegistry();
+
+    protected void checkLocalNamespaceAllowed(final Class<? extends IdentifierNamespace<?, ?>> type) {
+        // NOOP
+    }
+
+    /**
+     * Occurs when an item is added to model namespace.
+     *
+     * @throws SourceException instance of SourceException
+     */
+    protected <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceElementAdded(final Class<N> type, final K key,
+            final V value) {
+        // NOOP
+    }
+
+    /**
+     * Return a value associated with specified key within a namespace.
+     *
+     * @param type Namespace type
+     * @param key Key
+     * @param <K> namespace key type
+     * @param <V> namespace value type
+     * @param <N> namespace type
+     * @param <T> key type
+     * @return Value, or null if there is no element
+     * @throws NamespaceNotAvailableException when the namespace is not available.
+     */
+    public final <K, V, T extends K, N extends IdentifierNamespace<K, V>> V getFromNamespace(final Class<N> type,
+            final T key) {
+        return getBehaviourRegistry().getNamespaceBehaviour(type).getFrom(this, key);
+    }
+
+    public final <K, V, N extends IdentifierNamespace<K, V>> Optional<Entry<K, V>> getFromNamespace(
+            final Class<N> type, final NamespaceKeyCriterion<K> criterion) {
+        return getBehaviourRegistry().getNamespaceBehaviour(type).getFrom(this, criterion);
+    }
+
+    public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromNamespace(final Class<N> type) {
+        return getBehaviourRegistry().getNamespaceBehaviour(type).getAllFrom(this);
+    }
+
+    @SuppressWarnings("unchecked")
+    public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromCurrentStmtCtxNamespace(
+            final Class<N> type) {
+        return (Map<K, V>) namespaces.get(type);
+    }
+
+    /**
+     * Associate a value with a key within a namespace.
+     *
+     * @param type Namespace type
+     * @param key Key
+     * @param value value
+     * @param <K> namespace key type
+     * @param <V> namespace value type
+     * @param <N> namespace type
+     * @param <T> key type
+     * @param <U> value type
+     * @throws NamespaceNotAvailableException when the namespace is not available.
+     */
+    public final <K, V, T extends K, U extends V, N extends IdentifierNamespace<K, V>> void addToNs(
+            final Class<N> type, final T key, final U value) {
+        getBehaviourRegistry().getNamespaceBehaviour(type).addTo(this,key,value);
+    }
+
+    /**
+     * Associate a context with a key within a namespace.
+     *
+     * @param type Namespace type
+     * @param key Key
+     * @param value Context value
+     * @param <K> namespace key type
+     * @param <N> namespace type
+     * @throws NamespaceNotAvailableException when the namespace is not available.
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public final <K, N extends StatementNamespace<K, ?,?>> void addContextToNamespace(final Class<N> type, final K key,
+            final StmtContext<?, ?, ?> value) {
+        getBehaviourRegistry().getNamespaceBehaviour((Class)type).addTo(this, key, value);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> V getFromLocalStorage(final Class<N> type, final K key) {
+        final Map<K, V> localNamespace = (Map<K, V>) namespaces.get(type);
+        return localNamespace == null ? null : localNamespace.get(key);
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromLocalStorage(final Class<N> type) {
+        @SuppressWarnings("unchecked")
+        final Map<K, V> localNamespace = (Map<K, V>) namespaces.get(type);
+        return localNamespace;
+    }
+
+    private <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> ensureLocalNamespace(final Class<N> type) {
+        @SuppressWarnings("unchecked")
+        Map<K, V> ret = (Map<K,V>) namespaces.get(type);
+        if (ret == null) {
+            checkLocalNamespaceAllowed(type);
+            ret = new HashMap<>(1);
+
+            if (namespaces.isEmpty()) {
+                namespaces = new HashMap<>(1);
+            }
+            namespaces.put(type, ret);
+        }
+
+        return ret;
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorage(final Class<N> type, final K key,
+            final V value) {
+        final V ret = ensureLocalNamespace(type).put(key, value);
+        onNamespaceElementAdded(type, key, value);
+        return ret;
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorageIfAbsent(final Class<N> type, final K key,
+            final V value) {
+        final V ret = ensureLocalNamespace(type).putIfAbsent(key, value);
+        if (ret == null) {
+            onNamespaceElementAdded(type, key, value);
+        }
+        return ret;
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReactorDeclaredModel.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReactorDeclaredModel.java
new file mode 100644 (file)
index 0000000..a98a2d0
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+public class ReactorDeclaredModel implements Immutable {
+    private final ImmutableList<DeclaredStatement<?>> rootStatements;
+
+    public ReactorDeclaredModel(final List<DeclaredStatement<?>> rootStatements) {
+        this.rootStatements = ImmutableList.copyOf(rootStatements);
+    }
+
+    public ImmutableList<DeclaredStatement<?>> getRootStatements() {
+        return rootStatements;
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/RootStatementContext.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/RootStatementContext.java
new file mode 100644 (file)
index 0000000..ab5b9f9
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
+import org.opendaylight.yangtools.yang.parser.spi.source.IncludedModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+
+/**
+ * Root statement class for a YANG source. All statements defined in that YANG source are mapped underneath an instance
+ * of this class, hence recursive lookups from them cross this class.
+ */
+public class RootStatementContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>> extends
+        StatementContextBase<A, D, E> {
+
+    public static final YangVersion DEFAULT_VERSION = YangVersion.VERSION_1;
+
+    private final SourceSpecificContext sourceContext;
+    private final A argument;
+
+    private YangVersion rootVersion;
+    private Set<SourceIdentifier> requiredSources = ImmutableSet.of();
+    private SourceIdentifier rootIdentifier;
+
+    /**
+     * References to RootStatementContext of submodules which are included in this source.
+     */
+    private List<RootStatementContext<?, ?, ?>> includedContexts = ImmutableList.of();
+
+    RootStatementContext(final SourceSpecificContext sourceContext, final StatementDefinitionContext<A, D, E> def,
+        final StatementSourceReference ref, final String rawArgument) {
+        super(def, ref, rawArgument);
+        this.sourceContext = requireNonNull(sourceContext);
+        this.argument = def.parseArgumentValue(this, rawStatementArgument());
+    }
+
+    RootStatementContext(final SourceSpecificContext sourceContext, final StatementDefinitionContext<A, D, E> def,
+            final StatementSourceReference ref, final String rawArgument, final YangVersion version,
+            final SourceIdentifier identifier) {
+        this(sourceContext, def, ref, rawArgument);
+        this.setRootVersion(version);
+        this.setRootIdentifier(identifier);
+    }
+
+    @Override
+    public StatementContextBase<?, ?, ?> getParentContext() {
+        // null as root cannot have parent
+        return null;
+    }
+
+    @Override
+    public NamespaceStorageNode getParentNamespaceStorage() {
+        // namespace storage of source context
+        return sourceContext;
+    }
+
+    @Override
+    public Registry getBehaviourRegistry() {
+        return sourceContext;
+    }
+
+    @Override
+    public StorageNodeType getStorageNodeType() {
+        return StorageNodeType.ROOT_STATEMENT_LOCAL;
+    }
+
+    @Override
+    public RootStatementContext<?, ?, ?> getRoot() {
+        // this as its own root
+        return this;
+    }
+
+    SourceSpecificContext getSourceContext() {
+        return sourceContext;
+    }
+
+    @Override
+    public A getStatementArgument() {
+        return argument;
+    }
+
+    @Override
+    public Optional<SchemaPath> getSchemaPath() {
+        return Optional.of(SchemaPath.ROOT);
+    }
+
+    @Override
+    public boolean isConfiguration() {
+        return true;
+    }
+
+    @Override
+    public boolean isEnabledSemanticVersioning() {
+        return sourceContext.isEnabledSemanticVersioning();
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorage(final Class<N> type, final K key,
+            final V value) {
+        if (IncludedModuleContext.class.isAssignableFrom(type)) {
+            if (includedContexts.isEmpty()) {
+                includedContexts = new ArrayList<>(1);
+            }
+            verify(value instanceof RootStatementContext);
+            includedContexts.add((RootStatementContext<?, ?, ?>) value);
+        }
+        return super.putToLocalStorage(type, key, value);
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> V getFromLocalStorage(final Class<N> type, final K key) {
+        return getFromLocalStorage(type, key, new HashSet<>());
+    }
+
+    /*
+     * We need to track already checked RootStatementContexts due to possible
+     * circular chains of includes between submodules
+     */
+    private <K, V, N extends IdentifierNamespace<K, V>> @Nullable V getFromLocalStorage(final Class<N> type,
+            final K key, final HashSet<RootStatementContext<?, ?, ?>> alreadyChecked) {
+        final V potentialLocal = super.getFromLocalStorage(type, key);
+        if (potentialLocal != null) {
+            return potentialLocal;
+        }
+
+        alreadyChecked.add(this);
+        for (final RootStatementContext<?, ?, ?> includedSource : includedContexts) {
+            if (alreadyChecked.contains(includedSource)) {
+                continue;
+            }
+            final V potential = includedSource.getFromLocalStorage(type, key, alreadyChecked);
+            if (potential != null) {
+                return potential;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromLocalStorage(final Class<N> type) {
+        return getAllFromLocalStorage(type, new HashSet<>());
+    }
+
+    /*
+     * We need to track already checked RootStatementContexts due to possible
+     * circular chains of includes between submodules
+     */
+    private <K, V, N extends IdentifierNamespace<K, V>> @Nullable Map<K, V> getAllFromLocalStorage(final Class<N> type,
+            final HashSet<RootStatementContext<?, ?, ?>> alreadyChecked) {
+        final Map<K, V> potentialLocal = super.getAllFromLocalStorage(type);
+        if (potentialLocal != null) {
+            return potentialLocal;
+        }
+
+        alreadyChecked.add(this);
+        for (final RootStatementContext<?, ?, ?> includedSource : includedContexts) {
+            if (alreadyChecked.contains(includedSource)) {
+                continue;
+            }
+            final Map<K, V> potential = includedSource.getAllFromLocalStorage(type, alreadyChecked);
+            if (potential != null) {
+                return potential;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public YangVersion getRootVersion() {
+        return rootVersion == null ? DEFAULT_VERSION : rootVersion;
+    }
+
+    @Override
+    public void setRootVersion(final YangVersion version) {
+        checkArgument(sourceContext.getSupportedVersions().contains(version),
+                "Unsupported yang version %s in %s", version, getStatementSourceReference());
+        checkState(this.rootVersion == null, "Version of root %s has been already set to %s", argument,
+                this.rootVersion);
+        this.rootVersion = requireNonNull(version);
+    }
+
+    @Override
+    public void addMutableStmtToSeal(final MutableStatement mutableStatement) {
+        sourceContext.addMutableStmtToSeal(mutableStatement);
+    }
+
+    @Override
+    public void addRequiredSource(final SourceIdentifier dependency) {
+        checkState(sourceContext.getInProgressPhase() == ModelProcessingPhase.SOURCE_PRE_LINKAGE,
+                "Add required module is allowed only in ModelProcessingPhase.SOURCE_PRE_LINKAGE phase");
+        if (requiredSources.isEmpty()) {
+            requiredSources = new HashSet<>();
+        }
+        requiredSources.add(dependency);
+    }
+
+    /**
+     * Return the set of required sources.
+     *
+     * @return Required sources.
+     */
+    Collection<SourceIdentifier> getRequiredSources() {
+        return ImmutableSet.copyOf(requiredSources);
+    }
+
+    @Override
+    public void setRootIdentifier(final SourceIdentifier identifier) {
+        this.rootIdentifier = requireNonNull(identifier);
+    }
+
+    SourceIdentifier getRootIdentifier() {
+        return rootIdentifier;
+    }
+
+    @Override
+    protected boolean isIgnoringIfFeatures() {
+        return false;
+    }
+
+    @Override
+    protected boolean isIgnoringConfig() {
+        return false;
+    }
+
+    @Override
+    protected boolean isParentSupportedByFeatures() {
+        return true;
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SimpleNamespaceContext.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SimpleNamespaceContext.java
new file mode 100644 (file)
index 0000000..0442329
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+final class SimpleNamespaceContext<K, V, N extends IdentifierNamespace<K, V>>
+        extends NamespaceBehaviourWithListeners<K, V, N> {
+    // FIXME: Change this to Multimap, once issue with modules is resolved.
+    private List<KeyedValueAddedListener<K>> listeners;
+
+    private Collection<PredicateValueAddedListener<K, V>> predicateListeners;
+
+    SimpleNamespaceContext(final NamespaceBehaviour<K, V, N> delegate) {
+        super(delegate);
+    }
+
+    @Override
+    void addListener(final KeyedValueAddedListener<K> listener) {
+        if (listeners == null) {
+            listeners = new ArrayList<>();
+        }
+        listeners.add(listener);
+    }
+
+    @Override
+    void addListener(final PredicateValueAddedListener<K, V> listener) {
+        if (predicateListeners == null) {
+            predicateListeners = new ArrayList<>();
+        }
+        predicateListeners.add(listener);
+    }
+
+    @Override
+    public void addTo(final NamespaceStorageNode storage, final K key, final V value) {
+        delegate.addTo(storage, key, value);
+
+        if (listeners != null) {
+            notifyListeners(storage, listeners.iterator(), value);
+            if (listeners.isEmpty()) {
+                listeners = null;
+            }
+        }
+
+        if (predicateListeners != null) {
+            final Iterator<PredicateValueAddedListener<K, V>> it = predicateListeners.iterator();
+            while (it.hasNext()) {
+                if (it.next().onValueAdded(key, value)) {
+                    it.remove();
+                }
+            }
+            if (predicateListeners.isEmpty()) {
+                predicateListeners = null;
+            }
+        }
+
+        notifyDerivedNamespaces(storage, key, value);
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SourceSpecificContext.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SourceSpecificContext.java
new file mode 100644 (file)
index 0000000..43eb233
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementDefinitionNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportedModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
+import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
+import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModuleMap;
+import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinitionMap;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBehaviour.Registry, Mutable {
+
+    public enum PhaseCompletionProgress {
+        NO_PROGRESS,
+        PROGRESS,
+        FINISHED
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(SourceSpecificContext.class);
+
+    private final Multimap<ModelProcessingPhase, ModifierImpl> modifiers = HashMultimap.create();
+    private final QNameToStatementDefinitionMap qnameToStmtDefMap = new QNameToStatementDefinitionMap();
+    private final PrefixToModuleMap prefixToModuleMap = new PrefixToModuleMap();
+    private final BuildGlobalContext currentContext;
+    private final StatementStreamSource source;
+
+    /*
+     * "imported" namespaces in this source -- this points to RootStatementContexts of
+     * - modules imported via 'import' statement
+     * - parent module, declared via 'belongs-to' statement
+     */
+    private Collection<RootStatementContext<?, ?, ?>> importedNamespaces = ImmutableList.of();
+    private ModelProcessingPhase finishedPhase = ModelProcessingPhase.INIT;
+    private ModelProcessingPhase inProgressPhase;
+    private RootStatementContext<?, ?, ?> root;
+
+    SourceSpecificContext(final BuildGlobalContext currentContext, final StatementStreamSource source) {
+        this.currentContext = requireNonNull(currentContext);
+        this.source = requireNonNull(source);
+    }
+
+    boolean isEnabledSemanticVersioning() {
+        return currentContext.isEnabledSemanticVersioning();
+    }
+
+    ModelProcessingPhase getInProgressPhase() {
+        return inProgressPhase;
+    }
+
+    Optional<StatementContextBase<?, ?, ?>> lookupDeclaredChild(final StatementContextBase<?, ?, ?> current,
+            final int childId) {
+        if (current == null) {
+            return Optional.empty();
+        }
+
+        // Fast path: we are entering a statement which was emitted in previous phase
+        StatementContextBase<?, ?, ?> existing = current.lookupSubstatement(childId);
+        while (existing != null && StatementSource.CONTEXT == existing.getStatementSource()) {
+            existing = existing.lookupSubstatement(childId);
+        }
+
+        return Optional.ofNullable(existing);
+    }
+
+    StatementContextBase<?, ?, ?> createDeclaredChild(final StatementContextBase<?, ?, ?> current, final int childId,
+            final QName name, final String argument, final StatementSourceReference ref) {
+        StatementDefinitionContext<?, ?, ?> def = currentContext.getStatementDefinition(getRootVersion(), name);
+        if (def == null) {
+            def = currentContext.getModelDefinedStatementDefinition(name);
+            if (def == null) {
+                final StatementSupport<?, ?, ?> extension = qnameToStmtDefMap.get(name);
+                if (extension != null) {
+                    def = new StatementDefinitionContext<>(extension);
+                    currentContext.putModelDefinedStatementDefinition(name, def);
+                }
+            }
+        } else if (current != null && StmtContextUtils.isUnrecognizedStatement(current)) {
+            /*
+             * This code wraps statements encountered inside an extension so
+             * they do not get confused with regular statements.
+             */
+            def = checkNotNull(current.definition().getAsUnknownStatementDefinition(def),
+                    "Unable to create unknown statement definition of yang statement %s in unknown statement %s", def,
+                    current);
+        }
+
+        if (InferenceException.throwIfNull(def, ref, "Statement %s does not have type mapping defined.", name)
+                .getArgumentDefinition().isPresent()) {
+            SourceException.throwIfNull(argument, ref, "Statement %s requires an argument", name);
+        } else {
+            SourceException.throwIf(argument != null, ref, "Statement %s does not take argument", name);
+        }
+
+        /*
+         * If the current statement definition has argument specific
+         * sub-definitions, get argument specific sub-definition based on given
+         * argument (e.g. type statement need to be specialized based on its
+         * argument).
+         */
+        if (def.hasArgumentSpecificSubDefinitions()) {
+            def = def.getSubDefinitionSpecificForArgument(argument);
+        }
+
+        if (current != null) {
+            return current.createSubstatement(childId, def, ref, argument);
+        }
+
+        /*
+         * If root is null or root version is other than default,
+         * we need to create new root.
+         */
+        if (root == null) {
+            root = new RootStatementContext<>(this, def, ref, argument);
+        } else if (!RootStatementContext.DEFAULT_VERSION.equals(root.getRootVersion())
+                && inProgressPhase == ModelProcessingPhase.SOURCE_LINKAGE) {
+            root = new RootStatementContext<>(this, def, ref, argument, root.getRootVersion(),
+                    root.getRootIdentifier());
+        } else {
+            final QName rootStatement = root.definition().getStatementName();
+            final String rootArgument = root.rawStatementArgument();
+
+            checkState(Objects.equals(def.getStatementName(), rootStatement) && Objects.equals(argument, rootArgument),
+                "Root statement was already defined as '%s %s'.", rootStatement, rootArgument);
+        }
+        return root;
+    }
+
+    RootStatementContext<?, ?, ?> getRoot() {
+        return root;
+    }
+
+    /**
+     * Return version of root statement context.
+     *
+     * @return version of root statement context
+     */
+    YangVersion getRootVersion() {
+        return root != null ? root.getRootVersion() : RootStatementContext.DEFAULT_VERSION;
+    }
+
+    DeclaredStatement<?> buildDeclared() {
+        return root.buildDeclared();
+    }
+
+    EffectiveStatement<?, ?> buildEffective() {
+        return root.buildEffective();
+    }
+
+    void startPhase(final ModelProcessingPhase phase) {
+        final ModelProcessingPhase previousPhase = phase.getPreviousPhase();
+        verify(Objects.equals(previousPhase, finishedPhase),
+            "Phase sequencing violation: previous phase should be %s, source %s has %s", previousPhase, source,
+            finishedPhase);
+
+        final Collection<ModifierImpl> previousModifiers = modifiers.get(previousPhase);
+        checkState(previousModifiers.isEmpty(), "Previous phase %s has unresolved modifiers %s in source %s",
+            previousPhase, previousModifiers, source);
+
+        inProgressPhase = phase;
+        LOG.debug("Source {} started phase {}", source, phase);
+    }
+
+    private void updateImportedNamespaces(final Class<?> type, final Object value) {
+        if (BelongsToModuleContext.class.isAssignableFrom(type) || ImportedModuleContext.class.isAssignableFrom(type)) {
+            if (importedNamespaces.isEmpty()) {
+                importedNamespaces = new ArrayList<>(1);
+            }
+
+            verify(value instanceof RootStatementContext);
+            importedNamespaces.add((RootStatementContext<?, ?, ?>) value);
+        }
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorage(final Class<N> type, final K key,
+           final V value) {
+        // RootStatementContext takes care of IncludedModuleContext and the rest...
+        final V ret = getRoot().putToLocalStorage(type, key, value);
+        // FIXME: what about duplicates?
+        updateImportedNamespaces(type, value);
+        return ret;
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorageIfAbsent(final Class<N> type, final K key,
+           final V value) {
+        // RootStatementContext takes care of IncludedModuleContext and the rest...
+        final V ret = getRoot().putToLocalStorageIfAbsent(type, key, value);
+        if (ret == null) {
+            updateImportedNamespaces(type, value);
+        }
+        return ret;
+    }
+
+    @Override
+    public StorageNodeType getStorageNodeType() {
+        return StorageNodeType.SOURCE_LOCAL_SPECIAL;
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> V getFromLocalStorage(final Class<N> type, final K key) {
+        final V potentialLocal = getRoot().getFromLocalStorage(type, key);
+        if (potentialLocal != null) {
+            return potentialLocal;
+        }
+
+        for (final NamespaceStorageNode importedSource : importedNamespaces) {
+            final V potential = importedSource.getFromLocalStorage(type, key);
+            if (potential != null) {
+                return potential;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromLocalStorage(final Class<N> type) {
+        final Map<K, V> potentialLocal = getRoot().getAllFromLocalStorage(type);
+        if (potentialLocal != null) {
+            return potentialLocal;
+        }
+
+        for (final NamespaceStorageNode importedSource : importedNamespaces) {
+            final Map<K, V> potential = importedSource.getAllFromLocalStorage(type);
+
+            if (potential != null) {
+                return potential;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> getNamespaceBehaviour(
+            final Class<N> type) {
+        return currentContext.getNamespaceBehaviour(type);
+    }
+
+    @Override
+    public NamespaceStorageNode getParentNamespaceStorage() {
+        return currentContext;
+    }
+
+    PhaseCompletionProgress tryToCompletePhase(final ModelProcessingPhase phase) {
+        final Collection<ModifierImpl> currentPhaseModifiers = modifiers.get(phase);
+
+        boolean hasProgressed = tryToProgress(currentPhaseModifiers);
+        final boolean phaseCompleted = requireNonNull(root, "Malformed source. Valid root element is missing.")
+                .tryToCompletePhase(phase);
+
+        hasProgressed |= tryToProgress(currentPhaseModifiers);
+
+        if (phaseCompleted && currentPhaseModifiers.isEmpty()) {
+            finishedPhase = phase;
+            LOG.debug("Source {} finished phase {}", source, phase);
+            return PhaseCompletionProgress.FINISHED;
+
+        }
+
+        return hasProgressed ? PhaseCompletionProgress.PROGRESS : PhaseCompletionProgress.NO_PROGRESS;
+    }
+
+    private static boolean tryToProgress(final Collection<ModifierImpl> currentPhaseModifiers) {
+        boolean hasProgressed = false;
+
+        // Try making forward progress ...
+        final Iterator<ModifierImpl> modifier = currentPhaseModifiers.iterator();
+        while (modifier.hasNext()) {
+            if (modifier.next().tryApply()) {
+                modifier.remove();
+                hasProgressed = true;
+            }
+        }
+
+        return hasProgressed;
+    }
+
+    @NonNull ModelActionBuilder newInferenceAction(final @NonNull ModelProcessingPhase phase) {
+        final ModifierImpl action = new ModifierImpl();
+        modifiers.put(phase, action);
+        return action;
+    }
+
+    @Override
+    public String toString() {
+        return "SourceSpecificContext [source=" + source + ", current=" + inProgressPhase + ", finished="
+                + finishedPhase + "]";
+    }
+
+    Optional<SourceException> failModifiers(final ModelProcessingPhase identifier) {
+        final List<SourceException> exceptions = new ArrayList<>();
+        for (final ModifierImpl mod : modifiers.get(identifier)) {
+            try {
+                mod.failModifier();
+            } catch (final SourceException e) {
+                exceptions.add(e);
+            }
+        }
+
+        if (exceptions.isEmpty()) {
+            return Optional.empty();
+        }
+
+        final String message = String.format("Yang model processing phase %s failed", identifier);
+        final InferenceException e = new InferenceException(message, root.getStatementSourceReference(),
+            exceptions.get(0));
+        exceptions.listIterator(1).forEachRemaining(e::addSuppressed);
+
+        return Optional.of(e);
+    }
+
+    void loadStatements() {
+        LOG.trace("Source {} loading statements for phase {}", source, inProgressPhase);
+
+        switch (inProgressPhase) {
+            case SOURCE_PRE_LINKAGE:
+                source.writePreLinkage(new StatementContextWriter(this, inProgressPhase), stmtDef());
+                break;
+            case SOURCE_LINKAGE:
+                source.writeLinkage(new StatementContextWriter(this, inProgressPhase), stmtDef(), preLinkagePrefixes(),
+                    getRootVersion());
+                break;
+            case STATEMENT_DEFINITION:
+                source.writeLinkageAndStatementDefinitions(new StatementContextWriter(this, inProgressPhase), stmtDef(),
+                    prefixes(), getRootVersion());
+                break;
+            case FULL_DECLARATION:
+                source.writeFull(new StatementContextWriter(this, inProgressPhase), stmtDef(), prefixes(),
+                    getRootVersion());
+                break;
+            default:
+                break;
+        }
+    }
+
+    private PrefixToModule preLinkagePrefixes() {
+        final PrefixToModuleMap preLinkagePrefixes = new PrefixToModuleMap();
+        final Map<String, URI> prefixToNamespaceMap = getAllFromLocalStorage(ImpPrefixToNamespace.class);
+        if (prefixToNamespaceMap == null) {
+            //:FIXME if it is a submodule without any import, the map is null. Handle also submodules and includes...
+            return null;
+        }
+
+        prefixToNamespaceMap.forEach((key, value) -> preLinkagePrefixes.put(key, QNameModule.create(value)));
+        return preLinkagePrefixes;
+    }
+
+    private PrefixToModule prefixes() {
+        final Map<String, StmtContext<?, ?, ?>> allImports = getRoot().getAllFromNamespace(
+            ImportPrefixToModuleCtx.class);
+        if (allImports != null) {
+            allImports.forEach((key, value) ->
+                prefixToModuleMap.put(key, getRoot().getFromNamespace(ModuleCtxToModuleQName.class, value)));
+        }
+
+        final Map<String, StmtContext<?, ?, ?>> allBelongsTo = getRoot().getAllFromNamespace(
+            BelongsToPrefixToModuleCtx.class);
+        if (allBelongsTo != null) {
+            allBelongsTo.forEach((key, value) ->
+                prefixToModuleMap.put(key, getRoot().getFromNamespace(ModuleCtxToModuleQName.class, value)));
+        }
+
+        return prefixToModuleMap;
+    }
+
+    private QNameToStatementDefinition stmtDef() {
+        // regular YANG statements and extension supports added
+        final StatementSupportBundle supportsForPhase = currentContext.getSupportsForPhase(inProgressPhase);
+        qnameToStmtDefMap.putAll(supportsForPhase.getCommonDefinitions());
+        qnameToStmtDefMap.putAll(supportsForPhase.getDefinitionsSpecificForVersion(getRootVersion()));
+
+        // No further actions needed
+        if (inProgressPhase != ModelProcessingPhase.FULL_DECLARATION) {
+            return qnameToStmtDefMap;
+        }
+
+        // We need to any and all extension statements which have been declared in the context
+        final Map<QName, StatementSupport<?, ?, ?>> extensions = currentContext.getAllFromNamespace(
+                StatementDefinitionNamespace.class);
+        if (extensions != null) {
+            extensions.forEach((qname, support) -> {
+                final StatementSupport<?, ?, ?> existing = qnameToStmtDefMap.putIfAbsent(qname, support);
+                if (existing != null) {
+                    LOG.debug("Source {} already defines statement {} as {}", source, qname, existing);
+                } else {
+                    LOG.debug("Source {} defined statement {} as {}", source, qname, support);
+                }
+            });
+        }
+
+        return qnameToStmtDefMap;
+    }
+
+    public Set<YangVersion> getSupportedVersions() {
+        return currentContext.getSupportedVersions();
+    }
+
+    void addMutableStmtToSeal(final MutableStatement mutableStatement) {
+        currentContext.addMutableStmtToSeal(mutableStatement);
+    }
+
+    Collection<SourceIdentifier> getRequiredSources() {
+        return root.getRequiredSources();
+    }
+
+    SourceIdentifier getRootIdentifier() {
+        return root.getRootIdentifier();
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java
new file mode 100644 (file)
index 0000000..02443a9
--- /dev/null
@@ -0,0 +1,919 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.EventListener;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.util.OptionalBoolean;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ImplicitParentAwareStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImplicitSubstatement;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter.ResumedStatement;
+import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace.SupportedFeatures;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.KeyedValueAddedListener;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.PredicateValueAddedListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+        extends NamespaceStorageSupport implements Mutable<A, D, E>, ResumedStatement {
+    /**
+     * Event listener when an item is added to model namespace.
+     */
+    interface OnNamespaceItemAdded extends EventListener {
+        /**
+         * Invoked whenever a new item is added to a namespace.
+         */
+        void namespaceItemAdded(StatementContextBase<?, ?, ?> context, Class<?> namespace, Object key, Object value);
+    }
+
+    /**
+     * Event listener when a parsing {@link ModelProcessingPhase} is completed.
+     */
+    interface OnPhaseFinished extends EventListener {
+        /**
+         * Invoked whenever a processing phase has finished.
+         */
+        boolean phaseFinished(StatementContextBase<?, ?, ?> context, ModelProcessingPhase finishedPhase);
+    }
+
+    /**
+     * Interface for all mutations within an {@link ModelActionBuilder.InferenceAction}.
+     */
+    interface ContextMutation {
+
+        boolean isFinished();
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(StatementContextBase.class);
+
+    private final @NonNull StatementDefinitionContext<A, D, E> definition;
+    private final @NonNull StatementSourceReference statementDeclSource;
+    private final StmtContext<?, ?, ?> originalCtx;
+    private final StmtContext<?, ?, ?> prevCopyCtx;
+    private final CopyHistory copyHistory;
+    private final String rawArgument;
+
+    private Multimap<ModelProcessingPhase, OnPhaseFinished> phaseListeners = ImmutableMultimap.of();
+    private Multimap<ModelProcessingPhase, ContextMutation> phaseMutation = ImmutableMultimap.of();
+    private List<Mutable<?, ?, ?>> effective = ImmutableList.of();
+    private List<StmtContext<?, ?, ?>> effectOfStatement = ImmutableList.of();
+    private StatementMap substatements = StatementMap.empty();
+
+    private boolean isSupportedToBuildEffective = true;
+    private @Nullable ModelProcessingPhase completedPhase;
+    private @Nullable D declaredInstance;
+    private @Nullable E effectiveInstance;
+
+    // BooleanFields value
+    private byte supportedByFeatures;
+
+    private boolean fullyDefined;
+
+    StatementContextBase(final StatementDefinitionContext<A, D, E> def, final StatementSourceReference ref,
+            final String rawArgument) {
+        this.definition = requireNonNull(def);
+        this.statementDeclSource = requireNonNull(ref);
+        this.rawArgument = def.internArgument(rawArgument);
+        this.copyHistory = CopyHistory.original();
+        this.originalCtx = null;
+        this.prevCopyCtx = null;
+    }
+
+    StatementContextBase(final StatementDefinitionContext<A, D, E> def, final StatementSourceReference ref,
+        final String rawArgument, final CopyType copyType) {
+        this.definition = requireNonNull(def);
+        this.statementDeclSource = requireNonNull(ref);
+        this.rawArgument = rawArgument;
+        this.copyHistory = CopyHistory.of(copyType, CopyHistory.original());
+        this.originalCtx = null;
+        this.prevCopyCtx = null;
+    }
+
+    StatementContextBase(final StatementContextBase<A, D, E> original, final CopyType copyType) {
+        this.definition = original.definition;
+        this.statementDeclSource = original.statementDeclSource;
+        this.rawArgument = original.rawArgument;
+        this.copyHistory = CopyHistory.of(copyType, original.getCopyHistory());
+        this.originalCtx = original.getOriginalCtx().orElse(original);
+        this.prevCopyCtx = original;
+    }
+
+    StatementContextBase(final StatementContextBase<A, D, E> original) {
+        this.definition = original.definition;
+        this.statementDeclSource = original.statementDeclSource;
+        this.rawArgument = original.rawArgument;
+        this.copyHistory = original.getCopyHistory();
+        this.originalCtx = original.getOriginalCtx().orElse(original);
+        this.prevCopyCtx = original;
+        this.substatements = original.substatements;
+        this.effective = original.effective;
+    }
+
+    @Override
+    public Collection<? extends StmtContext<?, ?, ?>> getEffectOfStatement() {
+        return effectOfStatement;
+    }
+
+    @Override
+    public void addAsEffectOfStatement(final StmtContext<?, ?, ?> ctx) {
+        if (effectOfStatement.isEmpty()) {
+            effectOfStatement = new ArrayList<>(1);
+        }
+        effectOfStatement.add(ctx);
+    }
+
+    @Override
+    public void addAsEffectOfStatement(final Collection<? extends StmtContext<?, ?, ?>> ctxs) {
+        if (ctxs.isEmpty()) {
+            return;
+        }
+
+        if (effectOfStatement.isEmpty()) {
+            effectOfStatement = new ArrayList<>(ctxs.size());
+        }
+        effectOfStatement.addAll(ctxs);
+    }
+
+    @Override
+    public boolean isSupportedByFeatures() {
+        if (OptionalBoolean.isPresent(supportedByFeatures)) {
+            return OptionalBoolean.get(supportedByFeatures);
+        }
+
+        if (isIgnoringIfFeatures()) {
+            supportedByFeatures = OptionalBoolean.of(true);
+            return true;
+        }
+
+        final boolean isParentSupported = isParentSupportedByFeatures();
+        /*
+         * If parent is not supported, then this context is also not supported.
+         * So we do not need to check if-features statements of this context and
+         * we can return false immediately.
+         */
+        if (!isParentSupported) {
+            supportedByFeatures = OptionalBoolean.of(false);
+            return false;
+        }
+
+        /*
+         * If parent is supported, we need to check if-features statements of
+         * this context.
+         */
+        // If the set of supported features has not been provided, all features are supported by default.
+        final Set<QName> supportedFeatures = getFromNamespace(SupportedFeaturesNamespace.class,
+                SupportedFeatures.SUPPORTED_FEATURES);
+        final boolean ret = supportedFeatures == null || StmtContextUtils.checkFeatureSupport(this, supportedFeatures);
+        supportedByFeatures = OptionalBoolean.of(ret);
+        return ret;
+    }
+
+    protected abstract boolean isParentSupportedByFeatures();
+
+    protected abstract boolean isIgnoringIfFeatures();
+
+    protected abstract boolean isIgnoringConfig();
+
+    @Override
+    public boolean isSupportedToBuildEffective() {
+        return isSupportedToBuildEffective;
+    }
+
+    @Override
+    public void setIsSupportedToBuildEffective(final boolean isSupportedToBuildEffective) {
+        this.isSupportedToBuildEffective = isSupportedToBuildEffective;
+    }
+
+    @Override
+    public CopyHistory getCopyHistory() {
+        return copyHistory;
+    }
+
+    @Override
+    public Optional<StmtContext<?, ?, ?>> getOriginalCtx() {
+        return Optional.ofNullable(originalCtx);
+    }
+
+    @Override
+    public Optional<? extends StmtContext<?, ?, ?>> getPreviousCopyCtx() {
+        return Optional.ofNullable(prevCopyCtx);
+    }
+
+    @Override
+    public ModelProcessingPhase getCompletedPhase() {
+        return completedPhase;
+    }
+
+    @Override
+    public void setCompletedPhase(final ModelProcessingPhase completedPhase) {
+        this.completedPhase = completedPhase;
+    }
+
+    @Override
+    public abstract StatementContextBase<?, ?, ?> getParentContext();
+
+    /**
+     * Returns the model root for this statement.
+     *
+     * @return root context of statement
+     */
+    @Override
+    public abstract RootStatementContext<?, ?, ?> getRoot();
+
+    /**
+     * Returns the origin of the statement.
+     *
+     * @return origin of statement
+     */
+    @Override
+    public StatementSource getStatementSource() {
+        return statementDeclSource.getStatementSource();
+    }
+
+    /**
+     * Returns a reference to statement source.
+     *
+     * @return reference of statement source
+     */
+    @Override
+    public StatementSourceReference getStatementSourceReference() {
+        return statementDeclSource;
+    }
+
+    @Override
+    public final String rawStatementArgument() {
+        return rawArgument;
+    }
+
+    @Override
+    public Collection<? extends StmtContext<?, ?, ?>> declaredSubstatements() {
+        return substatements.values();
+    }
+
+    @Override
+    public Collection<? extends Mutable<?, ?, ?>> mutableDeclaredSubstatements() {
+        return substatements.values();
+    }
+
+    @Override
+    public Collection<? extends StmtContext<?, ?, ?>> effectiveSubstatements() {
+        return mutableEffectiveSubstatements();
+    }
+
+    @Override
+    public Collection<? extends Mutable<?, ?, ?>> mutableEffectiveSubstatements() {
+        if (effective instanceof ImmutableCollection) {
+            return effective;
+        }
+
+        return Collections.unmodifiableCollection(effective);
+    }
+
+    public void removeStatementsFromEffectiveSubstatements(
+            final Collection<? extends StmtContext<?, ?, ?>> statements) {
+        if (!effective.isEmpty()) {
+            effective.removeAll(statements);
+            shrinkEffective();
+        }
+    }
+
+    private void shrinkEffective() {
+        if (effective.isEmpty()) {
+            effective = ImmutableList.of();
+        }
+    }
+
+    public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef) {
+        if (effective.isEmpty()) {
+            return;
+        }
+
+        final Iterator<? extends StmtContext<?, ?, ?>> iterator = effective.iterator();
+        while (iterator.hasNext()) {
+            final StmtContext<?, ?, ?> next = iterator.next();
+            if (statementDef.equals(next.getPublicDefinition())) {
+                iterator.remove();
+            }
+        }
+
+        shrinkEffective();
+    }
+
+    /**
+     * Removes a statement context from the effective substatements based on its statement definition (i.e statement
+     * keyword) and raw (in String form) statement argument. The statement context is removed only if both statement
+     * definition and statement argument match with one of the effective substatements' statement definition
+     * and argument.
+     *
+     * <p>
+     * If the statementArg parameter is null, the statement context is removed based only on its statement definition.
+     *
+     * @param statementDef statement definition of the statement context to remove
+     * @param statementArg statement argument of the statement context to remove
+     */
+    public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef,
+            final String statementArg) {
+        if (statementArg == null) {
+            removeStatementFromEffectiveSubstatements(statementDef);
+        }
+
+        if (effective.isEmpty()) {
+            return;
+        }
+
+        final Iterator<Mutable<?, ?, ?>> iterator = effective.iterator();
+        while (iterator.hasNext()) {
+            final Mutable<?, ?, ?> next = iterator.next();
+            if (statementDef.equals(next.getPublicDefinition()) && statementArg.equals(next.rawStatementArgument())) {
+                iterator.remove();
+            }
+        }
+
+        shrinkEffective();
+    }
+
+    /**
+     * Adds an effective statement to collection of substatements.
+     *
+     * @param substatement substatement
+     * @throws IllegalStateException
+     *             if added in declared phase
+     * @throws NullPointerException
+     *             if statement parameter is null
+     */
+    public void addEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
+        beforeAddEffectiveStatement(1);
+        effective.add(substatement);
+    }
+
+    /**
+     * Adds an effective statement to collection of substatements.
+     *
+     * @param statements substatements
+     * @throws IllegalStateException
+     *             if added in declared phase
+     * @throws NullPointerException
+     *             if statement parameter is null
+     */
+    public void addEffectiveSubstatements(final Collection<? extends Mutable<?, ?, ?>> statements) {
+        if (statements.isEmpty()) {
+            return;
+        }
+
+        statements.forEach(Objects::requireNonNull);
+        beforeAddEffectiveStatement(statements.size());
+        effective.addAll(statements);
+    }
+
+    private void beforeAddEffectiveStatement(final int toAdd) {
+        final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
+        checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION
+                || inProgressPhase == ModelProcessingPhase.EFFECTIVE_MODEL,
+                "Effective statement cannot be added in declared phase at: %s", getStatementSourceReference());
+
+        if (effective.isEmpty()) {
+            effective = new ArrayList<>(toAdd);
+        }
+    }
+
+    /**
+     * Create a new substatement at the specified offset.
+     *
+     * @param offset Substatement offset
+     * @param def definition context
+     * @param ref source reference
+     * @param argument statement argument
+     * @param <X> new substatement argument type
+     * @param <Y> new substatement declared type
+     * @param <Z> new substatement effective type
+     * @return A new substatement
+     */
+    @SuppressWarnings("checkstyle:methodTypeParameterName")
+    public final <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>>
+            StatementContextBase<X, Y, Z> createSubstatement(final int offset,
+                    final StatementDefinitionContext<X, Y, Z> def, final StatementSourceReference ref,
+                    final String argument) {
+        final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
+        checkState(inProgressPhase != ModelProcessingPhase.EFFECTIVE_MODEL,
+                "Declared statement cannot be added in effective phase at: %s", getStatementSourceReference());
+
+        final Optional<StatementSupport<?, ?, ?>> implicitParent = definition.getImplicitParentFor(def.getPublicView());
+        if (implicitParent.isPresent()) {
+            return createImplicitParent(offset, implicitParent.get(), ref, argument).createSubstatement(offset, def,
+                    ref, argument);
+        }
+
+        final StatementContextBase<X, Y, Z> ret = new SubstatementContext<>(this, def, ref, argument);
+        substatements = substatements.put(offset, ret);
+        def.onStatementAdded(ret);
+        return ret;
+    }
+
+    private StatementContextBase<?, ?, ?> createImplicitParent(final int offset,
+            final StatementSupport<?, ?, ?> implicitParent, final StatementSourceReference ref, final String argument) {
+        final StatementDefinitionContext<?, ?, ?> def = new StatementDefinitionContext<>(implicitParent);
+        return createSubstatement(offset, def, ImplicitSubstatement.of(ref), argument);
+    }
+
+    public void appendImplicitStatement(final StatementSupport<?, ?, ?> statementToAdd) {
+        createSubstatement(substatements.capacity(), new StatementDefinitionContext<>(statementToAdd),
+                ImplicitSubstatement.of(getStatementSourceReference()), null);
+    }
+
+    /**
+     * Lookup substatement by its offset in this statement.
+     *
+     * @param offset Substatement offset
+     * @return Substatement, or null if substatement does not exist.
+     */
+    final StatementContextBase<?, ?, ?> lookupSubstatement(final int offset) {
+        return substatements.get(offset);
+    }
+
+    final void setFullyDefined() {
+        this.fullyDefined = true;
+    }
+
+    final void resizeSubstatements(final int expectedSize) {
+        substatements = substatements.ensureCapacity(expectedSize);
+    }
+
+    final void walkChildren(final ModelProcessingPhase phase) {
+        checkState(fullyDefined);
+        substatements.values().forEach(stmt -> {
+            stmt.walkChildren(phase);
+            stmt.endDeclared(phase);
+        });
+    }
+
+    @Override
+    public D buildDeclared() {
+        checkArgument(completedPhase == ModelProcessingPhase.FULL_DECLARATION
+                || completedPhase == ModelProcessingPhase.EFFECTIVE_MODEL);
+        if (declaredInstance == null) {
+            declaredInstance = definition().getFactory().createDeclared(this);
+        }
+        return declaredInstance;
+    }
+
+    @Override
+    public E buildEffective() {
+        if (effectiveInstance == null) {
+            effectiveInstance = definition().getFactory().createEffective(this);
+        }
+        return effectiveInstance;
+    }
+
+    /**
+     * tries to execute current {@link ModelProcessingPhase} of source parsing.
+     *
+     * @param phase
+     *            to be executed (completed)
+     * @return if phase was successfully completed
+     * @throws SourceException
+     *             when an error occurred in source parsing
+     */
+    boolean tryToCompletePhase(final ModelProcessingPhase phase) {
+
+        boolean finished = true;
+        final Collection<ContextMutation> openMutations = phaseMutation.get(phase);
+        if (!openMutations.isEmpty()) {
+            final Iterator<ContextMutation> it = openMutations.iterator();
+            while (it.hasNext()) {
+                final ContextMutation current = it.next();
+                if (current.isFinished()) {
+                    it.remove();
+                } else {
+                    finished = false;
+                }
+            }
+
+            if (openMutations.isEmpty()) {
+                phaseMutation.removeAll(phase);
+                if (phaseMutation.isEmpty()) {
+                    phaseMutation = ImmutableMultimap.of();
+                }
+            }
+        }
+
+        for (final StatementContextBase<?, ?, ?> child : substatements.values()) {
+            finished &= child.tryToCompletePhase(phase);
+        }
+        for (final Mutable<?, ?, ?> child : effective) {
+            if (child instanceof StatementContextBase) {
+                finished &= ((StatementContextBase<?, ?, ?>) child).tryToCompletePhase(phase);
+            }
+        }
+
+        if (finished) {
+            onPhaseCompleted(phase);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Occurs on end of {@link ModelProcessingPhase} of source parsing.
+     *
+     * @param phase
+     *            that was to be completed (finished)
+     * @throws SourceException
+     *             when an error occurred in source parsing
+     */
+    private void onPhaseCompleted(final ModelProcessingPhase phase) {
+        completedPhase = phase;
+
+        final Collection<OnPhaseFinished> listeners = phaseListeners.get(phase);
+        if (listeners.isEmpty()) {
+            return;
+        }
+
+        final Iterator<OnPhaseFinished> listener = listeners.iterator();
+        while (listener.hasNext()) {
+            final OnPhaseFinished next = listener.next();
+            if (next.phaseFinished(this, phase)) {
+                listener.remove();
+            }
+        }
+
+        if (listeners.isEmpty()) {
+            phaseListeners.removeAll(phase);
+            if (phaseListeners.isEmpty()) {
+                phaseListeners = ImmutableMultimap.of();
+            }
+        }
+    }
+
+    /**
+     * Ends declared section of current node.
+     */
+    void endDeclared(final ModelProcessingPhase phase) {
+        definition().onDeclarationFinished(this, phase);
+    }
+
+    /**
+     * Return the context in which this statement was defined.
+     *
+     * @return statement definition
+     */
+    protected final @NonNull StatementDefinitionContext<A, D, E> definition() {
+        return definition;
+    }
+
+    @Override
+    protected void checkLocalNamespaceAllowed(final Class<? extends IdentifierNamespace<?, ?>> type) {
+        definition().checkNamespaceAllowed(type);
+    }
+
+    @Override
+    protected <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceElementAdded(final Class<N> type, final K key,
+            final V value) {
+        // definition().onNamespaceElementAdded(this, type, key, value);
+    }
+
+    final <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceItemAddedAction(final Class<N> type, final K key,
+            final OnNamespaceItemAdded listener) {
+        final Object potential = getFromNamespace(type, key);
+        if (potential != null) {
+            LOG.trace("Listener on {} key {} satisfied immediately", type, key);
+            listener.namespaceItemAdded(this, type, key, potential);
+            return;
+        }
+
+        getBehaviour(type).addListener(new KeyedValueAddedListener<>(this, key) {
+            @Override
+            void onValueAdded(final Object value) {
+                listener.namespaceItemAdded(StatementContextBase.this, type, key, value);
+            }
+        });
+    }
+
+    final <K, V, N extends IdentifierNamespace<K, V>> void onNamespaceItemAddedAction(final Class<N> type,
+            final ModelProcessingPhase phase, final NamespaceKeyCriterion<K> criterion,
+            final OnNamespaceItemAdded listener) {
+        final Optional<Entry<K, V>> existing = getFromNamespace(type, criterion);
+        if (existing.isPresent()) {
+            final Entry<K, V> entry = existing.get();
+            LOG.debug("Listener on {} criterion {} found a pre-existing match: {}", type, criterion, entry);
+            waitForPhase(entry.getValue(), type, phase, criterion, listener);
+            return;
+        }
+
+        final NamespaceBehaviourWithListeners<K, V, N> behaviour = getBehaviour(type);
+        behaviour.addListener(new PredicateValueAddedListener<K, V>(this) {
+            @Override
+            boolean onValueAdded(final K key, final V value) {
+                if (criterion.match(key)) {
+                    LOG.debug("Listener on {} criterion {} matched added key {}", type, criterion, key);
+                    waitForPhase(value, type, phase, criterion, listener);
+                    return true;
+                }
+
+                return false;
+            }
+        });
+    }
+
+    final <K, V, N extends IdentifierNamespace<K, V>> void selectMatch(final Class<N> type,
+            final NamespaceKeyCriterion<K> criterion, final OnNamespaceItemAdded listener) {
+        final Optional<Entry<K, V>> optMatch = getFromNamespace(type, criterion);
+        checkState(optMatch.isPresent(), "Failed to find a match for criterion %s in namespace %s node %s", criterion,
+            type, this);
+        final Entry<K, V> match = optMatch.get();
+        listener.namespaceItemAdded(StatementContextBase.this, type, match.getKey(), match.getValue());
+    }
+
+    final <K, V, N extends IdentifierNamespace<K, V>> void waitForPhase(final Object value, final Class<N> type,
+            final ModelProcessingPhase phase, final NamespaceKeyCriterion<K> criterion,
+            final OnNamespaceItemAdded listener) {
+        ((StatementContextBase<?, ? ,?>) value).addPhaseCompletedListener(phase,
+            (context, phaseCompleted) -> {
+                selectMatch(type, criterion, listener);
+                return true;
+            });
+    }
+
+    private <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviourWithListeners<K, V, N> getBehaviour(
+            final Class<N> type) {
+        final NamespaceBehaviour<K, V, N> behaviour = getBehaviourRegistry().getNamespaceBehaviour(type);
+        checkArgument(behaviour instanceof NamespaceBehaviourWithListeners, "Namespace %s does not support listeners",
+            type);
+
+        return (NamespaceBehaviourWithListeners<K, V, N>) behaviour;
+    }
+
+    /**
+     * See {@link StatementSupport#getPublicView()}.
+     */
+    @Override
+    public StatementDefinition getPublicDefinition() {
+        return definition().getPublicView();
+    }
+
+    @Override
+    public ModelActionBuilder newInferenceAction(final ModelProcessingPhase phase) {
+        return getRoot().getSourceContext().newInferenceAction(phase);
+    }
+
+    private static <T> Multimap<ModelProcessingPhase, T> newMultimap() {
+        return Multimaps.newListMultimap(new EnumMap<>(ModelProcessingPhase.class), () -> new ArrayList<>(1));
+    }
+
+    /**
+     * Adds {@link OnPhaseFinished} listener for a {@link ModelProcessingPhase} end. If the base has already completed
+     * the listener is notified immediately.
+     *
+     * @param phase requested completion phase
+     * @param listener listener to invoke
+     * @throws NullPointerException if any of the arguments is null
+     */
+    void addPhaseCompletedListener(final ModelProcessingPhase phase, final OnPhaseFinished listener) {
+        checkNotNull(phase, "Statement context processing phase cannot be null at: %s", getStatementSourceReference());
+        checkNotNull(listener, "Statement context phase listener cannot be null at: %s", getStatementSourceReference());
+
+        ModelProcessingPhase finishedPhase = completedPhase;
+        while (finishedPhase != null) {
+            if (phase.equals(finishedPhase)) {
+                listener.phaseFinished(this, finishedPhase);
+                return;
+            }
+            finishedPhase = finishedPhase.getPreviousPhase();
+        }
+        if (phaseListeners.isEmpty()) {
+            phaseListeners = newMultimap();
+        }
+
+        phaseListeners.put(phase, listener);
+    }
+
+    /**
+     * Adds a {@link ContextMutation} to a {@link ModelProcessingPhase}.
+     *
+     * @throws IllegalStateException
+     *             when the mutation was registered after phase was completed
+     */
+    void addMutation(final ModelProcessingPhase phase, final ContextMutation mutation) {
+        ModelProcessingPhase finishedPhase = completedPhase;
+        while (finishedPhase != null) {
+            checkState(!phase.equals(finishedPhase), "Mutation registered after phase was completed at: %s",
+                getStatementSourceReference());
+            finishedPhase = finishedPhase.getPreviousPhase();
+        }
+
+        if (phaseMutation.isEmpty()) {
+            phaseMutation = newMultimap();
+        }
+        phaseMutation.put(phase, mutation);
+    }
+
+    @Override
+    public <K, KT extends K, N extends StatementNamespace<K, ?, ?>> void addContext(final Class<N> namespace,
+            final KT key,final StmtContext<?, ?, ?> stmt) {
+        addContextToNamespace(namespace, key, stmt);
+    }
+
+    @Override
+    public <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>> Mutable<X, Y, Z> childCopyOf(
+            final StmtContext<X, Y, Z> stmt, final CopyType type, final QNameModule targetModule) {
+        checkState(stmt.getCompletedPhase() == ModelProcessingPhase.EFFECTIVE_MODEL,
+                "Attempted to copy statement %s which has completed phase %s", stmt, stmt.getCompletedPhase());
+
+        checkArgument(stmt instanceof SubstatementContext, "Unsupported statement %s", stmt);
+
+        final SubstatementContext<X, Y, Z> original = (SubstatementContext<X, Y, Z>)stmt;
+        final Optional<StatementSupport<?, ?, ?>> implicitParent = definition.getImplicitParentFor(
+            original.getPublicDefinition());
+
+        final SubstatementContext<X, Y, Z> result;
+        final SubstatementContext<X, Y, Z> copy;
+
+        if (implicitParent.isPresent()) {
+            final StatementDefinitionContext<?, ?, ?> def = new StatementDefinitionContext<>(implicitParent.get());
+            result = new SubstatementContext(this, def, original.getSourceReference(),
+                original.rawStatementArgument(), original.getStatementArgument(), type);
+
+            final CopyType childCopyType;
+            switch (type) {
+                case ADDED_BY_AUGMENTATION:
+                    childCopyType = CopyType.ORIGINAL;
+                    break;
+                case ADDED_BY_USES_AUGMENTATION:
+                    childCopyType = CopyType.ADDED_BY_USES;
+                    break;
+                case ADDED_BY_USES:
+                case ORIGINAL:
+                default:
+                    childCopyType = type;
+            }
+
+            copy = new SubstatementContext<>(original, result, childCopyType, targetModule);
+            result.addEffectiveSubstatement(copy);
+            original.definition().onStatementAdded(copy);
+        } else {
+            result = copy = new SubstatementContext<>(original, this, type, targetModule);
+            original.definition().onStatementAdded(copy);
+        }
+
+        original.copyTo(copy, type, targetModule);
+        return result;
+    }
+
+    @Override
+    public @NonNull StatementDefinition getDefinition() {
+        return getPublicDefinition();
+    }
+
+    @Override
+    public @NonNull StatementSourceReference getSourceReference() {
+        return getStatementSourceReference();
+    }
+
+    @Override
+    public boolean isFullyDefined() {
+        return fullyDefined;
+    }
+
+    @Beta
+    public final boolean hasImplicitParentSupport() {
+        return definition.getFactory() instanceof ImplicitParentAwareStatementSupport;
+    }
+
+    @Beta
+    public final StatementContextBase<?, ?, ?> wrapWithImplicit(final StatementContextBase<?, ?, ?> original) {
+        final Optional<StatementSupport<?, ?, ?>> optImplicit = definition.getImplicitParentFor(
+            original.getPublicDefinition());
+        if (optImplicit.isEmpty()) {
+            return original;
+        }
+
+        final StatementDefinitionContext<?, ?, ?> def = new StatementDefinitionContext<>(optImplicit.get());
+        final CopyType type = original.getCopyHistory().getLastOperation();
+        final SubstatementContext<?, ?, ?> result = new SubstatementContext(original.getParentContext(), def,
+            original.getStatementSourceReference(), original.rawStatementArgument(), original.getStatementArgument(),
+            type);
+
+        result.addEffectiveSubstatement(new SubstatementContext<>(original, result));
+        result.setCompletedPhase(original.getCompletedPhase());
+        return result;
+    }
+
+    final void copyTo(final StatementContextBase<?, ?, ?> target, final CopyType typeOfCopy,
+            @Nullable final QNameModule targetModule) {
+        final Collection<Mutable<?, ?, ?>> buffer = new ArrayList<>(substatements.size() + effective.size());
+
+        for (final Mutable<?, ?, ?> stmtContext : substatements.values()) {
+            if (stmtContext.isSupportedByFeatures()) {
+                copySubstatement(stmtContext, target, typeOfCopy, targetModule, buffer);
+            }
+        }
+
+        for (final Mutable<?, ?, ?> stmtContext : effective) {
+            copySubstatement(stmtContext, target, typeOfCopy, targetModule, buffer);
+        }
+
+        target.addEffectiveSubstatements(buffer);
+    }
+
+    private void copySubstatement(final Mutable<?, ?, ?> stmtContext,  final Mutable<?, ?, ?> target,
+            final CopyType typeOfCopy, final QNameModule newQNameModule, final Collection<Mutable<?, ?, ?>> buffer) {
+        if (needToCopyByUses(stmtContext)) {
+            final Mutable<?, ?, ?> copy = target.childCopyOf(stmtContext, typeOfCopy, newQNameModule);
+            LOG.debug("Copying substatement {} for {} as {}", stmtContext, this, copy);
+            buffer.add(copy);
+        } else if (isReusedByUses(stmtContext)) {
+            LOG.debug("Reusing substatement {} for {}", stmtContext, this);
+            buffer.add(stmtContext);
+        } else {
+            LOG.debug("Skipping statement {}", stmtContext);
+        }
+    }
+
+    // FIXME: revise this, as it seems to be wrong
+    private static final ImmutableSet<YangStmtMapping> NOCOPY_FROM_GROUPING_SET = ImmutableSet.of(
+        YangStmtMapping.DESCRIPTION,
+        YangStmtMapping.REFERENCE,
+        YangStmtMapping.STATUS);
+    private static final ImmutableSet<YangStmtMapping> REUSED_DEF_SET = ImmutableSet.of(
+        YangStmtMapping.TYPE,
+        YangStmtMapping.TYPEDEF,
+        YangStmtMapping.USES);
+
+    private static boolean needToCopyByUses(final StmtContext<?, ?, ?> stmtContext) {
+        final StatementDefinition def = stmtContext.getPublicDefinition();
+        if (REUSED_DEF_SET.contains(def)) {
+            LOG.debug("Will reuse {} statement {}", def, stmtContext);
+            return false;
+        }
+        if (NOCOPY_FROM_GROUPING_SET.contains(def)) {
+            return !YangStmtMapping.GROUPING.equals(stmtContext.coerceParentContext().getPublicDefinition());
+        }
+
+        LOG.debug("Will copy {} statement {}", def, stmtContext);
+        return true;
+    }
+
+    private static boolean isReusedByUses(final StmtContext<?, ?, ?> stmtContext) {
+        return REUSED_DEF_SET.contains(stmtContext.getPublicDefinition());
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("definition", definition).add("rawArgument", rawArgument);
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextWriter.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextWriter.java
new file mode 100644 (file)
index 0000000..34e27ea
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
+
+final class StatementContextWriter implements StatementWriter {
+    private final ModelProcessingPhase phase;
+    private final SourceSpecificContext ctx;
+
+    private StatementContextBase<?, ?, ?> current;
+
+    StatementContextWriter(final SourceSpecificContext ctx, final ModelProcessingPhase phase) {
+        this.ctx = requireNonNull(ctx);
+        this.phase = requireNonNull(phase);
+    }
+
+    @Override
+    public Optional<? extends ResumedStatement> resumeStatement(final int childId) {
+        final Optional<StatementContextBase<?, ?, ?>> existing = ctx.lookupDeclaredChild(current, childId);
+        existing.ifPresent(this::resumeStatement);
+        return existing;
+    }
+
+    private void resumeStatement(final StatementContextBase<?, ?, ?> child) {
+        if (child.isFullyDefined()) {
+            child.walkChildren(phase);
+            child.endDeclared(phase);
+        } else {
+            current = child;
+        }
+    }
+
+    @Override
+    public void storeStatement(final int expectedChildren, final boolean fullyDefined) {
+        checkState(current != null);
+        checkArgument(expectedChildren >= 0);
+        current.resizeSubstatements(expectedChildren);
+
+        if (fullyDefined) {
+            current.setFullyDefined();
+        }
+    }
+
+    @Override
+    public void startStatement(final int childId, final QName name, final String argument,
+            final StatementSourceReference ref) {
+        final Optional<StatementContextBase<?, ?, ?>> existing = ctx.lookupDeclaredChild(current, childId);
+        current = existing.isPresent() ? existing.get()
+                :  verifyNotNull(ctx.createDeclaredChild(current, childId, name, argument, ref));
+    }
+
+    @Override
+    public void endStatement(final StatementSourceReference ref) {
+        checkState(current != null);
+        current.endDeclared(phase);
+        exitStatement();
+    }
+
+    @Override
+    public ModelProcessingPhase getPhase() {
+        return phase;
+    }
+
+    private void exitStatement() {
+        StatementContextBase<?, ?, ?> parentContext = current.getParentContext();
+        while (parentContext != null && StatementSource.CONTEXT == parentContext.getStatementSource()) {
+            parentContext.endDeclared(phase);
+            parentContext = parentContext.getParentContext();
+        }
+        current = parentContext;
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementDefinitionContext.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementDefinitionContext.java
new file mode 100644 (file)
index 0000000..570da62
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ImplicitParentAwareStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementFactory;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+
+public class StatementDefinitionContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>> {
+    private final @NonNull StatementSupport<A, D, E> support;
+    private final Map<String, StatementDefinitionContext<?, ?, ?>> argumentSpecificSubDefinitions;
+    private Map<StatementDefinitionContext<?, ?, ?>, StatementDefinitionContext<?,?,?>> unknownStmtDefsOfYangStmts;
+
+    public StatementDefinitionContext(final StatementSupport<A, D, E> support) {
+        this.support = requireNonNull(support);
+        this.argumentSpecificSubDefinitions = support.hasArgumentSpecificSupports() ? new HashMap<>() : null;
+    }
+
+    public @NonNull StatementFactory<A, D, E> getFactory() {
+        return support;
+    }
+
+    public A parseArgumentValue(final StmtContext<A, D, E> context, final String value) {
+        return support.parseArgumentValue(context, value);
+    }
+
+    public A adaptArgumentValue(final StmtContext<A, D, E> context, final QNameModule targetModule) {
+        return support.adaptArgumentValue(context, targetModule);
+    }
+
+    public void checkNamespaceAllowed(final Class<? extends IdentifierNamespace<?,?>> namespace) {
+        // Noop
+    }
+
+    public @NonNull StatementDefinition getPublicView() {
+        return support.getPublicView();
+    }
+
+    public Optional<StatementSupport<?, ?, ?>> getImplicitParentFor(final StatementDefinition stmtDef) {
+        return support instanceof ImplicitParentAwareStatementSupport
+                ? ((ImplicitParentAwareStatementSupport) support).getImplicitParentFor(stmtDef) : Optional.empty();
+    }
+
+    public boolean onStatementAdded(final Mutable<A, D, E> stmt) {
+        support.onStatementAdded(stmt);
+        return false;
+    }
+
+    public void onDeclarationFinished(final Mutable<A, D, E> statement, final ModelProcessingPhase phase) {
+        switch (phase) {
+            case SOURCE_PRE_LINKAGE:
+                support.onPreLinkageDeclared(statement);
+                break;
+            case SOURCE_LINKAGE:
+                support.onLinkageDeclared(statement);
+                break;
+            case STATEMENT_DEFINITION:
+                support.onStatementDefinitionDeclared(statement);
+                break;
+            case FULL_DECLARATION:
+                support.onFullDefinitionDeclared(statement);
+                break;
+            default:
+                break;
+        }
+    }
+
+    public @NonNull Class<?> getRepresentingClass() {
+        return support.getDeclaredRepresentationClass();
+    }
+
+    public @NonNull Optional<ArgumentDefinition> getArgumentDefinition() {
+        return support.getArgumentDefinition();
+    }
+
+    public @NonNull QName getStatementName() {
+        return support.getStatementName();
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("statement", getStatementName());
+    }
+
+    @NonNull StatementDefinitionContext<?, ?, ?> getSubDefinitionSpecificForArgument(final String argument) {
+        if (!hasArgumentSpecificSubDefinitions()) {
+            return this;
+        }
+
+        StatementDefinitionContext<?, ?, ?> potential = argumentSpecificSubDefinitions.get(argument);
+        if (potential == null) {
+            final StatementSupport<?, ?, ?> argumentSpecificSupport = support.getSupportSpecificForArgument(argument);
+            potential = argumentSpecificSupport != null ? new StatementDefinitionContext<>(argumentSpecificSupport)
+                    : this;
+            argumentSpecificSubDefinitions.put(argument, potential);
+        }
+
+        return potential;
+    }
+
+    boolean hasArgumentSpecificSubDefinitions() {
+        return support.hasArgumentSpecificSupports();
+    }
+
+    String internArgument(final String rawArgument) {
+        return support.internArgument(rawArgument);
+    }
+
+    StatementDefinitionContext<?, ?, ?> getAsUnknownStatementDefinition(
+            final StatementDefinitionContext<?, ?, ?> yangStmtDef) {
+        if (unknownStmtDefsOfYangStmts != null) {
+            final StatementDefinitionContext<?, ?, ?> existing = unknownStmtDefsOfYangStmts.get(yangStmtDef);
+            if (existing != null) {
+                return existing;
+            }
+        } else {
+            unknownStmtDefsOfYangStmts = new HashMap<>();
+        }
+
+        @SuppressWarnings("unchecked")
+        final StatementDefinitionContext<?, ?, ?> ret = support.getUnknownStatementDefinitionOf(
+            yangStmtDef.getPublicView()).map(StatementDefinitionContext::new).orElse(null);
+        if (ret != null) {
+            unknownStmtDefsOfYangStmts.put(yangStmtDef, ret);
+        }
+        return ret;
+    }
+
+    boolean isIgnoringIfFeatures() {
+        return support.isIgnoringIfFeatures();
+    }
+
+    boolean isIgnoringConfig() {
+        return support.isIgnoringConfig();
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementMap.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementMap.java
new file mode 100644 (file)
index 0000000..a901cdc
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2016 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.stmt.reactor;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.ImmutableList;
+import java.util.AbstractCollection;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.function.Consumer;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Simple integer-to-StatementContextBase map optimized for size and restricted in scope of operations. It does not
+ * implement {@link java.util.Map} for simplicity's sake.
+ *
+ * @author Robert Varga
+ */
+abstract class StatementMap {
+    private static final class Empty extends StatementMap {
+        @Override
+        StatementContextBase<?, ?, ?> get(final int index) {
+            return null;
+        }
+
+        @Override
+        StatementMap put(final int index, final StatementContextBase<?, ?, ?> obj) {
+            return index == 0 ? new Singleton(obj) : new Regular(index, obj);
+        }
+
+        @Override
+        Collection<StatementContextBase<?, ?, ?>> values() {
+            return ImmutableList.of();
+        }
+
+        @Override
+        int size() {
+            return 0;
+        }
+
+        @Override
+        StatementMap ensureCapacity(final int expectedLimit) {
+            return expectedLimit < 2 ? this : new Regular(expectedLimit);
+        }
+
+        @Override
+        int capacity() {
+            return 0;
+        }
+    }
+
+    private static final class Regular extends StatementMap {
+        private StatementContextBase<?, ?, ?>[] elements;
+        private int size;
+
+        Regular(final int expectedLimit) {
+            elements = new StatementContextBase<?, ?, ?>[expectedLimit];
+        }
+
+        Regular(final int index, final StatementContextBase<?, ?, ?> object) {
+            this(index + 1, index, object);
+        }
+
+        Regular(final StatementContextBase<?, ?, ?> object0, final int index,
+                final StatementContextBase<?, ?, ?> object) {
+            this(index + 1, 0, object0);
+            elements[index] = requireNonNull(object);
+            size = 2;
+        }
+
+        Regular(final int expectedLimit, final int index, final StatementContextBase<?, ?, ?> object) {
+            this(expectedLimit);
+            elements[index] = requireNonNull(object);
+            size = 1;
+        }
+
+        @Override
+        StatementContextBase<?, ?, ?> get(final int index) {
+            return index >= elements.length ? null : elements[index];
+        }
+
+        @Override
+        StatementMap put(final int index, final StatementContextBase<?, ?, ?> obj) {
+            if (index < elements.length) {
+                checkArgument(elements[index] == null);
+            } else {
+                // FIXME: detect linear growth
+                elements = Arrays.copyOf(elements, index + 1);
+            }
+
+            elements[index] = requireNonNull(obj);
+            size++;
+            return this;
+        }
+
+        @Override
+        Collection<StatementContextBase<?, ?, ?>> values() {
+            return new RegularAsCollection<>(elements, size);
+        }
+
+        @Override
+        int size() {
+            return size;
+        }
+
+        @Override
+        StatementMap ensureCapacity(final int expectedLimit) {
+            if (elements.length < expectedLimit) {
+                elements = Arrays.copyOf(elements, expectedLimit);
+            }
+            return this;
+        }
+
+        @Override
+        int capacity() {
+            return elements.length;
+        }
+    }
+
+    private static final class RegularAsCollection<T> extends AbstractCollection<T> {
+        private final T[] elements;
+        private final int size;
+
+        RegularAsCollection(final T[] elements, final int size) {
+            this.elements = requireNonNull(elements);
+            this.size = size;
+        }
+
+        @Override
+        public void forEach(final Consumer<? super T> action) {
+            for (T e : elements) {
+                if (e != null) {
+                    action.accept(e);
+                }
+            }
+        }
+
+        @Override
+        public Iterator<T> iterator() {
+            return new AbstractIterator<T>() {
+                private int nextOffset = 0;
+
+                @Override
+                protected T computeNext() {
+                    while (nextOffset < elements.length) {
+                        final T ret = elements[nextOffset++];
+                        if (ret != null) {
+                            return ret;
+                        }
+                    }
+
+                    return endOfData();
+                }
+            };
+        }
+
+        @Override
+        public int size() {
+            return size;
+        }
+    }
+
+    private static final class Singleton extends StatementMap {
+        private final StatementContextBase<?, ?, ?> object;
+
+        Singleton(final StatementContextBase<?, ?, ?> object) {
+            this.object = requireNonNull(object);
+        }
+
+        @Override
+        StatementContextBase<?, ?, ?> get(final int index) {
+            return index == 0 ? object : null;
+        }
+
+        @Override
+        StatementMap put(final int index, final StatementContextBase<?, ?, ?> obj) {
+            checkArgument(index != 0);
+            return new Regular(this.object, index, obj);
+        }
+
+        @Override
+        Collection<StatementContextBase<?, ?, ?>> values() {
+            return ImmutableList.of(object);
+        }
+
+        @Override
+        int size() {
+            return 1;
+        }
+
+        @Override
+        StatementMap ensureCapacity(final int expectedLimit) {
+            return expectedLimit < 2 ? this : new Regular(expectedLimit, 0, object);
+        }
+
+        @Override
+        int capacity() {
+            return 1;
+        }
+    }
+
+    private static final StatementMap EMPTY = new Empty();
+
+    static StatementMap empty() {
+        return EMPTY;
+    }
+
+    /**
+     * Return the statement context at specified index.
+     *
+     * @param index Element index, must be non-negative
+     * @return Requested element or null if there is no element at that index
+     */
+    abstract @Nullable StatementContextBase<?, ?, ?> get(int index);
+
+    /**
+     * Add a statement at specified index.
+     *
+     * @param index Element index, must be non-negative
+     * @param obj Object to store
+     * @return New statement map
+     * @throws IllegalArgumentException if the index is already occupied
+     */
+    abstract @NonNull StatementMap put(int index, @NonNull StatementContextBase<?, ?, ?> obj);
+
+    /**
+     * Return a read-only view of the elements in this map. Unlike other maps, this view does not detect concurrent
+     * modification. Iteration is performed in order of increasing offset. In face of concurrent modification, number
+     * of elements returned through iteration may not match the size reported via {@link Collection#size()}.
+     *
+     * @return Read-only view of available statements.
+     */
+    abstract @NonNull Collection<StatementContextBase<?, ?, ?>> values();
+
+    abstract int size();
+
+    abstract @NonNull StatementMap ensureCapacity(int expectedLimit);
+
+    abstract int capacity();
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(StatementMap.class).add("values", values()).toString();
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SubstatementContext.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SubstatementContext.java
new file mode 100644 (file)
index 0000000..e70a584
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Verify;
+import java.util.Optional;
+import org.opendaylight.yangtools.util.OptionalBoolean;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviationStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+
+final class SubstatementContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>> extends
+        StatementContextBase<A, D, E> {
+    private final StatementContextBase<?, ?, ?> parent;
+    private final A argument;
+
+    /**
+     * Config statements are not all that common which means we are performing a recursive search towards the root
+     * every time {@link #isConfiguration()} is invoked. This is quite expensive because it causes a linear search
+     * for the (usually non-existent) config statement.
+     *
+     * <p>
+     * This field maintains a resolution cache, so once we have returned a result, we will keep on returning the same
+     * result without performing any lookups.
+     */
+    // BooleanField value
+    private byte configuration;
+
+    /**
+     * This field maintains a resolution cache for ignore config, so once we have returned a result, we will
+     * keep on returning the same result without performing any lookups.
+     */
+    // BooleanField value
+    private byte ignoreConfig;
+
+    /**
+     * This field maintains a resolution cache for ignore if-feature, so once we have returned a result, we will
+     * keep on returning the same result without performing any lookups.
+     */
+    // BooleanField value
+    private byte ignoreIfFeature;
+
+    private volatile SchemaPath schemaPath;
+
+    SubstatementContext(final StatementContextBase<?, ?, ?> parent, final StatementDefinitionContext<A, D, E> def,
+            final StatementSourceReference ref, final String rawArgument) {
+        super(def, ref, rawArgument);
+        this.parent = requireNonNull(parent, "Parent must not be null");
+        this.argument = def.parseArgumentValue(this, rawStatementArgument());
+    }
+
+    SubstatementContext(final StatementContextBase<?, ?, ?> parent, final StatementDefinitionContext<A, D, E> def,
+        final StatementSourceReference ref, final String rawArgument, final A argument, final CopyType copyType) {
+        super(def, ref, rawArgument, copyType);
+        this.parent = requireNonNull(parent, "Parent must not be null");
+        this.argument = argument;
+    }
+
+    SubstatementContext(final StatementContextBase<A, D, E> original, final StatementContextBase<?, ?, ?> parent,
+            final CopyType copyType, final QNameModule targetModule) {
+        super(original, copyType);
+        this.parent = requireNonNull(parent, "Parent must not be null");
+        this.argument = targetModule == null ? original.getStatementArgument()
+                : original.definition().adaptArgumentValue(original, targetModule);
+    }
+
+    SubstatementContext(final StatementContextBase<A, D, E> original, final StatementContextBase<?, ?, ?> parent) {
+        super(original);
+        this.parent = requireNonNull(parent, "Parent must not be null");
+        this.argument = original.getStatementArgument();
+    }
+
+    @Override
+    public StatementContextBase<?, ?, ?> getParentContext() {
+        return parent;
+    }
+
+    @Override
+    public StorageNodeType getStorageNodeType() {
+        return StorageNodeType.STATEMENT_LOCAL;
+    }
+
+    @Override
+    public NamespaceStorageNode getParentNamespaceStorage() {
+        return parent;
+    }
+
+    @Override
+    public Registry getBehaviourRegistry() {
+        return parent.getBehaviourRegistry();
+    }
+
+    @Override
+    public RootStatementContext<?, ?, ?> getRoot() {
+        return parent.getRoot();
+    }
+
+    @Override
+    public A getStatementArgument() {
+        return argument;
+    }
+
+    private SchemaPath createSchemaPath() {
+        final Optional<SchemaPath> maybeParentPath = parent.getSchemaPath();
+        Verify.verify(maybeParentPath.isPresent(), "Parent %s does not have a SchemaPath", parent);
+        final SchemaPath parentPath = maybeParentPath.get();
+
+        if (StmtContextUtils.isUnknownStatement(this)) {
+            return parentPath.createChild(getPublicDefinition().getStatementName());
+        }
+        if (argument instanceof QName) {
+            final QName qname = (QName) argument;
+            if (StmtContextUtils.producesDeclared(this, UsesStatement.class)) {
+                return maybeParentPath.orElse(null);
+            }
+
+            return parentPath.createChild(qname);
+        }
+        if (argument instanceof String) {
+            // FIXME: This may yield illegal argument exceptions
+            final Optional<StmtContext<?, ?, ?>> originalCtx = getOriginalCtx();
+            final QName qname = StmtContextUtils.qnameFromArgument(originalCtx.orElse(this), (String) argument);
+            return parentPath.createChild(qname);
+        }
+        if (argument instanceof SchemaNodeIdentifier
+                && (StmtContextUtils.producesDeclared(this, AugmentStatement.class)
+                        || StmtContextUtils.producesDeclared(this, RefineStatement.class)
+                        || StmtContextUtils.producesDeclared(this, DeviationStatement.class))) {
+
+            return parentPath.createChild(((SchemaNodeIdentifier) argument).getPathFromRoot());
+        }
+
+        // FIXME: this does not look right
+        return maybeParentPath.orElse(null);
+    }
+
+    @Override
+    public Optional<SchemaPath> getSchemaPath() {
+        SchemaPath local = schemaPath;
+        if (local == null) {
+            synchronized (this) {
+                local = schemaPath;
+                if (local == null) {
+                    local = createSchemaPath();
+                    schemaPath = local;
+                }
+            }
+        }
+
+        return Optional.ofNullable(local);
+    }
+
+    @Override
+    public boolean isConfiguration() {
+        if (isIgnoringConfig()) {
+            return true;
+        }
+
+        if (OptionalBoolean.isPresent(configuration)) {
+            return OptionalBoolean.get(configuration);
+        }
+
+        final StmtContext<Boolean, ?, ?> configStatement = StmtContextUtils.findFirstSubstatement(this,
+            ConfigStatement.class);
+        final boolean parentIsConfig = parent.isConfiguration();
+
+        final boolean isConfig;
+        if (configStatement != null) {
+            isConfig = configStatement.coerceStatementArgument();
+
+            // Validity check: if parent is config=false this cannot be a config=true
+            InferenceException.throwIf(isConfig && !parentIsConfig, getStatementSourceReference(),
+                    "Parent node has config=false, this node must not be specifed as config=true");
+        } else {
+            // If "config" statement is not specified, the default is the same as the parent's "config" value.
+            isConfig = parentIsConfig;
+        }
+
+        // Resolved, make sure we cache this return
+        configuration = OptionalBoolean.of(isConfig);
+        return isConfig;
+    }
+
+    @Override
+    public boolean isEnabledSemanticVersioning() {
+        return parent.isEnabledSemanticVersioning();
+    }
+
+    @Override
+    public YangVersion getRootVersion() {
+        return getRoot().getRootVersion();
+    }
+
+    @Override
+    public void setRootVersion(final YangVersion version) {
+        getRoot().setRootVersion(version);
+    }
+
+    @Override
+    public void addMutableStmtToSeal(final MutableStatement mutableStatement) {
+        getRoot().addMutableStmtToSeal(mutableStatement);
+    }
+
+    @Override
+    public void addRequiredSource(final SourceIdentifier dependency) {
+        getRoot().addRequiredSource(dependency);
+    }
+
+    @Override
+    public void setRootIdentifier(final SourceIdentifier identifier) {
+        getRoot().setRootIdentifier(identifier);
+    }
+
+    @Override
+    protected boolean isIgnoringIfFeatures() {
+        if (OptionalBoolean.isPresent(ignoreIfFeature)) {
+            return OptionalBoolean.get(ignoreIfFeature);
+        }
+
+        final boolean ret = definition().isIgnoringIfFeatures() || parent.isIgnoringIfFeatures();
+        ignoreIfFeature = OptionalBoolean.of(ret);
+
+        return ret;
+    }
+
+    @Override
+    protected boolean isIgnoringConfig() {
+        if (OptionalBoolean.isPresent(ignoreConfig)) {
+            return OptionalBoolean.get(ignoreConfig);
+        }
+
+        final boolean ret = definition().isIgnoringConfig() || parent.isIgnoringConfig();
+        ignoreConfig = OptionalBoolean.of(ret);
+
+        return ret;
+    }
+
+    @Override
+    protected boolean isParentSupportedByFeatures() {
+        return parent.isSupportedByFeatures();
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/VirtualNamespaceContext.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/VirtualNamespaceContext.java
new file mode 100644 (file)
index 0000000..2df2fe5
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 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.stmt.reactor;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.DerivedNamespaceBehaviour;
+
+final class VirtualNamespaceContext<K, V, N extends IdentifierNamespace<K, V>, D>
+        extends NamespaceBehaviourWithListeners<K, V, N> {
+
+    private final Multimap<D, KeyedValueAddedListener<K>> listeners = HashMultimap.create();
+    private final DerivedNamespaceBehaviour<K, V, D, N, ?> derivedDelegate;
+
+    VirtualNamespaceContext(final DerivedNamespaceBehaviour<K, V, D, N, ?> delegate) {
+        super(delegate);
+        this.derivedDelegate = delegate;
+    }
+
+    @Override
+    void addListener(final KeyedValueAddedListener<K> listener) {
+        listeners.put(derivedDelegate.getSignificantKey(listener.getKey()), listener);
+    }
+
+    @Override
+    void addListener(final PredicateValueAddedListener<K, V> listener) {
+        throw new UnsupportedOperationException("Virtual namespaces support only exact lookups");
+    }
+
+    void addedToSourceNamespace(final NamespaceStorageNode storage, final D key, final V value) {
+        notifyListeners(storage, listeners.get(key).iterator(), value);
+    }
+
+    @Override
+    public void addTo(final NamespaceStorageNode storage, final K key, final V value) {
+        delegate.addTo(storage, key, value);
+        notifyListeners(storage, listeners.get(derivedDelegate.getSignificantKey(key)).iterator(), value);
+        notifyDerivedNamespaces(storage, key, value);
+    }
+}
diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/package-info.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/package-info.java
new file mode 100644 (file)
index 0000000..0636105
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 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
+ */
+
+/**
+ * Contains the implementation of the parser reactor
+ * {@link org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor}.
+ *
+ * <p>
+ * Classes provided by this package are responsible for performing the essential parsing processes and building the
+ * schema context {@link org.opendaylight.yangtools.yang.model.api.SchemaContext} as the result of the parsing process.
+ *
+ * <p>
+ * This package also contains:
+ * </p>
+ * <ul>
+ * <li> implementation of statement context which provides the information necessary for creation of declared and
+ * effective statements</li>
+ * <li> entry point to the parsing process provided by the cross source statement reactor
+ * {@link org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor}</li>
+ * </ul>
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
diff --git a/yang/yang-parser-rfc7950/pom.xml b/yang/yang-parser-rfc7950/pom.xml
new file mode 100644 (file)
index 0000000..bc09745
--- /dev/null
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-parser-rfc7950</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>RFC6020/RFC7950 parser support</description>
+
+    <properties>
+        <!-- FIXME: antlr-generated code does not pass SB -->
+        <odlparent.spotbugs.enforce>false</odlparent.spotbugs.enforce>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-reactor</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>openconfig-model-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>antlr4-runtime</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-xpath-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.antlr</groupId>
+                <artifactId>antlr4-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>antlr4</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <visitor>true</visitor>
+                    <listener>false</listener>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.parser.rfc7950</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/IfFeatureExpressionLexer.g4 b/yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/IfFeatureExpressionLexer.g4
new file mode 100644 (file)
index 0000000..51029d4
--- /dev/null
@@ -0,0 +1,17 @@
+//
+// Copyright (c) 2017 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
+//
+lexer grammar IfFeatureExpressionLexer;
+
+NOT : 'not';
+LP : '(';
+RP : ')';
+AND : 'and';
+OR : 'or';
+COLON : ':';
+SEP: [ \n\r\t]+;
+IDENTIFIER : [a-zA-Z][a-zA-Z0-9_-]*;
diff --git a/yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/IfFeatureExpressionParser.g4 b/yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/IfFeatureExpressionParser.g4
new file mode 100644 (file)
index 0000000..8e5cc46
--- /dev/null
@@ -0,0 +1,20 @@
+//
+// Copyright (c) 2017 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
+//
+parser grammar IfFeatureExpressionParser;
+
+options {
+    tokenVocab = IfFeatureExpressionLexer;
+}
+
+if_feature_expr: if_feature_term (SEP OR SEP if_feature_term)*;
+if_feature_term: if_feature_factor (SEP AND SEP if_feature_term)*;
+if_feature_factor: NOT SEP if_feature_factor
+                 | LP SEP? if_feature_expr SEP? RP
+                 | identifier_ref_arg;
+
+identifier_ref_arg : (IDENTIFIER COLON)? IDENTIFIER;
diff --git a/yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/LeafRefPathLexer.g4 b/yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/LeafRefPathLexer.g4
new file mode 100644 (file)
index 0000000..3d63ee9
--- /dev/null
@@ -0,0 +1,16 @@
+lexer grammar LeafRefPathLexer;
+
+COLON : ':' ;
+SLASH : '/' ;
+DOTS : '..' ;
+EQUAL : '=' ;
+LEFT_SQUARE_BRACKET : '[' ;
+RIGHT_SQUARE_BRACKET : ']' ;
+LEFT_PARENTHESIS : '(' ;
+RIGHT_PARENTHESIS : ')' ;
+
+CURRENT_KEYWORD : 'current';
+
+SEP: [ \n\r\t]+ ;
+IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_\-.]*;
+
diff --git a/yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/LeafRefPathParser.g4 b/yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/LeafRefPathParser.g4
new file mode 100644 (file)
index 0000000..ec60a4c
--- /dev/null
@@ -0,0 +1,30 @@
+parser grammar LeafRefPathParser;
+
+options {
+    tokenVocab = LeafRefPathLexer;
+}
+
+path_arg : absolute_path | relative_path;
+
+absolute_path : (SLASH node_identifier (path_predicate)*)+;
+
+relative_path : (DOTS SLASH)* descendant_path;
+
+descendant_path : node_identifier ((path_predicate)* absolute_path)?;
+
+path_predicate : LEFT_SQUARE_BRACKET SEP? path_equality_expr SEP? RIGHT_SQUARE_BRACKET;
+
+path_equality_expr : node_identifier SEP? EQUAL SEP? path_key_expr;
+
+path_key_expr : current_function_invocation SEP? SLASH SEP? rel_path_keyexpr;
+
+rel_path_keyexpr : (DOTS SEP? SLASH SEP?)* (node_identifier SEP? SLASH SEP?)* node_identifier;
+
+node_identifier : (prefix COLON)? identifier;
+
+current_function_invocation : CURRENT_KEYWORD SEP? LEFT_PARENTHESIS SEP? RIGHT_PARENTHESIS;
+
+prefix : identifier;
+
+identifier: IDENTIFIER | CURRENT_KEYWORD;
+
diff --git a/yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/YangStatementLexer.g4 b/yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/YangStatementLexer.g4
new file mode 100644 (file)
index 0000000..8185545
--- /dev/null
@@ -0,0 +1,42 @@
+//
+// Copyright (c) 2015 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
+//
+lexer grammar YangStatementLexer;
+
+tokens {
+    SEMICOLON,
+    LEFT_BRACE,
+    RIGHT_BRACE,
+    SEP,
+    IDENTIFIER,
+    COLON,
+    PLUS
+}
+
+SEMICOLON : ';' -> type(SEMICOLON);
+LEFT_BRACE : '{' -> type(LEFT_BRACE);
+RIGHT_BRACE : '}' -> type(RIGHT_BRACE);
+COLON : ':' -> type(COLON);
+PLUS : '+' -> type(PLUS);
+
+LINE_COMMENT :  [ \n\r\t]* ('//' (~[\r\n]*)) [ \n\r\t]* -> skip;
+
+START_BLOCK_COMMENT : '/*' ->pushMode(BLOCK_COMMENT_MODE), skip;
+
+SEP: [ \n\r\t]+ -> type(SEP);
+IDENTIFIER : [a-zA-Z_/][a-zA-Z0-9_\-.:/]* -> type(IDENTIFIER);
+
+fragment SUB_STRING : ('"' (ESC | ~["])*? '"') | ('\'' (ESC | ~['])* '\'');
+fragment ESC : '\\' (["\\/bfnrt] | UNICODE);
+fragment UNICODE : 'u' HEX HEX HEX HEX;
+fragment HEX : [0-9a-fA-F] ;
+
+STRING: ((~( '\r' | '\n' | '\t' | ' ' | ';' | '{' | '"' | '\'' | '}' | '/' | '+')~( '\r' | '\n' | '\t' | ' ' | ';' | '{' | '}' )* ) | SUB_STRING );
+
+mode BLOCK_COMMENT_MODE;
+END_BLOCK_COMMENT : '*/' -> popMode, skip;
+BLOCK_COMMENT :  . -> skip;
diff --git a/yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/YangStatementParser.g4 b/yang/yang-parser-rfc7950/src/main/antlr4/org/opendaylight/yangtools/antlrv4/code/gen/YangStatementParser.g4
new file mode 100644 (file)
index 0000000..9f836e2
--- /dev/null
@@ -0,0 +1,17 @@
+//
+// Copyright (c) 2015 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
+//
+parser grammar YangStatementParser;
+
+options {
+    tokenVocab = YangStatementLexer;
+}
+
+statement : SEP* keyword SEP* (argument)? SEP* (SEMICOLON | LEFT_BRACE SEP* (statement)* SEP* RIGHT_BRACE SEP*) SEP*;
+keyword : (IDENTIFIER COLON)? IDENTIFIER;
+
+argument : STRING (SEP* PLUS SEP* STRING)* | IDENTIFIER;
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/openconfig/stmt/OpenConfigVersionEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/openconfig/stmt/OpenConfigVersionEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..dbc8621
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016 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.openconfig.stmt;
+
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigVersionEffectiveStatement;
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigVersionStatement;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.UnknownEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class OpenConfigVersionEffectiveStatementImpl extends
+        UnknownEffectiveStatementBase<SemVer, OpenConfigVersionStatement>
+        implements OpenConfigVersionEffectiveStatement {
+
+    private final @NonNull SchemaPath path;
+
+    OpenConfigVersionEffectiveStatementImpl(final StmtContext<SemVer, OpenConfigVersionStatement, ?> ctx) {
+        super(ctx);
+        path = ctx.coerceParentContext().getSchemaPath().get().createChild(getNodeType());
+    }
+
+    @Override
+    public QName getQName() {
+        return getNodeType();
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return path;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(path, getNodeType(), getNodeParameter());
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof OpenConfigVersionEffectiveStatementImpl)) {
+            return false;
+        }
+        final OpenConfigVersionEffectiveStatementImpl other = (OpenConfigVersionEffectiveStatementImpl) obj;
+        return Objects.equals(path, other.path) && Objects.equals(getNodeType(), other.getNodeType())
+                && Objects.equals(getNodeParameter(), other.getNodeParameter());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/openconfig/stmt/OpenConfigVersionStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/openconfig/stmt/OpenConfigVersionStatementImpl.java
new file mode 100644 (file)
index 0000000..659a350
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2016 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.openconfig.stmt;
+
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigVersionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class OpenConfigVersionStatementImpl extends AbstractDeclaredStatement<SemVer>
+        implements OpenConfigVersionStatement {
+    OpenConfigVersionStatementImpl(final StmtContext<SemVer, OpenConfigVersionStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/openconfig/stmt/OpenConfigVersionSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/openconfig/stmt/OpenConfigVersionSupport.java
new file mode 100644 (file)
index 0000000..f30a998
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.openconfig.stmt;
+
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigVersionStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class OpenConfigVersionSupport extends AbstractStatementSupport<SemVer, OpenConfigVersionStatement,
+        EffectiveStatement<SemVer, OpenConfigVersionStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        OpenConfigStatements.OPENCONFIG_VERSION).build();
+    private static final OpenConfigVersionSupport INSTANCE = new OpenConfigVersionSupport();
+
+    private OpenConfigVersionSupport() {
+        super(OpenConfigStatements.OPENCONFIG_VERSION);
+    }
+
+    public static OpenConfigVersionSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public SemVer parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return SemVer.valueOf(value);
+    }
+
+    @Override
+    public void onLinkageDeclared(final Mutable<SemVer, OpenConfigVersionStatement,
+            EffectiveStatement<SemVer, OpenConfigVersionStatement>> stmt) {
+        stmt.addToNs(SemanticVersionNamespace.class, stmt.getParentContext(), stmt.getStatementArgument());
+    }
+
+    @Override
+    public OpenConfigVersionStatement createDeclared(final StmtContext<SemVer, OpenConfigVersionStatement, ?> ctx) {
+        return new OpenConfigVersionStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<SemVer, OpenConfigVersionStatement> createEffective(
+            final StmtContext<SemVer, OpenConfigVersionStatement,
+            EffectiveStatement<SemVer, OpenConfigVersionStatement>> ctx) {
+        return new OpenConfigVersionEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/antlr/AbstractParserErrorListener.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/antlr/AbstractParserErrorListener.java
new file mode 100644 (file)
index 0000000..eab3e43
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.rfc7950.antlr;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.annotations.Beta;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract base type for converting ANTLRErrorListener errors to Exceptions. This class is NOT thread-safe, nor are
+ * its subclasses expected to be thread-safe.
+ *
+ * @param <E> Reported exception type
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public abstract class AbstractParserErrorListener<E extends Exception> extends BaseErrorListener implements Mutable {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractParserErrorListener.class);
+
+    private final List<E> exceptions = new ArrayList<>();
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public final void syntaxError(final @Nullable Recognizer<?, ?> recognizer, final @Nullable Object offendingSymbol,
+            final int line, final int charPositionInLine, final @Nullable String msg,
+            final @Nullable RecognitionException e) {
+        LOG.debug("Syntax error at {}:{}: {}", line, charPositionInLine, msg, e);
+        exceptions.add(verifyNotNull(createException(verifyNotNull(recognizer), offendingSymbol, line,
+            charPositionInLine, verifyNotNull(msg), e)));
+    }
+
+    public final void validate() throws E {
+        if (!exceptions.isEmpty()) {
+            final Iterator<E> it = exceptions.iterator();
+            final E exception = it.next();
+            it.forEachRemaining(exception::addSuppressed);
+            throw exception;
+        }
+    }
+
+    protected abstract E createException(Recognizer<?, ?> recognizer, @Nullable Object offendingSymbol, int line,
+            int charPositionInLine, String msg, @Nullable RecognitionException cause);
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/antlr/SourceExceptionParser.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/antlr/SourceExceptionParser.java
new file mode 100644 (file)
index 0000000..a37da56
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.rfc7950.antlr;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.function.Supplier;
+import org.antlr.v4.runtime.Lexer;
+import org.antlr.v4.runtime.Parser;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+
+/**
+ * Utility class for converting ANTLRErrorListener errors to SourceExceptions. This class is NOT thread-safe.
+ *
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public final class SourceExceptionParser {
+    private static final class Listener extends AbstractParserErrorListener<SourceException> {
+        private final StatementSourceReference ref;
+
+        private Listener(final StatementSourceReference ref) {
+            this.ref = requireNonNull(ref);
+        }
+
+        @Override
+        protected SourceException createException(final Recognizer<?, ?> recognizer,
+                final @Nullable Object offendingSymbol, final int line, final int charPositionInLine,
+                final String msg, final @Nullable RecognitionException cause) {
+            return new SourceException(ref, cause, "%s at %s:%s", msg, line, charPositionInLine);
+        }
+    }
+
+    private SourceExceptionParser() {
+
+    }
+
+    /**
+     * Parse a Recognizer extracting its root item.
+     *
+     * @param recognizer Recognizer to use
+     * @param parseMethod Root item extractor method
+     * @param ref Source reference
+     * @return Parsed item
+     * @throws NullPointerException if any argument is null
+     * @throws SourceException if a parser error occurs
+     */
+    public static <T> T parse(final Recognizer<?, ?> recognizer, final Supplier<T> parseMethod,
+            final StatementSourceReference ref) {
+        final Listener listener = new Listener(ref);
+        recognizer.removeErrorListeners();
+        recognizer.addErrorListener(listener);
+
+        final T ret = parseMethod.get();
+        listener.validate();
+        return ret;
+    }
+
+    /**
+     * Use a Lexer/Parser pair extracting the parser's root item.
+     *
+     * @param lexer lexer to use
+     * @param parser parser to use
+     * @param parseMethod Root item extractor method
+     * @param ref Source reference
+     * @return Parsed item
+     * @throws NullPointerException if any argument is null
+     * @throws SourceException if a parser error occurs
+     */
+    public static <T> T parse(final Lexer lexer, final Parser parser, final Supplier<T> parseMethod,
+            final StatementSourceReference ref) {
+        final Listener listener = new Listener(ref);
+        lexer.removeErrorListeners();
+        lexer.addErrorListener(listener);
+        parser.removeErrorListeners();
+        parser.addErrorListener(listener);
+
+        final T ret = parseMethod.get();
+        listener.validate();
+        return ret;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/ChildSchemaNodeNamespace.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/ChildSchemaNodeNamespace.java
new file mode 100644 (file)
index 0000000..bcc92d1
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.namespace;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+/**
+ * Statement local namespace, which holds direct schema node descendants.
+ */
+@Beta
+public final class ChildSchemaNodeNamespace<D extends DeclaredStatement<QName>, E extends EffectiveStatement<QName, D>>
+    extends NamespaceBehaviour<QName, StmtContext<?, D, E>, ChildSchemaNodeNamespace<D, E>>
+    implements StatementNamespace<QName, D, E> {
+
+    public ChildSchemaNodeNamespace() {
+        super((Class) ChildSchemaNodeNamespace.class);
+    }
+
+    @Override
+    public StmtContext<?, D, E> get(final QName key) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public StmtContext<?, D, E> getFrom(final NamespaceStorageNode storage, final QName key) {
+        return globalOrStatementSpecific(storage).getFromLocalStorage(getIdentifier(), key);
+    }
+
+    @Override
+    public Map<QName, StmtContext<?, D, E>> getAllFrom(final NamespaceStorageNode storage) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void addTo(final NamespaceStorageNode storage, final QName key, final StmtContext<?, D, E> value) {
+        final StmtContext<?, D, E> prev = globalOrStatementSpecific(storage).putToLocalStorageIfAbsent(
+            ChildSchemaNodeNamespace.class, key, value);
+
+        if (prev != null) {
+            throw new SourceException(value.getStatementSourceReference(),
+                "Error in module '%s': cannot add '%s'. Node name collision: '%s' already declared at %s",
+                value.getRoot().getStatementArgument(), key, prev.getStatementArgument(),
+                prev.getStatementSourceReference());
+        }
+    }
+
+    /**
+     * Find statement context identified by interpreting specified {@link SchemaNodeIdentifier} starting at specified
+     * {@link StmtContext}.
+     *
+     * @param root Search root context
+     * @param identifier {@link SchemaNodeIdentifier} relative to search root
+     * @return Matching statement context, if present.
+     * @throws NullPointerException if any of the arguments is null
+     */
+    public static Optional<StmtContext<?, ?, ?>> findNode(final StmtContext<?, ?, ?> root,
+            final SchemaNodeIdentifier identifier) {
+        final Iterator<QName> iterator = identifier.getPathFromRoot().iterator();
+        if (!iterator.hasNext()) {
+            return Optional.of(root);
+        }
+
+        QName nextPath = iterator.next();
+        @SuppressWarnings("unchecked")
+        Mutable<?, ?, EffectiveStatement<?, ?>> current = (Mutable)root.getFromNamespace(ChildSchemaNodeNamespace.class,
+            nextPath);
+        if (current == null) {
+            return Optional.ofNullable(tryToFindUnknownStatement(nextPath.getLocalName(),
+                (Mutable<?, ?, EffectiveStatement<?, ?>>) root));
+        }
+        while (current != null && iterator.hasNext()) {
+            nextPath = iterator.next();
+            @SuppressWarnings("unchecked")
+            final Mutable<?, ?, EffectiveStatement<?, ?>> nextNodeCtx = (Mutable)current.getFromNamespace(
+                ChildSchemaNodeNamespace.class, nextPath);
+            if (nextNodeCtx == null) {
+                return Optional.ofNullable(tryToFindUnknownStatement(nextPath.getLocalName(), current));
+            }
+            current = nextNodeCtx;
+        }
+        return Optional.ofNullable(current);
+    }
+
+    @SuppressWarnings("unchecked")
+    static Mutable<?, ?, EffectiveStatement<?, ?>> tryToFindUnknownStatement(final String localName,
+            final Mutable<?, ?, EffectiveStatement<?, ?>> current) {
+        final Collection<? extends StmtContext<?, ?, ?>> unknownSubstatements = StmtContextUtils.findAllSubstatements(
+            current, UnknownStatement.class);
+        for (final StmtContext<?, ?, ?> unknownSubstatement : unknownSubstatements) {
+            if (localName.equals(unknownSubstatement.rawStatementArgument())) {
+                return (Mutable<?, ?, EffectiveStatement<?, ?>>) unknownSubstatement;
+            }
+        }
+        return null;
+    }
+
+    private static NamespaceStorageNode globalOrStatementSpecific(final NamespaceStorageNode storage) {
+        NamespaceStorageNode current = storage;
+        while (!isLocalOrGlobal(current.getStorageNodeType())) {
+            current = current.getParentNamespaceStorage();
+        }
+        return current;
+    }
+
+    private static boolean isLocalOrGlobal(final StorageNodeType type) {
+        return type == StorageNodeType.STATEMENT_LOCAL || type == StorageNodeType.GLOBAL;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/ModuleQNameToPrefix.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/ModuleQNameToPrefix.java
new file mode 100644 (file)
index 0000000..9118076
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7950.namespace;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Implementation-internal cache for looking up URI to import prefix. URIs are taken in as Strings to save ourselves
+ * some quality parsing time.
+ */
+@Beta
+public interface ModuleQNameToPrefix extends IdentifierNamespace<QNameModule, String> {
+    NamespaceBehaviour<QNameModule, String, @NonNull ModuleQNameToPrefix> BEHAVIOUR =
+            NamespaceBehaviour.rootStatementLocal(ModuleQNameToPrefix.class);
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/URIStringToImportPrefix.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/URIStringToImportPrefix.java
new file mode 100644 (file)
index 0000000..a22906b
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.namespace;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Implementation-internal cache for looking up URI to import prefix. URIs are taken in as Strings to save ourselves
+ * some quality parsing time.
+ *
+ * @deprecated Use {@link ModuleQNameToPrefix} instead.
+ */
+@Deprecated
+@Beta
+public interface URIStringToImportPrefix extends IdentifierNamespace<String, String> {
+    NamespaceBehaviour<String, String, @NonNull URIStringToImportPrefix> BEHAVIOUR =
+            NamespaceBehaviour.rootStatementLocal(URIStringToImportPrefix.class);
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/package-info.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/package-info.java
new file mode 100644 (file)
index 0000000..bb63521
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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 holding implementation-specific namespace declarations for RFC7950.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.yang.parser.rfc7950.namespace;
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/CustomCrossSourceStatementReactorBuilder.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/CustomCrossSourceStatementReactorBuilder.java
new file mode 100644 (file)
index 0000000..44884a2
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.reactor;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Table;
+import com.google.common.collect.Table.Cell;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
+import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+
+@Beta
+public class CustomCrossSourceStatementReactorBuilder implements Builder<CrossSourceStatementReactor> {
+    private final ImmutableMap<ModelProcessingPhase, StatementSupportBundle.Builder> reactorSupportBundles;
+    private final Map<ValidationBundleType, Collection<StatementDefinition>> reactorValidationBundles = new HashMap<>();
+
+    /**
+     * Creates a new CustomCrossSourceStatementReactorBuilder object initialized by specific version bundle. Statement
+     * parser will support all versions defined in given version bundle.
+     *
+     * @param supportedVersions
+     *            bundle of supported verions
+     */
+    CustomCrossSourceStatementReactorBuilder(final Set<YangVersion> supportedVersions) {
+        reactorSupportBundles = ImmutableMap.<ModelProcessingPhase, StatementSupportBundle.Builder>builder()
+                .put(ModelProcessingPhase.INIT, StatementSupportBundle.builder(supportedVersions))
+                .put(ModelProcessingPhase.SOURCE_PRE_LINKAGE, StatementSupportBundle.builder(supportedVersions))
+                .put(ModelProcessingPhase.SOURCE_LINKAGE, StatementSupportBundle.builder(supportedVersions))
+                .put(ModelProcessingPhase.STATEMENT_DEFINITION, StatementSupportBundle.builder(supportedVersions))
+                .put(ModelProcessingPhase.FULL_DECLARATION, StatementSupportBundle.builder(supportedVersions))
+                .put(ModelProcessingPhase.EFFECTIVE_MODEL, StatementSupportBundle.builder(supportedVersions)).build();
+    }
+
+    public CustomCrossSourceStatementReactorBuilder addStatementSupport(final ModelProcessingPhase phase,
+            final StatementSupport<?, ?, ?> stmtSupport) {
+        reactorSupportBundles.get(phase).addSupport(stmtSupport);
+        return this;
+    }
+
+    public CustomCrossSourceStatementReactorBuilder overrideStatementSupport(final ModelProcessingPhase phase,
+            final StatementSupport<?, ?, ?> stmtSupport) {
+        reactorSupportBundles.get(phase).overrideSupport(stmtSupport);
+        return this;
+    }
+
+    public CustomCrossSourceStatementReactorBuilder addNamespaceSupport(final ModelProcessingPhase phase,
+            final NamespaceBehaviour<?, ?, ?> namespaceSupport) {
+        reactorSupportBundles.get(phase).addSupport(namespaceSupport);
+        return this;
+    }
+
+    public CustomCrossSourceStatementReactorBuilder addValidationBundle(final ValidationBundleType validationBundleType,
+            final Collection<StatementDefinition> validationBundle) {
+        reactorValidationBundles.put(validationBundleType, validationBundle);
+        return this;
+    }
+
+    public CustomCrossSourceStatementReactorBuilder addAllSupports(final ModelProcessingPhase phase,
+            final StatementSupportBundle stmtSupportBundle) {
+        addAllCommonStatementSupports(phase, stmtSupportBundle.getCommonDefinitions().values());
+        addAllVersionSpecificSupports(phase, stmtSupportBundle.getAllVersionSpecificDefinitions());
+        addAllNamespaceSupports(phase, stmtSupportBundle.getNamespaceDefinitions().values());
+        return this;
+    }
+
+    public CustomCrossSourceStatementReactorBuilder addAllNamespaceSupports(final ModelProcessingPhase phase,
+            final Collection<NamespaceBehaviour<?, ?, ?>> namespaceSupports) {
+        final StatementSupportBundle.Builder stmtBundleBuilder = reactorSupportBundles.get(phase);
+        for (final NamespaceBehaviour<?, ?, ?> namespaceSupport : namespaceSupports) {
+            stmtBundleBuilder.addSupport(namespaceSupport);
+        }
+        return this;
+    }
+
+    public CustomCrossSourceStatementReactorBuilder addAllCommonStatementSupports(final ModelProcessingPhase phase,
+            final Collection<StatementSupport<?, ?, ?>> statementSupports) {
+        final StatementSupportBundle.Builder stmtBundleBuilder = reactorSupportBundles.get(phase);
+        for (final StatementSupport<?, ?, ?> statementSupport : statementSupports) {
+            stmtBundleBuilder.addSupport(statementSupport);
+        }
+        return this;
+    }
+
+    public CustomCrossSourceStatementReactorBuilder addAllVersionSpecificSupports(final ModelProcessingPhase phase,
+            final Table<YangVersion, QName, StatementSupport<?, ?, ?>> versionSpecificSupports) {
+        final StatementSupportBundle.Builder stmtBundleBuilder = reactorSupportBundles.get(phase);
+        for (final Cell<YangVersion, QName, StatementSupport<?, ?, ?>> cell : versionSpecificSupports.cellSet()) {
+            stmtBundleBuilder.addVersionSpecificSupport(cell.getRowKey(), cell.getValue());
+        }
+        return this;
+    }
+
+    @Override
+    public @NonNull CrossSourceStatementReactor build() {
+        final StatementSupportBundle initBundle = reactorSupportBundles.get(ModelProcessingPhase.INIT).build();
+        final StatementSupportBundle preLinkageBundle = reactorSupportBundles
+                .get(ModelProcessingPhase.SOURCE_PRE_LINKAGE).setParent(initBundle).build();
+        final StatementSupportBundle linkageBundle = reactorSupportBundles.get(ModelProcessingPhase.SOURCE_LINKAGE)
+                .setParent(preLinkageBundle).build();
+        final StatementSupportBundle stmtDefBundle = reactorSupportBundles
+                .get(ModelProcessingPhase.STATEMENT_DEFINITION).setParent(linkageBundle).build();
+        final StatementSupportBundle fullDeclBundle = reactorSupportBundles.get(ModelProcessingPhase.FULL_DECLARATION)
+                .setParent(stmtDefBundle).build();
+        final StatementSupportBundle effectiveBundle = reactorSupportBundles.get(ModelProcessingPhase.EFFECTIVE_MODEL)
+                .setParent(fullDeclBundle).build();
+
+        final CrossSourceStatementReactor.Builder reactorBuilder = CrossSourceStatementReactor.builder()
+                .setBundle(ModelProcessingPhase.INIT, initBundle)
+                .setBundle(ModelProcessingPhase.SOURCE_PRE_LINKAGE, preLinkageBundle)
+                .setBundle(ModelProcessingPhase.SOURCE_LINKAGE, linkageBundle)
+                .setBundle(ModelProcessingPhase.STATEMENT_DEFINITION, stmtDefBundle)
+                .setBundle(ModelProcessingPhase.FULL_DECLARATION, fullDeclBundle)
+                .setBundle(ModelProcessingPhase.EFFECTIVE_MODEL, effectiveBundle);
+
+        for (final Entry<ValidationBundleType, Collection<StatementDefinition>> entry : reactorValidationBundles
+                .entrySet()) {
+            reactorBuilder.setValidationBundle(entry.getKey(), entry.getValue());
+        }
+
+        return reactorBuilder.build();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/RFC7950Reactors.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/RFC7950Reactors.java
new file mode 100644 (file)
index 0000000..a0394e4
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.reactor;
+
+import static org.opendaylight.yangtools.yang.common.YangVersion.VERSION_1;
+import static org.opendaylight.yangtools.yang.common.YangVersion.VERSION_1_1;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.openconfig.stmt.OpenConfigVersionSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ModuleQNameToPrefix;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.URIStringToImportPrefix;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.action.ActionStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.anydata.AnydataStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.anyxml.AnyxmlStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.argument.ArgumentStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.augment.AugmentImplicitHandlingNamespace;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.augment.AugmentStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.augment.AugmentStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.base.BaseStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.belongs_to.BelongsToStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.bit.BitStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.bit.BitStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.case_.CaseStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.case_.CaseStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.choice.ChoiceStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.choice.ChoiceStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.config.ConfigStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.contact.ContactStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.container.ContainerStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.container.ContainerStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.default_.DefaultStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.description.DescriptionStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.deviate.DeviateStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.deviate.DeviateStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.deviation.DeviationStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.enum_.EnumStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.enum_.EnumStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.error_app_tag.ErrorAppTagStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.error_message.ErrorMessageStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.extension.ExtensionStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.feature.FeatureStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.fraction_digits.FractionDigitsStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.grouping.GroupingStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.grouping.GroupingStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.identity.IdentityStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.identity.IdentityStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.if_feature.IfFeatureStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.import_.ImportStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.import_.ImportStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.include.IncludeStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.include.IncludeStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.input.InputStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.input.InputStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.key.KeyStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.leaf.LeafStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.leaf_list.LeafListStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.leaf_list.LeafListStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.length.LengthStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.list.ListStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.list.ListStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.mandatory.MandatoryStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.max_elements.MaxElementsStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.min_elements.MinElementsStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.modifier.ModifierStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.module.ModuleStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.module.ModuleStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.must.MustStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.namespace.NamespaceStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.notification.NotificationStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.notification.NotificationStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ordered_by.OrderedByStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.organization.OrganizationStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.output.OutputStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.output.OutputStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.path.PathStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.pattern.PatternStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.pattern.PatternStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.position.PositionStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.prefix.PrefixStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.presence.PresenceStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.range.RangeStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.reference.ReferenceStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.refine.RefineStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.refine.RefineStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.require_instance.RequireInstanceStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.revision.RevisionStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.revision_date.RevisionDateStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.rpc.RpcStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.rpc.RpcStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.status.StatusStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.submodule.SubmoduleStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.submodule.SubmoduleStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.type.TypeStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.type.TypeStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.typedef.TypedefStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.unique.UniqueStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.units.UnitsStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.uses.UsesStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.value.ValueStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.when.WhenStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.yang_version.YangVersionStatementSupport;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.yin_element.YinElementStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.ExtensionNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.FeatureNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.GroupingNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.IdentityNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
+import org.opendaylight.yangtools.yang.parser.spi.PreLinkageModuleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.SubmoduleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.TypeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.DerivedIdentitiesNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionModuleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementDefinitionNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
+import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToSemVerSourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportedModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.IncludedModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.IncludedSubmoduleNameToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToSourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNamespaceForBelongsTo;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleQNameToModuleName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModulesDeviatedByModules;
+import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
+import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.source.StmtOrderingNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+
+/**
+ * Utility class holding entrypoints for assembling RFC6020/RFC7950 statement {@link CrossSourceStatementReactor}s.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class RFC7950Reactors {
+    private static final ImmutableSet<YangVersion> SUPPORTED_VERSIONS = Sets.immutableEnumSet(VERSION_1, VERSION_1_1);
+
+    private static final StatementSupportBundle INIT_BUNDLE = StatementSupportBundle.builder(SUPPORTED_VERSIONS)
+            .addSupport(ValidationBundlesNamespace.BEHAVIOUR)
+            .addSupport(SupportedFeaturesNamespace.BEHAVIOUR)
+            .addSupport(ModulesDeviatedByModules.BEHAVIOUR)
+            .build();
+
+    private static final StatementSupportBundle PRE_LINKAGE_BUNDLE = StatementSupportBundle.derivedFrom(INIT_BUNDLE)
+            .addVersionSpecificSupport(VERSION_1, ModuleStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, ModuleStatementRFC7950Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1, SubmoduleStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, SubmoduleStatementRFC7950Support.getInstance())
+            .addSupport(NamespaceStatementSupport.getInstance())
+            .addVersionSpecificSupport(VERSION_1, ImportStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, ImportStatementRFC7950Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1, IncludeStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, IncludeStatementRFC7950Support.getInstance())
+            .addSupport(BelongsToStatementSupport.getInstance())
+            .addSupport(PrefixStatementSupport.getInstance())
+            .addSupport(YangVersionStatementSupport.getInstance())
+            .addSupport(RevisionStatementSupport.getInstance())
+            .addSupport(RevisionDateStatementSupport.getInstance())
+            .addSupport(ModuleNameToNamespace.BEHAVIOUR)
+            .addSupport(PreLinkageModuleNamespace.BEHAVIOUR)
+            .addSupport(ImpPrefixToNamespace.BEHAVIOUR)
+            .addSupport(ModuleCtxToModuleQName.BEHAVIOUR)
+            .build();
+
+    private static final StatementSupportBundle LINKAGE_BUNDLE = StatementSupportBundle
+            .derivedFrom(PRE_LINKAGE_BUNDLE)
+            .addSupport(DescriptionStatementSupport.getInstance())
+            .addSupport(ReferenceStatementSupport.getInstance())
+            .addSupport(ContactStatementSupport.getInstance())
+            .addSupport(OrganizationStatementSupport.getInstance())
+            .addSupport(ModuleNamespace.BEHAVIOUR)
+            .addSupport(ModuleNamespaceForBelongsTo.BEHAVIOUR)
+            .addSupport(SubmoduleNamespace.BEHAVIOUR)
+            .addSupport(NamespaceToModule.BEHAVIOUR)
+            .addSupport(ModuleNameToModuleQName.BEHAVIOUR)
+            .addSupport(ModuleCtxToSourceIdentifier.BEHAVIOUR)
+            .addSupport(ModuleQNameToModuleName.BEHAVIOUR)
+            .addSupport(PrefixToModule.BEHAVIOUR)
+            .addSupport(QNameCacheNamespace.getInstance())
+            .addSupport(ImportedModuleContext.BEHAVIOUR)
+            .addSupport(IncludedModuleContext.BEHAVIOUR)
+            .addSupport(IncludedSubmoduleNameToModuleCtx.BEHAVIOUR)
+            .addSupport(ImportPrefixToModuleCtx.BEHAVIOUR)
+            .addSupport(BelongsToPrefixToModuleCtx.BEHAVIOUR)
+            .addSupport(ModuleQNameToPrefix.BEHAVIOUR)
+            .addSupport(URIStringToImportPrefix.BEHAVIOUR)
+            .addSupport(BelongsToModuleContext.BEHAVIOUR)
+            .addSupport(QNameToStatementDefinition.BEHAVIOUR)
+            .addSupport(BelongsToPrefixToModuleName.BEHAVIOUR)
+            .build();
+
+    private static final StatementSupportBundle STMT_DEF_BUNDLE = StatementSupportBundle
+            .derivedFrom(LINKAGE_BUNDLE)
+            .addSupport(YinElementStatementSupport.getInstance())
+            .addSupport(ArgumentStatementSupport.getInstance())
+            .addSupport(ExtensionStatementSupport.getInstance())
+            .addSupport(new ChildSchemaNodeNamespace<>())
+            .addSupport(ExtensionNamespace.BEHAVIOUR)
+            .addSupport(TypedefStatementSupport.getInstance())
+            .addSupport(TypeNamespace.BEHAVIOUR)
+            .addVersionSpecificSupport(VERSION_1, IdentityStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, IdentityStatementRFC7950Support.getInstance())
+            .addSupport(IdentityNamespace.BEHAVIOUR)
+            .addSupport(DefaultStatementSupport.getInstance())
+            .addSupport(StatusStatementSupport.getInstance())
+            .addVersionSpecificSupport(VERSION_1, TypeStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, TypeStatementRFC7950Support.getInstance())
+            .addSupport(UnitsStatementSupport.getInstance())
+            .addSupport(RequireInstanceStatementSupport.getInstance())
+            .addVersionSpecificSupport(VERSION_1, BitStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, BitStatementRFC7950Support.getInstance())
+            .addSupport(PathStatementSupport.strictInstance())
+            .addVersionSpecificSupport(VERSION_1, EnumStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, EnumStatementRFC7950Support.getInstance())
+            .addSupport(LengthStatementSupport.getInstance())
+            .addVersionSpecificSupport(VERSION_1, PatternStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, PatternStatementRFC7950Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, ModifierStatementSupport.getInstance())
+            .addSupport(RangeStatementSupport.getInstance())
+            .addSupport(KeyStatementSupport.getInstance())
+            .addVersionSpecificSupport(VERSION_1, ContainerStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, ContainerStatementRFC7950Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1, GroupingStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, GroupingStatementRFC7950Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1, ListStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, ListStatementRFC7950Support.getInstance())
+            .addSupport(UniqueStatementSupport.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, ActionStatementSupport.getInstance())
+            .addVersionSpecificSupport(VERSION_1, RpcStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, RpcStatementRFC7950Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1, InputStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, InputStatementRFC7950Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1, OutputStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, OutputStatementRFC7950Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1, NotificationStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, NotificationStatementRFC7950Support.getInstance())
+            .addSupport(FractionDigitsStatementSupport.getInstance())
+            .addSupport(BaseStatementSupport.getInstance())
+            .addSupport(DerivedIdentitiesNamespace.BEHAVIOUR)
+            .addSupport(StatementDefinitionNamespace.BEHAVIOUR)
+            .build();
+
+    private static final StatementSupportBundle FULL_DECL_BUNDLE = StatementSupportBundle
+            .derivedFrom(STMT_DEF_BUNDLE)
+            .addSupport(LeafStatementSupport.getInstance())
+            .addSupport(ConfigStatementSupport.getInstance())
+            .addSupport(DeviationStatementSupport.getInstance())
+            .addVersionSpecificSupport(VERSION_1, DeviateStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, DeviateStatementRFC7950Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1, ChoiceStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, ChoiceStatementRFC7950Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1, CaseStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, CaseStatementRFC7950Support.getInstance())
+            .addSupport(MustStatementSupport.getInstance())
+            .addSupport(MandatoryStatementSupport.getInstance())
+            .addSupport(AnyxmlStatementSupport.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, AnydataStatementSupport.getInstance())
+            .addSupport(FeatureNamespace.BEHAVIOUR)
+            .addSupport(IfFeatureStatementSupport.getInstance())
+            .addSupport(UsesStatementSupport.getInstance())
+            .addSupport(GroupingNamespace.BEHAVIOUR)
+            .addSupport(ErrorMessageStatementSupport.getInstance())
+            .addSupport(ErrorAppTagStatementSupport.getInstance())
+            .addVersionSpecificSupport(VERSION_1, LeafListStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, LeafListStatementRFC7950Support.getInstance())
+            .addSupport(PresenceStatementSupport.getInstance())
+            .addSupport(MaxElementsStatementSupport.getInstance())
+            .addSupport(MinElementsStatementSupport.getInstance())
+            .addSupport(OrderedByStatementSupport.getInstance())
+            .addSupport(WhenStatementSupport.getInstance())
+            .addSupport(AugmentImplicitHandlingNamespace.BEHAVIOUR)
+            .addVersionSpecificSupport(VERSION_1, AugmentStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, AugmentStatementRFC7950Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1, RefineStatementRFC6020Support.getInstance())
+            .addVersionSpecificSupport(VERSION_1_1, RefineStatementRFC7950Support.getInstance())
+            .addSupport(FeatureStatementSupport.getInstance())
+            .addSupport(PositionStatementSupport.getInstance())
+            .addSupport(ValueStatementSupport.getInstance())
+            .addSupport(StmtOrderingNamespace.BEHAVIOUR)
+            .build();
+
+    private static final Map<ModelProcessingPhase, StatementSupportBundle> RFC7950_BUNDLES =
+            ImmutableMap.<ModelProcessingPhase, StatementSupportBundle>builder()
+            .put(ModelProcessingPhase.INIT, INIT_BUNDLE)
+            .put(ModelProcessingPhase.SOURCE_PRE_LINKAGE, PRE_LINKAGE_BUNDLE)
+            .put(ModelProcessingPhase.SOURCE_LINKAGE, LINKAGE_BUNDLE)
+            .put(ModelProcessingPhase.STATEMENT_DEFINITION, STMT_DEF_BUNDLE)
+            .put(ModelProcessingPhase.FULL_DECLARATION, FULL_DECL_BUNDLE)
+            .put(ModelProcessingPhase.EFFECTIVE_MODEL, FULL_DECL_BUNDLE)
+            .build();
+
+    private static final Map<ValidationBundleType, Collection<StatementDefinition>> RFC6020_VALIDATION_BUNDLE =
+            ImmutableMap.<ValidationBundleType, Collection<StatementDefinition>>builder()
+            .put(ValidationBundleType.SUPPORTED_REFINE_SUBSTATEMENTS,
+                YangValidationBundles.SUPPORTED_REFINE_SUBSTATEMENTS)
+            .put(ValidationBundleType.SUPPORTED_AUGMENT_TARGETS, YangValidationBundles.SUPPORTED_AUGMENT_TARGETS)
+
+            // FIXME: 5.0.0: we do not seem to need this validation bundle
+            .put(ValidationBundleType.SUPPORTED_CASE_SHORTHANDS, YangValidationBundles.SUPPORTED_CASE_SHORTHANDS)
+
+            .put(ValidationBundleType.SUPPORTED_DATA_NODES, YangValidationBundles.SUPPORTED_DATA_NODES)
+            .build();
+
+    private static final CrossSourceStatementReactor DEFAULT_RFC6020_RFC7950_REACTOR = defaultReactorBuilder().build();
+    private static final CrossSourceStatementReactor VANILLA_RFC6020_RFC7950_REACTOR = vanillaReactorBuilder().build();
+
+    private RFC7950Reactors() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a pre-built {@link CrossSourceStatementReactor} supporting RFC6020 and RFC7950, along with OpenConfig
+     * semantic version extension. This is useful for parsing near-vanilla YANG models while providing complete
+     * support for semantic versions.
+     *
+     * @return A shared reactor instance.
+     */
+    public static CrossSourceStatementReactor defaultReactor() {
+        return DEFAULT_RFC6020_RFC7950_REACTOR;
+    }
+
+    /**
+     * Returns a partially-configured {@link CustomCrossSourceStatementReactorBuilder}, with RFC6020/RFC7950
+     * and OpenConfig semantic version support enabled.
+     *
+     * @return A new {@link CustomCrossSourceStatementReactorBuilder}.
+     */
+    public static CustomCrossSourceStatementReactorBuilder defaultReactorBuilder() {
+        return vanillaReactorBuilder()
+                // Semantic version support
+                .addStatementSupport(ModelProcessingPhase.SOURCE_LINKAGE, OpenConfigVersionSupport.getInstance())
+                .addNamespaceSupport(ModelProcessingPhase.SOURCE_LINKAGE, SemanticVersionNamespace.BEHAVIOUR)
+                .addNamespaceSupport(ModelProcessingPhase.SOURCE_LINKAGE, SemanticVersionModuleNamespace.BEHAVIOUR)
+                .addNamespaceSupport(ModelProcessingPhase.SOURCE_LINKAGE,
+                    ImportPrefixToSemVerSourceIdentifier.BEHAVIOUR);
+    }
+
+    /**
+     * Returns a pre-built {@link CrossSourceStatementReactor} supporting both RFC6020 and RFC7950. This is useful
+     * for parsing vanilla YANG models without any semantic support for extensions. Notably missing is the semantic
+     * version extension, hence attempts to use semantic version mode will cause failures.
+     *
+     * @return A shared reactor instance.
+     */
+    public static CrossSourceStatementReactor vanillaReactor() {
+        return VANILLA_RFC6020_RFC7950_REACTOR;
+    }
+
+    /**
+     * Returns a partially-configured {@link CustomCrossSourceStatementReactorBuilder}, with vanilla RFC6020/RFC7950
+     * support enabled.
+     *
+     * @return A new {@link CustomCrossSourceStatementReactorBuilder}.
+     */
+    public static CustomCrossSourceStatementReactorBuilder vanillaReactorBuilder() {
+        return addDefaultRFC7950Bundles(new CustomCrossSourceStatementReactorBuilder(SUPPORTED_VERSIONS));
+    }
+
+    private static CustomCrossSourceStatementReactorBuilder addDefaultRFC7950Bundles(
+            final CustomCrossSourceStatementReactorBuilder builder) {
+        RFC7950_BUNDLES.entrySet().forEach(entry -> builder.addAllSupports(entry.getKey(), entry.getValue()));
+        RFC6020_VALIDATION_BUNDLE.entrySet().forEach(
+            entry -> builder.addValidationBundle(entry.getKey(), entry.getValue()));
+        return builder;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/YangValidationBundles.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/YangValidationBundles.java
new file mode 100644 (file)
index 0000000..f0309a0
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.reactor;
+
+import static org.opendaylight.yangtools.yang.common.YangVersion.VERSION_1;
+import static org.opendaylight.yangtools.yang.common.YangVersion.VERSION_1_1;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+public final class YangValidationBundles {
+    private YangValidationBundles() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    public static final Set<StatementDefinition> SUPPORTED_REFINE_SUBSTATEMENTS = ImmutableSet.of(
+        YangStmtMapping.DEFAULT, YangStmtMapping.DESCRIPTION, YangStmtMapping.REFERENCE, YangStmtMapping.CONFIG,
+        YangStmtMapping.MANDATORY, YangStmtMapping.MUST, YangStmtMapping.PRESENCE, YangStmtMapping.MIN_ELEMENTS,
+        YangStmtMapping.MAX_ELEMENTS, YangStmtMapping.IF_FEATURE);
+
+    public static final Map<StatementDefinition, Set<StatementDefinition>> SUPPORTED_REFINE_TARGETS =
+            ImmutableMap.<StatementDefinition, Set<StatementDefinition>>builder()
+            .put(YangStmtMapping.DEFAULT, ImmutableSet.of(YangStmtMapping.LEAF, YangStmtMapping.CHOICE))
+            .put(YangStmtMapping.MANDATORY, ImmutableSet.of(
+                YangStmtMapping.LEAF, YangStmtMapping.CHOICE, YangStmtMapping.ANYXML, YangStmtMapping.ANYDATA))
+        .put(YangStmtMapping.PRESENCE, ImmutableSet.of(YangStmtMapping.CONTAINER))
+        .put(YangStmtMapping.MUST, ImmutableSet.of(
+                YangStmtMapping.CONTAINER, YangStmtMapping.LIST, YangStmtMapping.LEAF,
+                YangStmtMapping.LEAF_LIST, YangStmtMapping.ANYXML, YangStmtMapping.ANYDATA))
+        .put(YangStmtMapping.MIN_ELEMENTS, ImmutableSet.of(YangStmtMapping.LIST, YangStmtMapping.LEAF_LIST))
+        .put(YangStmtMapping.MAX_ELEMENTS, ImmutableSet.of(YangStmtMapping.LIST, YangStmtMapping.LEAF_LIST))
+        .build();
+
+    /**
+     * Supported deviation target statements for specific deviate substatements in specific yang-version.
+     * Example: deviate 'add' adds a 'default' substatement. In YANG 1.0, the target node of such deviation can be
+     * only a leaf or a choice statement. IN YANG 1.1, the target node of such deviation can be a leaf, a leaf-list or
+     * a choice.
+     *
+     * @deprecated This is an implementation leak from statement support and will be removed in next major version.
+     */
+    // FIXME: 5.0.0: move this to AbstractDeviateStatementSupport. This is not resolved from validation bundles at all,
+    //               hence it makes sense to co-locate this with its user.
+    @Deprecated
+    public static final Table<YangVersion, StatementDefinition, Set<StatementDefinition>> SUPPORTED_DEVIATION_TARGETS =
+            ImmutableTable.<YangVersion, StatementDefinition, Set<StatementDefinition>>builder()
+        .put(VERSION_1, YangStmtMapping.CONFIG, ImmutableSet.of(YangStmtMapping.CONTAINER, YangStmtMapping.LEAF,
+                YangStmtMapping.LEAF_LIST, YangStmtMapping.LIST, YangStmtMapping.CHOICE, YangStmtMapping.ANYDATA,
+                YangStmtMapping.ANYXML))
+        .put(VERSION_1, YangStmtMapping.DEFAULT, ImmutableSet.of(YangStmtMapping.LEAF, YangStmtMapping.CHOICE))
+        .put(VERSION_1_1, YangStmtMapping.DEFAULT, ImmutableSet.of(YangStmtMapping.LEAF, YangStmtMapping.LEAF_LIST,
+                YangStmtMapping.CHOICE))
+        .put(VERSION_1, YangStmtMapping.MANDATORY, ImmutableSet.of(YangStmtMapping.LEAF, YangStmtMapping.CHOICE,
+                YangStmtMapping.ANYDATA, YangStmtMapping.ANYXML))
+        .put(VERSION_1, YangStmtMapping.MAX_ELEMENTS, ImmutableSet.of(YangStmtMapping.LIST, YangStmtMapping.LEAF_LIST))
+        .put(VERSION_1, YangStmtMapping.MIN_ELEMENTS, ImmutableSet.of(YangStmtMapping.LIST, YangStmtMapping.LEAF_LIST))
+        .put(VERSION_1, YangStmtMapping.MUST, ImmutableSet.of(YangStmtMapping.CONTAINER, YangStmtMapping.LEAF,
+                YangStmtMapping.LEAF_LIST, YangStmtMapping.LIST, YangStmtMapping.ANYXML))
+        .put(VERSION_1_1, YangStmtMapping.MUST, ImmutableSet.of(YangStmtMapping.CONTAINER, YangStmtMapping.LEAF,
+                YangStmtMapping.LEAF_LIST, YangStmtMapping.LIST, YangStmtMapping.ANYDATA, YangStmtMapping.ANYXML,
+                YangStmtMapping.INPUT, YangStmtMapping.OUTPUT, YangStmtMapping.NOTIFICATION))
+        .put(VERSION_1, YangStmtMapping.TYPE, ImmutableSet.of(YangStmtMapping.LEAF, YangStmtMapping.LEAF_LIST))
+        .put(VERSION_1, YangStmtMapping.UNIQUE, ImmutableSet.of(YangStmtMapping.LIST))
+        .put(VERSION_1, YangStmtMapping.UNITS, ImmutableSet.of(YangStmtMapping.LEAF, YangStmtMapping.LEAF_LIST))
+        .build();
+
+    public static final Set<StatementDefinition> SUPPORTED_AUGMENT_TARGETS = ImmutableSet.of(
+        YangStmtMapping.CONTAINER, YangStmtMapping.LIST, YangStmtMapping.CASE, YangStmtMapping.INPUT,
+        YangStmtMapping.OUTPUT, YangStmtMapping.NOTIFICATION, YangStmtMapping.CHOICE, YangStmtMapping.RPC);
+
+    // FIXME: 5.0.0: consider hiding this list, as choice nodes are handling creation of implied shorthands themselves.
+    //               This has implications on other members of this class, as they really seem like something which
+    //               should live in corresponding StatementSupport classes.
+    public static final Set<StatementDefinition> SUPPORTED_CASE_SHORTHANDS = ImmutableSet.of(
+        YangStmtMapping.CONTAINER, YangStmtMapping.LIST, YangStmtMapping.LEAF, YangStmtMapping.LEAF_LIST,
+        YangStmtMapping.ANYXML, YangStmtMapping.ANYDATA);
+
+    public static final Set<StatementDefinition> SUPPORTED_DATA_NODES = ImmutableSet.of(
+        YangStmtMapping.CONTAINER, YangStmtMapping.LIST, YangStmtMapping.LEAF, YangStmtMapping.LEAF_LIST,
+        YangStmtMapping.ANYXML, YangStmtMapping.ANYDATA);
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/package-info.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/package-info.java
new file mode 100644 (file)
index 0000000..3164c39
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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
+ */
+/**
+ * Base RFC7950 reactor and supporting classes. This package is not intended to be consumed by most applications, but
+ * rather by tests in artifacts providing parser extensions.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.yang.parser.rfc7950.reactor;
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/ASTSchemaSource.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/ASTSchemaSource.java
new file mode 100644 (file)
index 0000000..cf059e5
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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.rfc7950.repo;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Abstract Syntax Tree representation of a schema source. This representation is internal to the YANG parser
+ * implementation, as it relies on ANTLR types.
+ *
+ * <p>
+ * 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 extends AbstractIdentifiable<SourceIdentifier>
+        implements SchemaSourceRepresentation {
+    private final @NonNull YangModelDependencyInfo depInfo;
+    private final @NonNull SemVerSourceIdentifier semVerId;
+    private final @NonNull ParserRuleContext tree;
+    private final @Nullable String symbolicName;
+
+    private ASTSchemaSource(final @NonNull SourceIdentifier identifier, final @NonNull SemVerSourceIdentifier semVerId,
+            final @NonNull ParserRuleContext tree, final @NonNull YangModelDependencyInfo depInfo,
+            @Nullable final String symbolicName) {
+        super(identifier);
+        this.depInfo = requireNonNull(depInfo);
+        this.tree = requireNonNull(tree);
+        this.semVerId = requireNonNull(semVerId);
+        this.symbolicName = symbolicName;
+    }
+
+    /**
+     * Create a new instance of AST representation for a abstract syntax tree, performing minimal semantic analysis
+     * to acquire dependency information.
+     *
+     * @param symbolicName
+     *            Symbolic name
+     * @param identifier
+     *            SourceIdentifier of YANG schema source.
+     * @param tree
+     *            ANTLR abstract syntax tree
+     * @return A new representation instance.
+     * @throws YangSyntaxErrorException
+     *             if we fail to extract dependency information.
+     */
+    static @NonNull ASTSchemaSource create(final @NonNull SourceIdentifier identifier,
+            final @Nullable String symbolicName, final @NonNull ParserRuleContext tree)
+                    throws YangSyntaxErrorException {
+        final YangModelDependencyInfo depInfo = YangModelDependencyInfo.fromAST(identifier, tree);
+        final SourceIdentifier id = getSourceId(depInfo);
+
+        final SemVerSourceIdentifier semVerId;
+        if (identifier instanceof SemVerSourceIdentifier && !depInfo.getSemanticVersion().isPresent()) {
+            semVerId = (SemVerSourceIdentifier) identifier;
+        } else {
+            semVerId = getSemVerSourceId(depInfo);
+        }
+
+        return new ASTSchemaSource(id, semVerId, tree, depInfo, symbolicName);
+    }
+
+    @Override
+    public Optional<String> getSymbolicName() {
+        return Optional.ofNullable(symbolicName);
+    }
+
+    public @NonNull SemVerSourceIdentifier getSemVerIdentifier() {
+        return semVerId;
+    }
+
+    @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.
+     *
+     * @return Dependency information.
+     */
+    // FIXME: this method should be extracted into a public interface in the model.api.repo class, relying solely
+    //        on model.api types.
+    public @NonNull YangModelDependencyInfo getDependencyInformation() {
+        return depInfo;
+    }
+
+    private static @NonNull SourceIdentifier getSourceId(final @NonNull YangModelDependencyInfo depInfo) {
+        final String name = depInfo.getName();
+        return depInfo.getFormattedRevision() == null ? RevisionSourceIdentifier.create(name)
+                : RevisionSourceIdentifier.create(name, depInfo.getRevision());
+    }
+
+    private static @NonNull SemVerSourceIdentifier getSemVerSourceId(final @NonNull YangModelDependencyInfo depInfo) {
+        return depInfo.getFormattedRevision() == null
+                ? SemVerSourceIdentifier.create(depInfo.getName(), depInfo.getSemanticVersion().orElse(null))
+                        : SemVerSourceIdentifier.create(depInfo.getName(), depInfo.getRevision(),
+                            depInfo.getSemanticVersion().orElse(null));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/ArgumentContextUtils.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/ArgumentContextUtils.java
new file mode 100644 (file)
index 0000000..329e323
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.repo;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.CharMatcher;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+import org.antlr.v4.runtime.tree.TerminalNode;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.ArgumentContext;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+
+final class ArgumentContextUtils {
+    private static final CharMatcher WHITESPACE_MATCHER = CharMatcher.whitespace();
+    private static final CharMatcher ANYQUOTE_MATCHER = CharMatcher.anyOf("'\"");
+    private static final Pattern ESCAPED_DQUOT = Pattern.compile("\\\"", Pattern.LITERAL);
+    private static final Pattern ESCAPED_BACKSLASH = Pattern.compile("\\\\", Pattern.LITERAL);
+    private static final Pattern ESCAPED_LF = Pattern.compile("\\n", Pattern.LITERAL);
+    private static final Pattern ESCAPED_TAB = Pattern.compile("\\t", Pattern.LITERAL);
+
+    private ArgumentContextUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    static String stringFromStringContext(final ArgumentContext context, final YangVersion yangVersion,
+            final StatementSourceReference ref) {
+        final StringBuilder sb = new StringBuilder();
+        List<TerminalNode> strings = context.STRING();
+        if (strings.isEmpty()) {
+            strings = Collections.singletonList(context.IDENTIFIER());
+        }
+        for (final TerminalNode stringNode : strings) {
+            final String str = stringNode.getText();
+            final char firstChar = str.charAt(0);
+            final char lastChar = str.charAt(str.length() - 1);
+            if (firstChar == '"' && lastChar == '"') {
+                final String innerStr = str.substring(1, str.length() - 1);
+                /*
+                 * Unescape escaped double quotes, tabs, new line and backslash
+                 * in the inner string and trim the result.
+                 */
+                checkDoubleQuotedString(innerStr, yangVersion, ref);
+
+                sb.append(ESCAPED_TAB.matcher(
+                    ESCAPED_LF.matcher(
+                        ESCAPED_BACKSLASH.matcher(
+                            ESCAPED_DQUOT.matcher(
+                                trimWhitespace(innerStr, stringNode.getSymbol().getCharPositionInLine()))
+                            .replaceAll("\\\""))
+                        .replaceAll("\\\\"))
+                    .replaceAll("\\\n"))
+                    .replaceAll("\\\t"));
+            } else if (firstChar == '\'' && lastChar == '\'') {
+                /*
+                 * According to RFC6020 a single quote character cannot occur in
+                 * a single-quoted string, even when preceded by a backslash.
+                 */
+                sb.append(str, 1, str.length() - 1);
+            } else {
+                checkUnquotedString(str, yangVersion, ref);
+                sb.append(str);
+            }
+        }
+        return sb.toString();
+    }
+
+    private static void checkUnquotedString(final String str, final YangVersion yangVersion,
+            final StatementSourceReference ref) {
+        if (yangVersion == YangVersion.VERSION_1_1) {
+            SourceException.throwIf(ANYQUOTE_MATCHER.matchesAnyOf(str), ref,
+                "YANG 1.1: unquoted string (%s) contains illegal characters", str);
+        }
+    }
+
+    private static void checkDoubleQuotedString(final String str, final YangVersion yangVersion,
+            final StatementSourceReference ref) {
+        if (yangVersion == YangVersion.VERSION_1_1) {
+            for (int i = 0; i < str.length() - 1; i++) {
+                if (str.charAt(i) == '\\') {
+                    switch (str.charAt(i + 1)) {
+                        case 'n':
+                        case 't':
+                        case '\\':
+                        case '\"':
+                            i++;
+                            break;
+                        default:
+                            throw new SourceException(ref, "YANG 1.1: illegal double quoted string (%s). In double "
+                                    + "quoted string the backslash must be followed by one of the following character "
+                                    + "[n,t,\",\\], but was '%s'.", str, str.charAt(i + 1));
+                    }
+                }
+            }
+        }
+    }
+
+    @VisibleForTesting
+    static String trimWhitespace(final String str, final int dquot) {
+        int brk = str.indexOf('\n');
+        if (brk == -1) {
+            // No need to trim whitespace
+            return str;
+        }
+
+        // Okay, we may need to do some trimming, set up a builder and append the first segment
+        final int length = str.length();
+        final StringBuilder sb = new StringBuilder(length);
+
+        // Append first segment, which needs only tail-trimming
+        sb.append(str, 0, trimTrailing(str, 0, brk)).append('\n');
+
+        // With that out of the way, setup our iteration state. The string segment we are looking at is
+        // str.substring(start, end), which is guaranteed not to include any line breaks, i.e. end <= brk unless we are
+        // at the last segment.
+        int start = brk + 1;
+        brk = str.indexOf('\n', start);
+
+        // Loop over inner strings
+        while (brk != -1) {
+            trimLeadingAndAppend(sb, dquot, str, start, trimTrailing(str, start, brk)).append('\n');
+            start = brk + 1;
+            brk = str.indexOf('\n', start);
+        }
+
+        return trimLeadingAndAppend(sb, dquot, str, start, length).toString();
+    }
+
+    private static StringBuilder trimLeadingAndAppend(final StringBuilder sb, final int dquot, final String str,
+            final int start, final int end) {
+        int offset = start;
+        int pos = 0;
+
+        while (pos <= dquot) {
+            if (offset == end) {
+                // We ran out of data, nothing to append
+                return sb;
+            }
+
+            final char ch = str.charAt(offset);
+            if (ch == '\t') {
+                // tabs are to be treated as 8 spaces
+                pos += 8;
+            } else if (WHITESPACE_MATCHER.matches(ch)) {
+                pos++;
+            } else {
+                break;
+            }
+
+            offset++;
+        }
+
+        // We have expanded beyond double quotes, push equivalent spaces
+        while (pos - 1 > dquot) {
+            sb.append(' ');
+            pos--;
+        }
+
+        return sb.append(str, offset, end);
+    }
+
+    private static int trimTrailing(final String str, final int start, final int end) {
+        int ret = end;
+        while (ret > start) {
+            final int prev = ret - 1;
+            if (!WHITESPACE_MATCHER.matches(str.charAt(prev))) {
+                break;
+            }
+            ret = prev;
+        }
+        return ret;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/StatementContextVisitor.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/StatementContextVisitor.java
new file mode 100644 (file)
index 0000000..5672a36
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.rfc7950.repo;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.ArgumentContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.KeywordContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
+import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter.ResumedStatement;
+
+class StatementContextVisitor {
+    private final QNameToStatementDefinition stmtDef;
+    private final StatementWriter writer;
+    private final YangVersion yangVersion;
+    private final PrefixToModule prefixes;
+    private final String sourceName;
+
+    StatementContextVisitor(final String sourceName, final StatementWriter writer,
+            final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes, final YangVersion yangVersion) {
+        this.writer = requireNonNull(writer);
+        this.stmtDef = requireNonNull(stmtDef);
+        this.yangVersion = requireNonNull(yangVersion);
+        this.sourceName = sourceName;
+        this.prefixes = prefixes;
+    }
+
+    void visit(final StatementContext context) {
+        processStatement(0, context);
+    }
+
+    /**
+     * Based on identifier read from source and collections of relevant prefixes and statement definitions mappings
+     * provided for actual phase, method resolves and returns valid QName for declared statement to be written.
+     * This applies to any declared statement, including unknown statements.
+     *
+     * @param prefixes collection of all relevant prefix mappings supplied for actual parsing phase
+     * @param stmtDef collection of all relevant statement definition mappings provided for actual parsing phase
+     * @param keywordText statement keyword text to parse from source
+     * @param ref Source reference
+     * @return valid QName for declared statement to be written, or null
+     */
+    QName getValidStatementDefinition(final String keywordText, final StatementSourceReference ref) {
+        final int firstColon = keywordText.indexOf(':');
+        if (firstColon == -1) {
+            final StatementDefinition def = stmtDef.get(QName.create(YangConstants.RFC6020_YIN_MODULE, keywordText));
+            return def != null ? def.getStatementName() : null;
+        }
+
+        SourceException.throwIf(firstColon == keywordText.length() - 1
+                || keywordText.indexOf(':', firstColon + 1) != -1, ref, "Malformed statement '%s'", keywordText);
+
+        if (prefixes == null) {
+            // No prefixes to look up from
+            return null;
+        }
+
+        final String prefix = keywordText.substring(0, firstColon);
+        final QNameModule qNameModule = prefixes.get(prefix);
+        if (qNameModule == null) {
+            // Failed to look the namespace
+            return null;
+        }
+
+        final String localName = keywordText.substring(firstColon + 1);
+        final StatementDefinition foundStmtDef = resolveStatement(qNameModule, localName);
+        return foundStmtDef != null ? foundStmtDef.getStatementName() : null;
+    }
+
+    StatementDefinition resolveStatement(final QNameModule module, final String localName) {
+        return stmtDef.get(QName.create(module, localName));
+    }
+
+    private boolean processStatement(final int myOffset, final StatementContext ctx) {
+        final Optional<? extends ResumedStatement> optResumed = writer.resumeStatement(myOffset);
+        final StatementSourceReference ref;
+        if (optResumed.isPresent()) {
+            final ResumedStatement resumed = optResumed.get();
+            if (resumed.isFullyDefined()) {
+                return true;
+            }
+
+            ref = resumed.getSourceReference();
+        } else {
+            final String keywordTxt = verifyNotNull(ctx.getChild(KeywordContext.class, 0)).getText();
+            ref = DeclarationInTextSource.atPosition(sourceName, ctx.getStart().getLine(),
+                ctx.getStart().getCharPositionInLine());
+            final QName def = getValidStatementDefinition(keywordTxt, ref);
+            if (def == null) {
+                return false;
+            }
+
+            final ArgumentContext argumentCtx = ctx.getChild(ArgumentContext.class, 0);
+            final String argument = argumentCtx == null ? null
+                    : ArgumentContextUtils.stringFromStringContext(argumentCtx, yangVersion, ref);
+            writer.startStatement(myOffset, def, argument, ref);
+        }
+
+        int childOffset = 0;
+        boolean fullyDefined = true;
+        if (ctx.children != null) {
+            for (ParseTree s : ctx.children) {
+                if (s instanceof StatementContext) {
+                    if (!processStatement(childOffset++, (StatementContext) s)) {
+                        fullyDefined = false;
+                    }
+                }
+            }
+        }
+
+        writer.storeStatement(childOffset, fullyDefined);
+        writer.endStatement(ref);
+        return fullyDefined;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/StatementSourceReferenceHandler.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/StatementSourceReferenceHandler.java
new file mode 100644 (file)
index 0000000..de13c70
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.repo;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+// adapted from https://stackoverflow.com/questions/4915422/get-line-number-from-xml-node-java
+final class StatementSourceReferenceHandler extends DefaultHandler {
+    private static final Logger LOG = LoggerFactory.getLogger(StatementSourceReferenceHandler.class);
+    private static final String USER_DATA_KEY = StatementSourceReference.class.getName();
+
+    private final Deque<Element> stack = new ArrayDeque<>();
+    private final StringBuilder sb = new StringBuilder();
+    private final Document doc;
+    private final String file;
+
+    private Locator documentLocator;
+
+    StatementSourceReferenceHandler(final Document doc, final String file) {
+        this.doc = requireNonNull(doc);
+        this.file = file;
+    }
+
+    static StatementSourceReference extractRef(final Element element) {
+        final Object value = element.getUserData(USER_DATA_KEY);
+        if (value instanceof StatementSourceReference) {
+            return (StatementSourceReference) value;
+        }
+        if (value != null) {
+            LOG.debug("Ignoring {} attached to key {}", value, USER_DATA_KEY);
+        }
+        return null;
+    }
+
+    @Override
+    public void setDocumentLocator(final Locator locator) {
+        // Save the locator, so that it can be used later for line tracking when traversing nodes.
+        this.documentLocator = locator;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public void startElement(final String uri, final String localName, final String qName,
+            final Attributes attributes) {
+        addTextIfNeeded();
+        final Element el = doc.createElementNS(uri, qName);
+        for (int i = 0, len = attributes.getLength(); i < len; i++) {
+            el.setAttributeNS(attributes.getURI(i), attributes.getQName(i), attributes.getValue(i));
+        }
+
+        final StatementSourceReference ref = DeclarationInTextSource.atPosition(file, documentLocator.getLineNumber(),
+            documentLocator.getColumnNumber());
+        el.setUserData(USER_DATA_KEY, ref, null);
+        stack.push(el);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public void endElement(final String uri, final String localName, final String qName) {
+        addTextIfNeeded();
+        final Element closedEl = stack.pop();
+        Node parentEl = stack.peek();
+        if (parentEl == null) {
+            // root element
+            parentEl = doc;
+        }
+
+        parentEl.appendChild(closedEl);
+    }
+
+    @Override
+    public void characters(final char[] ch, final int start, final int length) throws SAXException {
+        sb.append(ch, start, length);
+    }
+
+    // Outputs text accumulated under the current node
+    private void addTextIfNeeded() {
+        if (sb.length() > 0) {
+            stack.peek().appendChild(doc.createTextNode(sb.toString()));
+            sb.setLength(0);
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/TextToASTTransformer.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/TextToASTTransformer.java
new file mode 100644 (file)
index 0000000..4a5ce79
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.rfc7950.repo;
+
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.Futures;
+import java.io.IOException;
+import org.antlr.v4.runtime.ParserRuleContext;
+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.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> {
+    private static final Logger LOG = LoggerFactory.getLogger(TextToASTTransformer.class);
+
+    private TextToASTTransformer(final SchemaRepository provider, final SchemaSourceRegistry consumer) {
+        super(provider, YangTextSchemaSource.class, consumer, ASTSchemaSource.class,
+            input -> Futures.immediateFuture(transformText(input)));
+    }
+
+    public static TextToASTTransformer create(final SchemaRepository provider, final SchemaSourceRegistry consumer) {
+        return new TextToASTTransformer(provider, consumer);
+    }
+
+    public static ASTSchemaSource transformText(final YangTextSchemaSource text) throws SchemaSourceException,
+            IOException, YangSyntaxErrorException {
+        final YangStatementStreamSource src = YangStatementStreamSource.create(text);
+        final ParserRuleContext ctx = src.getYangAST();
+        LOG.debug("Model {} parsed successfully", text);
+
+        // TODO: missing validation (YangModelBasicValidationListener should be re-implemented to new parser)
+
+        return ASTSchemaSource.create(text.getIdentifier(), text.getSymbolicName().orElse(null), ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangErrorListener.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangErrorListener.java
new file mode 100644 (file)
index 0000000..e6d57ff
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.parser.rfc7950.repo;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class YangErrorListener extends BaseErrorListener {
+    private static final Logger LOG = LoggerFactory.getLogger(YangErrorListener.class);
+
+    private final List<YangSyntaxErrorException> exceptions = new ArrayList<>();
+    private final SourceIdentifier source;
+
+    public YangErrorListener(final SourceIdentifier source) {
+        this.source = requireNonNull(source);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public void syntaxError(final Recognizer<?, ?> recognizer, final Object offendingSymbol, final int line,
+            final int charPositionInLine, final String msg, final RecognitionException e) {
+        LOG.debug("Syntax error in {} at {}:{}: {}", source, line, charPositionInLine, msg, e);
+        exceptions.add(new YangSyntaxErrorException(source, line, charPositionInLine, msg, e));
+    }
+
+    public void validate() throws YangSyntaxErrorException {
+        if (exceptions.isEmpty()) {
+            return;
+        }
+
+        // Single exception: just throw it
+        if (exceptions.size() == 1) {
+            throw exceptions.get(0);
+        }
+
+        final StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        for (YangSyntaxErrorException e : exceptions) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append('\n');
+            }
+
+            sb.append(e.getFormattedMessage());
+        }
+
+        throw new YangSyntaxErrorException(source, 0, 0, sb.toString());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangModelDependencyInfo.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangModelDependencyInfo.java
new file mode 100644 (file)
index 0000000..d06e15e
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * 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.rfc7950.repo;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.ArgumentContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+
+/**
+ * Helper transfer object which holds basic and dependency information for YANG
+ * model.
+ *
+ * <p>
+ * There are two concrete implementations of this interface:
+ * <ul>
+ * <li>{@link ModuleDependencyInfo} - Dependency information for module</li>
+ * <li>{@link SubmoduleDependencyInfo} - Dependency information for submodule</li>
+ * </ul>
+ *
+ * @see ModuleDependencyInfo
+ * @see SubmoduleDependencyInfo
+ */
+public abstract class YangModelDependencyInfo {
+    private static final String BELONGS_TO = YangStmtMapping.BELONGS_TO.getStatementName().getLocalName();
+    private static final String IMPORT = YangStmtMapping.IMPORT.getStatementName().getLocalName();
+    private static final String INCLUDE = YangStmtMapping.INCLUDE.getStatementName().getLocalName();
+    private static final String MODULE = YangStmtMapping.MODULE.getStatementName().getLocalName();
+    private static final String REVISION = YangStmtMapping.REVISION.getStatementName().getLocalName();
+    private static final String REVISION_DATE = YangStmtMapping.REVISION_DATE.getStatementName().getLocalName();
+    private static final String SUBMODULE = YangStmtMapping.SUBMODULE.getStatementName().getLocalName();
+
+    private static final String OPENCONFIG_VERSION = OpenConfigStatements.OPENCONFIG_VERSION.getStatementName()
+            .getLocalName();
+    private static final Splitter COLON_SPLITTER = Splitter.on(":").omitEmptyStrings().trimResults();
+
+    private final String name;
+    private final Revision revision;
+    private final SemVer semVer;
+    private final ImmutableSet<ModuleImport> submoduleIncludes;
+    private final ImmutableSet<ModuleImport> moduleImports;
+    private final ImmutableSet<ModuleImport> dependencies;
+
+    YangModelDependencyInfo(final String name, final String formattedRevision,
+            final ImmutableSet<ModuleImport> imports,
+            final ImmutableSet<ModuleImport> includes) {
+        this(name, formattedRevision, imports, includes, Optional.empty());
+    }
+
+    YangModelDependencyInfo(final String name, final String formattedRevision,
+            final ImmutableSet<ModuleImport> imports,
+            final ImmutableSet<ModuleImport> includes,
+            final Optional<SemVer> semVer) {
+        this.name = name;
+        this.revision = Revision.ofNullable(formattedRevision).orElse(null);
+        this.moduleImports = imports;
+        this.submoduleIncludes = includes;
+        this.dependencies = ImmutableSet.<ModuleImport>builder()
+                .addAll(moduleImports).addAll(submoduleIncludes).build();
+        this.semVer = semVer.orElse(null);
+    }
+
+    /**
+     * Returns immutable collection of all module imports. This collection contains both <code>import</code> statements
+     * and <code>include</code> statements for submodules.
+     *
+     * @return Immutable collection of imports.
+     */
+    public ImmutableSet<ModuleImport> getDependencies() {
+        return dependencies;
+    }
+
+    /**
+     * Returns model name.
+     *
+     * @return model name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns formatted revision string.
+     *
+     * @return formatted revision string
+     */
+    public String getFormattedRevision() {
+        return revision != null ? revision.toString() : null;
+    }
+
+    /**
+     * Returns revision.
+     *
+     * @return revision, potentially null
+     */
+    public Optional<Revision> getRevision() {
+        return Optional.ofNullable(revision);
+    }
+
+    /**
+     * Returns semantic version of module.
+     *
+     * @return semantic version
+     */
+    public Optional<SemVer> getSemanticVersion() {
+        return Optional.ofNullable(semVer);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(name);
+        result = prime * result + Objects.hashCode(revision);
+        result = prime * result + Objects.hashCode(semVer);
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof YangModelDependencyInfo)) {
+            return false;
+        }
+        final YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
+        return Objects.equals(name, other.name) && Objects.equals(revision, other.revision)
+                && Objects.equals(semVer, other.semVer);
+    }
+
+    /**
+     * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of a YANG model.
+     *
+     * @param source Source identifier
+     * @param tree Abstract syntax tree
+     * @return {@link YangModelDependencyInfo}
+     * @throws YangSyntaxErrorException If the AST is not a valid YANG module/submodule
+     */
+    static @NonNull YangModelDependencyInfo fromAST(final SourceIdentifier source, final ParserRuleContext tree)
+            throws YangSyntaxErrorException {
+
+        if (tree instanceof StatementContext) {
+            final StatementContext rootStatement = (StatementContext) tree;
+            return parseAST(rootStatement, source);
+        }
+
+        throw new YangSyntaxErrorException(source, 0, 0, "Unknown YANG text type");
+    }
+
+    private static @NonNull YangModelDependencyInfo parseAST(final StatementContext rootStatement,
+            final SourceIdentifier source) {
+        final String keyWordText = rootStatement.keyword().getText();
+        if (MODULE.equals(keyWordText)) {
+            return parseModuleContext(rootStatement, source);
+        }
+        if (SUBMODULE.equals(keyWordText)) {
+            return parseSubmoduleContext(rootStatement, source);
+        }
+        throw new IllegalArgumentException("Root of parsed AST must be either module or submodule");
+    }
+
+    /**
+     * Extracts {@link YangModelDependencyInfo} from input stream containing a YANG model. This parsing does not
+     * validate full YANG module, only parses header up to the revisions and imports.
+     *
+     * @param refClass Base search class
+     * @param resourceName resource name, relative to refClass
+     * @return {@link YangModelDependencyInfo}
+     * @throws YangSyntaxErrorException If the resource does not pass syntactic analysis
+     * @throws IOException When the resource cannot be read
+     * @throws IllegalArgumentException
+     *             If input stream is not valid YANG stream
+     */
+    @VisibleForTesting
+    public static YangModelDependencyInfo forResource(final Class<?> refClass, final String resourceName)
+            throws IOException, YangSyntaxErrorException {
+        final YangStatementStreamSource source = YangStatementStreamSource.create(
+            YangTextSchemaSource.forResource(refClass, resourceName));
+        final ParserRuleContext ast = source.getYangAST();
+        checkArgument(ast instanceof StatementContext);
+        return parseAST((StatementContext) ast, source.getIdentifier());
+    }
+
+    private static @NonNull YangModelDependencyInfo parseModuleContext(final StatementContext module,
+            final SourceIdentifier source) {
+        final String name = safeStringArgument(source, module, "module name");
+        final String latestRevision = getLatestRevision(module, source);
+        final Optional<SemVer> semVer = Optional.ofNullable(findSemanticVersion(module, source));
+        final ImmutableSet<ModuleImport> imports = parseImports(module, source);
+        final ImmutableSet<ModuleImport> includes = parseIncludes(module, source);
+
+        return new ModuleDependencyInfo(name, latestRevision, imports, includes, semVer);
+    }
+
+    private static ImmutableSet<ModuleImport> parseImports(final StatementContext module,
+            final SourceIdentifier source) {
+        final Set<ModuleImport> result = new HashSet<>();
+        for (final StatementContext subStatementContext : module.statement()) {
+            if (IMPORT.equals(subStatementContext.keyword().getText())) {
+                final String importedModuleName = safeStringArgument(source, subStatementContext,
+                    "imported module name");
+                final String revisionDateStr = getRevisionDateString(subStatementContext, source);
+                final Revision revisionDate = Revision.ofNullable(revisionDateStr).orElse(null);
+                final SemVer importSemVer = findSemanticVersion(subStatementContext, source);
+                result.add(new ModuleImportImpl(importedModuleName, revisionDate, importSemVer));
+            }
+        }
+        return ImmutableSet.copyOf(result);
+    }
+
+    private static SemVer findSemanticVersion(final StatementContext statement, final SourceIdentifier source) {
+        String semVerString = null;
+        for (final StatementContext subStatement : statement.statement()) {
+            final String subStatementName = trimPrefix(subStatement.keyword().getText());
+            if (OPENCONFIG_VERSION.equals(subStatementName)) {
+                semVerString = safeStringArgument(source,  subStatement, "version string");
+                break;
+            }
+        }
+
+        return Strings.isNullOrEmpty(semVerString) ? null : SemVer.valueOf(semVerString);
+    }
+
+
+    private static String trimPrefix(final String identifier) {
+        final List<String> namesParts = COLON_SPLITTER.splitToList(identifier);
+        if (namesParts.size() == 2) {
+            return namesParts.get(1);
+        }
+        return identifier;
+    }
+
+
+    private static ImmutableSet<ModuleImport> parseIncludes(final StatementContext module,
+            final SourceIdentifier source) {
+        final Set<ModuleImport> result = new HashSet<>();
+        for (final StatementContext subStatementContext : module.statement()) {
+            if (INCLUDE.equals(subStatementContext.keyword().getText())) {
+                final String revisionDateStr = getRevisionDateString(subStatementContext, source);
+                final String IncludeModuleName = safeStringArgument(source, subStatementContext,
+                    "included submodule name");
+                final Revision revisionDate = Revision.ofNullable(revisionDateStr).orElse(null);
+                result.add(new ModuleImportImpl(IncludeModuleName, revisionDate));
+            }
+        }
+        return ImmutableSet.copyOf(result);
+    }
+
+    private static String getRevisionDateString(final StatementContext importStatement, final SourceIdentifier source) {
+        String revisionDateStr = null;
+        for (final StatementContext importSubStatement : importStatement.statement()) {
+            if (REVISION_DATE.equals(importSubStatement.keyword().getText())) {
+                revisionDateStr = safeStringArgument(source, importSubStatement, "imported module revision-date");
+            }
+        }
+        return revisionDateStr;
+    }
+
+    public static String getLatestRevision(final StatementContext module, final SourceIdentifier source) {
+        String latestRevision = null;
+        for (final StatementContext subStatementContext : module.statement()) {
+            if (REVISION.equals(subStatementContext.keyword().getText())) {
+                final String currentRevision = safeStringArgument(source, subStatementContext, "revision date");
+                if (latestRevision == null || latestRevision.compareTo(currentRevision) < 0) {
+                    latestRevision = currentRevision;
+                }
+            }
+        }
+        return latestRevision;
+    }
+
+    private static @NonNull YangModelDependencyInfo parseSubmoduleContext(final StatementContext submodule,
+            final SourceIdentifier source) {
+        final String name = safeStringArgument(source, submodule, "submodule name");
+        final String belongsTo = parseBelongsTo(submodule, source);
+
+        final String latestRevision = getLatestRevision(submodule, source);
+        final ImmutableSet<ModuleImport> imports = parseImports(submodule, source);
+        final ImmutableSet<ModuleImport> includes = parseIncludes(submodule, source);
+
+        return new SubmoduleDependencyInfo(name, latestRevision, belongsTo, imports, includes);
+    }
+
+    private static String parseBelongsTo(final StatementContext submodule, final SourceIdentifier source) {
+        for (final StatementContext subStatementContext : submodule.statement()) {
+            if (BELONGS_TO.equals(subStatementContext.keyword().getText())) {
+                return safeStringArgument(source, subStatementContext, "belongs-to module name");
+            }
+        }
+        return null;
+    }
+
+    private static String safeStringArgument(final SourceIdentifier source, final StatementContext stmt,
+            final String desc) {
+        final StatementSourceReference ref = getReference(source, stmt);
+        final ArgumentContext arg = stmt.argument();
+        checkArgument(arg != null, "Missing %s at %s", desc, ref);
+        return ArgumentContextUtils.stringFromStringContext(arg, YangVersion.VERSION_1, ref);
+    }
+
+    private static StatementSourceReference getReference(final SourceIdentifier source,
+            final StatementContext context) {
+        return DeclarationInTextSource.atPosition(source.getName(), context.getStart().getLine(),
+            context.getStart().getCharPositionInLine());
+    }
+
+    /**
+     * Dependency information for YANG module.
+     */
+    public static final class ModuleDependencyInfo extends YangModelDependencyInfo {
+        ModuleDependencyInfo(final String name, final String latestRevision, final ImmutableSet<ModuleImport> imports,
+                final ImmutableSet<ModuleImport> includes, final Optional<SemVer> semVer) {
+            super(name, latestRevision, imports, includes, semVer);
+        }
+
+        @Override
+        public String toString() {
+            return "Module [name=" + getName() + ", revision=" + getRevision()
+                + ", semanticVersion=" + getSemanticVersion().orElse(null)
+                + ", dependencies=" + getDependencies()
+                + "]";
+        }
+    }
+
+    /**
+     * Dependency information for submodule, also provides name for parent module.
+     */
+    public static final class SubmoduleDependencyInfo extends YangModelDependencyInfo {
+        private final String belongsTo;
+
+        private SubmoduleDependencyInfo(final String name, final String latestRevision, final String belongsTo,
+                final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
+            super(name, latestRevision, imports, includes);
+            this.belongsTo = belongsTo;
+        }
+
+        /**
+         * Returns name of parent module.
+         */
+        public String getParentModule() {
+            return belongsTo;
+        }
+
+        @Override
+        public String toString() {
+            return "Submodule [name=" + getName() + ", revision="
+                    + getRevision() + ", dependencies=" + getDependencies()
+                    + "]";
+        }
+    }
+
+    /**
+     * Utility implementation of {@link ModuleImport} to be used by {@link YangModelDependencyInfo}.
+     */
+    private static final class ModuleImportImpl implements ModuleImport {
+
+        private final Revision revision;
+        private final SemVer semVer;
+        private final String name;
+
+        ModuleImportImpl(final @NonNull String moduleName, final @Nullable Revision revision) {
+            this(moduleName, revision, null);
+        }
+
+        ModuleImportImpl(final @NonNull String moduleName, final @Nullable Revision revision,
+                final @Nullable SemVer semVer) {
+            this.name = requireNonNull(moduleName, "Module name must not be null.");
+            this.revision = revision;
+            this.semVer = semVer;
+        }
+
+        @Override
+        public String getModuleName() {
+            return name;
+        }
+
+        @Override
+        public Optional<Revision> getRevision() {
+            return Optional.ofNullable(revision);
+        }
+
+        @Override
+        public Optional<SemVer> getSemanticVersion() {
+            return Optional.ofNullable(semVer);
+        }
+
+        @Override
+        public String getPrefix() {
+            return null;
+        }
+
+        @Override
+        public Optional<String> getDescription() {
+            return Optional.empty();
+        }
+
+        @Override
+        public Optional<String> getReference() {
+            return Optional.empty();
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + Objects.hashCode(name);
+            result = prime * result + Objects.hashCode(revision);
+            result = prime * result + Objects.hashCode(semVer);
+            return result;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof ModuleImportImpl)) {
+                return false;
+            }
+            final ModuleImportImpl other = (ModuleImportImpl) obj;
+            return name.equals(other.name) && Objects.equals(revision, other.revision)
+                    && Objects.equals(getSemanticVersion(), other.getSemanticVersion());
+        }
+
+        @Override
+        public String toString() {
+            return "ModuleImportImpl [name=" + name + ", revision="
+                    + QName.formattedRevision(Optional.ofNullable(revision)) + ", semanticVersion=" + semVer + "]";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangStatementStreamSource.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangStatementStreamSource.java
new file mode 100644 (file)
index 0000000..354e8d9
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.repo;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.io.InputStream;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.tree.ErrorNode;
+import org.antlr.v4.runtime.tree.ParseTreeListener;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.antlr.v4.runtime.tree.TerminalNode;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementLexer;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
+import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
+
+/**
+ * This class represents implementation of StatementStreamSource in order to emit YANG statements using supplied
+ * StatementWriter.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class YangStatementStreamSource extends AbstractIdentifiable<SourceIdentifier>
+        implements StatementStreamSource {
+    private static final ParseTreeListener MAKE_IMMUTABLE_LISTENER = new ParseTreeListener() {
+        @Override
+        public void enterEveryRule(final ParserRuleContext ctx) {
+            // No-op
+        }
+
+        @Override
+        public void exitEveryRule(final ParserRuleContext ctx) {
+            ctx.children = ctx.children == null ? ImmutableList.of() : ImmutableList.copyOf(ctx.children);
+        }
+
+        @Override
+        public void visitTerminal(final TerminalNode node) {
+            // No-op
+        }
+
+        @Override
+        public void visitErrorNode(final ErrorNode node) {
+            // No-op
+        }
+    };
+
+    private final StatementContext context;
+    private final String sourceName;
+
+    private YangStatementStreamSource(final SourceIdentifier identifier,  final StatementContext context,
+            final String sourceName) {
+        super(identifier);
+        this.context = requireNonNull(context);
+        this.sourceName = sourceName;
+    }
+
+    /**
+     * Create a {@link YangStatementStreamSource} for a {@link YangTextSchemaSource}.
+     *
+     * @param source YangTextSchemaSource, must not be null
+     * @return A new {@link YangStatementStreamSource}
+     * @throws IOException When we fail to read the source
+     * @throws YangSyntaxErrorException If the source fails basic parsing
+     */
+    public static YangStatementStreamSource create(final YangTextSchemaSource source) throws IOException,
+            YangSyntaxErrorException {
+        final StatementContext context;
+        try (InputStream stream = source.openStream()) {
+            context = parseYangSource(source.getIdentifier(), stream);
+        }
+
+        return new YangStatementStreamSource(source.getIdentifier(), context, source.getSymbolicName().orElse(null));
+    }
+
+    /**
+     * Create a {@link YangStatementStreamSource} for a {@link ASTSchemaSource}.
+     *
+     * @param source YangTextSchemaSource, must not be null
+     * @return A new {@link YangStatementStreamSource}
+     */
+    public static YangStatementStreamSource create(final ASTSchemaSource source) {
+        final ParserRuleContext ast = source.getAST();
+        checkArgument(ast instanceof StatementContext,
+                "Unsupported context class %s for source %s", ast.getClass(), source.getIdentifier());
+        return create(source.getIdentifier(), (StatementContext) ast, source.getSymbolicName().orElse(null));
+    }
+
+    public static YangStatementStreamSource create(final SourceIdentifier identifier, final StatementContext context,
+        final String symbolicName) {
+        return new YangStatementStreamSource(identifier, context, symbolicName);
+    }
+
+    @Override
+    public void writePreLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
+        new StatementContextVisitor(sourceName, writer, stmtDef, null, YangVersion.VERSION_1).visit(context);
+    }
+
+    @Override
+    public void writeLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
+            final PrefixToModule preLinkagePrefixes, final YangVersion yangVersion) {
+        new StatementContextVisitor(sourceName, writer, stmtDef, preLinkagePrefixes, yangVersion) {
+            @Override
+            StatementDefinition resolveStatement(final QNameModule module, final String localName) {
+                return stmtDef.getByNamespaceAndLocalName(module.getNamespace(), localName);
+            }
+        }.visit(context);
+    }
+
+    @Override
+    public void writeLinkageAndStatementDefinitions(final StatementWriter writer,
+            final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes, final YangVersion yangVersion) {
+        new StatementContextVisitor(sourceName, writer, stmtDef, prefixes, yangVersion).visit(context);
+    }
+
+    @Override
+    public void writeFull(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
+            final PrefixToModule prefixes, final YangVersion yangVersion) {
+        new StatementContextVisitor(sourceName, writer, stmtDef, prefixes, yangVersion) {
+            @Override
+            QName getValidStatementDefinition(final String keywordText, final StatementSourceReference ref) {
+                return SourceException.throwIfNull(super.getValidStatementDefinition(keywordText, ref), ref,
+                    "%s is not a YANG statement or use of extension.", keywordText);
+            }
+        }.visit(context);
+    }
+
+    public ParserRuleContext getYangAST() {
+        return context;
+    }
+
+    private static StatementContext parseYangSource(final SourceIdentifier source, final InputStream stream)
+            throws IOException, YangSyntaxErrorException {
+        final YangStatementLexer lexer = new YangStatementLexer(CharStreams.fromStream(stream));
+        final YangStatementParser parser = new YangStatementParser(new CommonTokenStream(lexer));
+        // disconnect from console error output
+        lexer.removeErrorListeners();
+        parser.removeErrorListeners();
+
+        final YangErrorListener errorListener = new YangErrorListener(source);
+        lexer.addErrorListener(errorListener);
+        parser.addErrorListener(errorListener);
+
+        final StatementContext result = parser.statement();
+        errorListener.validate();
+
+        // Walk the resulting tree and replace each children with an immutable list, lowering memory requirements
+        // and making sure the resulting tree will not get accidentally modified. An alternative would be to use
+        // org.antlr.v4.runtime.Parser.TrimToSizeListener, but that does not make the tree immutable.
+        ParseTreeWalker.DEFAULT.walk(MAKE_IMMUTABLE_LISTENER, result);
+
+        return result;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YinStatementStreamSource.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YinStatementStreamSource.java
new file mode 100644 (file)
index 0000000..f00fdd2
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.repo;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.parser.rfc7950.repo.StatementSourceReferenceHandler.extractRef;
+
+import com.google.common.annotations.Beta;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Optional;
+import javax.xml.transform.TransformerException;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YinDomSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinXmlSchemaSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
+import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter.ResumedStatement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * A {@link StatementStreamSource} based on a {@link YinXmlSchemaSource}. Internal implementation works on top
+ * of {@link YinDomSchemaSource} and its DOM document.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class YinStatementStreamSource extends AbstractIdentifiable<SourceIdentifier>
+        implements StatementStreamSource {
+    private static final Logger LOG = LoggerFactory.getLogger(YinStatementStreamSource.class);
+    private static final LoadingCache<String, URI> URI_CACHE = CacheBuilder.newBuilder().weakValues().build(
+        new CacheLoader<String, URI>() {
+            @Override
+            public URI load(final String key) throws URISyntaxException {
+                return new URI(key);
+            }
+        });
+    private final Node root;
+
+    private YinStatementStreamSource(final SourceIdentifier identifier, final Node root) {
+        super(identifier);
+        this.root = requireNonNull(root);
+    }
+
+    public static StatementStreamSource create(final YinXmlSchemaSource source) throws TransformerException {
+        return create(YinDomSchemaSource.transform(source));
+    }
+
+    public static StatementStreamSource create(final YinDomSchemaSource source) {
+        return new YinStatementStreamSource(source.getIdentifier(), source.getSource().getNode());
+    }
+
+    private static StatementDefinition getValidDefinition(final Node node, final StatementWriter writer,
+            final QNameToStatementDefinition stmtDef, final StatementSourceReference ref) {
+        final URI uri = URI_CACHE.getUnchecked(node.getNamespaceURI());
+        final StatementDefinition def = stmtDef.getByNamespaceAndLocalName(uri, node.getLocalName());
+
+        if (def == null) {
+            SourceException.throwIf(writer.getPhase().equals(ModelProcessingPhase.FULL_DECLARATION), ref,
+                "%s is not a YIN statement or use of extension.", node.getLocalName());
+        }
+        return def;
+    }
+
+    private static boolean processAttribute(final int childId, final Attr attr, final StatementWriter writer,
+            final QNameToStatementDefinition stmtDef, final StatementSourceReference ref) {
+        final Optional<? extends ResumedStatement> optResumed = writer.resumeStatement(childId);
+        if (optResumed.isPresent()) {
+            final ResumedStatement resumed = optResumed.get();
+            checkState(resumed.isFullyDefined(), "Statement %s is not fully defined", resumed);
+            return true;
+        }
+
+        final StatementDefinition def = getValidDefinition(attr, writer, stmtDef, ref);
+        if (def == null) {
+            return false;
+        }
+
+        final String value = attr.getValue();
+        writer.startStatement(childId, def.getStatementName(), value.isEmpty() ? null : value, ref);
+        writer.storeStatement(0, true);
+        writer.endStatement(ref);
+        return true;
+    }
+
+    private static String getArgValue(final Element element, final QName argName, final boolean yinElement) {
+        if (yinElement) {
+            final NodeList children = element.getElementsByTagNameNS(argName.getNamespace().toString(),
+                argName.getLocalName());
+            if (children.getLength() == 0) {
+                return null;
+            }
+            return children.item(0).getTextContent();
+        }
+
+        final Attr attr = element.getAttributeNode(argName.getLocalName());
+        if (attr == null) {
+            return null;
+        }
+
+        return attr.getValue();
+    }
+
+    private static boolean processElement(final int childId, final Element element, final StatementWriter writer,
+            final QNameToStatementDefinition stmtDef) {
+
+        final Optional<? extends ResumedStatement> optResumed = writer.resumeStatement(childId);
+        final StatementSourceReference ref;
+        final QName argName;
+        final boolean allAttrs;
+        final boolean allElements;
+        if (optResumed.isPresent()) {
+            final ResumedStatement resumed = optResumed.get();
+            if (resumed.isFullyDefined()) {
+                return true;
+            }
+
+            final StatementDefinition def = resumed.getDefinition();
+            ref = resumed.getSourceReference();
+            final Optional<ArgumentDefinition> optArgDef = def.getArgumentDefinition();
+            if (optArgDef.isPresent()) {
+                final ArgumentDefinition argDef = optArgDef.get();
+                argName = argDef.getArgumentName();
+                allAttrs = argDef.isYinElement();
+                allElements = !allAttrs;
+            } else {
+                argName = null;
+                allAttrs = false;
+                allElements = false;
+            }
+        } else {
+            ref = extractRef(element);
+            final StatementDefinition def = getValidDefinition(element, writer, stmtDef, ref);
+            if (def == null) {
+                LOG.debug("Skipping element {}", element);
+                return false;
+            }
+
+            final String argValue;
+            final Optional<ArgumentDefinition> optArgDef = def.getArgumentDefinition();
+            if (optArgDef.isPresent()) {
+                final ArgumentDefinition argDef = optArgDef.get();
+                argName = argDef.getArgumentName();
+                allAttrs = argDef.isYinElement();
+                allElements = !allAttrs;
+
+                argValue = getArgValue(element, argName, allAttrs);
+                SourceException.throwIfNull(argValue, ref, "Statement {} is missing mandatory argument %s",
+                    def.getStatementName(), argName);
+            } else {
+                argName = null;
+                argValue = null;
+                allAttrs = false;
+                allElements = false;
+            }
+
+            writer.startStatement(childId, def.getStatementName(), argValue, ref);
+        }
+
+        // Child counter
+        int childCounter = 0;
+        boolean fullyDefined = true;
+
+        // First process any statements defined as attributes. We need to skip argument, if present
+        final NamedNodeMap attributes = element.getAttributes();
+        if (attributes != null) {
+            for (int i = 0, len = attributes.getLength(); i < len; ++i) {
+                final Attr attr = (Attr) attributes.item(i);
+                if ((allAttrs || !isArgument(argName, attr))
+                        && !processAttribute(childCounter++, attr, writer, stmtDef, ref)) {
+                    fullyDefined = false;
+                }
+            }
+        }
+
+        // Now process child elements, if present
+        final NodeList children = element.getChildNodes();
+        for (int i = 0, len = children.getLength(); i < len; ++i) {
+            final Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE && (allElements || !isArgument(argName, child))
+                    && !processElement(childCounter++, (Element) child, writer, stmtDef)) {
+                fullyDefined = false;
+            }
+        }
+
+        writer.storeStatement(childCounter, fullyDefined);
+        writer.endStatement(ref);
+        return fullyDefined;
+    }
+
+    private static boolean isArgument(final QName argName, final Node node) {
+        return argName != null && argName.getLocalName().equals(node.getLocalName()) && node.getPrefix() == null;
+    }
+
+    private void walkTree(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
+        final NodeList children = root.getChildNodes();
+
+        int childCounter = 0;
+        for (int i = 0, len = children.getLength(); i < len; ++i) {
+            final Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                processElement(childCounter++, (Element) child, writer, stmtDef);
+            }
+        }
+    }
+
+    @Override
+    public void writePreLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
+        walkTree(writer, stmtDef);
+    }
+
+    @Override
+    public void writeLinkage(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
+            final PrefixToModule preLinkagePrefixes, final YangVersion yangVersion) {
+        walkTree(writer, stmtDef);
+    }
+
+    @Override
+    public void writeLinkageAndStatementDefinitions(final StatementWriter writer,
+            final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes,  final YangVersion yangVersion) {
+        walkTree(writer, stmtDef);
+    }
+
+    @Override
+    public void writeFull(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
+            final PrefixToModule prefixes, final YangVersion yangVersion) {
+        walkTree(writer, stmtDef);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YinTextToDomTransformer.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YinTextToDomTransformer.java
new file mode 100644 (file)
index 0000000..aa6c332
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.repo;
+
+import com.google.common.annotations.Beta;
+import com.google.common.util.concurrent.Futures;
+import java.io.IOException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.YinDomSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
+import org.opendaylight.yangtools.yang.model.repo.util.SchemaSourceTransformer;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * A {@link SchemaSourceTransformer} which handles translation of models from
+ * {@link YinTextSchemaSource} representation into {@link YinDomSchemaSource}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class YinTextToDomTransformer extends SchemaSourceTransformer<YinTextSchemaSource, YinDomSchemaSource> {
+    private YinTextToDomTransformer(final SchemaRepository provider, final SchemaSourceRegistry consumer) {
+        super(provider, YinTextSchemaSource.class, consumer, YinDomSchemaSource.class,
+            input -> Futures.immediateFuture(transformSource(input)));
+    }
+
+    public static YinTextToDomTransformer create(final SchemaRepository provider, final SchemaSourceRegistry consumer) {
+        return new YinTextToDomTransformer(provider, consumer);
+    }
+
+    public static YinDomSchemaSource transformSource(final YinTextSchemaSource source) throws SAXException,
+            IOException {
+        final Document doc = UntrustedXML.newDocumentBuilder().newDocument();
+        final SAXParser parser = UntrustedXML.newSAXParser();
+        final DefaultHandler handler = new StatementSourceReferenceHandler(doc, null);
+        parser.parse(source.openStream(), handler);
+        return YinDomSchemaSource.create(source.getIdentifier(), new DOMSource(doc));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractConstraintEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractConstraintEffectiveStatement.java
new file mode 100644 (file)
index 0000000..72069a5
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.ConstraintMetaDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorAppTagEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModifierEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+@Beta
+public abstract class AbstractConstraintEffectiveStatement<A, D extends DeclaredStatement<A>> extends
+        DeclaredEffectiveStatementBase<A, D> implements ConstraintMetaDefinition {
+    private final String description;
+    private final String reference;
+    private final String errorAppTag;
+    private final String errorMessage;
+    private final ModifierKind modifier;
+    private final A constraints;
+
+    protected AbstractConstraintEffectiveStatement(final StmtContext<A, D, ?> ctx) {
+        super(ctx);
+        String descriptionInit = null;
+        String referenceInit = null;
+        String errorAppTagInit = null;
+        String errorMessageInit = null;
+        ModifierKind modifierInit = null;
+
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof DescriptionEffectiveStatement) {
+                descriptionInit = ((DescriptionEffectiveStatement) stmt).argument();
+            }
+            if (stmt instanceof ReferenceEffectiveStatement) {
+                referenceInit = ((ReferenceEffectiveStatement) stmt).argument();
+            }
+            if (stmt instanceof ErrorAppTagEffectiveStatement) {
+                errorAppTagInit = ((ErrorAppTagEffectiveStatement) stmt).argument();
+            }
+            if (stmt instanceof ErrorMessageEffectiveStatement) {
+                errorMessageInit = ((ErrorMessageEffectiveStatement) stmt).argument();
+            }
+            if (stmt instanceof ModifierEffectiveStatement) {
+                modifierInit = ((ModifierEffectiveStatement) stmt).argument();
+            }
+        }
+
+        this.description = descriptionInit;
+        this.reference = referenceInit;
+        this.errorAppTag = errorAppTagInit;
+        this.errorMessage = errorMessageInit;
+        this.modifier = modifierInit;
+        this.constraints = createConstraints(super.argument());
+    }
+
+    @Override
+    public final A argument() {
+        return constraints;
+    }
+
+    public final boolean isCustomizedStatement() {
+        return this.description != null || this.reference != null || this.errorAppTag != null
+                || this.errorMessage != null || this.modifier != null;
+    }
+
+    @Override
+    public final Optional<String> getDescription() {
+        return Optional.ofNullable(description);
+    }
+
+    public final ModifierKind getModifier() {
+        return modifier;
+    }
+
+    @Override
+    public final Optional<String> getReference() {
+        return Optional.ofNullable(reference);
+    }
+
+    @Override
+    public final Optional<String> getErrorAppTag() {
+        return Optional.ofNullable(errorAppTag);
+    }
+
+    @Override
+    public final Optional<String> getErrorMessage() {
+        return Optional.ofNullable(errorMessage);
+    }
+
+    protected abstract A createConstraints(A argument);
+}
+
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveContainerSchemaNode.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveContainerSchemaNode.java
new file mode 100644 (file)
index 0000000..5965c5e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.compat.ActionNodeContainerCompat;
+import org.opendaylight.yangtools.yang.model.api.stmt.compat.NotificationNodeContainerCompat;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public abstract class AbstractEffectiveContainerSchemaNode<D extends DeclaredStatement<QName>>
+        extends AbstractEffectiveSimpleDataNodeContainer<D>
+        implements ContainerSchemaNode, ActionNodeContainerCompat<QName, D>, NotificationNodeContainerCompat<QName, D> {
+    private final ImmutableSet<MustDefinition> mustConstraints;
+
+    protected AbstractEffectiveContainerSchemaNode(final StmtContext<QName, D, ?> ctx) {
+        super(ctx);
+        mustConstraints = ImmutableSet.copyOf(allSubstatementsOfType(MustDefinition.class));
+    }
+
+    @Override
+    public final Collection<MustDefinition> getMustConstraints() {
+        return mustConstraints;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveDataSchemaNode.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveDataSchemaNode.java
new file mode 100644 (file)
index 0000000..f085bcd
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public abstract class AbstractEffectiveDataSchemaNode<D extends DeclaredStatement<QName>> extends
+        AbstractEffectiveSchemaNode<D> implements DataSchemaNode {
+
+    private final RevisionAwareXPath whenCondition;
+    private final boolean configuration;
+    private final boolean addedByUses;
+    private final boolean augmenting;
+
+    protected AbstractEffectiveDataSchemaNode(final StmtContext<QName, D, ?> ctx) {
+        super(ctx);
+        this.configuration = ctx.isConfiguration();
+        whenCondition = findFirstEffectiveSubstatementArgument(WhenEffectiveStatement.class).orElse(null);
+
+        // initCopyType
+        final CopyHistory originalHistory = ctx.getCopyHistory();
+        if (originalHistory.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) {
+            this.augmenting = true;
+            this.addedByUses = true;
+        } else {
+            this.augmenting = originalHistory.contains(CopyType.ADDED_BY_AUGMENTATION);
+            this.addedByUses = originalHistory.contains(CopyType.ADDED_BY_USES);
+        }
+    }
+
+    @Deprecated
+    @Override
+    public final boolean isAugmenting() {
+        return augmenting;
+    }
+
+    @Deprecated
+    @Override
+    public final boolean isAddedByUses() {
+        return addedByUses;
+    }
+
+    @Override
+    public final boolean isConfiguration() {
+        return configuration;
+    }
+
+    @Override
+    public final Optional<RevisionAwareXPath> getWhenCondition() {
+        return Optional.ofNullable(whenCondition);
+    }
+
+    /**
+     * Reset {@link #isAugmenting()} to false.
+     *
+     * @deprecated This method is a violation of immutable contract and is a side-effect of bad/incomplete lifecycle,
+     *             which needs to be fixed. Do not introduce new callers. This deficiency is tracked in YANGTOOLS-724.
+     */
+    @Deprecated
+    public final void resetAugmenting() {
+        // Intentional no-op
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveDocumentedDataNodeContainer.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveDocumentedDataNodeContainer.java
new file mode 100644 (file)
index 0000000..5dfc8ec
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public abstract class AbstractEffectiveDocumentedDataNodeContainer<A, D extends DeclaredStatement<A>>
+        extends AbstractSchemaEffectiveDocumentedNode<A, D> implements DataNodeContainer {
+
+    private final ImmutableMap<QName, DataSchemaNode> childNodes;
+    private final ImmutableSet<GroupingDefinition> groupings;
+    private final ImmutableSet<UsesNode> uses;
+    private final ImmutableSet<TypeDefinition<?>> typeDefinitions;
+    private final ImmutableSet<DataSchemaNode> publicChildNodes;
+
+    protected AbstractEffectiveDocumentedDataNodeContainer(final StmtContext<A, D, ?> ctx) {
+        super(ctx);
+
+        Map<QName, DataSchemaNode> mutableChildNodes = new LinkedHashMap<>();
+        Set<GroupingDefinition> mutableGroupings = new HashSet<>();
+        Set<UsesNode> mutableUses = new HashSet<>();
+        Set<TypeDefinition<?>> mutableTypeDefinitions = new LinkedHashSet<>();
+        Set<DataSchemaNode> mutablePublicChildNodes = new LinkedHashSet<>();
+
+        for (EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof DataSchemaNode) {
+                final DataSchemaNode dataSchemaNode = (DataSchemaNode) stmt;
+                if (mutableChildNodes.containsKey(dataSchemaNode.getQName())) {
+                    throw EffectiveStmtUtils.createNameCollisionSourceException(ctx, stmt);
+                }
+
+                mutableChildNodes.put(dataSchemaNode.getQName(), dataSchemaNode);
+                mutablePublicChildNodes.add(dataSchemaNode);
+            }
+            if (stmt instanceof UsesNode) {
+                UsesNode usesNode = (UsesNode) stmt;
+                if (!mutableUses.contains(usesNode)) {
+                    mutableUses.add(usesNode);
+                } else {
+                    throw EffectiveStmtUtils.createNameCollisionSourceException(ctx, stmt);
+                }
+            }
+            if (stmt instanceof TypedefEffectiveStatement) {
+                TypedefEffectiveStatement typeDef = (TypedefEffectiveStatement) stmt;
+                TypeDefinition<?> type = typeDef.getTypeDefinition();
+                if (!mutableTypeDefinitions.contains(type)) {
+                    mutableTypeDefinitions.add(type);
+                } else {
+                    throw EffectiveStmtUtils.createNameCollisionSourceException(ctx, stmt);
+                }
+            }
+            if (stmt instanceof GroupingDefinition) {
+                GroupingDefinition grp = (GroupingDefinition) stmt;
+                if (!mutableGroupings.contains(grp)) {
+                    mutableGroupings.add(grp);
+                } else {
+                    throw EffectiveStmtUtils.createNameCollisionSourceException(ctx, stmt);
+                }
+            }
+        }
+
+        this.childNodes = ImmutableMap.copyOf(mutableChildNodes);
+        this.groupings = ImmutableSet.copyOf(mutableGroupings);
+        this.publicChildNodes = ImmutableSet.copyOf(mutablePublicChildNodes);
+        this.typeDefinitions = ImmutableSet.copyOf(mutableTypeDefinitions);
+        this.uses = ImmutableSet.copyOf(mutableUses);
+    }
+
+    @Override
+    public final Set<TypeDefinition<?>> getTypeDefinitions() {
+        return typeDefinitions;
+    }
+
+    @Override
+    public final Set<DataSchemaNode> getChildNodes() {
+        return publicChildNodes;
+    }
+
+    @Override
+    public final Set<GroupingDefinition> getGroupings() {
+        return groupings;
+    }
+
+    @Override
+    public final Optional<DataSchemaNode> findDataChildByName(final QName name) {
+        // Child nodes are keyed by their container name, so we can do a direct lookup
+        return Optional.ofNullable(childNodes.get(requireNonNull(name)));
+    }
+
+    @Override
+    public Set<UsesNode> getUses() {
+        return uses;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveDocumentedNode.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveDocumentedNode.java
new file mode 100644 (file)
index 0000000..d99e7d8
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public abstract class AbstractEffectiveDocumentedNode<A, D extends DeclaredStatement<A>>
+        extends DeclaredEffectiveStatementBase<A, D> implements DocumentedNode.WithStatus {
+
+    private final String description;
+    private final String reference;
+    private final @NonNull Status status;
+
+    /**
+     * Constructor.
+     *
+     * @param ctx
+     *            context of statement.
+     */
+    protected AbstractEffectiveDocumentedNode(final StmtContext<A, D, ?> ctx) {
+        super(ctx);
+        description = findFirstEffectiveSubstatementArgument(DescriptionEffectiveStatement.class).orElse(null);
+        reference = findFirstEffectiveSubstatementArgument(ReferenceEffectiveStatement.class).orElse(null);
+        status = findFirstEffectiveSubstatementArgument(StatusEffectiveStatement.class).orElse(Status.CURRENT);
+    }
+
+    @Override
+    public final Optional<String> getDescription() {
+        return Optional.ofNullable(description);
+    }
+
+    @Override
+    public final Optional<String> getReference() {
+        return Optional.ofNullable(reference);
+    }
+
+    @Override
+    public final Status getStatus() {
+        return status;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveModule.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveModule.java
new file mode 100644 (file)
index 0000000..8194d44
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Verify;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigVersionEffectiveStatement;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Deviation;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContactEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OrganizationEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YangVersionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.compat.NotificationNodeContainerCompat;
+import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.source.IncludedSubmoduleNameToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+@Beta
+public abstract class AbstractEffectiveModule<D extends DeclaredStatement<String>> extends
+        AbstractSchemaEffectiveDocumentedNode<String, D> implements Module, MutableStatement,
+        NotificationNodeContainerCompat<String, D> {
+    private final String name;
+    private final String prefix;
+    private final YangVersion yangVersion;
+    private final String organization;
+    private final String contact;
+    private final ImmutableSet<ModuleImport> imports;
+    private final ImmutableSet<FeatureDefinition> features;
+    private final @NonNull ImmutableSet<NotificationDefinition> notifications;
+    private final ImmutableSet<AugmentationSchemaNode> augmentations;
+    private final ImmutableSet<RpcDefinition> rpcs;
+    private final ImmutableSet<Deviation> deviations;
+    private final ImmutableList<ExtensionDefinition> extensionNodes;
+    private final ImmutableSet<IdentitySchemaNode> identities;
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
+    private final ImmutableMap<QName, DataSchemaNode> childNodes;
+    private final ImmutableSet<GroupingDefinition> groupings;
+    private final ImmutableSet<UsesNode> uses;
+    private final ImmutableSet<TypeDefinition<?>> typeDefinitions;
+    private final ImmutableSet<DataSchemaNode> publicChildNodes;
+    private final SemVer semanticVersion;
+
+    private Set<StmtContext<?, SubmoduleStatement, EffectiveStatement<String, SubmoduleStatement>>>
+        submoduleContextsToBuild;
+    private ImmutableSet<Module> submodules;
+    private boolean sealed;
+
+    protected AbstractEffectiveModule(final StmtContext<String, D, ? extends EffectiveStatement<String, ?>> ctx) {
+        super(ctx);
+
+        this.name = argument();
+
+        final EffectiveStatement<?, ?> parentOfPrefix;
+        if (ctx.getPublicDefinition() == YangStmtMapping.SUBMODULE) {
+            final Optional<BelongsToEffectiveStatement> optParent =
+                    findFirstEffectiveSubstatement(BelongsToEffectiveStatement.class);
+            SourceException.throwIf(!optParent.isPresent(), ctx.getStatementSourceReference(),
+                    "Unable to find belongs-to statement in submodule %s.", ctx.getStatementArgument());
+            parentOfPrefix = optParent.get();
+        } else {
+            parentOfPrefix = this;
+        }
+
+        final Optional<@NonNull PrefixEffectiveStatement> prefixStmt = parentOfPrefix.findFirstEffectiveSubstatement(
+            PrefixEffectiveStatement.class);
+        SourceException.throwIf(!prefixStmt.isPresent(), ctx.getStatementSourceReference(),
+                "Unable to resolve prefix for module or submodule %s.", ctx.getStatementArgument());
+        this.prefix = prefixStmt.get().argument();
+        this.yangVersion = findFirstEffectiveSubstatementArgument(YangVersionEffectiveStatement.class)
+                .orElse(YangVersion.VERSION_1);
+        this.semanticVersion = findFirstEffectiveSubstatementArgument(OpenConfigVersionEffectiveStatement.class)
+                .orElse(null);
+        this.organization = findFirstEffectiveSubstatementArgument(OrganizationEffectiveStatement.class)
+                .orElse(null);
+        this.contact = findFirstEffectiveSubstatementArgument(ContactEffectiveStatement.class)
+                .orElse(null);
+
+        // init submodules and substatements of submodules
+        final List<EffectiveStatement<?, ?>> substatementsOfSubmodules;
+        final Map<String, StmtContext<?, ?, ?>> includedSubmodulesMap = ctx
+                .getAllFromCurrentStmtCtxNamespace(IncludedSubmoduleNameToModuleCtx.class);
+
+        if (includedSubmodulesMap == null || includedSubmodulesMap.isEmpty()) {
+            this.submodules = ImmutableSet.of();
+            this.submoduleContextsToBuild = ImmutableSet.of();
+            substatementsOfSubmodules = ImmutableList.of();
+        } else if (YangStmtMapping.MODULE.equals(ctx.getPublicDefinition())) {
+            /*
+             * Aggregation of substatements from submodules should be done only
+             * for modules. In case of submodules it does not make sense because
+             * of possible circular chains of includes between submodules.
+             */
+            final Set<Module> submodulesInit = new HashSet<>();
+            final List<EffectiveStatement<?, ?>> substatementsOfSubmodulesInit = new ArrayList<>();
+            for (final StmtContext<?, ?, ?> submoduleCtx : includedSubmodulesMap.values()) {
+                final EffectiveStatement<?, ?> submodule = submoduleCtx.buildEffective();
+                Verify.verify(submodule instanceof SubmoduleEffectiveStatement);
+                Verify.verify(submodule instanceof Module, "Submodule statement %s is not a Module", submodule);
+                submodulesInit.add((Module) submodule);
+                substatementsOfSubmodulesInit.addAll(submodule.effectiveSubstatements().stream()
+                        .filter(sub -> sub instanceof SchemaNode || sub instanceof DataNodeContainer)
+                        .collect(Collectors.toList()));
+            }
+
+            this.submodules = ImmutableSet.copyOf(submodulesInit);
+            this.submoduleContextsToBuild = ImmutableSet.of();
+            substatementsOfSubmodules = ImmutableList.copyOf(substatementsOfSubmodulesInit);
+        } else {
+            /*
+             * Because of possible circular chains of includes between submodules we can
+             * collect only submodule contexts here and then build them during
+             * sealing of this statement.
+             */
+            final Set<StmtContext<?, SubmoduleStatement, EffectiveStatement<String, SubmoduleStatement>>>
+                submoduleContextsInit = new HashSet<>();
+            for (final StmtContext<?, ?, ?> submoduleCtx : includedSubmodulesMap.values()) {
+                submoduleContextsInit.add(
+                    (StmtContext<?, SubmoduleStatement, EffectiveStatement<String, SubmoduleStatement>>)submoduleCtx);
+            }
+
+            this.submoduleContextsToBuild = ImmutableSet.copyOf(submoduleContextsInit);
+            substatementsOfSubmodules = ImmutableList.of();
+        }
+
+        if (!submoduleContextsToBuild.isEmpty()) {
+            ((Mutable<?, ?, ?>) ctx).addMutableStmtToSeal(this);
+            sealed = false;
+        } else {
+            sealed = true;
+        }
+
+        // init substatements collections
+        final List<EffectiveStatement<?, ?>> effectiveSubstatements = new ArrayList<>();
+        effectiveSubstatements.addAll(effectiveSubstatements());
+        effectiveSubstatements.addAll(substatementsOfSubmodules);
+
+        final List<UnknownSchemaNode> unknownNodesInit = new ArrayList<>();
+        final Set<AugmentationSchemaNode> augmentationsInit = new LinkedHashSet<>();
+        final Set<ModuleImport> importsInit = new HashSet<>();
+        final Set<NotificationDefinition> notificationsInit = new HashSet<>();
+        final Set<RpcDefinition> rpcsInit = new HashSet<>();
+        final Set<Deviation> deviationsInit = new HashSet<>();
+        final Set<IdentitySchemaNode> identitiesInit = new HashSet<>();
+        final Set<FeatureDefinition> featuresInit = new HashSet<>();
+        final List<ExtensionDefinition> extensionNodesInit = new ArrayList<>();
+
+        final Map<QName, DataSchemaNode> mutableChildNodes = new LinkedHashMap<>();
+        final Set<GroupingDefinition> mutableGroupings = new HashSet<>();
+        final Set<UsesNode> mutableUses = new HashSet<>();
+        final Set<TypeDefinition<?>> mutableTypeDefinitions = new LinkedHashSet<>();
+        final Set<DataSchemaNode> mutablePublicChildNodes = new LinkedHashSet<>();
+
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements) {
+            if (effectiveStatement instanceof UnknownSchemaNode) {
+                unknownNodesInit.add((UnknownSchemaNode) effectiveStatement);
+            }
+            if (effectiveStatement instanceof AugmentationSchemaNode) {
+                augmentationsInit.add((AugmentationSchemaNode) effectiveStatement);
+            }
+            if (effectiveStatement instanceof ModuleImport) {
+                importsInit.add((ModuleImport) effectiveStatement);
+            }
+            if (effectiveStatement instanceof NotificationDefinition) {
+                notificationsInit.add((NotificationDefinition) effectiveStatement);
+            }
+            if (effectiveStatement instanceof RpcDefinition) {
+                rpcsInit.add((RpcDefinition) effectiveStatement);
+            }
+            if (effectiveStatement instanceof Deviation) {
+                deviationsInit.add((Deviation) effectiveStatement);
+            }
+            if (effectiveStatement instanceof IdentitySchemaNode) {
+                identitiesInit.add((IdentitySchemaNode) effectiveStatement);
+            }
+            if (effectiveStatement instanceof FeatureDefinition) {
+                featuresInit.add((FeatureDefinition) effectiveStatement);
+            }
+            if (effectiveStatement instanceof ExtensionDefinition) {
+                extensionNodesInit.add((ExtensionDefinition) effectiveStatement);
+            }
+            if (effectiveStatement instanceof DataSchemaNode) {
+                final DataSchemaNode dataSchemaNode = (DataSchemaNode) effectiveStatement;
+                if (!mutableChildNodes.containsKey(dataSchemaNode.getQName())) {
+                    mutableChildNodes.put(dataSchemaNode.getQName(), dataSchemaNode);
+                    mutablePublicChildNodes.add(dataSchemaNode);
+                } else {
+                    throw EffectiveStmtUtils.createNameCollisionSourceException(ctx, effectiveStatement);
+                }
+            }
+            if (effectiveStatement instanceof UsesNode) {
+                final UsesNode usesNode = (UsesNode) effectiveStatement;
+                if (!mutableUses.contains(usesNode)) {
+                    mutableUses.add(usesNode);
+                } else {
+                    throw EffectiveStmtUtils.createNameCollisionSourceException(ctx, effectiveStatement);
+                }
+            }
+            if (effectiveStatement instanceof TypedefEffectiveStatement) {
+                final TypedefEffectiveStatement typeDef = (TypedefEffectiveStatement) effectiveStatement;
+                final TypeDefinition<?> type = typeDef.getTypeDefinition();
+                if (!mutableTypeDefinitions.contains(type)) {
+                    mutableTypeDefinitions.add(type);
+                } else {
+                    throw EffectiveStmtUtils.createNameCollisionSourceException(ctx, effectiveStatement);
+                }
+            }
+            if (effectiveStatement instanceof GroupingDefinition) {
+                final GroupingDefinition grp = (GroupingDefinition) effectiveStatement;
+                if (!mutableGroupings.contains(grp)) {
+                    mutableGroupings.add(grp);
+                } else {
+                    throw EffectiveStmtUtils.createNameCollisionSourceException(ctx, effectiveStatement);
+                }
+            }
+        }
+
+        this.unknownNodes = ImmutableList.copyOf(unknownNodesInit);
+        this.augmentations = ImmutableSet.copyOf(augmentationsInit);
+        this.imports = ImmutableSet.copyOf(importsInit);
+        this.notifications = ImmutableSet.copyOf(notificationsInit);
+        this.rpcs = ImmutableSet.copyOf(rpcsInit);
+        this.deviations = ImmutableSet.copyOf(deviationsInit);
+        this.identities = ImmutableSet.copyOf(identitiesInit);
+        this.features = ImmutableSet.copyOf(featuresInit);
+        this.extensionNodes = ImmutableList.copyOf(extensionNodesInit);
+
+        this.childNodes = ImmutableMap.copyOf(mutableChildNodes);
+        this.groupings = ImmutableSet.copyOf(mutableGroupings);
+        this.publicChildNodes = ImmutableSet.copyOf(mutablePublicChildNodes);
+        this.typeDefinitions = ImmutableSet.copyOf(mutableTypeDefinitions);
+        this.uses = ImmutableSet.copyOf(mutableUses);
+
+    }
+
+    @Override
+    public URI getNamespace() {
+        return getQNameModule().getNamespace();
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public Optional<Revision> getRevision() {
+        return getQNameModule().getRevision();
+    }
+
+    @Override
+    public String getPrefix() {
+        return prefix;
+    }
+
+    @Override
+    public YangVersion getYangVersion() {
+        return yangVersion;
+    }
+
+    @Override
+    public Optional<String> getOrganization() {
+        return Optional.ofNullable(organization);
+    }
+
+    @Override
+    public Optional<String> getContact() {
+        return Optional.ofNullable(contact);
+    }
+
+    @Override
+    public Set<ModuleImport> getImports() {
+        return imports;
+    }
+
+    @Override
+    public Set<Module> getSubmodules() {
+        checkState(sealed, "Attempt to get base submodules from unsealed submodule effective statement %s",
+            getQNameModule());
+        return submodules;
+    }
+
+    @Override
+    public Set<FeatureDefinition> getFeatures() {
+        return features;
+    }
+
+    @Override
+    public Set<NotificationDefinition> getNotifications() {
+        return notifications;
+    }
+
+    @Override
+    public Set<AugmentationSchemaNode> getAugmentations() {
+        return augmentations;
+    }
+
+    @Override
+    public Set<RpcDefinition> getRpcs() {
+        return rpcs;
+    }
+
+    @Override
+    public Set<Deviation> getDeviations() {
+        return deviations;
+    }
+
+    @Override
+    public List<ExtensionDefinition> getExtensionSchemaNodes() {
+        return extensionNodes;
+    }
+
+    @Override
+    public Set<IdentitySchemaNode> getIdentities() {
+        return identities;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownNodes;
+    }
+
+    @Override
+    public final Set<TypeDefinition<?>> getTypeDefinitions() {
+        return typeDefinitions;
+    }
+
+    @Override
+    public final Set<DataSchemaNode> getChildNodes() {
+        return publicChildNodes;
+    }
+
+    @Override
+    public final Set<GroupingDefinition> getGroupings() {
+        return groupings;
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:hiddenField")
+    public final Optional<DataSchemaNode> findDataChildByName(final QName name) {
+        // Child nodes are keyed by their container name, so we can do a direct lookup
+        return Optional.ofNullable(childNodes.get(requireNonNull(name)));
+    }
+
+    @Override
+    public Set<UsesNode> getUses() {
+        return uses;
+    }
+
+    @Override
+    public Optional<SemVer> getSemanticVersion() {
+        return Optional.ofNullable(semanticVersion);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).omitNullValues()
+                .add("name", name)
+                .add("namespace", getNamespace())
+                .add("revision", getRevision().orElse(null))
+                .add("prefix", prefix)
+                .add("yangVersion", yangVersion)
+                .toString();
+    }
+
+    @Override
+    public void seal() {
+        if (!sealed) {
+            submodules = ImmutableSet.copyOf(Iterables.transform(submoduleContextsToBuild,
+                ctx -> (Module) ctx.buildEffective()));
+            submoduleContextsToBuild = ImmutableSet.of();
+            sealed = true;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveOperationContainerSchemaNode.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveOperationContainerSchemaNode.java
new file mode 100644 (file)
index 0000000..ac6661c
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.rfc7950.stmt;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import java.util.Objects;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+@Beta
+public abstract class AbstractEffectiveOperationContainerSchemaNode<D extends DeclaredStatement<QName>>
+        extends AbstractEffectiveContainerSchemaNode<D> {
+    protected AbstractEffectiveOperationContainerSchemaNode(final StmtContext<QName, D, ?> ctx) {
+        super(ctx);
+    }
+
+    @Override
+    public final Set<ActionDefinition> getActions() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public final Set<NotificationDefinition> getNotifications() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public final boolean isPresenceContainer() {
+        // FIXME: this should not really be here
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(getQName(), getPath());
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        AbstractEffectiveOperationContainerSchemaNode<?> other = (AbstractEffectiveOperationContainerSchemaNode<?>) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(this).add("path", getPath()).toString();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveOperationDefinition.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveOperationDefinition.java
new file mode 100644 (file)
index 0000000..8d2880d
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.rfc7950.stmt;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Verify;
+import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+@Beta
+public abstract class AbstractEffectiveOperationDefinition<D extends DeclaredStatement<QName>>
+        extends AbstractEffectiveSchemaNode<D> implements OperationDefinition {
+    private final ImmutableSet<TypeDefinition<?>> typeDefinitions;
+    private final ImmutableSet<GroupingDefinition> groupings;
+    private final ContainerSchemaNode input;
+    private final ContainerSchemaNode output;
+
+    protected AbstractEffectiveOperationDefinition(final StmtContext<QName, D, ?> ctx) {
+        super(ctx);
+        input = findAsContainer(this, InputEffectiveStatement.class);
+        output = findAsContainer(this, OutputEffectiveStatement.class);
+
+        // initSubstatements
+        final Set<GroupingDefinition> groupingsInit = new HashSet<>();
+        final Set<TypeDefinition<?>> mutableTypeDefinitions = new LinkedHashSet<>();
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof GroupingDefinition) {
+                final GroupingDefinition groupingDefinition = (GroupingDefinition) effectiveStatement;
+                groupingsInit.add(groupingDefinition);
+            }
+            if (effectiveStatement instanceof TypedefEffectiveStatement) {
+                final TypedefEffectiveStatement typeDef = (TypedefEffectiveStatement) effectiveStatement;
+                final TypeDefinition<?> type = typeDef.getTypeDefinition();
+                if (!mutableTypeDefinitions.contains(type)) {
+                    mutableTypeDefinitions.add(type);
+                } else {
+                    throw EffectiveStmtUtils.createNameCollisionSourceException(ctx, effectiveStatement);
+                }
+            }
+        }
+        this.groupings = ImmutableSet.copyOf(groupingsInit);
+        this.typeDefinitions = ImmutableSet.copyOf(mutableTypeDefinitions);
+    }
+
+    private static ContainerSchemaNode findAsContainer(final EffectiveStatement<?, ?> parent,
+            final Class<? extends EffectiveStatement<QName, ?>> statementType) {
+        final EffectiveStatement<?, ?> statement = parent.findFirstEffectiveSubstatement(statementType).get();
+        Verify.verify(statement instanceof ContainerSchemaNode, "Child statement %s is not a ContainerSchemaNode");
+        return (ContainerSchemaNode) statement;
+    }
+
+    @Override
+    public final ContainerSchemaNode getInput() {
+        return input;
+    }
+
+    @Override
+    public final ContainerSchemaNode getOutput() {
+        return output;
+    }
+
+    @Override
+    public final Set<TypeDefinition<?>> getTypeDefinitions() {
+        return typeDefinitions;
+    }
+
+    @Override
+    public final Set<GroupingDefinition> getGroupings() {
+        return groupings;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(getQName(), getPath());
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final AbstractEffectiveOperationDefinition<?> other =
+                (AbstractEffectiveOperationDefinition<?>) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(this).add("qname", getQName()).add("path", getPath()).add("input", input)
+                .add("output", output).toString();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveSchemaNode.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveSchemaNode.java
new file mode 100644 (file)
index 0000000..2c65331
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public abstract class AbstractEffectiveSchemaNode<D extends DeclaredStatement<QName>> extends
+        AbstractSchemaEffectiveDocumentedNode<QName, D> implements SchemaNode {
+
+    private final @NonNull SchemaPath path;
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
+
+    protected AbstractEffectiveSchemaNode(final StmtContext<QName, D, ?> ctx) {
+        super(ctx);
+        this.path = ctx.getSchemaPath().get();
+
+        ImmutableList.Builder<UnknownSchemaNode> listBuilder = new ImmutableList.Builder<>();
+        for (EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof UnknownSchemaNode) {
+                listBuilder.add((UnknownSchemaNode) effectiveStatement);
+            }
+        }
+        this.unknownNodes = listBuilder.build();
+    }
+
+    @Override
+    public final QName getQName() {
+        return path.getLastComponent();
+    }
+
+    @Override
+    public final SchemaPath getPath() {
+        return path;
+    }
+
+    @Override
+    public final List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownNodes;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveSimpleDataNodeContainer.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractEffectiveSimpleDataNodeContainer.java
new file mode 100644 (file)
index 0000000..b208c23
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public abstract class AbstractEffectiveSimpleDataNodeContainer<D extends DeclaredStatement<QName>> extends
+        AbstractEffectiveDocumentedDataNodeContainer<QName, D> implements AugmentationTarget, DataSchemaNode {
+
+    private final ImmutableSet<AugmentationSchemaNode> augmentations;
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
+    private final RevisionAwareXPath whenCondition;
+    private final @NonNull SchemaPath path;
+    private final boolean configuration;
+    private final boolean addedByUses;
+    private final boolean augmenting;
+
+    protected AbstractEffectiveSimpleDataNodeContainer(final StmtContext<QName, D, ?> ctx) {
+        super(ctx);
+
+        this.path = ctx.getSchemaPath().get();
+        this.configuration = ctx.isConfiguration();
+
+        whenCondition = findFirstEffectiveSubstatementArgument(WhenEffectiveStatement.class).orElse(null);
+
+        // initSubstatementCollectionsAndFields
+
+        List<UnknownSchemaNode> unknownNodesInit = new ArrayList<>();
+        Set<AugmentationSchemaNode> augmentationsInit = new LinkedHashSet<>();
+        for (EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof UnknownSchemaNode) {
+                unknownNodesInit.add((UnknownSchemaNode) effectiveStatement);
+            }
+            if (effectiveStatement instanceof AugmentationSchemaNode) {
+                augmentationsInit.add((AugmentationSchemaNode) effectiveStatement);
+            }
+        }
+        this.unknownNodes = ImmutableList.copyOf(unknownNodesInit);
+        this.augmentations = ImmutableSet.copyOf(augmentationsInit);
+
+        // initCopyType
+        final CopyHistory copyTypesFromOriginal = ctx.getCopyHistory();
+        if (copyTypesFromOriginal.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) {
+            this.augmenting = true;
+            this.addedByUses = true;
+        } else {
+            this.augmenting = copyTypesFromOriginal.contains(CopyType.ADDED_BY_AUGMENTATION);
+            this.addedByUses = copyTypesFromOriginal.contains(CopyType.ADDED_BY_USES);
+        }
+    }
+
+    @Override
+    public QName getQName() {
+        return path.getLastComponent();
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return path;
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAugmenting() {
+        return augmenting;
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAddedByUses() {
+        return addedByUses;
+    }
+
+    @Override
+    public boolean isConfiguration() {
+        return configuration;
+    }
+
+    @Override
+    public Set<AugmentationSchemaNode> getAvailableAugmentations() {
+        return augmentations;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownNodes;
+    }
+
+    @Override
+    public final Optional<RevisionAwareXPath> getWhenCondition() {
+        return Optional.ofNullable(whenCondition);
+    }
+
+    /**
+     * Reset {@link #isAugmenting()} to false.
+     *
+     * @deprecated This method is a violation of immutable contract and is a side-effect of bad/incomplete lifecycle,
+     *             which needs to be fixed. Do not introduce new callers. This deficiency is tracked in YANGTOOLS-724.
+     */
+    @Deprecated
+    public final void resetAugmenting() {
+        // Intentional no-op
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractListConstraintEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractListConstraintEffectiveStatement.java
new file mode 100644 (file)
index 0000000..1700c1d
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+@Beta
+public abstract class AbstractListConstraintEffectiveStatement<T, D extends DeclaredStatement<List<T>>>
+        extends AbstractConstraintEffectiveStatement<List<T>, D> {
+
+    protected AbstractListConstraintEffectiveStatement(final StmtContext<List<T>, D, ?> ctx) {
+        super(ctx);
+    }
+
+    @Override
+    protected final List<T> createConstraints(final List<T> argument) {
+        return ImmutableList.copyOf(argument);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractRootStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractRootStatement.java
new file mode 100644 (file)
index 0000000..f6c28f0
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BodyDeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LinkageDeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MetaDeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionAwareDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public abstract class AbstractRootStatement<T extends DeclaredStatement<String>>
+        extends AbstractDeclaredStatement<String> implements LinkageDeclaredStatement, MetaDeclaredStatement<String>,
+        RevisionAwareDeclaredStatement, BodyDeclaredStatement {
+
+    protected AbstractRootStatement(final StmtContext<String, T,?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractSchemaEffectiveDocumentedNode.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/AbstractSchemaEffectiveDocumentedNode.java
new file mode 100644 (file)
index 0000000..6e0bd75
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.rfc7950.stmt;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeAwareEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+
+/**
+ * An {@link AbstractEffectiveDocumentedNode} which can optionally support {@link SchemaTreeAwareEffectiveStatement}.
+ *
+ * @param <A> Argument type ({@link Void} if statement does not have argument.)
+ * @param <D> Class representing declared version of this statement.
+ * @author Robert Varga
+ */
+@Beta
+public abstract class AbstractSchemaEffectiveDocumentedNode<A, D extends DeclaredStatement<A>>
+        extends AbstractEffectiveDocumentedNode<A, D> {
+    private final ImmutableMap<QName, DataTreeEffectiveStatement<?>> dataTreeNamespace;
+    private final ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace;
+
+    protected AbstractSchemaEffectiveDocumentedNode(final StmtContext<A, D, ?> ctx) {
+        super(ctx);
+
+        if (this instanceof SchemaTreeAwareEffectiveStatement) {
+            final StatementSourceReference ref = ctx.getStatementSourceReference();
+            final Map<QName, SchemaTreeEffectiveStatement<?>> schemaChildren = new LinkedHashMap<>();
+            streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class).forEach(child -> {
+                putChild(schemaChildren, child, ref, "schema");
+            });
+            schemaTreeNamespace = ImmutableMap.copyOf(schemaChildren);
+
+            if (this instanceof DataTreeAwareEffectiveStatement && !schemaTreeNamespace.isEmpty()) {
+                final Map<QName, DataTreeEffectiveStatement<?>> dataChildren = new LinkedHashMap<>();
+                boolean sameAsSchema = true;
+
+                for (SchemaTreeEffectiveStatement<?> child : schemaTreeNamespace.values()) {
+                    if (child instanceof DataTreeEffectiveStatement) {
+                        putChild(dataChildren, (DataTreeEffectiveStatement<?>) child, ref, "data");
+                    } else {
+                        sameAsSchema = false;
+                        putChoiceDataChildren(dataChildren, ref, child);
+                    }
+                }
+
+                // This is a mighty hack to lower memory usage: if we consumed all schema tree children as data nodes,
+                // the two maps are equal and hence we can share the instance.
+                dataTreeNamespace = sameAsSchema ? (ImmutableMap) schemaTreeNamespace
+                        : ImmutableMap.copyOf(dataChildren);
+            } else {
+                dataTreeNamespace = ImmutableMap.of();
+            }
+        } else {
+            dataTreeNamespace = ImmutableMap.of();
+            schemaTreeNamespace = ImmutableMap.of();
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
+            final Class<N> namespace) {
+        if (this instanceof SchemaTreeAwareEffectiveStatement
+                && SchemaTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
+            return Optional.of((Map<K, V>) schemaTreeNamespace);
+        }
+        if (this instanceof DataTreeAwareEffectiveStatement
+                && DataTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
+            return Optional.of((Map<K, V>) dataTreeNamespace);
+        }
+        return super.getNamespaceContents(namespace);
+    }
+
+    private static <T extends SchemaTreeEffectiveStatement<?>> void putChild(final Map<QName, T> map,
+            final T child, final StatementSourceReference ref, final String tree) {
+        final QName id = child.getIdentifier();
+        final T prev = map.putIfAbsent(id, child);
+        SourceException.throwIf(prev != null, ref,
+                "Cannot add %s tree child with name %s, a conflicting child already exists", tree, id);
+    }
+
+    private static void putChoiceDataChildren(final Map<QName, DataTreeEffectiveStatement<?>> map,
+            final StatementSourceReference ref, final SchemaTreeEffectiveStatement<?> child) {
+        // For choice statements go through all their cases and fetch their data children
+        if (child instanceof ChoiceEffectiveStatement) {
+            child.streamEffectiveSubstatements(CaseEffectiveStatement.class).forEach(
+                caseStmt -> caseStmt.streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class).forEach(stmt -> {
+                    if (stmt instanceof DataTreeEffectiveStatement) {
+                        putChild(map, (DataTreeEffectiveStatement<?>) stmt, ref, "data");
+                    } else {
+                        putChoiceDataChildren(map, ref, stmt);
+                    }
+                }));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ArgumentUtils.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ArgumentUtils.java
new file mode 100644 (file)
index 0000000..138822d
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Splitter;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import org.checkerframework.checker.regex.qual.Regex;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for dealing with arguments encountered by StatementSupport classes.
+ */
+@Beta
+public final class ArgumentUtils {
+    public static final Splitter PIPE_SPLITTER = Splitter.on('|').trimResults();
+    public static final Splitter TWO_DOTS_SPLITTER = Splitter.on("..").trimResults();
+
+    private static final Logger LOG = LoggerFactory.getLogger(ArgumentUtils.class);
+
+    @Regex
+    private static final String PATH_ABS_STR = "/[^/].*";
+    private static final Pattern PATH_ABS = Pattern.compile(PATH_ABS_STR);
+    private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings().trimResults();
+
+    // these objects are to compare whether range has MAX or MIN value
+    // none of these values should appear as Yang number according to spec so they are safe to use
+    private static final BigDecimal YANG_MIN_NUM = BigDecimal.valueOf(-Double.MAX_VALUE);
+    private static final BigDecimal YANG_MAX_NUM = BigDecimal.valueOf(Double.MAX_VALUE);
+
+    private ArgumentUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static int compareNumbers(final Number n1, final Number n2) {
+        final BigDecimal num1 = yangConstraintToBigDecimal(n1);
+        final BigDecimal num2 = yangConstraintToBigDecimal(n2);
+        return new BigDecimal(num1.toString()).compareTo(new BigDecimal(num2.toString()));
+    }
+
+    public static String internBoolean(final String input) {
+        if ("true".equals(input)) {
+            return "true";
+        } else if ("false".equals(input)) {
+            return "false";
+        } else {
+            return input;
+        }
+    }
+
+    public static @NonNull Boolean parseBoolean(final StmtContext<?, ?, ?> ctx, final String input) {
+        if ("true".equals(input)) {
+            return Boolean.TRUE;
+        } else if ("false".equals(input)) {
+            return Boolean.FALSE;
+        } else {
+            final StatementDefinition def = ctx.getPublicDefinition();
+            throw new SourceException(ctx.getStatementSourceReference(),
+                "Invalid '%s' statement %s '%s', it can be either 'true' or 'false'",
+                def.getStatementName(), def.getArgumentDefinition().get().getArgumentName(), input);
+        }
+    }
+
+    public static RevisionAwareXPath parseXPath(final StmtContext<?, ?, ?> ctx, final String path) {
+        return XPathSupport.parseXPath(ctx, path);
+    }
+
+    public static boolean isAbsoluteXPath(final String path) {
+        return PATH_ABS.matcher(path).matches();
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    public static SchemaNodeIdentifier nodeIdentifierFromPath(final StmtContext<?, ?, ?> ctx, final String path) {
+        // FIXME: is the path trimming really necessary??
+        final List<QName> qNames = new ArrayList<>();
+        for (final String nodeName : SLASH_SPLITTER.split(trimSingleLastSlashFromXPath(path))) {
+            try {
+                qNames.add(StmtContextUtils.parseNodeIdentifier(ctx, nodeName));
+            } catch (final RuntimeException e) {
+                throw new SourceException(ctx.getStatementSourceReference(), e,
+                        "Failed to parse node '%s' in path '%s'", nodeName, path);
+            }
+        }
+
+        return SchemaNodeIdentifier.create(qNames, PATH_ABS.matcher(path).matches());
+    }
+
+    private static String trimSingleLastSlashFromXPath(final String path) {
+        return path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
+    }
+
+    private static BigDecimal yangConstraintToBigDecimal(final Number number) {
+        if (UnresolvedNumber.max().equals(number)) {
+            return YANG_MAX_NUM;
+        }
+        if (UnresolvedNumber.min().equals(number)) {
+            return YANG_MIN_NUM;
+        }
+
+        return new BigDecimal(number.toString());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/DeclaredEffectiveStatementBase.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/DeclaredEffectiveStatementBase.java
new file mode 100644 (file)
index 0000000..b78b571
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import com.google.common.base.Verify;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public abstract class DeclaredEffectiveStatementBase<A, D extends DeclaredStatement<A>> extends
+        EffectiveStatementBase<A, D> {
+
+    private final @NonNull StatementSource statementSource;
+    private final A argument;
+    private final @NonNull D declaredInstance;
+
+    /**
+     * Constructor.
+     *
+     * @param ctx
+     *            context of statement.
+     */
+    protected DeclaredEffectiveStatementBase(final StmtContext<A, D, ?> ctx) {
+        super(ctx);
+
+        this.argument = ctx.getStatementArgument();
+        this.statementSource = ctx.getStatementSource();
+
+        /*
+         * Share original instance of declared statement between all effective
+         * statements which have been copied or derived from this original
+         * declared statement.
+         */
+        @SuppressWarnings("unchecked")
+        final StmtContext<?, D, ?> lookupCtx = (StmtContext<?, D, ?>) ctx.getOriginalCtx().orElse(ctx);
+        declaredInstance = Verify.verifyNotNull(lookupCtx.buildDeclared(),
+            "Statement %s failed to build declared statement", lookupCtx);
+    }
+
+    @Override
+    public final StatementDefinition statementDefinition() {
+        return declaredInstance.statementDefinition();
+    }
+
+    @Override
+    public A argument() {
+        return argument;
+    }
+
+    @Override
+    public final StatementSource getStatementSource() {
+        return statementSource;
+    }
+
+    @Override
+    public final D getDeclared() {
+        return declaredInstance;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStatementBase.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStatementBase.java
new file mode 100644 (file)
index 0000000..2ae9957
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Predicate;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
+
+public abstract class EffectiveStatementBase<A, D extends DeclaredStatement<A>> implements EffectiveStatement<A, D> {
+    private final @NonNull ImmutableList<? extends EffectiveStatement<?, ?>> substatements;
+
+    /**
+     * Constructor.
+     *
+     * @param ctx context of statement.
+     */
+    protected EffectiveStatementBase(final StmtContext<A, D, ?> ctx) {
+        final Collection<? extends StmtContext<?, ?, ?>> effectiveSubstatements = ctx.effectiveSubstatements();
+        final Collection<StmtContext<?, ?, ?>> substatementsInit = new ArrayList<>();
+
+        final Collection<? extends StmtContext<?, ?, ?>> supportedDeclaredSubStmts = Collections2.filter(
+                ctx.declaredSubstatements(), StmtContext::isSupportedByFeatures);
+        for (final StmtContext<?, ?, ?> declaredSubstatement : supportedDeclaredSubStmts) {
+            if (YangStmtMapping.USES == declaredSubstatement.getPublicDefinition()) {
+                substatementsInit.add(declaredSubstatement);
+                substatementsInit.addAll(declaredSubstatement.getEffectOfStatement());
+                ((StatementContextBase<?, ?, ?>) ctx).removeStatementsFromEffectiveSubstatements(declaredSubstatement
+                        .getEffectOfStatement());
+            } else {
+                substatementsInit.add(declaredSubstatement);
+            }
+        }
+        substatementsInit.addAll(effectiveSubstatements);
+
+        this.substatements = ImmutableList.copyOf(initSubstatements(ctx, substatementsInit));
+    }
+
+    @Beta
+    protected Collection<? extends EffectiveStatement<?, ?>> initSubstatements(final StmtContext<A, D, ?> ctx,
+            final Collection<? extends StmtContext<?, ?, ?>> substatementsInit) {
+        return initSubstatements(substatementsInit);
+    }
+
+    /**
+     * Create a set of substatements. This method is split out so it can be overridden in
+     * ExtensionEffectiveStatementImpl to leak a not-fully-initialized instance.
+     *
+     * @param substatementsInit proposed substatements
+     * @return Filtered substatements
+     */
+    protected Collection<? extends EffectiveStatement<?, ?>> initSubstatements(
+            final Collection<? extends StmtContext<?, ?, ?>> substatementsInit) {
+        return Collections2.transform(Collections2.filter(substatementsInit,
+            StmtContext::isSupportedToBuildEffective), StmtContext::buildEffective);
+    }
+
+    @Override
+    public final <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends V> get(final Class<N> namespace,
+            final K identifier) {
+        return Optional.ofNullable(getAll(namespace).get(requireNonNull(identifier)));
+    }
+
+    @Override
+    public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAll(final Class<N> namespace) {
+        final Optional<? extends Map<K, V>> ret = getNamespaceContents(requireNonNull(namespace));
+        return ret.isPresent() ? ret.get() : ImmutableMap.of();
+    }
+
+    /**
+     * Return the statement-specific contents of specified namespace, if available.
+     *
+     * @param namespace Requested namespace
+     * @return Namespace contents, if available.
+     */
+    @Beta
+    protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
+            final @NonNull Class<N> namespace) {
+        return Optional.empty();
+    }
+
+    @Override
+    public final Collection<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
+        return substatements;
+    }
+
+    protected final <S extends SchemaNode> S firstSchemaNode(final Class<S> type) {
+        return substatements.stream().filter(type::isInstance).findFirst().map(type::cast).orElse(null);
+    }
+
+    @SuppressWarnings("unchecked")
+    public final <T> Collection<T> allSubstatementsOfType(final Class<T> type) {
+        return Collection.class.cast(Collections2.filter(substatements, type::isInstance));
+    }
+
+    protected final <T> @Nullable T firstSubstatementOfType(final Class<T> type) {
+        return substatements.stream().filter(type::isInstance).findFirst().map(type::cast).orElse(null);
+    }
+
+    protected final <R> R firstSubstatementOfType(final Class<?> type, final Class<R> returnType) {
+        return substatements.stream()
+                .filter(((Predicate<Object>)type::isInstance).and(returnType::isInstance))
+                .findFirst().map(returnType::cast).orElse(null);
+    }
+
+    protected final EffectiveStatement<?, ?> firstEffectiveSubstatementOfType(final Class<?> type) {
+        return substatements.stream().filter(type::isInstance).findFirst().orElse(null);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStmtUtils.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/EffectiveStmtUtils.java
new file mode 100644 (file)
index 0000000..9f8787b
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Strings;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MaxElementsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+@Beta
+public final class EffectiveStmtUtils {
+    // FIXME: this should reside somewhere in max_elements
+    private static final String UNBOUNDED_STR = "unbounded";
+
+    private EffectiveStmtUtils() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    public static SourceException createNameCollisionSourceException(final StmtContext<?, ?, ?> ctx,
+            final EffectiveStatement<?, ?> effectiveStatement) {
+        return new SourceException(ctx.getStatementSourceReference(),
+            "Error in module '%s': cannot add '%s'. Node name collision: '%s' already declared.",
+            ctx.getRoot().getStatementArgument(),
+            effectiveStatement.argument(),
+            effectiveStatement.argument());
+    }
+
+    public static Optional<ElementCountConstraint> createElementCountConstraint(final EffectiveStatement<?, ?> stmt) {
+        final Integer minElements;
+        final Optional<Integer> min = stmt.findFirstEffectiveSubstatementArgument(MinElementsEffectiveStatement.class);
+        if (min.isPresent()) {
+            final Integer m = min.get();
+            minElements = m > 0 ? m : null;
+        } else {
+            minElements = null;
+        }
+
+        final Integer maxElements;
+        final String max = stmt.findFirstEffectiveSubstatementArgument(MaxElementsEffectiveStatement.class)
+                .orElse(UNBOUNDED_STR);
+        if (!UNBOUNDED_STR.equals(max)) {
+            final Integer m = Integer.valueOf(max);
+            maxElements = m < Integer.MAX_VALUE ? m : null;
+        } else {
+            maxElements = null;
+        }
+
+        return ElementCountConstraint.forNullable(minElements, maxElements);
+    }
+
+
+    /**
+     * Checks whether supplied type has any of specified default values marked
+     * with an if-feature. This method creates mutable copy of supplied set of
+     * default values.
+     *
+     * @param yangVersion
+     *            yang version
+     * @param typeStmt
+     *            type statement which should be checked
+     * @param defaultValues
+     *            set of default values which should be checked. The method
+     *            creates mutable copy of this set
+     *
+     * @return true if any of specified default values is marked with an
+     *         if-feature, otherwise false
+     */
+    public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
+            final TypeEffectiveStatement<?> typeStmt, final Set<String> defaultValues) {
+        return !defaultValues.isEmpty() && yangVersion == YangVersion.VERSION_1_1
+                && isRelevantForIfFeatureCheck(typeStmt)
+                && isAnyDefaultValueMarkedWithIfFeature(typeStmt, new HashSet<>(defaultValues));
+    }
+
+    /**
+     * Checks whether supplied type has specified default value marked with an
+     * if-feature. This method creates mutable set of supplied default value.
+     *
+     * @param yangVersion
+     *            yang version
+     * @param typeStmt
+     *            type statement which should be checked
+     * @param defaultValue
+     *            default value to be checked
+     *
+     * @return true if specified default value is marked with an if-feature,
+     *         otherwise false
+     */
+    public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
+            final TypeEffectiveStatement<?> typeStmt, final String defaultValue) {
+        final HashSet<String> defaultValues = new HashSet<>();
+        defaultValues.add(defaultValue);
+        return !Strings.isNullOrEmpty(defaultValue) && yangVersion == YangVersion.VERSION_1_1
+                && isRelevantForIfFeatureCheck(typeStmt)
+                && isAnyDefaultValueMarkedWithIfFeature(typeStmt, defaultValues);
+    }
+
+    private static boolean isRelevantForIfFeatureCheck(final TypeEffectiveStatement<?> typeStmt) {
+        final TypeDefinition<?> typeDefinition = typeStmt.getTypeDefinition();
+        return typeDefinition instanceof EnumTypeDefinition || typeDefinition instanceof BitsTypeDefinition
+                || typeDefinition instanceof UnionTypeDefinition;
+    }
+
+    private static boolean isAnyDefaultValueMarkedWithIfFeature(final TypeEffectiveStatement<?> typeStmt,
+            final Set<String> defaultValues) {
+        final Iterator<? extends EffectiveStatement<?, ?>> iter = typeStmt.effectiveSubstatements().iterator();
+        while (iter.hasNext() && !defaultValues.isEmpty()) {
+            final EffectiveStatement<?, ?> effectiveSubstatement = iter.next();
+            if (YangStmtMapping.BIT.equals(effectiveSubstatement.statementDefinition())) {
+                final QName bitQName = (QName) effectiveSubstatement.argument();
+                if (defaultValues.remove(bitQName.getLocalName()) && containsIfFeature(effectiveSubstatement)) {
+                    return true;
+                }
+            } else if (YangStmtMapping.ENUM.equals(effectiveSubstatement.statementDefinition())
+                    && defaultValues.remove(effectiveSubstatement.argument())
+                    && containsIfFeature(effectiveSubstatement)) {
+                return true;
+            } else if (effectiveSubstatement instanceof TypeEffectiveStatement && isAnyDefaultValueMarkedWithIfFeature(
+                    (TypeEffectiveStatement<?>) effectiveSubstatement, defaultValues)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private static boolean containsIfFeature(final EffectiveStatement<?, ?> effectiveStatement) {
+        for (final EffectiveStatement<?, ?> effectiveSubstatement : effectiveStatement.effectiveSubstatements()) {
+            if (YangStmtMapping.IF_FEATURE.equals(effectiveSubstatement.statementDefinition())) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/StmtNamespaceContext.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/StmtNamespaceContext.java
new file mode 100644 (file)
index 0000000..ae97fc1
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import javax.xml.namespace.NamespaceContext;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ModuleQNameToPrefix;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+/**
+ * A {@link NamespaceContext} implementation based on the set of imports and local module namespace.
+ */
+// TODO: this is a useful utility, so it may be useful to expose it either in this package, or yang.parser.spi.source.
+final class StmtNamespaceContext implements YangNamespaceContext {
+    // FIXME: add serialization barrier
+    private static final long serialVersionUID = 1L;
+
+    private final StmtContext<?, ?, ?> ctx;
+
+    StmtNamespaceContext(final StmtContext<?, ?, ?> ctx) {
+        this.ctx = requireNonNull(ctx);
+    }
+
+    @Override
+    public Optional<String> findPrefixForNamespace(final QNameModule namespace) {
+        return Optional.ofNullable(ctx.getFromNamespace(ModuleQNameToPrefix.class, namespace));
+    }
+
+    @Override
+    public Optional<QNameModule> findNamespaceForPrefix(final String prefix) {
+        // TODO: perform caching?
+        return Optional.ofNullable(StmtContextUtils.getModuleQNameByPrefix(ctx, prefix));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/UndeclaredEffectiveStatementBase.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/UndeclaredEffectiveStatementBase.java
new file mode 100644 (file)
index 0000000..67b3ded
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import com.google.common.base.Verify;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public abstract class UndeclaredEffectiveStatementBase<A, D extends DeclaredStatement<A>>
+        extends EffectiveStatementBase<A, D> {
+
+    private final @NonNull StatementSource statementSource;
+    private final @NonNull StatementDefinition statementDefinition;
+    private final A argument;
+
+    protected UndeclaredEffectiveStatementBase(final StmtContext<A, D, ?> ctx) {
+        super(ctx);
+        this.statementDefinition = ctx.getPublicDefinition();
+        this.argument = ctx.getStatementArgument();
+        this.statementSource = ctx.getStatementSource();
+
+        final D declareInstance = ctx.buildDeclared();
+        Verify.verify(declareInstance == null, "Statement %s resulted in declared statement %s", declareInstance);
+    }
+
+    @Override
+    public final StatementDefinition statementDefinition() {
+        return statementDefinition;
+    }
+
+    @Override
+    public final A argument() {
+        return argument;
+    }
+
+    @Override
+    public final StatementSource getStatementSource() {
+        return statementSource;
+    }
+
+    @Override
+    public final D getDeclared() {
+        return null;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/UnknownEffectiveStatementBase.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/UnknownEffectiveStatementBase.java
new file mode 100644 (file)
index 0000000..487f004
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+import org.opendaylight.yangtools.yang.parser.spi.ExtensionNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public abstract class UnknownEffectiveStatementBase<A, D extends UnknownStatement<A>>
+        extends AbstractSchemaEffectiveDocumentedNode<A, D> implements UnknownSchemaNode {
+
+    private final boolean addedByUses;
+    private final boolean addedByAugmentation;
+
+    private final ExtensionDefinition extension;
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
+    private final QName nodeType;
+    private final String nodeParameter;
+
+    protected UnknownEffectiveStatementBase(final StmtContext<A, D, ?> ctx) {
+        super(ctx);
+
+        final StmtContext<?, ExtensionStatement, ExtensionEffectiveStatement> extensionInit =
+                ctx.getFromNamespace(ExtensionNamespace.class, ctx.getPublicDefinition().getStatementName());
+
+        if (extensionInit == null) {
+            extension = null;
+            nodeType = ctx.getPublicDefinition().getStatementName();
+        } else {
+            final EffectiveStatement<QName, ExtensionStatement> effective = extensionInit.buildEffective();
+            Preconditions.checkState(effective instanceof ExtensionDefinition,
+                "Statement %s is not an ExtensionDefinition", effective);
+            extension = (ExtensionDefinition) extensionInit.buildEffective();
+            nodeType = null;
+        }
+
+        // initCopyType
+        final CopyHistory copyTypesFromOriginal = ctx.getCopyHistory();
+        if (copyTypesFromOriginal.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) {
+            this.addedByAugmentation = true;
+            this.addedByUses = true;
+        } else {
+            this.addedByAugmentation = copyTypesFromOriginal.contains(CopyType.ADDED_BY_AUGMENTATION);
+            this.addedByUses = copyTypesFromOriginal.contains(CopyType.ADDED_BY_USES);
+        }
+
+        nodeParameter = ctx.rawStatementArgument() == null ? "" : ctx.rawStatementArgument();
+
+        // TODO init other fields (see Bug1412Test)
+        final Builder<UnknownSchemaNode> builder = ImmutableList.builder();
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof UnknownSchemaNode) {
+                builder.add((UnknownSchemaNode) effectiveStatement);
+            }
+        }
+        unknownNodes = builder.build();
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAddedByAugmentation() {
+        return addedByAugmentation;
+    }
+
+    @Override
+    public QName getNodeType() {
+        return extension == null ? nodeType : extension.getQName();
+    }
+
+    @Override
+    public String getNodeParameter() {
+        return nodeParameter;
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAddedByUses() {
+        return addedByUses;
+    }
+
+    @Override
+    public ExtensionDefinition getExtensionDefinition() {
+        return extension;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownNodes;
+    }
+
+    @Override
+    public String toString() {
+        final QName type = getNodeType();
+        return String.valueOf(type.getNamespace()) + ":" + type.getLocalName() + " " + nodeParameter;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/WithExpressionImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/WithExpressionImpl.java
new file mode 100644 (file)
index 0000000..34c81da
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.rfc7950.stmt;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath.WithExpression;
+import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression.QualifiedBound;
+
+// TODO: disconnect from RevisionAwareXPathImpl
+// FIXME: absolute should not be needed: since the expression is qualified, we known how to interpret it
+// FIXME: originalString() is an escape hatch: everybody should operate of QualifiedBound
+@NonNullByDefault
+final class WithExpressionImpl extends RevisionAwareXPathImpl implements WithExpression {
+    private final QualifiedBound xpathExpression;
+
+    WithExpressionImpl(final String xpath, final boolean absolute, final QualifiedBound xpathExpression) {
+        super(xpath, absolute);
+        this.xpathExpression = requireNonNull(xpathExpression);
+    }
+
+    @Override
+    public QualifiedBound getXPathExpression() {
+        return xpathExpression;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("xpath", getOriginalString()).toString();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/XPathSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/XPathSupport.java
new file mode 100644 (file)
index 0000000..c9f6027
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7950.stmt;
+
+import java.util.Iterator;
+import java.util.ServiceLoader;
+import javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression.QualifiedBound;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathParserFactory;
+import org.slf4j.Logger;
+
+@NonNullByDefault
+abstract class XPathSupport {
+    private static final Logger LOG = org.slf4j.LoggerFactory.getLogger(XPathSupport.class);
+
+    private static final XPathSupport INSTANCE;
+
+    static {
+        final Iterator<YangXPathParserFactory> it = ServiceLoader.load(YangXPathParserFactory.class).iterator();
+        if (!it.hasNext()) {
+            LOG.warn("Failed to find XPath parser factory, no XPath validation will be performed");
+            INSTANCE = new Noop();
+        } else {
+            INSTANCE = new XPathImpl(it.next());
+        }
+    }
+
+    static RevisionAwareXPath parseXPath(final StmtContext<?, ?, ?> ctx, final String xpath) {
+        return INSTANCE.parseXPath(xpath, ctx);
+    }
+
+    abstract RevisionAwareXPath parseXPath(String xpath, StmtContext<?, ?, ?> ctx);
+
+    private static final class Noop extends XPathSupport {
+        @Override
+        RevisionAwareXPath parseXPath(final String xpath, final StmtContext<?, ?, ?> ctx) {
+            return new RevisionAwareXPathImpl(xpath, ArgumentUtils.isAbsoluteXPath(xpath));
+        }
+    }
+
+    private static final class XPathImpl extends XPathSupport {
+        private final YangXPathParserFactory factory;
+
+        XPathImpl(final YangXPathParserFactory factory) {
+            this.factory = factory;
+        }
+
+        @Override
+        RevisionAwareXPath parseXPath(final String xpath, final StmtContext<?, ?, ?> ctx) {
+            final boolean isAbsolute = ArgumentUtils.isAbsoluteXPath(xpath);
+            final YangXPathParser.QualifiedBound parser = factory.newParser(new StmtNamespaceContext(ctx));
+            final QualifiedBound parsed;
+            try {
+                parsed = parser.parseExpression(xpath);
+            } catch (XPathExpressionException e) {
+                LOG.warn("Argument \"{}\" is not valid XPath string at \"{}\"", xpath,
+                    ctx.getStatementSourceReference(), e);
+                return new RevisionAwareXPathImpl(xpath, isAbsolute);
+            }
+
+            if (ctx.getRootVersion().compareTo(parsed.getYangVersion()) < 0) {
+                LOG.warn("{} features required in {} context to parse expression '{}' [at {}]",
+                    parsed.getYangVersion().getReference(), ctx.getRootVersion().getReference(), xpath,
+                    ctx.getStatementSourceReference());
+            }
+            return new WithExpressionImpl(xpath, isAbsolute, parsed);
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/action/ActionEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/action/ActionEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..0319282
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.action;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ActionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ActionStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveOperationDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ActionEffectiveStatementImpl extends AbstractEffectiveOperationDefinition<ActionStatement>
+        implements ActionDefinition, ActionEffectiveStatement {
+    private final boolean augmenting;
+    private final boolean addedByUses;
+
+    ActionEffectiveStatementImpl(
+            final StmtContext<QName, ActionStatement, EffectiveStatement<QName, ActionStatement>> ctx) {
+        super(ctx);
+
+        // initCopyType
+        final CopyHistory copyTypesFromOriginal = ctx.getCopyHistory();
+        if (copyTypesFromOriginal.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) {
+            this.augmenting = true;
+            this.addedByUses = true;
+        } else {
+            this.augmenting = copyTypesFromOriginal.contains(CopyType.ADDED_BY_AUGMENTATION);
+            this.addedByUses = copyTypesFromOriginal.contains(CopyType.ADDED_BY_USES);
+        }
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAugmenting() {
+        return augmenting;
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAddedByUses() {
+        return addedByUses;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/action/ActionStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/action/ActionStatementImpl.java
new file mode 100644 (file)
index 0000000..f0d3155
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.action;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.ActionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ActionStatementImpl extends AbstractDeclaredStatement<QName> implements ActionStatement {
+    ActionStatementImpl(final StmtContext<QName, ActionStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/action/ActionStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/action/ActionStatementSupport.java
new file mode 100644 (file)
index 0000000..b0acf7e
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.action;
+
+import com.google.common.collect.ImmutableSet;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.ActionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.InputStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OutputStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.input.InputStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.output.OutputStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
+
+public final class ActionStatementSupport
+        extends AbstractQNameStatementSupport<ActionStatement, EffectiveStatement<QName, ActionStatement>> {
+    private static final ImmutableSet<StatementDefinition> ILLEGAL_PARENTS = ImmutableSet.of(
+            YangStmtMapping.NOTIFICATION, YangStmtMapping.RPC, YangStmtMapping.ACTION);
+
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.ACTION)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.GROUPING)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addOptional(YangStmtMapping.INPUT)
+        .addOptional(YangStmtMapping.OUTPUT)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addAny(YangStmtMapping.TYPEDEF)
+        .build();
+    private static final ActionStatementSupport INSTANCE = new ActionStatementSupport();
+
+    private ActionStatementSupport() {
+        super(YangStmtMapping.ACTION);
+    }
+
+    public static ActionStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public void onStatementAdded(
+            final StmtContext.Mutable<QName, ActionStatement, EffectiveStatement<QName, ActionStatement>> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public ActionStatement createDeclared(
+            final StmtContext<QName, ActionStatement, ?> ctx) {
+        return new ActionStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<QName, ActionStatement> createEffective(
+            final StmtContext<QName, ActionStatement, EffectiveStatement<QName, ActionStatement>> ctx) {
+        SourceException.throwIf(StmtContextUtils.hasAncestorOfType(ctx, ILLEGAL_PARENTS),
+                ctx.getStatementSourceReference(),
+                "Action %s is defined within a notification, rpc or another action", ctx.getStatementArgument());
+        SourceException.throwIf(!StmtContextUtils.hasAncestorOfTypeWithChildOfType(ctx, YangStmtMapping.LIST,
+                YangStmtMapping.KEY), ctx.getStatementSourceReference(),
+                "Action %s is defined within a list that has no key statement", ctx.getStatementArgument());
+        SourceException.throwIf(StmtContextUtils.hasParentOfType(ctx, YangStmtMapping.CASE),
+                ctx.getStatementSourceReference(), "Action %s is defined within a case statement",
+                ctx.getStatementArgument());
+        SourceException.throwIf(StmtContextUtils.hasParentOfType(ctx, YangStmtMapping.MODULE),
+                ctx.getStatementSourceReference(), "Action %s is defined at the top level of a module",
+                ctx.getStatementArgument());
+        return new ActionEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public void onFullDefinitionDeclared(final StmtContext.Mutable<QName, ActionStatement,
+            EffectiveStatement<QName, ActionStatement>> stmt) {
+        super.onFullDefinitionDeclared(stmt);
+
+        if (StmtContextUtils.findFirstDeclaredSubstatement(stmt, InputStatement.class) == null) {
+            ((StatementContextBase<?, ?, ?>) stmt).appendImplicitStatement(InputStatementRFC7950Support.getInstance());
+        }
+
+        if (StmtContextUtils.findFirstDeclaredSubstatement(stmt, OutputStatement.class) == null) {
+            ((StatementContextBase<?, ?, ?>) stmt).appendImplicitStatement(OutputStatementRFC7950Support.getInstance());
+        }
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anydata/AnydataEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anydata/AnydataEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..f9031ab
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.anydata;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnydataEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnydataStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDataSchemaNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * YANG 1.1 AnyData effective statement implementation.
+ */
+@Beta
+final class AnydataEffectiveStatementImpl extends AbstractEffectiveDataSchemaNode<AnydataStatement>
+        implements AnydataEffectiveStatement, AnydataSchemaNode, DerivableSchemaNode {
+
+    private final ImmutableSet<MustDefinition> mustConstraints;
+    private final AnydataSchemaNode original;
+    private final ContainerSchemaNode schema;
+    private final boolean mandatory;
+
+    AnydataEffectiveStatementImpl(
+            final StmtContext<QName, AnydataStatement, EffectiveStatement<QName, AnydataStatement>> ctx) {
+        super(ctx);
+        this.original = (AnydataSchemaNode) ctx.getOriginalCtx().map(StmtContext::buildEffective).orElse(null);
+        mandatory = findFirstEffectiveSubstatementArgument(MandatoryEffectiveStatement.class).orElse(Boolean.FALSE)
+                .booleanValue();
+        mustConstraints = ImmutableSet.copyOf(allSubstatementsOfType(MustDefinition.class));
+
+        /*
+         * :TODO we need to determine a way how to set schema of AnyData
+         */
+        this.schema = null;
+    }
+
+    @Override
+    public Optional<AnydataSchemaNode> getOriginal() {
+        return Optional.ofNullable(original);
+    }
+
+    @Override
+    public Optional<ContainerSchemaNode> getDataSchema() {
+        return Optional.ofNullable(schema);
+    }
+
+    @Override
+    public boolean isMandatory() {
+        return mandatory;
+    }
+
+    @Override
+    public Collection<MustDefinition> getMustConstraints() {
+        return mustConstraints;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getQName(),getPath());
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        final AnydataEffectiveStatementImpl other = (AnydataEffectiveStatementImpl) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("qname", getQName()).add("path", getPath()).toString();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anydata/AnydataStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anydata/AnydataStatementImpl.java
new file mode 100644 (file)
index 0000000..6948d15
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.anydata;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnydataStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * YANG 1.1 AnyData declared statement implementation.
+ */
+@Beta
+final class AnydataStatementImpl extends AbstractDeclaredStatement<QName> implements AnydataStatement {
+    AnydataStatementImpl(final StmtContext<QName, AnydataStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anydata/AnydataStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anydata/AnydataStatementSupport.java
new file mode 100644 (file)
index 0000000..9172664
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.anydata;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnydataStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class AnydataStatementSupport extends
+        AbstractQNameStatementSupport<AnydataStatement, EffectiveStatement<QName, AnydataStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.ANYDATA)
+        .addOptional(YangStmtMapping.CONFIG)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addOptional(YangStmtMapping.MANDATORY)
+        .addAny(YangStmtMapping.MUST)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addOptional(YangStmtMapping.WHEN)
+        .build();
+    private static final AnydataStatementSupport INSTANCE = new AnydataStatementSupport();
+
+    private AnydataStatementSupport() {
+        super(YangStmtMapping.ANYDATA);
+    }
+
+    public static AnydataStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public void onStatementAdded(final Mutable<QName, AnydataStatement,
+            EffectiveStatement<QName, AnydataStatement>> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public AnydataStatement createDeclared(final StmtContext<QName, AnydataStatement, ?> ctx) {
+        return new AnydataStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<QName, AnydataStatement> createEffective(
+            final StmtContext<QName, AnydataStatement, EffectiveStatement<QName, AnydataStatement>> ctx) {
+        return new AnydataEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anyxml/AnyxmlEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anyxml/AnyxmlEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..7f29cf5
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.anyxml;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDataSchemaNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class AnyxmlEffectiveStatementImpl extends AbstractEffectiveDataSchemaNode<AnyxmlStatement>
+        implements AnyxmlEffectiveStatement, AnyxmlSchemaNode, DerivableSchemaNode {
+
+    private final ImmutableSet<MustDefinition> mustConstraints;
+    private final AnyxmlSchemaNode original;
+    private final boolean mandatory;
+
+    AnyxmlEffectiveStatementImpl(final StmtContext<QName, AnyxmlStatement, AnyxmlEffectiveStatement> ctx) {
+        super(ctx);
+        this.original = (AnyxmlSchemaNode) ctx.getOriginalCtx().map(StmtContext::buildEffective).orElse(null);
+        mandatory = findFirstEffectiveSubstatementArgument(MandatoryEffectiveStatement.class).orElse(Boolean.FALSE)
+                .booleanValue();
+        mustConstraints = ImmutableSet.copyOf(allSubstatementsOfType(MustDefinition.class));
+    }
+
+    @Override
+    public boolean isMandatory() {
+        return mandatory;
+    }
+
+    @Override
+    public Collection<MustDefinition> getMustConstraints() {
+        return mustConstraints;
+    }
+
+    @Override
+    public Optional<AnyxmlSchemaNode> getOriginal() {
+        return Optional.ofNullable(original);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getQName());
+        result = prime * result + Objects.hashCode(getPath());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        AnyxmlEffectiveStatementImpl other = (AnyxmlEffectiveStatementImpl) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return AnyxmlEffectiveStatementImpl.class.getSimpleName() + "["
+                + "qname=" + getQName()
+                + ", path=" + getPath()
+                + "]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anyxml/AnyxmlStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anyxml/AnyxmlStatementImpl.java
new file mode 100644 (file)
index 0000000..ae94414
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.anyxml;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class AnyxmlStatementImpl extends AbstractDeclaredStatement<QName> implements AnyxmlStatement {
+    AnyxmlStatementImpl(final StmtContext<QName, AnyxmlStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anyxml/AnyxmlStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/anyxml/AnyxmlStatementSupport.java
new file mode 100644 (file)
index 0000000..f2ed909
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.anyxml;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class AnyxmlStatementSupport
+        extends AbstractQNameStatementSupport<AnyxmlStatement, AnyxmlEffectiveStatement> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .ANYXML)
+        .addOptional(YangStmtMapping.CONFIG)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addOptional(YangStmtMapping.MANDATORY)
+        .addAny(YangStmtMapping.MUST)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addOptional(YangStmtMapping.WHEN)
+        .build();
+    private static final AnyxmlStatementSupport INSTANCE = new AnyxmlStatementSupport();
+
+    private AnyxmlStatementSupport() {
+        super(YangStmtMapping.ANYXML);
+    }
+
+    public static AnyxmlStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public void onStatementAdded(final Mutable<QName, AnyxmlStatement, AnyxmlEffectiveStatement> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public AnyxmlStatement createDeclared(final StmtContext<QName, AnyxmlStatement, ?> ctx) {
+        return new AnyxmlStatementImpl(ctx);
+    }
+
+    @Override
+    public AnyxmlEffectiveStatement createEffective(
+            final StmtContext<QName, AnyxmlStatement, AnyxmlEffectiveStatement> ctx) {
+        return new AnyxmlEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/argument/ArgumentEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/argument/ArgumentEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..9392c95
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.argument;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.ArgumentEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ArgumentStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ArgumentEffectiveStatementImpl extends DeclaredEffectiveStatementBase<QName, ArgumentStatement>
+        implements ArgumentEffectiveStatement {
+    ArgumentEffectiveStatementImpl(final StmtContext<QName, ArgumentStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/argument/ArgumentStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/argument/ArgumentStatementImpl.java
new file mode 100644 (file)
index 0000000..41dbe58
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.argument;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.ArgumentStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ArgumentStatementImpl extends AbstractDeclaredStatement<QName> implements ArgumentStatement {
+    ArgumentStatementImpl(final StmtContext<QName, ArgumentStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/argument/ArgumentStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/argument/ArgumentStatementSupport.java
new file mode 100644 (file)
index 0000000..6258721
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.argument;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ArgumentStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ArgumentStatementSupport
+        extends AbstractQNameStatementSupport<ArgumentStatement, EffectiveStatement<QName, ArgumentStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .ARGUMENT)
+        .addOptional(YangStmtMapping.YIN_ELEMENT)
+        .build();
+    private static final ArgumentStatementSupport INSTANCE = new ArgumentStatementSupport();
+
+    private ArgumentStatementSupport() {
+        super(YangStmtMapping.ARGUMENT);
+    }
+
+    public static ArgumentStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public ArgumentStatement createDeclared(
+            final StmtContext<QName, ArgumentStatement, ?> ctx) {
+        return new ArgumentStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<QName, ArgumentStatement> createEffective(
+            final StmtContext<QName, ArgumentStatement, EffectiveStatement<QName, ArgumentStatement>> ctx) {
+        return new ArgumentEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AbstractAugmentStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AbstractAugmentStatementSupport.java
new file mode 100644 (file)
index 0000000..dff3296
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.augment;
+
+import com.google.common.base.Verify;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DataDefinitionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StmtOrderingNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractAugmentStatementSupport extends AbstractStatementSupport<SchemaNodeIdentifier, AugmentStatement,
+        EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractAugmentStatementSupport.class);
+    private static final Pattern PATH_REL_PATTERN1 = Pattern.compile("\\.\\.?\\s*/(.+)");
+    private static final Pattern PATH_REL_PATTERN2 = Pattern.compile("//.*");
+
+    AbstractAugmentStatementSupport() {
+        super(YangStmtMapping.AUGMENT);
+    }
+
+    @Override
+    public final SchemaNodeIdentifier parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        SourceException.throwIf(PATH_REL_PATTERN1.matcher(value).matches()
+            || PATH_REL_PATTERN2.matcher(value).matches(), ctx.getStatementSourceReference(),
+            "Augment argument \'%s\' is not valid, it can be only absolute path; or descendant if used in uses",
+            value);
+
+        return ArgumentUtils.nodeIdentifierFromPath(ctx, value);
+    }
+
+    @Override
+    public final AugmentStatement createDeclared(final StmtContext<SchemaNodeIdentifier, AugmentStatement, ?> ctx) {
+        return new AugmentStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<SchemaNodeIdentifier, AugmentStatement> createEffective(
+            final StmtContext<SchemaNodeIdentifier, AugmentStatement,
+            EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> ctx) {
+        return new AugmentEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public final void onFullDefinitionDeclared(final Mutable<SchemaNodeIdentifier, AugmentStatement,
+            EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> augmentNode) {
+        if (!augmentNode.isSupportedByFeatures()) {
+            return;
+        }
+
+        super.onFullDefinitionDeclared(augmentNode);
+
+        if (StmtContextUtils.isInExtensionBody(augmentNode)) {
+            return;
+        }
+
+        final ModelActionBuilder augmentAction = augmentNode.newInferenceAction(
+            ModelProcessingPhase.EFFECTIVE_MODEL);
+        final Prerequisite<StmtContext<SchemaNodeIdentifier, AugmentStatement,
+            EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>>> sourceCtxPrereq =
+                augmentAction.requiresCtx(augmentNode, ModelProcessingPhase.EFFECTIVE_MODEL);
+        final Prerequisite<Mutable<?, ?, EffectiveStatement<?, ?>>> target =
+                augmentAction.mutatesEffectiveCtxPath(getSearchRoot(augmentNode),
+                    ChildSchemaNodeNamespace.class, augmentNode.coerceStatementArgument().getPathFromRoot());
+
+        augmentAction.apply(new InferenceAction() {
+            @Override
+            public void apply(final InferenceContext ctx) {
+                final StatementContextBase<?, ?, ?> augmentTargetCtx =
+                        (StatementContextBase<?, ?, ?>) target.resolve(ctx);
+                if (!isSupportedAugmentTarget(augmentTargetCtx)
+                        || StmtContextUtils.isInExtensionBody(augmentTargetCtx)) {
+                    augmentNode.setIsSupportedToBuildEffective(false);
+                    return;
+                }
+
+                // We are targeting a context which is creating implicit nodes. In order to keep things consistent,
+                // we will need to circle back when creating effective statements.
+                if (augmentTargetCtx.hasImplicitParentSupport()) {
+                    augmentNode.addToNs(AugmentImplicitHandlingNamespace.class, augmentNode, augmentTargetCtx);
+                }
+
+                final StatementContextBase<?, ?, ?> augmentSourceCtx = (StatementContextBase<?, ?, ?>) augmentNode;
+                // FIXME: this is a workaround for models which augment a node which is added via an extension
+                //        which we do not handle. This needs to be reworked in terms of unknown schema nodes.
+                try {
+                    copyFromSourceToTarget(augmentSourceCtx, augmentTargetCtx);
+                    augmentTargetCtx.addEffectiveSubstatement(augmentSourceCtx);
+                    updateAugmentOrder(augmentSourceCtx);
+                } catch (final SourceException e) {
+                    LOG.warn("Failed to add augmentation {} defined at {}",
+                        augmentTargetCtx.getStatementSourceReference(),
+                            augmentSourceCtx.getStatementSourceReference(), e);
+                }
+            }
+
+            private void updateAugmentOrder(final StatementContextBase<?, ?, ?> augmentSourceCtx) {
+                Integer currentOrder = augmentSourceCtx.getFromNamespace(StmtOrderingNamespace.class,
+                    YangStmtMapping.AUGMENT);
+                if (currentOrder == null) {
+                    currentOrder = 1;
+                } else {
+                    currentOrder++;
+                }
+
+                augmentSourceCtx.addToNs(StmtOrderingNamespace.class, YangStmtMapping.AUGMENT, currentOrder);
+            }
+
+            @Override
+            public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
+                /*
+                 * Do not fail, if it is an uses-augment to an unknown node.
+                 */
+                if (YangStmtMapping.USES == augmentNode.coerceParentContext().getPublicDefinition()) {
+                    final SchemaNodeIdentifier augmentArg = augmentNode.coerceStatementArgument();
+                    final Optional<StmtContext<?, ?, ?>> targetNode = ChildSchemaNodeNamespace.findNode(
+                        getSearchRoot(augmentNode), augmentArg);
+                    if (targetNode.isPresent() && StmtContextUtils.isUnknownStatement(targetNode.get())) {
+                        augmentNode.setIsSupportedToBuildEffective(false);
+                        LOG.warn("Uses-augment to unknown node {}. Augmentation has not been performed. At line: {}",
+                            augmentArg, augmentNode.getStatementSourceReference());
+                        return;
+                    }
+                }
+
+                throw new InferenceException(augmentNode.getStatementSourceReference(),
+                        "Augment target '%s' not found", augmentNode.getStatementArgument());
+            }
+        });
+    }
+
+    private static StmtContext<?, ?, ?> getSearchRoot(final StmtContext<?, ?, ?> augmentContext) {
+        // Augment is in uses - we need to augment instantiated nodes in parent.
+        final StmtContext<?, ?, ?> parent = augmentContext.coerceParentContext();
+        if (YangStmtMapping.USES == parent.getPublicDefinition()) {
+            return parent.getParentContext();
+        }
+        return parent;
+    }
+
+    static void copyFromSourceToTarget(final StatementContextBase<?, ?, ?> sourceCtx,
+            final StatementContextBase<?, ?, ?> targetCtx) {
+        final CopyType typeOfCopy = UsesStatement.class.equals(sourceCtx.coerceParentContext().getPublicDefinition()
+                .getDeclaredRepresentationClass()) ? CopyType.ADDED_BY_USES_AUGMENTATION
+                : CopyType.ADDED_BY_AUGMENTATION;
+        /*
+         * Since Yang 1.1, if an augmentation is made conditional with a
+         * "when" statement, it is allowed to add mandatory nodes.
+         */
+        final boolean skipCheckOfMandatoryNodes = YangVersion.VERSION_1_1.equals(sourceCtx.getRootVersion())
+                && isConditionalAugmentStmt(sourceCtx);
+
+        final Collection<? extends Mutable<?, ?, ?>> declared = sourceCtx.mutableDeclaredSubstatements();
+        final Collection<? extends Mutable<?, ?, ?>> effective = sourceCtx.mutableEffectiveSubstatements();
+        final Collection<Mutable<?, ?, ?>> buffer = new ArrayList<>(declared.size() + effective.size());
+
+        for (final Mutable<?, ?, ?> originalStmtCtx : declared) {
+            if (originalStmtCtx.isSupportedByFeatures()) {
+                copyStatement(originalStmtCtx, targetCtx, typeOfCopy, buffer, skipCheckOfMandatoryNodes);
+            }
+        }
+        for (final Mutable<?, ?, ?> originalStmtCtx : effective) {
+            copyStatement(originalStmtCtx, targetCtx, typeOfCopy, buffer, skipCheckOfMandatoryNodes);
+        }
+
+        targetCtx.addEffectiveSubstatements(buffer);
+    }
+
+    /**
+     * Checks whether supplied statement context is conditional augment
+     * statement.
+     *
+     * @param ctx
+     *            statement context to be checked
+     *
+     * @return true if supplied statement context is conditional augment
+     *         statement, otherwise false
+     */
+    private static boolean isConditionalAugmentStmt(final StmtContext<?, ?, ?> ctx) {
+        return ctx.getPublicDefinition() == YangStmtMapping.AUGMENT && hasWhenSubstatement(ctx);
+    }
+
+    private static boolean hasWhenSubstatement(final StmtContext<?, ?, ?> ctx) {
+        return StmtContextUtils.findFirstSubstatement(ctx, WhenStatement.class) != null;
+    }
+
+    private static void copyStatement(final Mutable<?, ?, ?> original, final StatementContextBase<?, ?, ?> target,
+            final CopyType typeOfCopy, final Collection<Mutable<?, ?, ?>> buffer,
+            final boolean skipCheckOfMandatoryNodes) {
+        if (needToCopyByAugment(original)) {
+            validateNodeCanBeCopiedByAugment(original, target, typeOfCopy, skipCheckOfMandatoryNodes);
+
+            buffer.add(target.childCopyOf(original, typeOfCopy));
+        } else if (isReusedByAugment(original)) {
+            buffer.add(original);
+        }
+    }
+
+    private static void validateNodeCanBeCopiedByAugment(final StmtContext<?, ?, ?> sourceCtx,
+            final StatementContextBase<?, ?, ?> targetCtx, final CopyType typeOfCopy,
+            final boolean skipCheckOfMandatoryNodes) {
+
+        if (WhenStatement.class.equals(sourceCtx.getPublicDefinition().getDeclaredRepresentationClass())) {
+            return;
+        }
+
+        if (!skipCheckOfMandatoryNodes && typeOfCopy == CopyType.ADDED_BY_AUGMENTATION
+                && requireCheckOfMandatoryNodes(sourceCtx, targetCtx)) {
+            checkForMandatoryNodes(sourceCtx);
+        }
+
+        // Data definition statements must not collide on their namespace
+        if (DataDefinitionStatement.class.isAssignableFrom(
+            sourceCtx.getPublicDefinition().getDeclaredRepresentationClass())) {
+            for (final StmtContext<?, ?, ?> subStatement : targetCtx.allSubstatements()) {
+                if (DataDefinitionStatement.class.isAssignableFrom(
+                    subStatement.getPublicDefinition().getDeclaredRepresentationClass())) {
+
+                    InferenceException.throwIf(
+                        Objects.equals(sourceCtx.getStatementArgument(), subStatement.getStatementArgument()),
+                        sourceCtx.getStatementSourceReference(),
+                        "An augment cannot add node named '%s' because this name is already used in target",
+                        sourceCtx.rawStatementArgument());
+                }
+            }
+        }
+    }
+
+    private static void checkForMandatoryNodes(final StmtContext<?, ?, ?> sourceCtx) {
+        if (StmtContextUtils.isNonPresenceContainer(sourceCtx)) {
+            /*
+             * We need to iterate over both declared and effective sub-statements,
+             * because a mandatory node can be:
+             * a) declared in augment body
+             * b) added to augment body also via uses of a grouping and
+             * such sub-statements are stored in effective sub-statements collection.
+             */
+            sourceCtx.allSubstatementsStream().forEach(AbstractAugmentStatementSupport::checkForMandatoryNodes);
+        }
+
+        InferenceException.throwIf(StmtContextUtils.isMandatoryNode(sourceCtx),
+                sourceCtx.getStatementSourceReference(),
+                "An augment cannot add node '%s' because it is mandatory and in module different than target",
+                sourceCtx.rawStatementArgument());
+    }
+
+    private static boolean requireCheckOfMandatoryNodes(final StmtContext<?, ?, ?> sourceCtx,
+            Mutable<?, ?, ?> targetCtx) {
+        /*
+         * If the statement argument is not QName, it cannot be mandatory
+         * statement, therefore return false and skip mandatory nodes validation
+         */
+        final Object arg = sourceCtx.getStatementArgument();
+        if (!(arg instanceof QName)) {
+            return false;
+        }
+        final QName sourceStmtQName = (QName) arg;
+
+        // RootStatementContext, for example
+        final Mutable<?, ?, ?> root = targetCtx.getRoot();
+        do {
+            final Object targetArg = targetCtx.getStatementArgument();
+            Verify.verify(targetArg instanceof QName, "Argument of augment target statement must be QName, not %s",
+                targetArg);
+            final QName targetStmtQName = (QName) targetArg;
+            /*
+             * If target is from another module, return true and perform mandatory nodes validation
+             */
+            if (!targetStmtQName.getModule().equals(sourceStmtQName.getModule())) {
+                return true;
+            }
+
+            /*
+             * If target or one of the target's ancestors from the same namespace
+             * is a presence container
+             * or is non-mandatory choice
+             * or is non-mandatory list
+             * return false and skip mandatory nodes validation, because these nodes
+             * are not mandatory node containers according to RFC 6020 section 3.1.
+             */
+            if (StmtContextUtils.isPresenceContainer(targetCtx)
+                    || StmtContextUtils.isNotMandatoryNodeOfType(targetCtx, YangStmtMapping.CHOICE)
+                    || StmtContextUtils.isNotMandatoryNodeOfType(targetCtx, YangStmtMapping.LIST)) {
+                return false;
+            }
+
+            // This could be an augmentation stacked on top of a previous augmentation from the same module, which is
+            // conditional -- in which case we do not run further checks
+            if (targetCtx.getCopyHistory().getLastOperation() == CopyType.ADDED_BY_AUGMENTATION) {
+                final Optional<? extends StmtContext<?, ?, ?>> optPrevCopy = targetCtx.getPreviousCopyCtx();
+                if (optPrevCopy.isPresent()) {
+                    final StmtContext<?, ?, ?> original = optPrevCopy.get();
+                    final Object origArg = original.coerceStatementArgument();
+                    Verify.verify(origArg instanceof QName, "Unexpected statement argument %s", origArg);
+
+                    if (sourceStmtQName.getModule().equals(((QName) origArg).getModule())
+                            && hasWhenSubstatement(getParentAugmentation(original))) {
+                        return false;
+                    }
+                }
+            }
+        } while ((targetCtx = targetCtx.getParentContext()) != root);
+
+        /*
+         * All target node's parents belong to the same module as source node,
+         * therefore return false and skip mandatory nodes validation.
+         */
+        return false;
+    }
+
+    private static StmtContext<?, ?, ?> getParentAugmentation(final StmtContext<?, ?, ?> child) {
+        StmtContext<?, ?, ?> parent = Verify.verifyNotNull(child.getParentContext(), "Child %s has not parent", child);
+        while (parent.getPublicDefinition() != YangStmtMapping.AUGMENT) {
+            parent = Verify.verifyNotNull(parent.getParentContext(), "Failed to find augmentation parent of %s", child);
+        }
+        return parent;
+    }
+
+    private static final ImmutableSet<YangStmtMapping> NOCOPY_DEF_SET = ImmutableSet.of(YangStmtMapping.USES,
+        YangStmtMapping.WHEN, YangStmtMapping.DESCRIPTION, YangStmtMapping.REFERENCE, YangStmtMapping.STATUS);
+
+    private static boolean needToCopyByAugment(final StmtContext<?, ?, ?> stmtContext) {
+        return !NOCOPY_DEF_SET.contains(stmtContext.getPublicDefinition());
+    }
+
+    private static final ImmutableSet<YangStmtMapping> REUSED_DEF_SET = ImmutableSet.of(YangStmtMapping.TYPEDEF);
+
+    private static boolean isReusedByAugment(final StmtContext<?, ?, ?> stmtContext) {
+        return REUSED_DEF_SET.contains(stmtContext.getPublicDefinition());
+    }
+
+    static boolean isSupportedAugmentTarget(final StmtContext<?, ?, ?> substatementCtx) {
+        /*
+         * :TODO Substatement must be allowed augment target type e.g.
+         * Container, etc... and must not be for example grouping, identity etc.
+         * It is problem in case when more than one substatements have the same
+         * QName, for example Grouping and Container are siblings and they have
+         * the same QName. We must find the Container and the Grouping must be
+         * ignored as disallowed augment target.
+         */
+        final Collection<?> allowedAugmentTargets = substatementCtx.getFromNamespace(
+            ValidationBundlesNamespace.class, ValidationBundleType.SUPPORTED_AUGMENT_TARGETS);
+
+        // if no allowed target is returned we consider all targets allowed
+        return allowedAugmentTargets == null || allowedAugmentTargets.isEmpty()
+                || allowedAugmentTargets.contains(substatementCtx.getPublicDefinition());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..524329e
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.augment;
+
+import static com.google.common.base.Verify.verify;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.net.URI;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.NamespaceRevisionAware;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AugmentEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.compat.ActionNodeContainerCompat;
+import org.opendaylight.yangtools.yang.model.api.stmt.compat.NotificationNodeContainerCompat;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDocumentedDataNodeContainer;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
+
+final class AugmentEffectiveStatementImpl
+        extends AbstractEffectiveDocumentedDataNodeContainer<SchemaNodeIdentifier, AugmentStatement>
+        implements AugmentEffectiveStatement, AugmentationSchemaNode, NamespaceRevisionAware,
+            ActionNodeContainerCompat<SchemaNodeIdentifier, AugmentStatement>,
+            NotificationNodeContainerCompat<SchemaNodeIdentifier, AugmentStatement> {
+    private final SchemaPath targetPath;
+    private final URI namespace;
+    private final Revision revision;
+    private final @NonNull ImmutableSet<ActionDefinition> actions;
+    private final @NonNull ImmutableSet<NotificationDefinition> notifications;
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
+    private final RevisionAwareXPath whenCondition;
+    private final AugmentationSchemaNode copyOf;
+
+    AugmentEffectiveStatementImpl(final StmtContext<SchemaNodeIdentifier, AugmentStatement,
+            EffectiveStatement<SchemaNodeIdentifier, AugmentStatement>> ctx) {
+        super(ctx);
+
+        this.targetPath = ctx.coerceStatementArgument().asSchemaPath();
+
+        final QNameModule rootModuleQName = StmtContextUtils.getRootModuleQName(ctx);
+        this.namespace = rootModuleQName.getNamespace();
+        this.revision = rootModuleQName.getRevision().orElse(null);
+
+        this.copyOf = (AugmentationSchemaNode) ctx.getOriginalCtx().map(StmtContext::buildEffective).orElse(null);
+        whenCondition = findFirstEffectiveSubstatementArgument(WhenEffectiveStatement.class).orElse(null);
+
+        // initSubstatementCollections
+        final ImmutableSet.Builder<ActionDefinition> actionsBuilder = ImmutableSet.builder();
+        final ImmutableSet.Builder<NotificationDefinition> notificationsBuilder = ImmutableSet.builder();
+        final ImmutableList.Builder<UnknownSchemaNode> listBuilder = new ImmutableList.Builder<>();
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof ActionDefinition) {
+                actionsBuilder.add((ActionDefinition) effectiveStatement);
+            } else if (effectiveStatement instanceof NotificationDefinition) {
+                notificationsBuilder.add((NotificationDefinition) effectiveStatement);
+            } else if (effectiveStatement instanceof UnknownSchemaNode) {
+                listBuilder.add((UnknownSchemaNode) effectiveStatement);
+            }
+        }
+
+        this.actions = actionsBuilder.build();
+        this.notifications = notificationsBuilder.build();
+        this.unknownNodes = listBuilder.build();
+    }
+
+    @Override
+    protected Collection<? extends EffectiveStatement<?, ?>> initSubstatements(
+            final StmtContext<SchemaNodeIdentifier, AugmentStatement, ?> ctx,
+            final Collection<? extends StmtContext<?, ?, ?>> substatementsInit) {
+        final StatementContextBase<?, ?, ?> implicitDef = ctx.getFromNamespace(AugmentImplicitHandlingNamespace.class,
+            ctx);
+        return implicitDef == null ? super.initSubstatements(ctx, substatementsInit)
+                : Collections2.transform(Collections2.filter(substatementsInit,
+                    StmtContext::isSupportedToBuildEffective),
+                    subCtx -> {
+                        verify(subCtx instanceof StatementContextBase);
+                        return implicitDef.wrapWithImplicit((StatementContextBase<?, ?, ?>) subCtx).buildEffective();
+                    });
+    }
+
+    @Override
+    public Optional<AugmentationSchemaNode> getOriginalDefinition() {
+        return Optional.ofNullable(this.copyOf);
+    }
+
+    @Override
+    public SchemaPath getTargetPath() {
+        return targetPath;
+    }
+
+    @Override
+    public Optional<RevisionAwareXPath> getWhenCondition() {
+        return Optional.ofNullable(whenCondition);
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownNodes;
+    }
+
+    @Override
+    public URI getNamespace() {
+        return namespace;
+    }
+
+    @Override
+    public Set<ActionDefinition> getActions() {
+        return actions;
+    }
+
+    @Override
+    public Set<NotificationDefinition> getNotifications() {
+        return notifications;
+    }
+
+    @Override
+    public Optional<Revision> getRevision() {
+        return Optional.ofNullable(revision);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 17;
+        int result = 1;
+        result = prime * result + Objects.hashCode(targetPath);
+        result = prime * result + Objects.hashCode(whenCondition);
+        result = prime * result + getChildNodes().hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof AugmentEffectiveStatementImpl)) {
+            return false;
+        }
+        final AugmentEffectiveStatementImpl other = (AugmentEffectiveStatementImpl) obj;
+        return Objects.equals(targetPath, other.targetPath) && Objects.equals(whenCondition, other.whenCondition)
+                && getChildNodes().equals(other.getChildNodes());
+    }
+
+    @Override
+    public String toString() {
+        return AugmentEffectiveStatementImpl.class.getSimpleName() + "[" + "targetPath=" + targetPath + ", when="
+                + whenCondition + "]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentImplicitHandlingNamespace.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentImplicitHandlingNamespace.java
new file mode 100644 (file)
index 0000000..60cfa3c
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.rfc7950.stmt.augment;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
+
+/**
+ * Helper namespace for attaching target information to augmentation statements. This is then used to ensure that
+ * the effective augment has correct implicit statements created.
+ */
+@Beta
+public interface AugmentImplicitHandlingNamespace
+        extends IdentifierNamespace<StmtContext<?, ?, ?>, StatementContextBase<?, ?, ?>> {
+    NamespaceBehaviour<StmtContext<?, ?, ?>, StatementContextBase<?, ?, ?>, @NonNull AugmentImplicitHandlingNamespace>
+        BEHAVIOUR = NamespaceBehaviour.treeScoped(AugmentImplicitHandlingNamespace.class);
+
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentStatementImpl.java
new file mode 100644 (file)
index 0000000..6ffa639
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.augment;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class AugmentStatementImpl extends AbstractDeclaredStatement<SchemaNodeIdentifier> implements AugmentStatement {
+    AugmentStatementImpl(final StmtContext<SchemaNodeIdentifier, AugmentStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..d411e43
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.augment;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class AugmentStatementRFC6020Support extends AbstractAugmentStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator
+            .builder(YangStmtMapping.AUGMENT)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.CASE)
+            .addAny(YangStmtMapping.CHOICE)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.IF_FEATURE)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.STATUS)
+            .addAny(YangStmtMapping.USES)
+            .addOptional(YangStmtMapping.WHEN)
+            .build();
+    private static final AugmentStatementRFC6020Support INSTANCE = new AugmentStatementRFC6020Support();
+
+    private AugmentStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static AugmentStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/augment/AugmentStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..28edb69
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.augment;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Augment statement.
+ */
+@Beta
+public final class AugmentStatementRFC7950Support extends AbstractAugmentStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator
+            .builder(YangStmtMapping.AUGMENT)
+            .addAny(YangStmtMapping.ACTION)
+            .addAny(YangStmtMapping.ANYDATA)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.CASE)
+            .addAny(YangStmtMapping.CHOICE)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.IF_FEATURE)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addAny(YangStmtMapping.NOTIFICATION)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.STATUS)
+            .addAny(YangStmtMapping.USES)
+            .addOptional(YangStmtMapping.WHEN)
+            .build();
+    private static final AugmentStatementRFC7950Support INSTANCE = new AugmentStatementRFC7950Support();
+
+    private AugmentStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static AugmentStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/base/BaseEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/base/BaseEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..1bf6a70
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.base;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.BaseEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BaseStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class BaseEffectiveStatementImpl extends DeclaredEffectiveStatementBase<QName, BaseStatement>
+        implements BaseEffectiveStatement {
+    BaseEffectiveStatementImpl(final StmtContext<QName, BaseStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/base/BaseStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/base/BaseStatementImpl.java
new file mode 100644 (file)
index 0000000..15594f9
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.base;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.BaseStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class BaseStatementImpl extends AbstractDeclaredStatement<QName> implements BaseStatement {
+    BaseStatementImpl(final StmtContext<QName, BaseStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/base/BaseStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/base/BaseStatementSupport.java
new file mode 100644 (file)
index 0000000..2d38253
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.base;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BaseStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityStatement;
+import org.opendaylight.yangtools.yang.parser.spi.IdentityNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.DerivedIdentitiesNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class BaseStatementSupport
+        extends AbstractQNameStatementSupport<BaseStatement, EffectiveStatement<QName, BaseStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR =
+            SubstatementValidator.builder(YangStmtMapping.BASE).build();
+    private static final BaseStatementSupport INSTANCE = new BaseStatementSupport();
+
+    private BaseStatementSupport() {
+        super(YangStmtMapping.BASE);
+    }
+
+    public static BaseStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseNodeIdentifier(ctx, value);
+    }
+
+    @Override
+    public BaseStatement createDeclared(final StmtContext<QName, BaseStatement, ?> ctx) {
+        return new BaseStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<QName, BaseStatement> createEffective(
+            final StmtContext<QName, BaseStatement, EffectiveStatement<QName, BaseStatement>> ctx) {
+        return new BaseEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public void onStatementDefinitionDeclared(
+            final Mutable<QName, BaseStatement, EffectiveStatement<QName, BaseStatement>> baseStmtCtx) {
+        final Mutable<?, ?, ?> baseParentCtx = baseStmtCtx.getParentContext();
+        if (StmtContextUtils.producesDeclared(baseParentCtx, IdentityStatement.class)) {
+
+            final QName baseIdentityQName = baseStmtCtx.coerceStatementArgument();
+            final ModelActionBuilder baseIdentityAction = baseStmtCtx.newInferenceAction(
+                ModelProcessingPhase.STATEMENT_DEFINITION);
+            final Prerequisite<StmtContext<?, ?, ?>> requiresPrereq = baseIdentityAction.requiresCtx(baseStmtCtx,
+                IdentityNamespace.class, baseIdentityQName, ModelProcessingPhase.STATEMENT_DEFINITION);
+            final Prerequisite<StmtContext.Mutable<?, ?, ?>> mutatesPrereq = baseIdentityAction.mutatesCtx(
+                baseParentCtx, ModelProcessingPhase.STATEMENT_DEFINITION);
+
+            baseIdentityAction.apply(new InferenceAction() {
+                @Override
+                public void apply(final InferenceContext ctx) {
+                    List<StmtContext<?, ?, ?>> derivedIdentities = baseStmtCtx.getFromNamespace(
+                        DerivedIdentitiesNamespace.class, baseIdentityQName);
+                    if (derivedIdentities == null) {
+                        derivedIdentities = new ArrayList<>(1);
+                        baseStmtCtx.addToNs(DerivedIdentitiesNamespace.class, baseIdentityQName, derivedIdentities);
+                    }
+                    derivedIdentities.add(baseParentCtx);
+                }
+
+                @Override
+                public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
+                    throw new InferenceException(baseStmtCtx.getStatementSourceReference(),
+                        "Unable to resolve identity %s and base identity %s",
+                        baseParentCtx.getStatementArgument(), baseStmtCtx.getStatementArgument());
+                }
+            });
+        }
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/belongs_to/BelongsToEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/belongs_to/BelongsToEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..06690e0
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.belongs_to;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class BelongsToEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, BelongsToStatement>
+        implements BelongsToEffectiveStatement {
+    BelongsToEffectiveStatementImpl(final StmtContext<String, BelongsToStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/belongs_to/BelongsToStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/belongs_to/BelongsToStatementImpl.java
new file mode 100644 (file)
index 0000000..6495913
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.belongs_to;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class BelongsToStatementImpl extends AbstractDeclaredStatement<String>
+        implements BelongsToStatement {
+    BelongsToStatementImpl(final StmtContext<String, BelongsToStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/belongs_to/BelongsToStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/belongs_to/BelongsToStatementSupport.java
new file mode 100644 (file)
index 0000000..d4f2ff8
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.belongs_to;
+
+import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.findFirstDeclaredSubstatement;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNamespaceForBelongsTo;
+
+public final class BelongsToStatementSupport extends
+        AbstractStatementSupport<String, BelongsToStatement, EffectiveStatement<String, BelongsToStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR =
+            SubstatementValidator.builder(YangStmtMapping.BELONGS_TO).addMandatory(YangStmtMapping.PREFIX).build();
+    private static final BelongsToStatementSupport INSTANCE = new BelongsToStatementSupport();
+
+    private BelongsToStatementSupport() {
+        super(YangStmtMapping.BELONGS_TO);
+    }
+
+    public static BelongsToStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public BelongsToStatement createDeclared(
+            final StmtContext<String, BelongsToStatement, ?> ctx) {
+        return new BelongsToStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, BelongsToStatement> createEffective(
+            final StmtContext<String, BelongsToStatement, EffectiveStatement<String, BelongsToStatement>> ctx) {
+        return new BelongsToEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public void onPreLinkageDeclared(final StmtContext.Mutable<String, BelongsToStatement,
+            EffectiveStatement<String, BelongsToStatement>> belongsToCtx) {
+        belongsToCtx.addRequiredSource(getSourceIdentifier(belongsToCtx));
+    }
+
+    @Override
+    public void onLinkageDeclared(final Mutable<String, BelongsToStatement,
+            EffectiveStatement<String, BelongsToStatement>> belongsToCtx) {
+        ModelActionBuilder belongsToAction = belongsToCtx.newInferenceAction(ModelProcessingPhase.SOURCE_LINKAGE);
+
+        final SourceIdentifier belongsToSourceIdentifier = getSourceIdentifier(belongsToCtx);
+        final Prerequisite<StmtContext<?, ?, ?>> belongsToPrereq = belongsToAction.requiresCtx(belongsToCtx,
+            ModuleNamespaceForBelongsTo.class, belongsToCtx.coerceStatementArgument(),
+            ModelProcessingPhase.SOURCE_LINKAGE);
+
+        belongsToAction.apply(new InferenceAction() {
+            @Override
+            public void apply(final InferenceContext ctx) {
+                StmtContext<?, ?, ?> belongsToModuleCtx = belongsToPrereq.resolve(ctx);
+
+                belongsToCtx.addToNs(BelongsToModuleContext.class, belongsToSourceIdentifier, belongsToModuleCtx);
+                belongsToCtx.addToNs(BelongsToPrefixToModuleCtx.class,
+                    findFirstDeclaredSubstatement(belongsToCtx, PrefixStatement.class).coerceStatementArgument(),
+                    belongsToModuleCtx);
+            }
+
+            @Override
+            public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
+                if (failed.contains(belongsToPrereq)) {
+                    throw new InferenceException(belongsToCtx.getStatementSourceReference(),
+                        "Module '%s' from belongs-to was not found", belongsToCtx.getStatementArgument());
+                }
+            }
+        });
+    }
+
+    private static SourceIdentifier getSourceIdentifier(final StmtContext<String, BelongsToStatement,
+            EffectiveStatement<String, BelongsToStatement>> belongsToCtx) {
+        return RevisionSourceIdentifier.create(belongsToCtx.coerceStatementArgument());
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/AbstractBitStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/AbstractBitStatementSupport.java
new file mode 100644 (file)
index 0000000..353dbff
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.bit;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BitStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+abstract class AbstractBitStatementSupport extends
+        AbstractQNameStatementSupport<BitStatement, EffectiveStatement<QName, BitStatement>> {
+    AbstractBitStatementSupport() {
+        super(YangStmtMapping.BIT);
+    }
+
+    @Override
+    public final QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public final BitStatement createDeclared(final StmtContext<QName, BitStatement, ?> ctx) {
+        return new BitStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<QName, BitStatement> createEffective(
+            final StmtContext<QName, BitStatement, EffectiveStatement<QName, BitStatement>> ctx) {
+        return new BitEffectiveStatementImpl(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/BitEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/BitEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..e6e3c2f
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.bit;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BitEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BitStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PositionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDocumentedNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+// FIXME: hide this class
+public final class BitEffectiveStatementImpl extends AbstractEffectiveDocumentedNode<QName, BitStatement>
+        implements BitEffectiveStatement {
+
+    private final QName qname;
+    private final SchemaPath schemaPath;
+    private final Long declaredPosition;
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownSchemaNodes;
+
+    BitEffectiveStatementImpl(final StmtContext<QName, BitStatement, ?> ctx) {
+        super(ctx);
+
+        qname = ctx.getStatementArgument();
+        schemaPath = ctx.getSchemaPath().get();
+
+        final List<UnknownSchemaNode> unknownSchemaNodesInit = new ArrayList<>();
+        Long declaredPositionInit = null;
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof PositionEffectiveStatement) {
+                declaredPositionInit = ((PositionEffectiveStatement) effectiveStatement).argument();
+            }
+            if (effectiveStatement instanceof UnknownSchemaNode) {
+                unknownSchemaNodesInit.add((UnknownSchemaNode) effectiveStatement);
+            }
+        }
+
+        declaredPosition = declaredPositionInit;
+        unknownSchemaNodes = ImmutableList.copyOf(unknownSchemaNodesInit);
+    }
+
+    public Long getDeclaredPosition() {
+        return declaredPosition;
+    }
+
+    public String getName() {
+        return qname.getLocalName();
+    }
+
+    public QName getQName() {
+        return qname;
+    }
+
+    public SchemaPath getPath() {
+        return schemaPath;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownSchemaNodes;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + qname.hashCode();
+        result = prime * result + schemaPath.hashCode();
+        result = prime * result + Objects.hashCode(declaredPosition);
+        result = prime * result + Objects.hashCode(unknownSchemaNodes);
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final BitsTypeDefinition.Bit other = (BitsTypeDefinition.Bit) obj;
+        return Objects.equals(qname, other.getQName()) && Objects.equals(schemaPath, other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return BitEffectiveStatementImpl.class.getSimpleName() + "[name=" + qname.getLocalName() + ", position="
+                + declaredPosition + "]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/BitStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/BitStatementImpl.java
new file mode 100644 (file)
index 0000000..bbb3b5d
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.bit;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.BitStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class BitStatementImpl extends AbstractDeclaredStatement<QName> implements BitStatement {
+    BitStatementImpl(final StmtContext<QName, BitStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/BitStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/BitStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..9f9b4f2
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.bit;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class BitStatementRFC6020Support extends AbstractBitStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.BIT)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addOptional(YangStmtMapping.POSITION)
+        .build();
+    private static final BitStatementRFC6020Support INSTANCE = new BitStatementRFC6020Support();
+
+    private BitStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static BitStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/BitStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/bit/BitStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..c195ea2
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.bit;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Bit statement.
+ */
+@Beta
+public final class BitStatementRFC7950Support extends AbstractBitStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .BIT)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.IF_FEATURE)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.STATUS)
+            .addOptional(YangStmtMapping.POSITION)
+            .build();
+    private static final BitStatementRFC7950Support INSTANCE = new BitStatementRFC7950Support();
+
+    private BitStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static BitStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/AbstractCaseStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/AbstractCaseStatementSupport.java
new file mode 100644 (file)
index 0000000..2b3d6ef
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.case_;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.CaseStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+abstract class AbstractCaseStatementSupport
+        extends AbstractQNameStatementSupport<CaseStatement, EffectiveStatement<QName, CaseStatement>> {
+
+    AbstractCaseStatementSupport() {
+        super(YangStmtMapping.CASE);
+    }
+
+    @Override
+    public final QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public final void onStatementAdded(
+            final Mutable<QName, CaseStatement, EffectiveStatement<QName, CaseStatement>> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public final CaseStatement createDeclared(final StmtContext<QName, CaseStatement, ?> ctx) {
+        return new CaseStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<QName, CaseStatement> createEffective(
+            final StmtContext<QName, CaseStatement, EffectiveStatement<QName, CaseStatement>> ctx) {
+        return new CaseEffectiveStatementImpl(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/CaseEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/CaseEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..757794c
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.case_;
+
+import java.util.Objects;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.CaseStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveSimpleDataNodeContainer;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class CaseEffectiveStatementImpl extends AbstractEffectiveSimpleDataNodeContainer<CaseStatement>
+        implements CaseEffectiveStatement, CaseSchemaNode, DerivableSchemaNode {
+
+    private final CaseSchemaNode original;
+    private final boolean configuration;
+
+    CaseEffectiveStatementImpl(final StmtContext<QName, CaseStatement, EffectiveStatement<QName, CaseStatement>> ctx) {
+        super(ctx);
+        this.original = (CaseSchemaNode) ctx.getOriginalCtx().map(StmtContext::buildEffective).orElse(null);
+
+        if (ctx.isConfiguration()) {
+            configuration = ctx.allSubstatementsStream().anyMatch(StmtContext::isConfiguration);
+        } else {
+            configuration = false;
+        }
+    }
+
+    @Override
+    public Optional<CaseSchemaNode> getOriginal() {
+        return Optional.ofNullable(original);
+    }
+
+    @Override
+    public boolean isConfiguration() {
+        return configuration;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getQName());
+        result = prime * result + Objects.hashCode(getPath());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        CaseEffectiveStatementImpl other = (CaseEffectiveStatementImpl) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return CaseEffectiveStatementImpl.class.getSimpleName() + "["
+                + "qname=" + getQName()
+                + "]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/CaseStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/CaseStatementImpl.java
new file mode 100644 (file)
index 0000000..863ca70
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.case_;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.CaseStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class CaseStatementImpl extends AbstractDeclaredStatement<QName> implements CaseStatement {
+    CaseStatementImpl(final StmtContext<QName, CaseStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/CaseStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/CaseStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..df5db1b
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.case_;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class CaseStatementRFC6020Support extends AbstractCaseStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.CASE)
+        .addAny(YangStmtMapping.ANYXML)
+        .addAny(YangStmtMapping.CHOICE)
+        .addAny(YangStmtMapping.CONTAINER)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addAny(YangStmtMapping.LEAF)
+        .addAny(YangStmtMapping.LEAF_LIST)
+        .addAny(YangStmtMapping.LIST)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addAny(YangStmtMapping.USES)
+        .addOptional(YangStmtMapping.WHEN)
+        .build();
+    private static final CaseStatementRFC6020Support INSTANCE = new CaseStatementRFC6020Support();
+
+    private CaseStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static CaseStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/CaseStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/case_/CaseStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..dc9d3f6
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.case_;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Case statement.
+ */
+@Beta
+public final class CaseStatementRFC7950Support extends AbstractCaseStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .CASE)
+            .addAny(YangStmtMapping.ANYDATA)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.CHOICE)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.IF_FEATURE)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.STATUS)
+            .addAny(YangStmtMapping.USES)
+            .addOptional(YangStmtMapping.WHEN)
+            .build();
+    private static final CaseStatementRFC7950Support INSTANCE = new CaseStatementRFC7950Support();
+
+    private CaseStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static CaseStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/AbstractChoiceStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/AbstractChoiceStatementSupport.java
new file mode 100644 (file)
index 0000000..7f78ccf
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.choice;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.YangValidationBundles;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ImplicitParentAwareStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+abstract class AbstractChoiceStatementSupport extends
+        AbstractQNameStatementSupport<ChoiceStatement, EffectiveStatement<QName, ChoiceStatement>>
+        implements ImplicitParentAwareStatementSupport {
+    AbstractChoiceStatementSupport() {
+        super(YangStmtMapping.CHOICE);
+    }
+
+    @Override
+    public final QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public final Optional<StatementSupport<?, ?, ?>> getImplicitParentFor(final StatementDefinition stmtDef) {
+        if (YangValidationBundles.SUPPORTED_CASE_SHORTHANDS.contains(stmtDef)) {
+            return Optional.of(implictCase());
+        }
+        return Optional.empty();
+    }
+
+    @Override
+    public final void onStatementAdded(
+            final Mutable<QName, ChoiceStatement, EffectiveStatement<QName, ChoiceStatement>> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public final ChoiceStatement createDeclared(final StmtContext<QName, ChoiceStatement, ?> ctx) {
+        return new ChoiceStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<QName, ChoiceStatement> createEffective(
+            final StmtContext<QName, ChoiceStatement, EffectiveStatement<QName, ChoiceStatement>> ctx) {
+        return new ChoiceEffectiveStatementImpl(ctx);
+    }
+
+    abstract StatementSupport<?, ?, ?> implictCase();
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/ChoiceEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/ChoiceEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..5974156
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.choice;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedMap;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DefaultEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDataSchemaNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+final class ChoiceEffectiveStatementImpl extends AbstractEffectiveDataSchemaNode<ChoiceStatement>
+        implements ChoiceEffectiveStatement, ChoiceSchemaNode, DerivableSchemaNode {
+
+    private final ImmutableSet<AugmentationSchemaNode> augmentations;
+    private final ImmutableSortedMap<QName, CaseSchemaNode> cases;
+    private final CaseSchemaNode defaultCase;
+    private final ChoiceSchemaNode original;
+    private final boolean mandatory;
+
+    ChoiceEffectiveStatementImpl(
+            final StmtContext<QName, ChoiceStatement, EffectiveStatement<QName, ChoiceStatement>> ctx) {
+        super(ctx);
+        this.original = (ChoiceSchemaNode) ctx.getOriginalCtx().map(StmtContext::buildEffective).orElse(null);
+
+        // initSubstatementCollectionsAndFields
+        final Set<AugmentationSchemaNode> augmentationsInit = new LinkedHashSet<>();
+        final SortedMap<QName, CaseSchemaNode> casesInit = new TreeMap<>();
+
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof AugmentationSchemaNode) {
+                final AugmentationSchemaNode augmentationSchema = (AugmentationSchemaNode) effectiveStatement;
+                augmentationsInit.add(augmentationSchema);
+            }
+            if (effectiveStatement instanceof CaseSchemaNode) {
+                final CaseSchemaNode choiceCaseNode = (CaseSchemaNode) effectiveStatement;
+                // FIXME: we may be overwriting a previous entry, is that really okay?
+                casesInit.put(choiceCaseNode.getQName(), choiceCaseNode);
+            }
+        }
+
+        this.augmentations = ImmutableSet.copyOf(augmentationsInit);
+        this.cases = ImmutableSortedMap.copyOfSorted(casesInit);
+
+        final Optional<String> defaultArg = findFirstEffectiveSubstatementArgument(DefaultEffectiveStatement.class);
+        if (defaultArg.isPresent()) {
+            final String arg = defaultArg.get();
+            final QName qname;
+            try {
+                qname = QName.create(getQName(), arg);
+            } catch (IllegalArgumentException e) {
+                throw new SourceException(ctx.getStatementSourceReference(), "Default statement has invalid name '%s'",
+                    arg, e);
+            }
+
+            // FIXME: this does not work with submodules, as they are
+            defaultCase = InferenceException.throwIfNull(cases.get(qname), ctx.getStatementSourceReference(),
+                "Default statement refers to missing case %s", qname);
+        } else {
+            defaultCase = null;
+        }
+
+        mandatory = findFirstEffectiveSubstatementArgument(MandatoryEffectiveStatement.class).orElse(Boolean.FALSE)
+                .booleanValue();
+    }
+
+    @Override
+    public Optional<ChoiceSchemaNode> getOriginal() {
+        return Optional.ofNullable(original);
+    }
+
+    @Override
+    public Set<AugmentationSchemaNode> getAvailableAugmentations() {
+        return augmentations;
+    }
+
+    @Override
+    public SortedMap<QName, CaseSchemaNode> getCases() {
+        return cases;
+    }
+
+    @Override
+    public Optional<CaseSchemaNode> getDefaultCase() {
+        return Optional.ofNullable(defaultCase);
+    }
+
+    @Override
+    public boolean isMandatory() {
+        return mandatory;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getQName());
+        result = prime * result + Objects.hashCode(getPath());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ChoiceEffectiveStatementImpl other = (ChoiceEffectiveStatementImpl) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return ChoiceEffectiveStatementImpl.class.getSimpleName() + "["
+                + "qname=" + getQName()
+                + "]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/ChoiceStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/ChoiceStatementImpl.java
new file mode 100644 (file)
index 0000000..f985bb8
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.choice;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ChoiceStatementImpl extends AbstractDeclaredStatement<QName>
+        implements ChoiceStatement {
+    ChoiceStatementImpl(final StmtContext<QName, ChoiceStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/ChoiceStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/ChoiceStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..7c6e98b
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.choice;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.case_.CaseStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ChoiceStatementRFC6020Support extends AbstractChoiceStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .CHOICE)
+        .addAny(YangStmtMapping.ANYXML)
+        .addAny(YangStmtMapping.CASE)
+        .addOptional(YangStmtMapping.CONFIG)
+        .addAny(YangStmtMapping.CONTAINER)
+        .addOptional(YangStmtMapping.DEFAULT)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addAny(YangStmtMapping.LEAF)
+        .addAny(YangStmtMapping.LEAF_LIST)
+        .addAny(YangStmtMapping.LIST)
+        .addOptional(YangStmtMapping.MANDATORY)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addOptional(YangStmtMapping.WHEN)
+        .build();
+    private static final ChoiceStatementRFC6020Support INSTANCE = new ChoiceStatementRFC6020Support();
+
+    private ChoiceStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static ChoiceStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    StatementSupport<?, ?, ?> implictCase() {
+        return CaseStatementRFC6020Support.getInstance();
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/ChoiceStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/choice/ChoiceStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..8dce7a6
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.choice;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.case_.CaseStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Choice statement.
+ */
+@Beta
+public final class ChoiceStatementRFC7950Support extends AbstractChoiceStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .CHOICE)
+            .addAny(YangStmtMapping.ANYDATA)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.CASE)
+            .addOptional(YangStmtMapping.CONFIG)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addOptional(YangStmtMapping.DEFAULT)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.IF_FEATURE)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addOptional(YangStmtMapping.MANDATORY)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.STATUS)
+            .addOptional(YangStmtMapping.WHEN)
+            .build();
+
+    private static final ChoiceStatementRFC7950Support INSTANCE = new ChoiceStatementRFC7950Support();
+
+    private ChoiceStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static ChoiceStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    StatementSupport<?, ?, ?> implictCase() {
+        return CaseStatementRFC7950Support.getInstance();
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/ConfigEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/ConfigEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..ef31f93
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.config;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public final class ConfigEffectiveStatementImpl extends DeclaredEffectiveStatementBase<Boolean, ConfigStatement>
+        implements ConfigEffectiveStatement {
+    public ConfigEffectiveStatementImpl(final StmtContext<Boolean, ConfigStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/ConfigStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/ConfigStatementImpl.java
new file mode 100644 (file)
index 0000000..30895ac
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.config;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ConfigStatementImpl extends AbstractDeclaredStatement<Boolean> implements ConfigStatement {
+    ConfigStatementImpl(final StmtContext<Boolean, ConfigStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/ConfigStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/ConfigStatementSupport.java
new file mode 100644 (file)
index 0000000..a09e22d
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.config;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ConfigStatementSupport extends
+    AbstractStatementSupport<Boolean, ConfigStatement, EffectiveStatement<Boolean, ConfigStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.CONFIG).build();
+    private static final ConfigStatementSupport INSTANCE = new ConfigStatementSupport();
+
+    private ConfigStatementSupport() {
+        super(YangStmtMapping.CONFIG);
+    }
+
+    public static ConfigStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Boolean parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return ArgumentUtils.parseBoolean(ctx, value);
+    }
+
+    @Override
+    public ConfigStatement createDeclared(final StmtContext<Boolean, ConfigStatement, ?> ctx) {
+        final ConfigStatement ret = new ConfigStatementImpl(ctx);
+
+        if (EmptyConfigStatement.FALSE.equals(ret)) {
+            return EmptyConfigStatement.FALSE;
+        } else if (EmptyConfigStatement.TRUE.equals(ret)) {
+            return EmptyConfigStatement.TRUE;
+        } else {
+            return ret;
+        }
+    }
+
+    @Override
+    public EffectiveStatement<Boolean, ConfigStatement> createEffective(
+            final StmtContext<Boolean, ConfigStatement, EffectiveStatement<Boolean, ConfigStatement>> ctx) {
+        final EffectiveStatement<Boolean, ConfigStatement> ret = new ConfigEffectiveStatementImpl(ctx);
+        final ConfigStatement declared = ret.getDeclared();
+        if (declared instanceof EmptyConfigStatement && ret.effectiveSubstatements().isEmpty()) {
+            return ((EmptyConfigStatement)declared).toEffective();
+        }
+        return ret;
+    }
+
+    @Override
+    public String internArgument(final String rawArgument) {
+        return ArgumentUtils.internBoolean(rawArgument);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/EmptyConfigEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/EmptyConfigEffectiveStatement.java
new file mode 100644 (file)
index 0000000..4d74b79
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.config;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement;
+
+abstract class EmptyConfigEffectiveStatement implements ConfigEffectiveStatement {
+    static final EmptyConfigEffectiveStatement FALSE = new EmptyConfigEffectiveStatement() {
+        @Override
+        public ConfigStatement getDeclared() {
+            return EmptyConfigStatement.FALSE;
+        }
+    };
+
+    static final EmptyConfigEffectiveStatement TRUE = new EmptyConfigEffectiveStatement() {
+        @Override
+        public ConfigStatement getDeclared() {
+            return EmptyConfigStatement.TRUE;
+        }
+    };
+
+    private EmptyConfigEffectiveStatement() {
+        // Hidden
+    }
+
+    @Override
+    public final StatementDefinition statementDefinition() {
+        return getDeclared().statementDefinition();
+    }
+
+    @Override
+    public final Boolean argument() {
+        return getDeclared().argument();
+    }
+
+    @Override
+    public final StatementSource getStatementSource() {
+        return getDeclared().getStatementSource();
+    }
+
+    @Override
+    public final <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends V> get(final Class<N> namespace,
+            final K identifier) {
+        throw new UnsupportedOperationException("Not implemented yet.");
+    }
+
+    @Override
+    public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAll(final Class<N> namespace) {
+        throw new UnsupportedOperationException("Not implemented yet.");
+    }
+
+    @Override
+    public final Collection<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public abstract @NonNull ConfigStatement getDeclared();
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/EmptyConfigStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/config/EmptyConfigStatement.java
new file mode 100644 (file)
index 0000000..388d033
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.config;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement;
+
+abstract class EmptyConfigStatement implements ConfigStatement {
+    static final @NonNull ConfigStatement FALSE = new EmptyConfigStatement() {
+        @Override
+        public Boolean argument() {
+            return Boolean.FALSE;
+        }
+
+        @Override
+        EffectiveStatement<Boolean, ConfigStatement> toEffective() {
+            return EmptyConfigEffectiveStatement.FALSE;
+        }
+    };
+
+    static final @NonNull ConfigStatement TRUE = new EmptyConfigStatement() {
+        @Override
+        public Boolean argument() {
+            return Boolean.TRUE;
+        }
+
+        @Override
+        EffectiveStatement<Boolean, ConfigStatement> toEffective() {
+            return EmptyConfigEffectiveStatement.TRUE;
+        }
+    };
+
+    abstract EffectiveStatement<Boolean, ConfigStatement> toEffective();
+
+    @Override
+    public final Collection<? extends DeclaredStatement<?>> declaredSubstatements() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public final StatementDefinition statementDefinition() {
+        return YangStmtMapping.CONFIG;
+    }
+
+    @Override
+    public final String rawArgument() {
+        return Boolean.toString(getValue());
+    }
+
+    @Override
+    public final StatementSource getStatementSource() {
+        return StatementSource.DECLARATION;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(statementDefinition(), getStatementSource(), argument(), rawArgument(),
+            declaredSubstatements(), getValue());
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof ConfigStatement)) {
+            return false;
+        }
+
+        final ConfigStatement other = (ConfigStatement) obj;
+
+        return getValue() == other.getValue() && argument().equals(other.argument())
+                && rawArgument().equals(other.rawArgument())
+                && statementDefinition().equals(other.statementDefinition())
+                && getStatementSource().equals(other.getStatementSource())
+                && declaredSubstatements().equals(other.declaredSubstatements());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/contact/ContactEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/contact/ContactEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..bbcbf89
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.contact;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ContactEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContactStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ContactEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, ContactStatement>
+        implements ContactEffectiveStatement {
+    ContactEffectiveStatementImpl(final StmtContext<String, ContactStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/contact/ContactStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/contact/ContactStatementImpl.java
new file mode 100644 (file)
index 0000000..be618e6
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.contact;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ContactStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ContactStatementImpl extends AbstractDeclaredStatement<String> implements ContactStatement {
+    ContactStatementImpl(final StmtContext<String, ContactStatement,?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/contact/ContactStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/contact/ContactStatementSupport.java
new file mode 100644 (file)
index 0000000..201ab5d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.contact;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContactStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ContactStatementSupport
+        extends AbstractStatementSupport<String, ContactStatement,EffectiveStatement<String, ContactStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.CONTACT).build();
+    private static final ContactStatementSupport INSTANCE = new ContactStatementSupport();
+
+    private ContactStatementSupport() {
+        super(YangStmtMapping.CONTACT);
+    }
+
+    public static ContactStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public ContactStatement createDeclared(final StmtContext<String, ContactStatement, ?> ctx) {
+        return new ContactStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, ContactStatement> createEffective(
+            final StmtContext<String, ContactStatement, EffectiveStatement<String, ContactStatement>> ctx) {
+        return new ContactEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/AbstractContainerStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/AbstractContainerStatementSupport.java
new file mode 100644 (file)
index 0000000..809158d
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.container;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+abstract class AbstractContainerStatementSupport
+        extends AbstractQNameStatementSupport<ContainerStatement, EffectiveStatement<QName, ContainerStatement>> {
+
+    AbstractContainerStatementSupport() {
+        super(YangStmtMapping.CONTAINER);
+    }
+
+    @Override
+    public final QName parseArgumentValue(final StmtContext<?,?,?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public final void onStatementAdded(
+            final Mutable<QName, ContainerStatement, EffectiveStatement<QName, ContainerStatement>> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public final ContainerStatement createDeclared(final StmtContext<QName, ContainerStatement,?> ctx) {
+        return new ContainerStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<QName,ContainerStatement> createEffective(
+            final StmtContext<QName, ContainerStatement, EffectiveStatement<QName,ContainerStatement>> ctx) {
+        return new ContainerEffectiveStatementImpl(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/ContainerEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/ContainerEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..a64b323
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.container;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PresenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveContainerSchemaNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ContainerEffectiveStatementImpl extends AbstractEffectiveContainerSchemaNode<ContainerStatement>
+        implements ContainerEffectiveStatement, DerivableSchemaNode {
+    private final @NonNull ImmutableSet<ActionDefinition> actions;
+    private final @NonNull ImmutableSet<NotificationDefinition> notifications;
+    private final @Nullable ContainerSchemaNode original;
+    private final boolean presence;
+
+    ContainerEffectiveStatementImpl(
+            final StmtContext<QName, ContainerStatement, EffectiveStatement<QName, ContainerStatement>> ctx) {
+        super(ctx);
+        this.original = (ContainerSchemaNode) ctx.getOriginalCtx().map(StmtContext::buildEffective).orElse(null);
+        final ImmutableSet.Builder<ActionDefinition> actionsBuilder = ImmutableSet.builder();
+        final Builder<NotificationDefinition> notificationsBuilder = ImmutableSet.builder();
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof ActionDefinition) {
+                actionsBuilder.add((ActionDefinition) effectiveStatement);
+            }
+
+            if (effectiveStatement instanceof NotificationDefinition) {
+                notificationsBuilder.add((NotificationDefinition) effectiveStatement);
+            }
+        }
+
+        this.actions = actionsBuilder.build();
+        this.notifications = notificationsBuilder.build();
+        presence = findFirstEffectiveSubstatement(PresenceEffectiveStatement.class).isPresent();
+    }
+
+    @Override
+    public Optional<ContainerSchemaNode> getOriginal() {
+        return Optional.ofNullable(original);
+    }
+
+    @Override
+    public Set<ActionDefinition> getActions() {
+        return actions;
+    }
+
+    @Override
+    public Set<NotificationDefinition> getNotifications() {
+        return notifications;
+    }
+
+    @Override
+    public boolean isPresenceContainer() {
+        return presence;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getQName());
+        result = prime * result + Objects.hashCode(getPath());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ContainerEffectiveStatementImpl other = (ContainerEffectiveStatementImpl) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return "container " + getQName().getLocalName();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/ContainerStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/ContainerStatementImpl.java
new file mode 100644 (file)
index 0000000..412079d
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.container;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ContainerStatementImpl extends AbstractDeclaredStatement<QName> implements ContainerStatement {
+    ContainerStatementImpl(final StmtContext<QName, ContainerStatement,?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/ContainerStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/ContainerStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..7d75192
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.container;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ContainerStatementRFC6020Support extends AbstractContainerStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.CONTAINER)
+        .addAny(YangStmtMapping.ANYXML)
+        .addAny(YangStmtMapping.CHOICE)
+        .addOptional(YangStmtMapping.CONFIG)
+        .addAny(YangStmtMapping.CONTAINER)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.GROUPING)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addAny(YangStmtMapping.LEAF)
+        .addAny(YangStmtMapping.LEAF_LIST)
+        .addAny(YangStmtMapping.LIST)
+        .addAny(YangStmtMapping.MUST)
+        .addOptional(YangStmtMapping.PRESENCE)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addAny(YangStmtMapping.TYPEDEF)
+        .addAny(YangStmtMapping.USES)
+        .addOptional(YangStmtMapping.WHEN)
+        .build();
+    private static final ContainerStatementRFC6020Support INSTANCE = new ContainerStatementRFC6020Support();
+
+    private ContainerStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static ContainerStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/ContainerStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/container/ContainerStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..ad87af8
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.container;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Container statement.
+ */
+@Beta
+public final class ContainerStatementRFC7950Support extends AbstractContainerStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .CONTAINER)
+            .addAny(YangStmtMapping.ACTION)
+            .addAny(YangStmtMapping.ANYDATA)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.CHOICE)
+            .addOptional(YangStmtMapping.CONFIG)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.GROUPING)
+            .addAny(YangStmtMapping.IF_FEATURE)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addAny(YangStmtMapping.MUST)
+            .addAny(YangStmtMapping.NOTIFICATION)
+            .addOptional(YangStmtMapping.PRESENCE)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.STATUS)
+            .addAny(YangStmtMapping.TYPEDEF)
+            .addAny(YangStmtMapping.USES)
+            .addOptional(YangStmtMapping.WHEN)
+            .build();
+    private static final ContainerStatementRFC7950Support INSTANCE = new ContainerStatementRFC7950Support();
+
+    private ContainerStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static ContainerStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/default_/DefaultEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/default_/DefaultEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..3430cb8
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.default_;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.DefaultEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DefaultStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class DefaultEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, DefaultStatement>
+        implements DefaultEffectiveStatement {
+    DefaultEffectiveStatementImpl(final StmtContext<String, DefaultStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/default_/DefaultStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/default_/DefaultStatementImpl.java
new file mode 100644 (file)
index 0000000..2ad75aa
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.default_;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.DefaultStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class DefaultStatementImpl extends AbstractDeclaredStatement<String> implements
+        DefaultStatement {
+    DefaultStatementImpl(final StmtContext<String, DefaultStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/default_/DefaultStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/default_/DefaultStatementSupport.java
new file mode 100644 (file)
index 0000000..d0e42d1
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.default_;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DefaultStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class DefaultStatementSupport
+        extends AbstractStatementSupport<String, DefaultStatement, EffectiveStatement<String, DefaultStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.DEFAULT).build();
+    private static final DefaultStatementSupport INSTANCE = new DefaultStatementSupport();
+
+    private DefaultStatementSupport() {
+        super(YangStmtMapping.DEFAULT);
+    }
+
+    public static DefaultStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public DefaultStatement createDeclared(final StmtContext<String, DefaultStatement, ?> ctx) {
+        return new DefaultStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, DefaultStatement> createEffective(
+            final StmtContext<String, DefaultStatement, EffectiveStatement<String, DefaultStatement>> ctx) {
+        return new DefaultEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/description/DescriptionEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/description/DescriptionEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..102fa81
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.description;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class DescriptionEffectiveStatementImpl
+        extends DeclaredEffectiveStatementBase<String, DescriptionStatement> implements DescriptionEffectiveStatement {
+    DescriptionEffectiveStatementImpl(final StmtContext<String, DescriptionStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/description/DescriptionStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/description/DescriptionStatementImpl.java
new file mode 100644 (file)
index 0000000..a61f7a8
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.description;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class DescriptionStatementImpl extends AbstractDeclaredStatement<String> implements DescriptionStatement {
+    DescriptionStatementImpl(final StmtContext<String, DescriptionStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/description/DescriptionStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/description/DescriptionStatementSupport.java
new file mode 100644 (file)
index 0000000..501b9c4
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.description;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class DescriptionStatementSupport extends AbstractStatementSupport<String, DescriptionStatement,
+        EffectiveStatement<String, DescriptionStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.DESCRIPTION).build();
+    private static final DescriptionStatementSupport INSTANCE = new DescriptionStatementSupport();
+
+    private DescriptionStatementSupport() {
+        super(YangStmtMapping.DESCRIPTION);
+    }
+
+    public static DescriptionStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public DescriptionStatement createDeclared(final StmtContext<String, DescriptionStatement, ?> ctx) {
+        return new DescriptionStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, DescriptionStatement> createEffective(
+            final StmtContext<String, DescriptionStatement, EffectiveStatement<String, DescriptionStatement>> ctx) {
+        return new DescriptionEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/AbstractDeviateStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/AbstractDeviateStatementSupport.java
new file mode 100644 (file)
index 0000000..7e5c90f
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.deviate;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.SetMultimap;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.DeviateKind;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviateStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.YangValidationBundles;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModulesDeviatedByModules;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModulesDeviatedByModules.SupportedModules;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractDeviateStatementSupport extends AbstractStatementSupport<DeviateKind, DeviateStatement,
+        EffectiveStatement<DeviateKind, DeviateStatement>> {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractDeviateStatementSupport.class);
+
+    private static final SubstatementValidator DEVIATE_NOT_SUPPORTED_SUBSTATEMENT_VALIDATOR =
+            SubstatementValidator.builder(YangStmtMapping.DEVIATE).build();
+
+    private static final SubstatementValidator DEVIATE_ADD_SUBSTATEMENT_VALIDATOR =
+            SubstatementValidator.builder(YangStmtMapping.DEVIATE)
+                .addOptional(YangStmtMapping.CONFIG)
+                .addOptional(YangStmtMapping.DEFAULT)
+                .addOptional(YangStmtMapping.MANDATORY)
+                .addOptional(YangStmtMapping.MAX_ELEMENTS)
+                .addOptional(YangStmtMapping.MIN_ELEMENTS)
+                .addAny(YangStmtMapping.MUST)
+                .addAny(YangStmtMapping.UNIQUE)
+                .addOptional(YangStmtMapping.UNITS)
+                .build();
+
+    private static final SubstatementValidator DEVIATE_REPLACE_SUBSTATEMENT_VALIDATOR =
+            SubstatementValidator.builder(YangStmtMapping.DEVIATE)
+                .addOptional(YangStmtMapping.CONFIG)
+                .addOptional(YangStmtMapping.DEFAULT)
+                .addOptional(YangStmtMapping.MANDATORY)
+                .addOptional(YangStmtMapping.MAX_ELEMENTS)
+                .addOptional(YangStmtMapping.MIN_ELEMENTS)
+                .addOptional(YangStmtMapping.TYPE)
+                .addOptional(YangStmtMapping.UNITS)
+                .build();
+
+    private static final SubstatementValidator DEVIATE_DELETE_SUBSTATEMENT_VALIDATOR =
+            SubstatementValidator.builder(YangStmtMapping.DEVIATE)
+                .addOptional(YangStmtMapping.DEFAULT)
+                .addAny(YangStmtMapping.MUST)
+                .addAny(YangStmtMapping.UNIQUE)
+                .addOptional(YangStmtMapping.UNITS)
+                .build();
+
+    private static final ImmutableMap<String, DeviateKind> KEYWORD_TO_DEVIATE_MAP =
+            Maps.uniqueIndex(Arrays.asList(DeviateKind.values()), DeviateKind::getKeyword);
+
+    private static final ImmutableSet<YangStmtMapping> SINGLETON_STATEMENTS = ImmutableSet.of(
+            YangStmtMapping.UNITS, YangStmtMapping.CONFIG, YangStmtMapping.MANDATORY,
+            YangStmtMapping.MIN_ELEMENTS, YangStmtMapping.MAX_ELEMENTS);
+
+    private static final ImmutableSet<YangStmtMapping> IMPLICIT_STATEMENTS = ImmutableSet.of(YangStmtMapping.CONFIG,
+            YangStmtMapping.MANDATORY, YangStmtMapping.MAX_ELEMENTS, YangStmtMapping.MIN_ELEMENTS);
+
+    AbstractDeviateStatementSupport() {
+        super(YangStmtMapping.DEVIATE);
+    }
+
+    @Override
+    public final DeviateKind parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return SourceException.throwIfNull(KEYWORD_TO_DEVIATE_MAP.get(value),
+            ctx.getStatementSourceReference(), "String '%s' is not valid deviate argument", value);
+    }
+
+    @Override
+    public final DeviateStatement createDeclared(final StmtContext<DeviateKind, DeviateStatement, ?> ctx) {
+        return new DeviateStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<DeviateKind, DeviateStatement> createEffective(
+            final StmtContext<DeviateKind, DeviateStatement, EffectiveStatement<DeviateKind,
+                    DeviateStatement>> ctx) {
+        return new DeviateEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public final void onFullDefinitionDeclared(final Mutable<DeviateKind, DeviateStatement,
+            EffectiveStatement<DeviateKind, DeviateStatement>> deviateStmtCtx) {
+        final DeviateKind deviateKind = deviateStmtCtx.getStatementArgument();
+        getSubstatementValidatorForDeviate(deviateKind).validate(deviateStmtCtx);
+
+        final SchemaNodeIdentifier deviationTarget =
+                (SchemaNodeIdentifier) deviateStmtCtx.coerceParentContext().getStatementArgument();
+
+        if (!isDeviationSupported(deviateStmtCtx, deviationTarget)) {
+            return;
+        }
+
+        final ModelActionBuilder deviateAction = deviateStmtCtx.newInferenceAction(
+                ModelProcessingPhase.EFFECTIVE_MODEL);
+
+        final Prerequisite<StmtContext<DeviateKind, DeviateStatement, EffectiveStatement<DeviateKind,
+                DeviateStatement>>> sourceCtxPrerequisite =
+                deviateAction.requiresCtx(deviateStmtCtx, ModelProcessingPhase.EFFECTIVE_MODEL);
+
+        final Prerequisite<Mutable<?, ?, EffectiveStatement<?, ?>>> targetCtxPrerequisite =
+                deviateAction.mutatesEffectiveCtxPath(deviateStmtCtx.getRoot(),
+                    ChildSchemaNodeNamespace.class, deviationTarget.getPathFromRoot());
+
+        deviateAction.apply(new InferenceAction() {
+            @Override
+            public void apply(final InferenceContext ctx) {
+                // FIXME once BUG-7760 gets fixed, there will be no need for these dirty casts
+                final StatementContextBase<?, ?, ?> sourceNodeStmtCtx =
+                        (StatementContextBase<?, ?, ?>) sourceCtxPrerequisite.resolve(ctx);
+                final StatementContextBase<?, ?, ?> targetNodeStmtCtx =
+                        (StatementContextBase<?, ?, ?>) targetCtxPrerequisite.resolve(ctx);
+
+                switch (deviateKind) {
+                    case NOT_SUPPORTED:
+                        targetNodeStmtCtx.setIsSupportedToBuildEffective(false);
+                        break;
+                    case ADD:
+                        performDeviateAdd(sourceNodeStmtCtx, targetNodeStmtCtx);
+                        break;
+                    case REPLACE:
+                        performDeviateReplace(sourceNodeStmtCtx, targetNodeStmtCtx);
+                        break;
+                    case DELETE:
+                        performDeviateDelete(sourceNodeStmtCtx, targetNodeStmtCtx);
+                        break;
+                    default:
+                        throw new IllegalStateException("Unsupported deviate " + deviateKind);
+                }
+            }
+
+            @Override
+            public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
+                throw new InferenceException(deviateStmtCtx.coerceParentContext().getStatementSourceReference(),
+                    "Deviation target '%s' not found.", deviationTarget);
+            }
+        });
+    }
+
+    @Override
+    public String internArgument(final String rawArgument) {
+        if ("add".equals(rawArgument)) {
+            return "add";
+        } else if ("delete".equals(rawArgument)) {
+            return "delete";
+        } else if ("replace".equals(rawArgument)) {
+            return "replace";
+        } else if ("not-supported".equals(rawArgument)) {
+            return "not-supported";
+        } else {
+            return rawArgument;
+        }
+    }
+
+    @Override
+    protected final SubstatementValidator getSubstatementValidator() {
+        return null;
+    }
+
+    protected SubstatementValidator getSubstatementValidatorForDeviate(final DeviateKind deviateKind) {
+        switch (deviateKind) {
+            case NOT_SUPPORTED:
+                return DEVIATE_NOT_SUPPORTED_SUBSTATEMENT_VALIDATOR;
+            case ADD:
+                return DEVIATE_ADD_SUBSTATEMENT_VALIDATOR;
+            case REPLACE:
+                return DEVIATE_REPLACE_SUBSTATEMENT_VALIDATOR;
+            case DELETE:
+                return DEVIATE_DELETE_SUBSTATEMENT_VALIDATOR;
+            default:
+                throw new IllegalStateException(String.format(
+                        "Substatement validator for deviate %s has not been defined.", deviateKind));
+        }
+    }
+
+    private static boolean isDeviationSupported(final Mutable<DeviateKind, DeviateStatement,
+            EffectiveStatement<DeviateKind, DeviateStatement>> deviateStmtCtx,
+            final SchemaNodeIdentifier deviationTarget) {
+        final SetMultimap<QNameModule, QNameModule> modulesDeviatedByModules = deviateStmtCtx.getFromNamespace(
+                ModulesDeviatedByModules.class, SupportedModules.SUPPORTED_MODULES);
+        if (modulesDeviatedByModules == null) {
+            return true;
+        }
+
+        final QNameModule currentModule = deviateStmtCtx.getFromNamespace(ModuleCtxToModuleQName.class,
+                deviateStmtCtx.getRoot());
+        final QNameModule targetModule = deviationTarget.getLastComponent().getModule();
+
+        final Set<QNameModule> deviationModulesSupportedByTargetModule = modulesDeviatedByModules.get(targetModule);
+        if (deviationModulesSupportedByTargetModule != null) {
+            return deviationModulesSupportedByTargetModule.contains(currentModule);
+        }
+
+        return false;
+    }
+
+    private static void performDeviateAdd(final StatementContextBase<?, ?, ?> deviateStmtCtx,
+            final StatementContextBase<?, ?, ?> targetCtx) {
+        for (Mutable<?, ?, ?> originalStmtCtx : deviateStmtCtx.mutableDeclaredSubstatements()) {
+            validateDeviationTarget(originalStmtCtx, targetCtx);
+            addStatement(originalStmtCtx, targetCtx);
+        }
+    }
+
+    private static void addStatement(final Mutable<?, ?, ?> stmtCtxToBeAdded,
+            final StatementContextBase<?, ?, ?> targetCtx) {
+        if (!StmtContextUtils.isUnknownStatement(stmtCtxToBeAdded)) {
+            final StatementDefinition stmtToBeAdded = stmtCtxToBeAdded.getPublicDefinition();
+            if (SINGLETON_STATEMENTS.contains(stmtToBeAdded) || YangStmtMapping.DEFAULT.equals(stmtToBeAdded)
+                    && YangStmtMapping.LEAF.equals(targetCtx.getPublicDefinition())) {
+                for (final StmtContext<?, ?, ?> targetCtxSubstatement : targetCtx.allSubstatements()) {
+                    InferenceException.throwIf(stmtToBeAdded.equals(targetCtxSubstatement.getPublicDefinition()),
+                        stmtCtxToBeAdded.getStatementSourceReference(),
+                        "Deviation cannot add substatement %s to target node %s because it is already defined "
+                        + "in target and can appear only once.",
+                        stmtToBeAdded.getStatementName(), targetCtx.getStatementArgument());
+                }
+            }
+        }
+
+        copyStatement(stmtCtxToBeAdded, targetCtx);
+    }
+
+    private static void performDeviateReplace(final StatementContextBase<?, ?, ?> deviateStmtCtx,
+            final StatementContextBase<?, ?, ?> targetCtx) {
+        for (Mutable<?, ?, ?> originalStmtCtx : deviateStmtCtx.mutableDeclaredSubstatements()) {
+            validateDeviationTarget(originalStmtCtx, targetCtx);
+            replaceStatement(originalStmtCtx, targetCtx);
+        }
+    }
+
+    private static void replaceStatement(final Mutable<?, ?, ?> stmtCtxToBeReplaced,
+            final StatementContextBase<?, ?, ?> targetCtx) {
+        final StatementDefinition stmtToBeReplaced = stmtCtxToBeReplaced.getPublicDefinition();
+
+        if (YangStmtMapping.DEFAULT.equals(stmtToBeReplaced)
+                && YangStmtMapping.LEAF_LIST.equals(targetCtx.getPublicDefinition())) {
+            LOG.error("Deviation cannot replace substatement {} in target leaf-list {} because a leaf-list can "
+                    + "have multiple default statements. At line: {}", stmtToBeReplaced.getStatementName(),
+                    targetCtx.getStatementArgument(), stmtCtxToBeReplaced.getStatementSourceReference());
+            return;
+        }
+
+        for (final StmtContext<?, ?, ?> targetCtxSubstatement : targetCtx.effectiveSubstatements()) {
+            if (stmtToBeReplaced.equals(targetCtxSubstatement.getPublicDefinition())) {
+                targetCtx.removeStatementFromEffectiveSubstatements(stmtToBeReplaced);
+                copyStatement(stmtCtxToBeReplaced, targetCtx);
+                return;
+            }
+        }
+
+        for (final Mutable<?, ?, ?> targetCtxSubstatement : targetCtx.mutableDeclaredSubstatements()) {
+            if (stmtToBeReplaced.equals(targetCtxSubstatement.getPublicDefinition())) {
+                targetCtxSubstatement.setIsSupportedToBuildEffective(false);
+                copyStatement(stmtCtxToBeReplaced, targetCtx);
+                return;
+            }
+        }
+
+        // This is a special case when deviate replace of a config/mandatory/max/min-elements substatement targets
+        // a node which does not contain an explicitly declared config/mandatory/max/min-elements.
+        // However, according to RFC6020/RFC7950, these properties are always implicitly present.
+        if (IMPLICIT_STATEMENTS.contains(stmtToBeReplaced)) {
+            addStatement(stmtCtxToBeReplaced, targetCtx);
+            return;
+        }
+
+        throw new InferenceException(stmtCtxToBeReplaced.getStatementSourceReference(), "Deviation cannot replace "
+                + "substatement %s in target node %s because it does not exist in target node.",
+                stmtToBeReplaced.getStatementName(), targetCtx.getStatementArgument());
+    }
+
+    private static void performDeviateDelete(final StatementContextBase<?, ?, ?> deviateStmtCtx,
+            final StatementContextBase<?, ?, ?> targetCtx) {
+        for (Mutable<?, ?, ?> originalStmtCtx : deviateStmtCtx.mutableDeclaredSubstatements()) {
+            validateDeviationTarget(originalStmtCtx, targetCtx);
+            deleteStatement(originalStmtCtx, targetCtx);
+        }
+    }
+
+    private static void deleteStatement(final StmtContext<?, ?, ?> stmtCtxToBeDeleted,
+            final StatementContextBase<?, ?, ?> targetCtx) {
+        final StatementDefinition stmtToBeDeleted = stmtCtxToBeDeleted.getPublicDefinition();
+        final String stmtArgument = stmtCtxToBeDeleted.rawStatementArgument();
+
+        for (final Mutable<?, ?, ?> targetCtxSubstatement : targetCtx.mutableEffectiveSubstatements()) {
+            if (statementsAreEqual(stmtToBeDeleted, stmtArgument, targetCtxSubstatement.getPublicDefinition(),
+                    targetCtxSubstatement.rawStatementArgument())) {
+                targetCtx.removeStatementFromEffectiveSubstatements(stmtToBeDeleted, stmtArgument);
+                return;
+            }
+        }
+
+        for (final Mutable<?, ?, ?> targetCtxSubstatement : targetCtx.mutableDeclaredSubstatements()) {
+            if (statementsAreEqual(stmtToBeDeleted, stmtArgument, targetCtxSubstatement.getPublicDefinition(),
+                    targetCtxSubstatement.rawStatementArgument())) {
+                targetCtxSubstatement.setIsSupportedToBuildEffective(false);
+                return;
+            }
+        }
+
+        LOG.error("Deviation cannot delete substatement {} with argument '{}' in target node {} because it does "
+                + "not exist in the target node. At line: {}", stmtToBeDeleted.getStatementName(), stmtArgument,
+                targetCtx.getStatementArgument(), stmtCtxToBeDeleted.getStatementSourceReference());
+    }
+
+    private static void copyStatement(final Mutable<?, ?, ?> stmtCtxToBeCopied,
+            final StatementContextBase<?, ?, ?> targetCtx) {
+        // we need to make a copy of the statement context only if it is an unknown statement, otherwise
+        // we can reuse the original statement context
+        if (!StmtContextUtils.isUnknownStatement(stmtCtxToBeCopied)) {
+            targetCtx.addEffectiveSubstatement(stmtCtxToBeCopied);
+        } else {
+            targetCtx.addEffectiveSubstatement(targetCtx.childCopyOf(stmtCtxToBeCopied, CopyType.ORIGINAL));
+        }
+    }
+
+    private static boolean statementsAreEqual(final StatementDefinition firstStmtDef, final String firstStmtArg,
+            final StatementDefinition secondStmtDef, final String secondStmtArg) {
+        return firstStmtDef.equals(secondStmtDef) && Objects.equals(firstStmtArg, secondStmtArg);
+    }
+
+    private static void validateDeviationTarget(final StmtContext<?, ?, ?> deviateSubStmtCtx,
+            final StmtContext<?, ?, ?> targetCtx) {
+        InferenceException.throwIf(!isSupportedDeviationTarget(deviateSubStmtCtx, targetCtx,
+                targetCtx.getRootVersion()), deviateSubStmtCtx.getStatementSourceReference(),
+                "%s is not a valid deviation target for substatement %s.",
+                targetCtx.getStatementArgument(), deviateSubStmtCtx.getPublicDefinition().getStatementName());
+    }
+
+    private static boolean isSupportedDeviationTarget(final StmtContext<?, ?, ?> deviateSubstatementCtx,
+            final StmtContext<?, ?, ?> deviateTargetCtx, final YangVersion yangVersion) {
+        Set<StatementDefinition> supportedDeviationTargets =
+                YangValidationBundles.SUPPORTED_DEVIATION_TARGETS.get(deviateTargetCtx.getRootVersion(),
+                        deviateSubstatementCtx.getPublicDefinition());
+
+        if (supportedDeviationTargets == null) {
+            supportedDeviationTargets = YangValidationBundles.SUPPORTED_DEVIATION_TARGETS.get(YangVersion.VERSION_1,
+                    deviateSubstatementCtx.getPublicDefinition());
+        }
+
+        // if supportedDeviationTargets is null, it means that the deviate substatement is an unknown statement
+        return supportedDeviationTargets == null || supportedDeviationTargets.contains(
+                deviateTargetCtx.getPublicDefinition());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/DeviateEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/DeviateEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..3c60245
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.deviate;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+import org.opendaylight.yangtools.util.OptionalBoolean;
+import org.opendaylight.yangtools.yang.model.api.DeviateDefinition;
+import org.opendaylight.yangtools.yang.model.api.DeviateKind;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UniqueConstraint;
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DefaultEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviateEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviateStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MaxElementsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnitsEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class DeviateEffectiveStatementImpl extends DeclaredEffectiveStatementBase<DeviateKind, DeviateStatement>
+        implements DeviateDefinition, DeviateEffectiveStatement {
+
+    private final DeviateKind deviateType;
+    private final String deviatedDefault;
+    private final Integer deviatedMaxElements;
+    private final Integer deviatedMinElements;
+    private final ImmutableSet<MustDefinition> deviatedMustDefinitions;
+    private final TypeDefinition<?> deviatedType;
+    private final ImmutableList<UniqueConstraint> deviatedUniqueConstraints;
+    private final String deviatedUnits;
+
+    private final byte deviatedConfig;
+    private final byte deviatedMandatory;
+
+    DeviateEffectiveStatementImpl(final StmtContext<DeviateKind, DeviateStatement, ?> ctx) {
+        super(ctx);
+
+        deviateType = argument();
+        deviatedConfig = OptionalBoolean.ofNullable(findFirstEffectiveSubstatementArgument(
+            ConfigEffectiveStatement.class).orElse(null));
+        deviatedMandatory = OptionalBoolean.ofNullable(findFirstEffectiveSubstatementArgument(
+            MandatoryEffectiveStatement.class).orElse(null));
+        deviatedDefault = findFirstEffectiveSubstatementArgument(DefaultEffectiveStatement.class).orElse(null);
+        deviatedMaxElements = findFirstEffectiveSubstatementArgument(MaxElementsEffectiveStatement.class)
+                // FIXME: this does not handle 'unbounded'
+                .map(Integer::valueOf).orElse(null);
+        deviatedMinElements = findFirstEffectiveSubstatementArgument(MinElementsEffectiveStatement.class).orElse(null);
+        deviatedType = findFirstEffectiveSubstatement(TypeEffectiveStatement.class)
+                .map(TypeEffectiveStatement::getTypeDefinition).orElse(null);
+        deviatedUnits = findFirstEffectiveSubstatementArgument(UnitsEffectiveStatement.class).orElse(null);
+
+        deviatedMustDefinitions = ImmutableSet.copyOf(allSubstatementsOfType(MustDefinition.class));
+        deviatedUniqueConstraints = ImmutableList.copyOf(allSubstatementsOfType(UniqueConstraint.class));
+    }
+
+    @Override
+    public DeviateKind getDeviateType() {
+        return deviateType;
+    }
+
+    @Override
+    public Boolean getDeviatedConfig() {
+        return OptionalBoolean.toNullable(deviatedConfig);
+    }
+
+    @Override
+    public String getDeviatedDefault() {
+        return deviatedDefault;
+    }
+
+    @Override
+    public Boolean getDeviatedMandatory() {
+        return OptionalBoolean.toNullable(deviatedMandatory);
+    }
+
+    @Override
+    public Integer getDeviatedMaxElements() {
+        return deviatedMaxElements;
+    }
+
+    @Override
+    public Integer getDeviatedMinElements() {
+        return deviatedMinElements;
+    }
+
+    @Override
+    public Set<MustDefinition> getDeviatedMusts() {
+        return deviatedMustDefinitions;
+    }
+
+    @Override
+    public TypeDefinition<?> getDeviatedType() {
+        return deviatedType;
+    }
+
+    @Override
+    public Collection<UniqueConstraint> getDeviatedUniques() {
+        return deviatedUniqueConstraints;
+    }
+
+    @Override
+    public String getDeviatedUnits() {
+        return deviatedUnits;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        DeviateEffectiveStatementImpl other = (DeviateEffectiveStatementImpl) obj;
+        return Objects.equals(deviateType, other.deviateType)
+                && deviatedConfig == other.deviatedConfig
+                && Objects.equals(deviatedDefault, other.deviatedDefault)
+                && deviatedMandatory == other.deviatedMandatory
+                && Objects.equals(deviatedMaxElements, other.deviatedMaxElements)
+                && Objects.equals(deviatedMinElements, other.deviatedMinElements)
+                && Objects.equals(deviatedMustDefinitions, other.deviatedMustDefinitions)
+                && Objects.equals(deviatedType, other.deviatedType)
+                && Objects.equals(deviatedUniqueConstraints, other.deviatedUniqueConstraints)
+                && Objects.equals(deviatedUnits, other.deviatedUnits);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(deviateType, deviatedConfig, deviatedDefault, deviatedMandatory, deviatedMaxElements,
+                deviatedMinElements, deviatedMustDefinitions, deviatedType, deviatedUniqueConstraints, deviatedUnits);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/DeviateStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/DeviateStatementImpl.java
new file mode 100644 (file)
index 0000000..d8d57cc
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.deviate;
+
+import org.opendaylight.yangtools.yang.model.api.DeviateKind;
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviateStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class DeviateStatementImpl extends AbstractDeclaredStatement<DeviateKind> implements DeviateStatement {
+    DeviateStatementImpl(final StmtContext<DeviateKind, DeviateStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/DeviateStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/DeviateStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..c025ee3
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.deviate;
+
+public final class DeviateStatementRFC6020Support extends AbstractDeviateStatementSupport {
+    private static final DeviateStatementRFC6020Support INSTANCE = new DeviateStatementRFC6020Support();
+
+    private DeviateStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static DeviateStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/DeviateStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviate/DeviateStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..91f730f
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2017 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.rfc7950.stmt.deviate;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.DeviateKind;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 deviate statement.
+ */
+@Beta
+public final class DeviateStatementRFC7950Support extends AbstractDeviateStatementSupport {
+
+    private static final SubstatementValidator DEVIATE_ADD_SUBSTATEMENT_VALIDATOR =
+            SubstatementValidator.builder(YangStmtMapping.DEVIATE)
+                .addOptional(YangStmtMapping.CONFIG)
+                .addAny(YangStmtMapping.DEFAULT)
+                .addOptional(YangStmtMapping.MANDATORY)
+                .addOptional(YangStmtMapping.MAX_ELEMENTS)
+                .addOptional(YangStmtMapping.MIN_ELEMENTS)
+                .addAny(YangStmtMapping.MUST)
+                .addAny(YangStmtMapping.UNIQUE)
+                .addOptional(YangStmtMapping.UNITS)
+                .build();
+
+    private static final SubstatementValidator DEVIATE_DELETE_SUBSTATEMENT_VALIDATOR =
+            SubstatementValidator.builder(YangStmtMapping.DEVIATE)
+                .addAny(YangStmtMapping.DEFAULT)
+                .addAny(YangStmtMapping.MUST)
+                .addAny(YangStmtMapping.UNIQUE)
+                .addOptional(YangStmtMapping.UNITS)
+                .build();
+    private static final DeviateStatementRFC7950Support INSTANCE = new DeviateStatementRFC7950Support();
+
+    private DeviateStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static DeviateStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidatorForDeviate(final DeviateKind deviateKind) {
+        switch (deviateKind) {
+            case NOT_SUPPORTED:
+                return super.getSubstatementValidatorForDeviate(deviateKind);
+            case ADD:
+                return DEVIATE_ADD_SUBSTATEMENT_VALIDATOR;
+            case REPLACE:
+                return super.getSubstatementValidatorForDeviate(deviateKind);
+            case DELETE:
+                return DEVIATE_DELETE_SUBSTATEMENT_VALIDATOR;
+            default:
+                throw new IllegalStateException(String.format(
+                        "Substatement validator for deviate %s has not been defined.", deviateKind));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviation/DeviationEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviation/DeviationEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..55fd25d
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.deviation;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.DeviateDefinition;
+import org.opendaylight.yangtools.yang.model.api.Deviation;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviationEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviationStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class DeviationEffectiveStatementImpl
+        extends DeclaredEffectiveStatementBase<SchemaNodeIdentifier, DeviationStatement>
+        implements Deviation, DeviationEffectiveStatement, Immutable {
+    private final SchemaPath targetPath;
+    private final String description;
+    private final String reference;
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownSchemaNodes;
+    private final ImmutableList<DeviateDefinition> deviateDefinitions;
+
+    DeviationEffectiveStatementImpl(final StmtContext<SchemaNodeIdentifier, DeviationStatement, ?> ctx) {
+        super(ctx);
+        this.targetPath = ctx.getStatementArgument().asSchemaPath();
+
+        this.deviateDefinitions = ImmutableList.copyOf(allSubstatementsOfType(DeviateDefinition.class));
+
+        description = findFirstEffectiveSubstatementArgument(DescriptionEffectiveStatement.class).orElse(null);
+        reference = findFirstEffectiveSubstatementArgument(ReferenceEffectiveStatement.class).orElse(null);
+
+        List<UnknownSchemaNode> unknownSchemaNodesInit = new ArrayList<>();
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof UnknownSchemaNode) {
+                unknownSchemaNodesInit.add((UnknownSchemaNode) effectiveStatement);
+            }
+        }
+        unknownSchemaNodes = ImmutableList.copyOf(unknownSchemaNodesInit);
+    }
+
+    @Override
+    public SchemaPath getTargetPath() {
+        return targetPath;
+    }
+
+    @Override
+    public List<DeviateDefinition> getDeviates() {
+        return deviateDefinitions;
+    }
+
+    @Override
+    public Optional<String> getDescription() {
+        return Optional.ofNullable(description);
+    }
+
+    @Override
+    public Optional<String> getReference() {
+        return Optional.ofNullable(reference);
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownSchemaNodes;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(targetPath, deviateDefinitions, description, reference);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof DeviationEffectiveStatementImpl)) {
+            return false;
+        }
+        final DeviationEffectiveStatementImpl other = (DeviationEffectiveStatementImpl) obj;
+        return Objects.equals(targetPath, other.targetPath)
+                && Objects.equals(deviateDefinitions, other.deviateDefinitions)
+                && Objects.equals(description, other.description) && Objects.equals(reference, other.reference);
+    }
+
+    @Override
+    public String toString() {
+        return DeviationEffectiveStatementImpl.class.getSimpleName() + "["
+                + "targetPath=" + targetPath
+                + ", deviates=" + deviateDefinitions
+                + ", description=" + description
+                + ", reference=" + reference
+                + "]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviation/DeviationStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviation/DeviationStatementImpl.java
new file mode 100644 (file)
index 0000000..7e7f73d
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.deviation;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviationStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class DeviationStatementImpl extends AbstractDeclaredStatement<SchemaNodeIdentifier>
+        implements DeviationStatement {
+    DeviationStatementImpl(final StmtContext<SchemaNodeIdentifier, DeviationStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviation/DeviationStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/deviation/DeviationStatementSupport.java
new file mode 100644 (file)
index 0000000..d38acdf
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.deviation;
+
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DeviationStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
+
+public final class DeviationStatementSupport extends AbstractStatementSupport<SchemaNodeIdentifier, DeviationStatement,
+        EffectiveStatement<SchemaNodeIdentifier, DeviationStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .DEVIATION)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.DEVIATE)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .build();
+    private static final DeviationStatementSupport INSTANCE = new DeviationStatementSupport();
+
+    private DeviationStatementSupport() {
+        super(YangStmtMapping.DEVIATION);
+    }
+
+    public static DeviationStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public SchemaNodeIdentifier parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return ArgumentUtils.nodeIdentifierFromPath(ctx, value);
+    }
+
+    @Override
+    public DeviationStatement createDeclared(final StmtContext<SchemaNodeIdentifier, DeviationStatement, ?> ctx) {
+        return new DeviationStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<SchemaNodeIdentifier, DeviationStatement> createEffective(
+            final StmtContext<SchemaNodeIdentifier, DeviationStatement,
+            EffectiveStatement<SchemaNodeIdentifier, DeviationStatement>> ctx) {
+        return new DeviationEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public void onFullDefinitionDeclared(final StmtContext.Mutable<SchemaNodeIdentifier, DeviationStatement,
+            EffectiveStatement<SchemaNodeIdentifier, DeviationStatement>> ctx) {
+        final QNameModule currentModule = ctx.getFromNamespace(ModuleCtxToModuleQName.class,
+                ctx.getRoot());
+        final QNameModule targetModule = ctx.coerceStatementArgument().getLastComponent().getModule();
+
+        if (currentModule.equals(targetModule)) {
+            throw new InferenceException(ctx.getStatementSourceReference(),
+                    "Deviation must not target the same module as the one it is defined in: %s", currentModule);
+        }
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/AbstractEnumStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/AbstractEnumStatementSupport.java
new file mode 100644 (file)
index 0000000..227fb8c
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.enum_;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.EnumStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+abstract class AbstractEnumStatementSupport extends
+        AbstractStatementSupport<String, EnumStatement, EffectiveStatement<String, EnumStatement>> {
+
+    AbstractEnumStatementSupport() {
+        super(YangStmtMapping.ENUM);
+    }
+
+    @Override
+    public final String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        // FIXME: Checks for real value
+        return value;
+    }
+
+    @Override
+    public final EnumStatement createDeclared(final StmtContext<String, EnumStatement, ?> ctx) {
+        return new EnumStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<String, EnumStatement> createEffective(
+            final StmtContext<String, EnumStatement, EffectiveStatement<String, EnumStatement>> ctx) {
+        return new EnumEffectiveStatementImpl(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/EnumEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/EnumEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..7b150d6
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.enum_;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.EnumEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.EnumStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDocumentedNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+// FIXME: hide this class
+public final class EnumEffectiveStatementImpl extends AbstractEffectiveDocumentedNode<String, EnumStatement>
+        implements EnumEffectiveStatement {
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownSchemaNodes;
+    private final String name;
+    private final Integer declaredValue;
+
+    EnumEffectiveStatementImpl(final StmtContext<String, EnumStatement, ?> ctx) {
+        super(ctx);
+
+        name = ctx.rawStatementArgument();
+
+        final List<UnknownSchemaNode> unknownSchemaNodesInit = new ArrayList<>();
+        Integer declaredValueInit = null;
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof ValueEffectiveStatement) {
+                declaredValueInit = ((ValueEffectiveStatement) effectiveStatement).argument();
+            }
+            if (effectiveStatement instanceof UnknownSchemaNode) {
+                unknownSchemaNodesInit.add((UnknownSchemaNode) effectiveStatement);
+            }
+        }
+
+        declaredValue = declaredValueInit;
+        unknownSchemaNodes = ImmutableList.copyOf(unknownSchemaNodesInit);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Integer getDeclaredValue() {
+        return declaredValue;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownSchemaNodes;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/EnumStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/EnumStatementImpl.java
new file mode 100644 (file)
index 0000000..b2867ad
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.enum_;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.EnumStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class EnumStatementImpl extends AbstractDeclaredStatement<String> implements EnumStatement {
+    EnumStatementImpl(final StmtContext<String, EnumStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/EnumStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/EnumStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..705a54a
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.enum_;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class EnumStatementRFC6020Support extends AbstractEnumStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.ENUM)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addOptional(YangStmtMapping.VALUE)
+        .build();
+    private static final EnumStatementRFC6020Support INSTANCE = new EnumStatementRFC6020Support();
+
+    private EnumStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static EnumStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/EnumStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/enum_/EnumStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..1008c34
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.enum_;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Enum statement.
+ */
+@Beta
+public final class EnumStatementRFC7950Support extends AbstractEnumStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+            YangStmtMapping.ENUM)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.IF_FEATURE)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.STATUS)
+            .addOptional(YangStmtMapping.VALUE)
+            .build();
+    private static final EnumStatementRFC7950Support INSTANCE = new EnumStatementRFC7950Support();
+
+    private EnumStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static EnumStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_app_tag/ErrorAppTagEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_app_tag/ErrorAppTagEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..208eb32
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.error_app_tag;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorAppTagEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorAppTagStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ErrorAppTagEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, ErrorAppTagStatement>
+        implements ErrorAppTagEffectiveStatement {
+    ErrorAppTagEffectiveStatementImpl(final StmtContext<String, ErrorAppTagStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_app_tag/ErrorAppTagStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_app_tag/ErrorAppTagStatementImpl.java
new file mode 100644 (file)
index 0000000..c60f1de
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.error_app_tag;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorAppTagStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ErrorAppTagStatementImpl extends AbstractDeclaredStatement<String> implements ErrorAppTagStatement {
+    ErrorAppTagStatementImpl(final StmtContext<String, ErrorAppTagStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_app_tag/ErrorAppTagStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_app_tag/ErrorAppTagStatementSupport.java
new file mode 100644 (file)
index 0000000..db48d9f
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.error_app_tag;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorAppTagStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ErrorAppTagStatementSupport extends
+        AbstractStatementSupport<String, ErrorAppTagStatement, EffectiveStatement<String, ErrorAppTagStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.ERROR_APP_TAG).build();
+    private static final ErrorAppTagStatementSupport INSTANCE = new ErrorAppTagStatementSupport();
+
+    private ErrorAppTagStatementSupport() {
+        super(YangStmtMapping.ERROR_APP_TAG);
+    }
+
+    public static ErrorAppTagStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public ErrorAppTagStatement createDeclared(
+            final StmtContext<String, ErrorAppTagStatement, ?> ctx) {
+        return new ErrorAppTagStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, ErrorAppTagStatement> createEffective(
+            final StmtContext<String, ErrorAppTagStatement, EffectiveStatement<String, ErrorAppTagStatement>> ctx) {
+        return new ErrorAppTagEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_message/ErrorMessageEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_message/ErrorMessageEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..f68a615
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.error_message;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ErrorMessageEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, ErrorMessageStatement>
+        implements ErrorMessageEffectiveStatement {
+    ErrorMessageEffectiveStatementImpl(final StmtContext<String, ErrorMessageStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_message/ErrorMessageStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_message/ErrorMessageStatementImpl.java
new file mode 100644 (file)
index 0000000..b791646
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.error_message;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ErrorMessageStatementImpl extends AbstractDeclaredStatement<String> implements ErrorMessageStatement {
+    ErrorMessageStatementImpl(final StmtContext<String, ErrorMessageStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_message/ErrorMessageStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/error_message/ErrorMessageStatementSupport.java
new file mode 100644 (file)
index 0000000..fa74543
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.error_message;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ErrorMessageStatementSupport extends
+        AbstractStatementSupport<String, ErrorMessageStatement, EffectiveStatement<String, ErrorMessageStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .ERROR_MESSAGE)
+        .build();
+    private static final ErrorMessageStatementSupport INSTANCE = new ErrorMessageStatementSupport();
+
+    private ErrorMessageStatementSupport() {
+        super(YangStmtMapping.ERROR_MESSAGE);
+    }
+
+    public static ErrorMessageStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public ErrorMessageStatement createDeclared(final StmtContext<String, ErrorMessageStatement, ?> ctx) {
+        return new ErrorMessageStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, ErrorMessageStatement> createEffective(
+            final StmtContext<String, ErrorMessageStatement,
+            EffectiveStatement<String, ErrorMessageStatement>> ctx) {
+        return new ErrorMessageEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ExtensionEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ExtensionEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..13e0824
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.extension;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.util.RecursiveObjectLeaker;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ArgumentEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YinElementEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDocumentedNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ExtensionEffectiveStatementImpl extends AbstractEffectiveDocumentedNode<QName, ExtensionStatement>
+        implements ExtensionDefinition, ExtensionEffectiveStatement {
+    private static final class RecursionDetector extends ThreadLocal<Deque<ExtensionEffectiveStatementImpl>> {
+        boolean check(final ExtensionEffectiveStatementImpl current) {
+            final Deque<ExtensionEffectiveStatementImpl> stack = get();
+            if (stack != null) {
+                for (ExtensionEffectiveStatementImpl s : stack) {
+                    if (s == current) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        void push(final ExtensionEffectiveStatementImpl current) {
+            Deque<ExtensionEffectiveStatementImpl> stack = get();
+            if (stack == null) {
+                stack = new ArrayDeque<>(1);
+                set(stack);
+            }
+
+            stack.push(current);
+        }
+
+        void pop() {
+            Deque<ExtensionEffectiveStatementImpl> stack = get();
+            stack.pop();
+            if (stack.isEmpty()) {
+                remove();
+            }
+        }
+    }
+
+    private static final RecursionDetector TOSTRING_DETECTOR = new RecursionDetector();
+
+    private final @NonNull QName qname;
+    private final @Nullable String argument;
+    private final @NonNull SchemaPath schemaPath;
+
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
+    private final boolean yin;
+
+    private ExtensionEffectiveStatementImpl(
+            final StmtContext<QName, ExtensionStatement, EffectiveStatement<QName, ExtensionStatement>> ctx) {
+        super(ctx);
+        this.qname = ctx.coerceStatementArgument();
+        this.schemaPath = ctx.getSchemaPath().get();
+
+        final List<UnknownSchemaNode> unknownNodesInit = new ArrayList<>();
+        for (EffectiveStatement<?, ?> unknownNode : effectiveSubstatements()) {
+            if (unknownNode instanceof UnknownSchemaNode) {
+                unknownNodesInit.add((UnknownSchemaNode) unknownNode);
+            }
+        }
+        this.unknownNodes = ImmutableList.copyOf(unknownNodesInit);
+
+        // initFields
+        final Optional<ArgumentEffectiveStatement> optArgumentSubstatement = findFirstEffectiveSubstatement(
+            ArgumentEffectiveStatement.class);
+        if (optArgumentSubstatement.isPresent()) {
+            final ArgumentEffectiveStatement argumentStatement = optArgumentSubstatement.get();
+            this.argument = argumentStatement.argument().getLocalName();
+            this.yin = argumentStatement.findFirstEffectiveSubstatement(YinElementEffectiveStatement.class)
+                    .map(YinElementEffectiveStatement::argument).orElse(Boolean.FALSE).booleanValue();
+        } else {
+            this.argument = null;
+            this.yin = false;
+        }
+    }
+
+    /**
+     * Create a new ExtensionEffectiveStatement, dealing with potential recursion.
+     *
+     * @param ctx Statement context
+     * @return A potentially under-initialized instance
+     */
+    static EffectiveStatement<QName, ExtensionStatement> create(
+            final StmtContext<QName, ExtensionStatement, EffectiveStatement<QName, ExtensionStatement>> ctx) {
+        // Look at the thread-local leak in case we are invoked recursively
+        final ExtensionEffectiveStatementImpl existing = RecursiveObjectLeaker.lookup(ctx,
+            ExtensionEffectiveStatementImpl.class);
+        if (existing != null) {
+            // Careful! this object is not fully initialized!
+            return existing;
+        }
+
+        RecursiveObjectLeaker.beforeConstructor(ctx);
+        try {
+            // This result is fine, we know it has been completely initialized
+            return new ExtensionEffectiveStatementImpl(ctx);
+        } finally {
+            RecursiveObjectLeaker.afterConstructor(ctx);
+        }
+    }
+
+    @Override
+    protected Collection<? extends EffectiveStatement<?, ?>> initSubstatements(
+            final Collection<? extends StmtContext<?, ?, ?>> substatementsInit) {
+        // WARNING: this leaks an incompletely-initialized object
+        RecursiveObjectLeaker.inConstructor(this);
+
+        return super.initSubstatements(substatementsInit);
+    }
+
+    @Override
+    public QName getQName() {
+        return qname;
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return schemaPath;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownNodes;
+    }
+
+    @Override
+    public String getArgument() {
+        return argument;
+    }
+
+    @Override
+    public boolean isYinElement() {
+        return yin;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(qname);
+        result = prime * result + Objects.hashCode(schemaPath);
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        ExtensionEffectiveStatementImpl other = (ExtensionEffectiveStatementImpl) obj;
+        return Objects.equals(qname, other.qname) && Objects.equals(schemaPath, other.schemaPath);
+    }
+
+    @Override
+    public String toString() {
+        if (TOSTRING_DETECTOR.check(this)) {
+            return recursedToString();
+        }
+
+        TOSTRING_DETECTOR.push(this);
+        try {
+            return ExtensionEffectiveStatementImpl.class.getSimpleName() + "["
+                    + "argument=" + argument
+                    + ", qname=" + qname
+                    + ", schemaPath=" + schemaPath
+                    + ", extensionSchemaNodes=" + unknownNodes
+                    + ", yin=" + yin
+                    + "]";
+        } finally {
+            TOSTRING_DETECTOR.pop();
+        }
+    }
+
+    private String recursedToString() {
+        return ExtensionEffectiveStatementImpl.class.getSimpleName() + "["
+                + "argument=" + argument
+                + ", qname=" + qname
+                + ", schemaPath=" + schemaPath
+                + ", yin=" + yin
+                + " <RECURSIVE> ]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ExtensionStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ExtensionStatementImpl.java
new file mode 100644 (file)
index 0000000..97e663d
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.extension;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ExtensionStatementImpl extends AbstractDeclaredStatement<QName> implements ExtensionStatement {
+    ExtensionStatementImpl(final StmtContext<QName, ExtensionStatement,?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ExtensionStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ExtensionStatementSupport.java
new file mode 100644 (file)
index 0000000..8f154ac
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.extension;
+
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ArgumentStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YinElementStatement;
+import org.opendaylight.yangtools.yang.parser.spi.ExtensionNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementDefinitionNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ExtensionStatementSupport
+        extends AbstractQNameStatementSupport<ExtensionStatement, EffectiveStatement<QName, ExtensionStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .EXTENSION)
+        .addOptional(YangStmtMapping.ARGUMENT)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .build();
+    private static final ExtensionStatementSupport INSTANCE = new ExtensionStatementSupport();
+
+    private ExtensionStatementSupport() {
+        super(YangStmtMapping.EXTENSION);
+    }
+
+    public static ExtensionStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public QName parseArgumentValue(final StmtContext<?,?,?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public ExtensionStatement createDeclared(final StmtContext<QName, ExtensionStatement,?> ctx) {
+        return new ExtensionStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<QName, ExtensionStatement> createEffective(
+            final StmtContext<QName, ExtensionStatement, EffectiveStatement<QName,ExtensionStatement>> ctx) {
+        return ExtensionEffectiveStatementImpl.create(ctx);
+    }
+
+    @Override
+    public void onStatementDefinitionDeclared(
+            final Mutable<QName, ExtensionStatement, EffectiveStatement<QName, ExtensionStatement>> stmt) {
+        super.onStatementDefinitionDeclared(stmt);
+
+        QName stmtName = stmt.coerceStatementArgument();
+        if (OpenConfigStatements.OPENCONFIG_VERSION.getStatementName().isEqualWithoutRevision(stmtName)) {
+            stmtName = stmtName.withoutRevision();
+        }
+
+        stmt.addContext(ExtensionNamespace.class, stmtName, stmt);
+
+        final StmtContext<QName, ?, ?> argument = StmtContextUtils.findFirstDeclaredSubstatement(stmt,
+            ArgumentStatement.class);
+        final StmtContext<Boolean, ?, ?> yinElement = StmtContextUtils.findFirstDeclaredSubstatement(stmt,
+            YinElementStatement.class);
+
+        stmt.addToNs(StatementDefinitionNamespace.class, stmt.getStatementArgument(),
+            new ModelDefinedStatementSupport(new ModelDefinedStatementDefinition(stmt.getStatementArgument(),
+                argument != null ? argument.getStatementArgument() : null,
+                        yinElement != null && yinElement.getStatementArgument())));
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ModelDefinedStatementDefinition.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ModelDefinedStatementDefinition.java
new file mode 100644 (file)
index 0000000..3e90f74
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.extension;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.AbstractStatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnrecognizedEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnrecognizedStatement;
+
+/**
+ * Public definition for statements declared by extensions. This class is instantiated for every extension that is seen
+ * to be declared in a model.
+ */
+@NonNullByDefault
+final class ModelDefinedStatementDefinition extends AbstractStatementDefinition {
+    ModelDefinedStatementDefinition(final QName statementName) {
+        super(statementName);
+    }
+
+    ModelDefinedStatementDefinition(final QName statementName, final @Nullable QName argumentName,
+            final boolean yinElement) {
+        super(statementName, yinElement, argumentName);
+    }
+
+    @Override
+    public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+        return UnrecognizedStatement.class;
+    }
+
+    @Override
+    public Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass() {
+        return UnrecognizedEffectiveStatement.class;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ModelDefinedStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/ModelDefinedStatementSupport.java
new file mode 100644 (file)
index 0000000..3c8a75a
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.extension;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnrecognizedStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * StatementSupport for statements defined via YANG extensions. This is implemented by piggy-backing
+ * to a {@link UnrecognizedStatementSupport}.
+ *
+ * @author Robert Varga
+ */
+final class ModelDefinedStatementSupport extends AbstractStatementSupport<String,
+        UnrecognizedStatement, EffectiveStatement<String, UnrecognizedStatement>> {
+    private final UnrecognizedStatementSupport definition;
+
+    ModelDefinedStatementSupport(final ModelDefinedStatementDefinition publicDefinition) {
+        super(publicDefinition);
+        this.definition = new UnrecognizedStatementSupport(publicDefinition);
+    }
+
+    @Override
+    public UnrecognizedStatement createDeclared(final StmtContext<String, UnrecognizedStatement, ?> ctx) {
+        return definition.createDeclared(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, UnrecognizedStatement> createEffective(
+            final StmtContext<String, UnrecognizedStatement, EffectiveStatement<String, UnrecognizedStatement>> ctx) {
+        return definition.createEffective(ctx);
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return definition.parseArgumentValue(ctx, value);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return null;
+    }
+
+    @Override
+    public Optional<StatementSupport<?, ?, ?>> getUnknownStatementDefinitionOf(final StatementDefinition yangStmtDef) {
+        return definition.getUnknownStatementDefinitionOf(yangStmtDef);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/UnrecognizedEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/UnrecognizedEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..4d3f62d
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.extension;
+
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnrecognizedEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnrecognizedStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.UnknownEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class UnrecognizedEffectiveStatementImpl extends UnknownEffectiveStatementBase<String, UnrecognizedStatement>
+        implements UnrecognizedEffectiveStatement {
+    private static final Logger LOG = LoggerFactory.getLogger(UnrecognizedEffectiveStatementImpl.class);
+
+    private final QName maybeQNameArgument;
+    private final @NonNull SchemaPath path;
+
+    UnrecognizedEffectiveStatementImpl(final StmtContext<String, UnrecognizedStatement, ?> ctx) {
+        super(ctx);
+
+        // FIXME: Remove following section after fixing 4380
+        final UnknownSchemaNode original = (UnknownSchemaNode) ctx.getOriginalCtx().map(StmtContext::buildEffective)
+                .orElse(null);
+        if (original != null) {
+            this.maybeQNameArgument = original.getQName();
+        } else {
+            QName maybeQNameArgumentInit = null;
+            try {
+                maybeQNameArgumentInit = StmtContextUtils.qnameFromArgument(ctx, argument());
+            } catch (SourceException e) {
+                LOG.debug("Not constructing QName from {}", argument(), e);
+                maybeQNameArgumentInit = getNodeType();
+            }
+            this.maybeQNameArgument = maybeQNameArgumentInit;
+        }
+
+        SchemaPath maybePath;
+        try {
+            maybePath = ctx.coerceParentContext().getSchemaPath()
+                    .map(parentPath -> parentPath.createChild(maybeQNameArgument)).orElse(null);
+        } catch (IllegalArgumentException | SourceException e) {
+            LOG.debug("Cannot construct path for {}, attempting to recover", ctx, e);
+            maybePath = null;
+        }
+        path = maybePath;
+    }
+
+    @Override
+    public QName getQName() {
+        return maybeQNameArgument;
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return path;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(maybeQNameArgument);
+        result = prime * result + Objects.hashCode(path);
+        result = prime * result + Objects.hashCode(getNodeType());
+        result = prime * result + Objects.hashCode(getNodeParameter());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof UnrecognizedEffectiveStatementImpl)) {
+            return false;
+        }
+        UnrecognizedEffectiveStatementImpl other = (UnrecognizedEffectiveStatementImpl) obj;
+        return Objects.equals(maybeQNameArgument, other.maybeQNameArgument) && Objects.equals(path, other.path)
+                && Objects.equals(getNodeType(), other.getNodeType())
+                && Objects.equals(getNodeParameter(), other.getNodeParameter());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/UnrecognizedStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/UnrecognizedStatementImpl.java
new file mode 100644 (file)
index 0000000..e60ccdf
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.extension;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.UnrecognizedStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class UnrecognizedStatementImpl extends AbstractDeclaredStatement<String> implements UnrecognizedStatement {
+    UnrecognizedStatementImpl(final StmtContext<String, ?, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/UnrecognizedStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/extension/UnrecognizedStatementSupport.java
new file mode 100644 (file)
index 0000000..ad17d61
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.extension;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnrecognizedStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+final class UnrecognizedStatementSupport extends AbstractStatementSupport<String, UnrecognizedStatement,
+        EffectiveStatement<String, UnrecognizedStatement>> {
+    UnrecognizedStatementSupport(final StatementDefinition publicDefinition) {
+        super(publicDefinition);
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public UnrecognizedStatement createDeclared(final StmtContext<String, UnrecognizedStatement, ?> ctx) {
+        return new UnrecognizedStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, UnrecognizedStatement> createEffective(
+            final StmtContext<String, UnrecognizedStatement,
+            EffectiveStatement<String, UnrecognizedStatement>> ctx) {
+        return new UnrecognizedEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return null;
+    }
+
+    @Override
+    public Optional<StatementSupport<?, ?, ?>> getUnknownStatementDefinitionOf(
+            final StatementDefinition yangStmtDef) {
+        final QName baseQName = getStatementName();
+        final QName statementName = QName.create(baseQName, yangStmtDef.getStatementName().getLocalName());
+
+        final ModelDefinedStatementDefinition def;
+        final Optional<ArgumentDefinition> optArgDef = yangStmtDef.getArgumentDefinition();
+        if (optArgDef.isPresent()) {
+            final ArgumentDefinition argDef = optArgDef.get();
+            def = new ModelDefinedStatementDefinition(statementName, argDef.getArgumentName(), argDef.isYinElement());
+        } else {
+            def = new ModelDefinedStatementDefinition(statementName);
+        }
+        return Optional.of(new ModelDefinedStatementSupport(def));
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/feature/FeatureEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/feature/FeatureEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..2d39b45
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.feature;
+
+import java.util.Objects;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveSchemaNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class FeatureEffectiveStatementImpl extends AbstractEffectiveSchemaNode<FeatureStatement>
+        implements FeatureDefinition, FeatureEffectiveStatement {
+
+    FeatureEffectiveStatementImpl(final StmtContext<QName, FeatureStatement, ?> ctx) {
+        super(ctx);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getQName());
+        result = prime * result + Objects.hashCode(getPath());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        FeatureEffectiveStatementImpl other = (FeatureEffectiveStatementImpl) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return FeatureEffectiveStatementImpl.class.getSimpleName() + "[name=" + getQName() + "]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/feature/FeatureStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/feature/FeatureStatementImpl.java
new file mode 100644 (file)
index 0000000..fe43dfe
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.feature;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class FeatureStatementImpl extends AbstractDeclaredStatement<QName> implements FeatureStatement {
+    FeatureStatementImpl(final StmtContext<QName, FeatureStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/feature/FeatureStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/feature/FeatureStatementSupport.java
new file mode 100644 (file)
index 0000000..207805f
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.feature;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureStatement;
+import org.opendaylight.yangtools.yang.parser.spi.FeatureNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class FeatureStatementSupport
+        extends AbstractQNameStatementSupport<FeatureStatement, EffectiveStatement<QName, FeatureStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.FEATURE)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .build();
+    private static final FeatureStatementSupport INSTANCE = new FeatureStatementSupport();
+
+    private FeatureStatementSupport() {
+        super(YangStmtMapping.FEATURE);
+    }
+
+    public static FeatureStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public void onFullDefinitionDeclared(final Mutable<QName, FeatureStatement,
+            EffectiveStatement<QName, FeatureStatement>> stmt) {
+        super.onFullDefinitionDeclared(stmt);
+        stmt.addContext(FeatureNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public FeatureStatement createDeclared(final StmtContext<QName, FeatureStatement, ?> ctx) {
+        return new FeatureStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<QName, FeatureStatement> createEffective(
+            final StmtContext<QName, FeatureStatement, EffectiveStatement<QName, FeatureStatement>> ctx) {
+        return new FeatureEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/fraction_digits/FractionDigitsEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/fraction_digits/FractionDigitsEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..4dc9a83
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.fraction_digits;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.FractionDigitsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FractionDigitsStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class FractionDigitsEffectiveStatementImpl
+        extends DeclaredEffectiveStatementBase<Integer, FractionDigitsStatement>
+        implements FractionDigitsEffectiveStatement {
+    FractionDigitsEffectiveStatementImpl(final StmtContext<Integer, FractionDigitsStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/fraction_digits/FractionDigitsStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/fraction_digits/FractionDigitsStatementImpl.java
new file mode 100644 (file)
index 0000000..e012f3b
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.fraction_digits;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.FractionDigitsStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class FractionDigitsStatementImpl extends AbstractDeclaredStatement<Integer> implements FractionDigitsStatement {
+    FractionDigitsStatementImpl(final StmtContext<Integer, FractionDigitsStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/fraction_digits/FractionDigitsStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/fraction_digits/FractionDigitsStatementSupport.java
new file mode 100644 (file)
index 0000000..d306c5c
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.fraction_digits;
+
+import com.google.common.collect.Range;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FractionDigitsStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class FractionDigitsStatementSupport extends AbstractStatementSupport<Integer, FractionDigitsStatement,
+        EffectiveStatement<Integer, FractionDigitsStatement>> {
+    private static final Range<Integer> FRAC_DIGITS_ALLOWED = Range.closed(1, 18);
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.FRACTION_DIGITS)
+        .build();
+    private static final FractionDigitsStatementSupport INSTANCE = new FractionDigitsStatementSupport();
+
+    private FractionDigitsStatementSupport() {
+        super(YangStmtMapping.FRACTION_DIGITS);
+    }
+
+    public static FractionDigitsStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Integer parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        final Integer fractionDigits;
+        try {
+            fractionDigits = Integer.valueOf(value);
+        } catch (NumberFormatException e) {
+            throw new SourceException(ctx.getStatementSourceReference(), e,
+                "%s is not valid fraction-digits integer argument", value);
+        }
+
+        SourceException.throwIf(!FRAC_DIGITS_ALLOWED.contains(fractionDigits), ctx.getStatementSourceReference(),
+            "fraction-digits argument should be integer within %s", FRAC_DIGITS_ALLOWED);
+        return fractionDigits;
+    }
+
+    @Override
+    public FractionDigitsStatement createDeclared(final StmtContext<Integer, FractionDigitsStatement, ?> ctx) {
+        return new FractionDigitsStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<Integer, FractionDigitsStatement> createEffective(
+            final StmtContext<Integer, FractionDigitsStatement,
+            EffectiveStatement<Integer, FractionDigitsStatement>> ctx) {
+        return new FractionDigitsEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/AbstractGroupingStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/AbstractGroupingStatementSupport.java
new file mode 100644 (file)
index 0000000..b270412
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.grouping;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.GroupingStatement;
+import org.opendaylight.yangtools.yang.parser.spi.GroupingNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+abstract class AbstractGroupingStatementSupport
+        extends AbstractQNameStatementSupport<GroupingStatement, EffectiveStatement<QName, GroupingStatement>> {
+
+    AbstractGroupingStatementSupport() {
+        super(YangStmtMapping.GROUPING);
+    }
+
+    @Override
+    public final QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public final GroupingStatement createDeclared(final StmtContext<QName, GroupingStatement, ?> ctx) {
+        // Shadowing check: make sure grandparent does not see a conflicting definition. This is required to ensure
+        // that a grouping in child scope does not shadow a grouping in parent scope which occurs later in the text.
+        final StmtContext<?, ?, ?> parent = ctx.getParentContext();
+        if (parent != null) {
+            final StmtContext<?, ?, ?> grandParent = parent.getParentContext();
+            if (grandParent != null) {
+                checkConflict(grandParent, ctx);
+            }
+        }
+
+        return new GroupingStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<QName, GroupingStatement> createEffective(
+            final StmtContext<QName, GroupingStatement, EffectiveStatement<QName, GroupingStatement>> ctx) {
+        return new GroupingEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public final void onFullDefinitionDeclared(final Mutable<QName, GroupingStatement,
+            EffectiveStatement<QName, GroupingStatement>> stmt) {
+        super.onFullDefinitionDeclared(stmt);
+
+        if (stmt != null) {
+            final Mutable<?, ?, ?> parent = stmt.getParentContext();
+            if (parent != null) {
+                // Shadowing check: make sure we do not trample on pre-existing definitions. This catches sibling
+                // declarations and parent declarations which have already been declared.
+                checkConflict(parent, stmt);
+                parent.addContext(GroupingNamespace.class, stmt.coerceStatementArgument(), stmt);
+            }
+        }
+    }
+
+    private static void checkConflict(final StmtContext<?, ?, ?> parent, final StmtContext<QName, ?, ?> stmt) {
+        final QName arg = stmt.coerceStatementArgument();
+        final StmtContext<?, ?, ?> existing = parent.getFromNamespace(GroupingNamespace.class, arg);
+        SourceException.throwIf(existing != null, stmt.getStatementSourceReference(), "Duplicate name for grouping %s",
+                arg);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/GroupingEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/GroupingEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..af6447e
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.grouping;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.GroupingEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.GroupingStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.compat.ActionNodeContainerCompat;
+import org.opendaylight.yangtools.yang.model.api.stmt.compat.NotificationNodeContainerCompat;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDocumentedDataNodeContainer;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class GroupingEffectiveStatementImpl
+        extends AbstractEffectiveDocumentedDataNodeContainer<QName, GroupingStatement>
+        implements GroupingDefinition, GroupingEffectiveStatement,
+            ActionNodeContainerCompat<QName, GroupingStatement>,
+            NotificationNodeContainerCompat<QName, GroupingStatement> {
+    private final @NonNull QName qname;
+    private final @NonNull SchemaPath path;
+    private final boolean addedByUses;
+    private final @NonNull ImmutableSet<ActionDefinition> actions;
+    private final @NonNull ImmutableSet<NotificationDefinition> notifications;
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
+
+    GroupingEffectiveStatementImpl(
+            final StmtContext<QName, GroupingStatement, EffectiveStatement<QName, GroupingStatement>> ctx) {
+        super(ctx);
+
+        qname = ctx.coerceStatementArgument();
+        path = ctx.getSchemaPath().get();
+        addedByUses = ctx.getCopyHistory().contains(CopyType.ADDED_BY_USES);
+
+        final ImmutableSet.Builder<ActionDefinition> actionsBuilder = ImmutableSet.builder();
+        final ImmutableSet.Builder<NotificationDefinition> notificationsBuilder = ImmutableSet.builder();
+        final ImmutableList.Builder<UnknownSchemaNode> b = ImmutableList.builder();
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof ActionDefinition) {
+                actionsBuilder.add((ActionDefinition) effectiveStatement);
+            }
+
+            if (effectiveStatement instanceof NotificationDefinition) {
+                notificationsBuilder.add((NotificationDefinition) effectiveStatement);
+            }
+
+            if (effectiveStatement instanceof UnknownSchemaNode) {
+                b.add((UnknownSchemaNode) effectiveStatement);
+            }
+        }
+
+        this.actions = actionsBuilder.build();
+        this.notifications = notificationsBuilder.build();
+        unknownNodes = b.build();
+    }
+
+    @Override
+    public QName getQName() {
+        return qname;
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return path;
+    }
+
+    @Override
+    public boolean isAddedByUses() {
+        return addedByUses;
+    }
+
+    @Override
+    public Set<ActionDefinition> getActions() {
+        return actions;
+    }
+
+    @Override
+    public Set<NotificationDefinition> getNotifications() {
+        return notifications;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownNodes;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(qname);
+        result = prime * result + Objects.hashCode(path);
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final GroupingEffectiveStatementImpl other = (GroupingEffectiveStatementImpl) obj;
+        return Objects.equals(qname, other.qname) && Objects.equals(path, other.path);
+    }
+
+    @Override
+    public String toString() {
+        return GroupingEffectiveStatementImpl.class.getSimpleName() + "[" + "qname=" + qname + "]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/GroupingStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/GroupingStatementImpl.java
new file mode 100644 (file)
index 0000000..bd86dbc
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.grouping;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.GroupingStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public class GroupingStatementImpl extends AbstractDeclaredStatement<QName>
+        implements GroupingStatement {
+    GroupingStatementImpl(final StmtContext<QName, GroupingStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/GroupingStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/GroupingStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..d19b019
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.grouping;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class GroupingStatementRFC6020Support extends AbstractGroupingStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .GROUPING)
+        .addAny(YangStmtMapping.ANYXML)
+        .addAny(YangStmtMapping.CHOICE)
+        .addAny(YangStmtMapping.CONTAINER)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.GROUPING)
+        .addAny(YangStmtMapping.LEAF)
+        .addAny(YangStmtMapping.LEAF_LIST)
+        .addAny(YangStmtMapping.LIST)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addAny(YangStmtMapping.TYPEDEF)
+        .addAny(YangStmtMapping.USES)
+        .build();
+    private static final GroupingStatementRFC6020Support INSTANCE = new GroupingStatementRFC6020Support();
+
+    private GroupingStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static GroupingStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/GroupingStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/grouping/GroupingStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..ce3a2a5
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.grouping;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Grouping statement.
+ */
+@Beta
+public final class GroupingStatementRFC7950Support extends AbstractGroupingStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .GROUPING)
+            .addAny(YangStmtMapping.ACTION)
+            .addAny(YangStmtMapping.ANYDATA)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.CHOICE)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.GROUPING)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addAny(YangStmtMapping.NOTIFICATION)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.STATUS)
+            .addAny(YangStmtMapping.TYPEDEF)
+            .addAny(YangStmtMapping.USES)
+            .build();
+    private static final GroupingStatementRFC7950Support INSTANCE = new GroupingStatementRFC7950Support();
+
+    private GroupingStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static GroupingStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/AbstractIdentityStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/AbstractIdentityStatementSupport.java
new file mode 100644 (file)
index 0000000..b1037db
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.identity;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityStatement;
+import org.opendaylight.yangtools.yang.parser.spi.IdentityNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+abstract class AbstractIdentityStatementSupport
+        extends AbstractQNameStatementSupport<IdentityStatement, IdentityEffectiveStatement> {
+
+    AbstractIdentityStatementSupport() {
+        super(YangStmtMapping.IDENTITY);
+    }
+
+    @Override
+    public final QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public final IdentityStatement createDeclared(final StmtContext<QName, IdentityStatement, ?> ctx) {
+        return new IdentityStatementImpl(ctx);
+    }
+
+    @Override
+    public final IdentityEffectiveStatement createEffective(
+            final StmtContext<QName, IdentityStatement, IdentityEffectiveStatement> ctx) {
+        return new IdentityEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public final void onStatementDefinitionDeclared(
+            final Mutable<QName, IdentityStatement, IdentityEffectiveStatement> stmt) {
+        stmt.addToNs(IdentityNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/IdentityEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/IdentityEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..fd555f8
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.identity;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveSchemaNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.DerivedIdentitiesNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class IdentityEffectiveStatementImpl extends AbstractEffectiveSchemaNode<IdentityStatement>
+        implements IdentityEffectiveStatement, IdentitySchemaNode, MutableStatement {
+    private final ImmutableSet<IdentitySchemaNode> derivedIdentities;
+    private @NonNull Set<IdentitySchemaNode> baseIdentities;
+    private boolean sealed;
+
+    IdentityEffectiveStatementImpl(final StmtContext<QName, IdentityStatement, IdentityEffectiveStatement> ctx) {
+        super(ctx);
+
+        this.baseIdentities = new HashSet<>();
+        ((StmtContext.Mutable<?, ?, ?>) ctx).addMutableStmtToSeal(this);
+
+        // initDerivedIdentities
+        final Set<IdentitySchemaNode> derivedIdentitiesInit = new HashSet<>();
+        final List<StmtContext<?, ?, ?>> derivedIdentitiesCtxList = ctx.getFromNamespace(
+                DerivedIdentitiesNamespace.class, ctx.coerceStatementArgument());
+        if (derivedIdentitiesCtxList == null) {
+            this.derivedIdentities = ImmutableSet.of();
+            return;
+        }
+        for (final StmtContext<?, ?, ?> derivedIdentityCtx : derivedIdentitiesCtxList) {
+            final IdentityEffectiveStatementImpl derivedIdentity = (IdentityEffectiveStatementImpl) derivedIdentityCtx
+                    .buildEffective();
+            derivedIdentity.addBaseIdentity(this);
+            derivedIdentitiesInit.add(derivedIdentity);
+        }
+        this.derivedIdentities = ImmutableSet.copyOf(derivedIdentitiesInit);
+    }
+
+    private void addBaseIdentity(final IdentityEffectiveStatementImpl baseIdentity) {
+        checkState(!sealed, "Attempt to modify sealed identity effective statement %s", getQName());
+        this.baseIdentities.add(baseIdentity);
+    }
+
+    @Override
+    public Set<IdentitySchemaNode> getBaseIdentities() {
+        checkState(sealed, "Attempt to get base identities from unsealed identity effective statement %s", getQName());
+        return baseIdentities;
+    }
+
+    @Override
+    public Set<IdentitySchemaNode> getDerivedIdentities() {
+        return Collections.unmodifiableSet(derivedIdentities);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getQName());
+        result = prime * result + Objects.hashCode(getPath());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final IdentityEffectiveStatementImpl other = (IdentityEffectiveStatementImpl) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("qname", getQName()).add("path", getPath()).toString();
+    }
+
+    @Override
+    public void seal() {
+        if (!sealed) {
+            baseIdentities = ImmutableSet.copyOf(baseIdentities);
+            sealed = true;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/IdentityStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/IdentityStatementImpl.java
new file mode 100644 (file)
index 0000000..62a0e6b
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.identity;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class IdentityStatementImpl extends AbstractDeclaredStatement<QName> implements IdentityStatement {
+    IdentityStatementImpl(final StmtContext<QName, IdentityStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/IdentityStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/IdentityStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..d6d235e
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.identity;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class IdentityStatementRFC6020Support extends AbstractIdentityStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.IDENTITY)
+        .addOptional(YangStmtMapping.BASE)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .build();
+    private static final IdentityStatementRFC6020Support INSTANCE = new IdentityStatementRFC6020Support();
+
+    private IdentityStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static IdentityStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/IdentityStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/identity/IdentityStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..fe5aeaa
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.identity;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Identity statement.
+ */
+@Beta
+public final class IdentityStatementRFC7950Support extends AbstractIdentityStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.IDENTITY)
+            .addAny(YangStmtMapping.BASE)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.IF_FEATURE)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.STATUS)
+            .build();
+    private static final IdentityStatementRFC7950Support INSTANCE = new IdentityStatementRFC7950Support();
+
+    private IdentityStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static IdentityStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeatureEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeatureEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..1cbb7f7
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.if_feature;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureExpr;
+import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class IfFeatureEffectiveStatementImpl extends DeclaredEffectiveStatementBase<IfFeatureExpr, IfFeatureStatement>
+        implements IfFeatureEffectiveStatement {
+    IfFeatureEffectiveStatementImpl(final StmtContext<IfFeatureExpr, IfFeatureStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeaturePredicateVisitor.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeaturePredicateVisitor.java
new file mode 100644 (file)
index 0000000..1171016
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.if_feature;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.List;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionLexer;
+import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionParser;
+import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionParser.Identifier_ref_argContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionParser.If_feature_exprContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionParser.If_feature_factorContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionParser.If_feature_termContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.IfFeatureExpressionParserBaseVisitor;
+import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureExpr;
+import org.opendaylight.yangtools.yang.parser.rfc7950.antlr.SourceExceptionParser;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+@NonNullByDefault
+final class IfFeaturePredicateVisitor extends IfFeatureExpressionParserBaseVisitor<IfFeatureExpr> {
+    private final StmtContext<?, ?, ?> stmtCtx;
+
+    private IfFeaturePredicateVisitor(final StmtContext<?, ?, ?> ctx) {
+        this.stmtCtx = requireNonNull(ctx);
+    }
+
+    static IfFeatureExpr parseIfFeatureExpression(final StmtContext<?, ?, ?> ctx, final String value) {
+        final IfFeatureExpressionLexer lexer = new IfFeatureExpressionLexer(CharStreams.fromString(value));
+        final IfFeatureExpressionParser parser = new IfFeatureExpressionParser(new CommonTokenStream(lexer));
+        final IfFeatureExpr ret = new IfFeaturePredicateVisitor(ctx).visit(SourceExceptionParser.parse(lexer, parser,
+            parser::if_feature_expr, ctx.getStatementSourceReference()));
+
+        return ret;
+    }
+
+    @Override
+    public IfFeatureExpr visitIf_feature_expr(final @Nullable If_feature_exprContext ctx) {
+        return IfFeatureExpr.or(ctx.if_feature_term().stream()
+            .map(this::visitIf_feature_term)
+            .collect(ImmutableSet.toImmutableSet()));
+    }
+
+    @Override
+    public IfFeatureExpr visitIf_feature_term(final @Nullable If_feature_termContext ctx) {
+        final IfFeatureExpr factor = visitIf_feature_factor(ctx.if_feature_factor());
+        final List<If_feature_termContext> terms = ctx.if_feature_term();
+        if (terms == null || terms.isEmpty()) {
+            return factor;
+        }
+        final List<IfFeatureExpr> factors = new ArrayList<>(terms.size() + 1);
+        factors.add(factor);
+        for (If_feature_termContext term : terms) {
+            factors.add(visitIf_feature_term(term));
+        }
+        return IfFeatureExpr.and(ImmutableSet.copyOf(factors));
+    }
+
+    @Override
+    public IfFeatureExpr visitIf_feature_factor(final @Nullable If_feature_factorContext ctx) {
+        if (ctx.if_feature_expr() != null) {
+            return visitIf_feature_expr(ctx.if_feature_expr());
+        } else if (ctx.if_feature_factor() != null) {
+            return visitIf_feature_factor(ctx.if_feature_factor()).negate();
+        } else if (ctx.identifier_ref_arg() != null) {
+            return visitIdentifier_ref_arg(ctx.identifier_ref_arg());
+        }
+
+        throw new SourceException("Unexpected grammar context during parsing of IfFeature expression. "
+                + "Most probably IfFeature grammar has been changed.", stmtCtx.getStatementSourceReference());
+    }
+
+    @Override
+    public IfFeatureExpr visitIdentifier_ref_arg(final @Nullable Identifier_ref_argContext ctx) {
+        return IfFeatureExpr.isPresent(StmtContextUtils.parseNodeIdentifier(stmtCtx, ctx.getText()));
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeatureStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeatureStatementImpl.java
new file mode 100644 (file)
index 0000000..c5e1274
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.if_feature;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureExpr;
+import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class IfFeatureStatementImpl extends AbstractDeclaredStatement<IfFeatureExpr> implements IfFeatureStatement {
+    IfFeatureStatementImpl(final StmtContext<IfFeatureExpr, IfFeatureStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeatureStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/if_feature/IfFeatureStatementSupport.java
new file mode 100644 (file)
index 0000000..f99b0dd
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.if_feature;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureExpr;
+import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureStatement;
+import org.opendaylight.yangtools.yang.parser.spi.FeatureNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class IfFeatureStatementSupport extends AbstractStatementSupport<IfFeatureExpr, IfFeatureStatement,
+        EffectiveStatement<IfFeatureExpr, IfFeatureStatement>> {
+    private static final Logger LOG = LoggerFactory.getLogger(IfFeatureStatementSupport.class);
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.IF_FEATURE)
+        .build();
+    private static final IfFeatureStatementSupport INSTANCE = new IfFeatureStatementSupport();
+
+    private IfFeatureStatementSupport() {
+        super(YangStmtMapping.IF_FEATURE);
+    }
+
+    public static IfFeatureStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public IfFeatureExpr parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        if (YangVersion.VERSION_1_1.equals(ctx.getRootVersion())) {
+            return IfFeaturePredicateVisitor.parseIfFeatureExpression(ctx, value);
+        }
+        return IfFeatureExpr.isPresent(StmtContextUtils.parseNodeIdentifier(ctx, value));
+    }
+
+    @Override
+    public void onFullDefinitionDeclared(final Mutable<IfFeatureExpr, IfFeatureStatement,
+            EffectiveStatement<IfFeatureExpr, IfFeatureStatement>> stmt) {
+        super.onFullDefinitionDeclared(stmt);
+
+        final ModelActionBuilder verifyFeatures = stmt.newInferenceAction(ModelProcessingPhase.EFFECTIVE_MODEL);
+        final Map<Prerequisite<?>, QName> backRef = new HashMap<>();
+        for (QName feature : stmt.getStatementArgument().getReferencedFeatures()) {
+            backRef.put(verifyFeatures.requiresCtx(stmt, FeatureNamespace.class, feature,
+                ModelProcessingPhase.EFFECTIVE_MODEL), feature);
+        }
+
+        verifyFeatures.apply(new InferenceAction() {
+            @Override
+            public void apply(final InferenceContext ctx) {
+                LOG.debug("Resolved all feature references in {}", backRef.values());
+            }
+
+            @Override
+            public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
+                final Set<QName> unresolvedFeatures = new HashSet<>();
+                for (Prerequisite<?> prereq : failed) {
+                    unresolvedFeatures.add(verifyNotNull(backRef.get(prereq)));
+                }
+
+                throw new InferenceException(stmt.getStatementSourceReference(),
+                    "Failed to resolve feature references %s in \"%s\"", unresolvedFeatures,
+                    stmt.rawStatementArgument());
+            }
+        });
+    }
+
+    @Override
+    public IfFeatureStatement createDeclared(final StmtContext<IfFeatureExpr, IfFeatureStatement, ?> ctx) {
+        return new IfFeatureStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<IfFeatureExpr, IfFeatureStatement> createEffective(
+            final StmtContext<IfFeatureExpr, IfFeatureStatement,
+            EffectiveStatement<IfFeatureExpr, IfFeatureStatement>> ctx) {
+        return new IfFeatureEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/AbstractImportStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/AbstractImportStatementSupport.java
new file mode 100644 (file)
index 0000000..a391c55
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.import_;
+
+import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.SOURCE_PRE_LINKAGE;
+import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
+
+import com.google.common.base.Verify;
+import java.net.URI;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ImportStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.parser.spi.PreLinkageModuleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+abstract class AbstractImportStatementSupport extends
+        AbstractStatementSupport<String, ImportStatement, EffectiveStatement<String, ImportStatement>> {
+    AbstractImportStatementSupport() {
+        super(YangStmtMapping.IMPORT);
+    }
+
+    @Override
+    public final String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public final ImportStatement createDeclared(final StmtContext<String, ImportStatement, ?> ctx) {
+        return new ImportStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<String, ImportStatement> createEffective(
+            final StmtContext<String, ImportStatement, EffectiveStatement<String, ImportStatement>> ctx) {
+        return new ImportEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public final void onPreLinkageDeclared(final Mutable<String, ImportStatement,
+            EffectiveStatement<String, ImportStatement>> stmt) {
+        /*
+         * Add ModuleIdentifier of a module which is required by this module.
+         * Based on this information, required modules are searched from library
+         * sources.
+         */
+        stmt.addRequiredSource(RevisionImport.getImportedSourceIdentifier(stmt));
+
+        final String moduleName = stmt.coerceStatementArgument();
+        final ModelActionBuilder importAction = stmt.newInferenceAction(SOURCE_PRE_LINKAGE);
+        final Prerequisite<StmtContext<?, ?, ?>> imported = importAction.requiresCtx(stmt,
+                PreLinkageModuleNamespace.class, moduleName, SOURCE_PRE_LINKAGE);
+        final Prerequisite<Mutable<?, ?, ?>> linkageTarget = importAction
+                .mutatesCtx(stmt.getRoot(), SOURCE_PRE_LINKAGE);
+
+        importAction.apply(new InferenceAction() {
+            @Override
+            public void apply(final InferenceContext ctx) {
+                final StmtContext<?, ?, ?> importedModuleContext = imported.resolve(ctx);
+                Verify.verify(moduleName.equals(importedModuleContext.getStatementArgument()));
+                final URI importedModuleNamespace = importedModuleContext.getFromNamespace(ModuleNameToNamespace.class,
+                        moduleName);
+                Verify.verifyNotNull(importedModuleNamespace);
+                final String impPrefix = SourceException.throwIfNull(
+                    firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class),
+                    stmt.getStatementSourceReference(), "Missing prefix statement");
+
+                stmt.addToNs(ImpPrefixToNamespace.class, impPrefix, importedModuleNamespace);
+            }
+
+            @Override
+            public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
+                InferenceException.throwIf(failed.contains(imported), stmt.getStatementSourceReference(),
+                        "Imported module [%s] was not found.", moduleName);
+            }
+        });
+    }
+
+    @Override
+    public final void onLinkageDeclared(
+            final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt) {
+        if (stmt.isEnabledSemanticVersioning()) {
+            SemanticVersionImport.onLinkageDeclared(stmt);
+        } else {
+            RevisionImport.onLinkageDeclared(stmt);
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/ImportEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/ImportEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..fe4928b
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.import_;
+
+import com.google.common.base.MoreObjects;
+import java.util.Objects;
+import java.util.Optional;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ImportEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ImportStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.MissingSubstatementException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToSemVerSourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+final class ImportEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, ImportStatement>
+        implements ImportEffectiveStatement, ModuleImport {
+
+    private final String moduleName;
+    private final Revision revision;
+    private final SemVer semVer;
+    private final String prefix;
+    private final String description;
+    private final String reference;
+
+    ImportEffectiveStatementImpl(final StmtContext<String, ImportStatement, ?> ctx) {
+        super(ctx);
+
+        moduleName = ctx.coerceStatementArgument();
+        final Optional<String> prefixStmt = findFirstEffectiveSubstatementArgument(PrefixEffectiveStatement.class);
+        MissingSubstatementException.throwIf(!prefixStmt.isPresent(), ctx.getStatementSourceReference(),
+            "Prefix is mandatory substatement of import statement");
+        this.prefix = prefixStmt.get();
+
+        if (!ctx.isEnabledSemanticVersioning()) {
+            final Optional<Revision> optRev = findFirstEffectiveSubstatementArgument(
+                RevisionDateEffectiveStatement.class);
+            this.revision = optRev.isPresent() ? optRev.get() : getImportedRevision(ctx);
+            this.semVer = null;
+        } else {
+            final SemVerSourceIdentifier importedModuleIdentifier = ctx.getFromNamespace(
+                ImportPrefixToSemVerSourceIdentifier.class, prefix);
+            revision = importedModuleIdentifier.getRevision().orElse(null);
+            semVer = importedModuleIdentifier.getSemanticVersion().orElse(null);
+        }
+
+        description = findFirstEffectiveSubstatementArgument(DescriptionEffectiveStatement.class).orElse(null);
+        reference = findFirstEffectiveSubstatementArgument(ReferenceEffectiveStatement.class).orElse(null);
+    }
+
+    private Revision getImportedRevision(final StmtContext<String, ImportStatement, ?> ctx) {
+        /*
+         * When 'revision-date' of an import is not specified in yang source, we
+         * need to find revision of imported module.
+         */
+        final QNameModule importedModule = StmtContextUtils.getModuleQNameByPrefix(ctx, this.prefix);
+        SourceException.throwIfNull(importedModule, ctx.getStatementSourceReference(),
+                "Unable to find import of module %s with prefix %s.", this.moduleName, this.prefix);
+        return importedModule.getRevision().orElse(null);
+    }
+
+    @Override
+    public String getModuleName() {
+        return moduleName;
+    }
+
+    @Override
+    public Optional<Revision> getRevision() {
+        return Optional.ofNullable(revision);
+    }
+
+    @Override
+    public Optional<SemVer> getSemanticVersion() {
+        return Optional.ofNullable(semVer);
+    }
+
+    @Override
+    public String getPrefix() {
+        return prefix;
+    }
+
+    @Override
+    public Optional<String> getDescription() {
+        return Optional.ofNullable(description);
+    }
+
+    @Override
+    public Optional<String> getReference() {
+        return Optional.ofNullable(reference);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(moduleName, revision, prefix, semVer, description, reference);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ImportEffectiveStatementImpl other = (ImportEffectiveStatementImpl) obj;
+        return Objects.equals(moduleName, other.moduleName) && Objects.equals(revision, other.revision)
+                && Objects.equals(semVer, other.semVer) && Objects.equals(prefix, other.prefix)
+                && Objects.equals(description, other.description) && Objects.equals(reference, other.reference);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).omitNullValues().add("moduleName", getModuleName())
+                .add("revision", revision).add("version", semVer).add("prefix", getPrefix())
+                .add("description", description).add("reference", reference).toString();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/ImportStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/ImportStatementImpl.java
new file mode 100644 (file)
index 0000000..bb5ddc9
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.import_;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ImportStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ImportStatementImpl extends AbstractDeclaredStatement<String> implements ImportStatement {
+    ImportStatementImpl(final StmtContext<String, ImportStatement,?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/ImportStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/ImportStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..89889bd
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.import_;
+
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ImportStatementRFC6020Support extends AbstractImportStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator
+            .builder(YangStmtMapping.IMPORT)
+            .addMandatory(YangStmtMapping.PREFIX)
+            .addOptional(YangStmtMapping.REVISION_DATE)
+            .addOptional(OpenConfigStatements.OPENCONFIG_VERSION)
+            .build();
+    private static final ImportStatementRFC6020Support INSTANCE = new ImportStatementRFC6020Support();
+
+    private ImportStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static ImportStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/ImportStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/ImportStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..9a74fa4
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016 Inocybe Technologies 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.rfc7950.stmt.import_;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Implementation of import statement definition for Yang 1.1 (RFC 7950) to
+ * allow import statement to have "description" and "reference" as substatements.
+ */
+@Beta
+public final class ImportStatementRFC7950Support extends AbstractImportStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator
+            .builder(YangStmtMapping.IMPORT)
+            .addMandatory(YangStmtMapping.PREFIX)
+            .addOptional(YangStmtMapping.REVISION_DATE)
+            .addOptional(OpenConfigStatements.OPENCONFIG_VERSION)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .build();
+    private static final ImportStatementRFC7950Support INSTANCE = new ImportStatementRFC7950Support();
+
+    private ImportStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static ImportStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/RevisionImport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/RevisionImport.java
new file mode 100644 (file)
index 0000000..8c52e0b
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.import_;
+
+import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.SOURCE_LINKAGE;
+import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.findFirstDeclaredSubstatement;
+import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ImportStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ModuleQNameToPrefix;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.URIStringToImportPrefix;
+import org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportedModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToSourceIdentifier;
+
+final class RevisionImport {
+
+    private RevisionImport() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    static void onLinkageDeclared(
+            final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt) {
+        final ModelActionBuilder importAction = stmt.newInferenceAction(SOURCE_LINKAGE);
+        final Prerequisite<StmtContext<?, ?, ?>> imported;
+        final String moduleName = stmt.coerceStatementArgument();
+        final Revision revision = firstAttributeOf(stmt.declaredSubstatements(), RevisionDateStatement.class);
+        if (revision == null) {
+            imported = importAction.requiresCtx(stmt, ModuleNamespace.class,
+                NamespaceKeyCriterion.latestRevisionModule(moduleName), SOURCE_LINKAGE);
+        } else {
+            imported = importAction.requiresCtx(stmt, ModuleNamespace.class,
+                RevisionSourceIdentifier.create(moduleName, Optional.of(revision)), SOURCE_LINKAGE);
+        }
+
+        final Prerequisite<Mutable<?, ?, ?>> linkageTarget = importAction.mutatesCtx(stmt.getRoot(),
+            SOURCE_LINKAGE);
+
+        importAction.apply(new InferenceAction() {
+            @Override
+            public void apply(final InferenceContext ctx) {
+                final StmtContext<?, ?, ?> importedModule = imported.resolve(ctx);
+
+                final QNameModule mod = InferenceException.throwIfNull(stmt.getFromNamespace(
+                    ModuleCtxToModuleQName.class, importedModule), stmt.getStatementSourceReference(),
+                    "Failed to find module of %s", importedModule);
+
+                linkageTarget.resolve(ctx).addToNs(ImportedModuleContext.class,
+                    stmt.getFromNamespace(ModuleCtxToSourceIdentifier.class, importedModule), importedModule);
+                final String impPrefix = firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class);
+                final URI modNs = firstAttributeOf(importedModule.declaredSubstatements(),
+                    NamespaceStatement.class);
+                stmt.addToNs(ImportPrefixToModuleCtx.class, impPrefix, importedModule);
+                stmt.addToNs(ModuleQNameToPrefix.class, mod, impPrefix);
+                stmt.addToNs(URIStringToImportPrefix.class, modNs.toString(), impPrefix);
+            }
+
+            @Override
+            public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
+                if (failed.contains(imported)) {
+                    throw new InferenceException(stmt.getStatementSourceReference(),
+                            "Imported module [%s] was not found.", moduleName);
+                }
+            }
+        });
+    }
+
+    static SourceIdentifier getImportedSourceIdentifier(final StmtContext<String, ImportStatement, ?> stmt) {
+        final StmtContext<Revision, ?, ?> revision = findFirstDeclaredSubstatement(stmt,
+            RevisionDateStatement.class);
+        return revision == null ? RevisionSourceIdentifier.create(stmt.getStatementArgument())
+                : RevisionSourceIdentifier.create(stmt.getStatementArgument(), revision.getStatementArgument());
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/SemanticVersionImport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/SemanticVersionImport.java
new file mode 100644 (file)
index 0000000..17ea58a
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.import_;
+
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.SOURCE_LINKAGE;
+import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Optional;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ImportStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ModuleQNameToPrefix;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.URIStringToImportPrefix;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionModuleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToSemVerSourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportedModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToSourceIdentifier;
+
+final class SemanticVersionImport {
+
+    private abstract static class CompatibleCriterion extends NamespaceKeyCriterion<SemVerSourceIdentifier> {
+        private final String moduleName;
+
+        CompatibleCriterion(final String moduleName) {
+            this.moduleName = requireNonNull(moduleName);
+        }
+
+        @Override
+        public boolean match(final SemVerSourceIdentifier key) {
+            return moduleName.equals(key.getName());
+        }
+
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+            return toStringHelper.add("moduleName", moduleName);
+        }
+    }
+
+    private static final class NoVerCompatibleCriterion extends SemanticVersionImport.CompatibleCriterion {
+        NoVerCompatibleCriterion(final String moduleName) {
+            super(moduleName);
+        }
+
+        @Override
+        public SemVerSourceIdentifier select(final SemVerSourceIdentifier first,
+                final SemVerSourceIdentifier second) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+    }
+
+    private static final class SemVerCompatibleCriterion extends SemanticVersionImport.CompatibleCriterion {
+        private final SemVer semVer;
+
+        SemVerCompatibleCriterion(final String moduleName, final SemVer semVer) {
+            super(moduleName);
+            this.semVer = requireNonNull(semVer);
+        }
+
+        @Override
+        public boolean match(final SemVerSourceIdentifier key) {
+            if (!super.match(key)) {
+                return false;
+            }
+            final Optional<SemVer> optKeyVer = key.getSemanticVersion();
+            if (!optKeyVer.isPresent()) {
+                return false;
+            }
+
+            final SemVer keyVer = optKeyVer.get();
+            if (semVer.getMajor() != keyVer.getMajor()) {
+                return false;
+            }
+            if (semVer.getMinor() > keyVer.getMinor()) {
+                return false;
+            }
+            return semVer.getMinor() < keyVer.getMinor() || semVer.getPatch() <= keyVer.getPatch();
+        }
+
+        @Override
+        public SemVerSourceIdentifier select(final SemVerSourceIdentifier first,
+                final SemVerSourceIdentifier second) {
+            return first.getSemanticVersion().get().compareTo(second.getSemanticVersion().get()) >= 0 ? first
+                    : second;
+        }
+
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+            return super.addToStringAttributes(toStringHelper).add("version", semVer);
+        }
+    }
+
+
+    private SemanticVersionImport() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    static void onLinkageDeclared(
+            final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt) {
+        final ModelActionBuilder importAction = stmt.newInferenceAction(SOURCE_LINKAGE);
+        final String moduleName = stmt.coerceStatementArgument();
+        final SemVer semanticVersion = stmt.getFromNamespace(SemanticVersionNamespace.class, stmt);
+        final SemanticVersionImport.CompatibleCriterion criterion = semanticVersion == null
+                ? new NoVerCompatibleCriterion(moduleName)
+                        : new SemVerCompatibleCriterion(moduleName, semanticVersion);
+
+        final Prerequisite<StmtContext<?, ?, ?>> imported = importAction.requiresCtx(stmt,
+            SemanticVersionModuleNamespace.class, criterion, SOURCE_LINKAGE);
+        final Prerequisite<Mutable<?, ?, ?>> linkageTarget = importAction.mutatesCtx(stmt.getRoot(),
+            SOURCE_LINKAGE);
+
+        importAction.apply(new InferenceAction() {
+            @Override
+            public void apply(final InferenceContext ctx) {
+                final StmtContext<?, ?, ?> importedModule = imported.resolve(ctx);
+                final SemVer importedVersion = stmt.getFromNamespace(SemanticVersionNamespace.class, stmt);
+                final SourceIdentifier importedModuleIdentifier = importedModule.getFromNamespace(
+                    ModuleCtxToSourceIdentifier.class, importedModule);
+                final SemVerSourceIdentifier semVerModuleIdentifier = createSemVerModuleIdentifier(
+                    importedModuleIdentifier, importedVersion);
+
+                linkageTarget.resolve(ctx).addToNs(ImportedModuleContext.class, importedModuleIdentifier,
+                    importedModule);
+                final String impPrefix = firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class);
+                stmt.addToNs(ImportPrefixToModuleCtx.class, impPrefix, importedModule);
+                stmt.addToNs(ImportPrefixToSemVerSourceIdentifier.class, impPrefix, semVerModuleIdentifier);
+
+                final QNameModule mod = InferenceException.throwIfNull(stmt.getFromNamespace(
+                    ModuleCtxToModuleQName.class, importedModule), stmt.getStatementSourceReference(),
+                    "Failed to find module of %s", importedModule);
+
+                final URI modNs = firstAttributeOf(importedModule.declaredSubstatements(),
+                    NamespaceStatement.class);
+                stmt.addToNs(ModuleQNameToPrefix.class, mod, impPrefix);
+                stmt.addToNs(URIStringToImportPrefix.class, modNs.toString(), impPrefix);
+            }
+
+            @Override
+            public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
+                if (failed.contains(imported)) {
+                    throw new InferenceException(stmt.getStatementSourceReference(),
+                            "Unable to find module compatible with requested import [%s(%s)].", moduleName,
+                            getRequestedImportVersionString(stmt));
+                }
+            }
+        });
+    }
+
+    private static Optional<SemVer> getRequestedImportVersion(final StmtContext<?, ?, ?> stmt) {
+        return Optional.ofNullable(stmt.getFromNamespace(SemanticVersionNamespace.class, stmt));
+    }
+
+    private static String getRequestedImportVersionString(final StmtContext<?, ?, ?> stmt) {
+        return getRequestedImportVersion(stmt).map(SemVer::toString).orElse("<any>");
+    }
+
+    private static SemVerSourceIdentifier createSemVerModuleIdentifier(
+            final SourceIdentifier importedModuleIdentifier, final SemVer semVer) {
+        return SemVerSourceIdentifier.create(importedModuleIdentifier.getName(),
+            importedModuleIdentifier.getRevision(), semVer);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/AbstractIncludeStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/AbstractIncludeStatementSupport.java
new file mode 100644 (file)
index 0000000..e6e0b1f
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.include;
+
+import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.SOURCE_LINKAGE;
+import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.findFirstDeclaredSubstatement;
+
+import java.util.Collection;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IncludeStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.SubmoduleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.source.IncludedModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.IncludedSubmoduleNameToModuleCtx;
+
+abstract class AbstractIncludeStatementSupport extends
+        AbstractStatementSupport<String, IncludeStatement, EffectiveStatement<String, IncludeStatement>> {
+
+    AbstractIncludeStatementSupport() {
+        super(YangStmtMapping.INCLUDE);
+    }
+
+    @Override
+    public final String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public final IncludeStatement createDeclared(final StmtContext<String, IncludeStatement, ?> ctx) {
+        return new IncludeStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<String, IncludeStatement> createEffective(
+            final StmtContext<String, IncludeStatement, EffectiveStatement<String, IncludeStatement>> ctx) {
+        return new IncludeEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public final void onPreLinkageDeclared(
+            final Mutable<String, IncludeStatement, EffectiveStatement<String, IncludeStatement>> stmt) {
+        final StmtContext<Revision, ?, ?> revision = findFirstDeclaredSubstatement(stmt,
+            RevisionDateStatement.class);
+        stmt.addRequiredSource(revision == null ? RevisionSourceIdentifier.create(stmt.getStatementArgument())
+            : RevisionSourceIdentifier.create(stmt.getStatementArgument(), revision.getStatementArgument()));
+    }
+
+    @Override
+    public final void onLinkageDeclared(
+            final Mutable<String, IncludeStatement, EffectiveStatement<String, IncludeStatement>> stmt) {
+        final String submoduleName = stmt.coerceStatementArgument();
+        final StmtContext<Revision, ?, ?> revision = findFirstDeclaredSubstatement(stmt,
+            RevisionDateStatement.class);
+
+        final ModelActionBuilder includeAction = stmt.newInferenceAction(SOURCE_LINKAGE);
+        final Prerequisite<StmtContext<?, ?, ?>> requiresCtxPrerequisite;
+        if (revision == null) {
+            requiresCtxPrerequisite = includeAction.requiresCtx(stmt, SubmoduleNamespace.class,
+                NamespaceKeyCriterion.latestRevisionModule(submoduleName), SOURCE_LINKAGE);
+        } else {
+            requiresCtxPrerequisite = includeAction.requiresCtx(stmt, SubmoduleNamespace.class,
+                RevisionSourceIdentifier.create(submoduleName, Optional.of(revision.getStatementArgument())),
+                SOURCE_LINKAGE);
+        }
+
+        includeAction.apply(new InferenceAction() {
+            @Override
+            public void apply(final InferenceContext ctx) {
+                final StmtContext<?, ?, ?> includedSubModuleContext = requiresCtxPrerequisite.resolve(ctx);
+
+                stmt.addToNs(IncludedModuleContext.class, revision != null
+                        ? RevisionSourceIdentifier.create(submoduleName, revision.getStatementArgument())
+                                : RevisionSourceIdentifier.create(submoduleName), includedSubModuleContext);
+                stmt.addToNs(IncludedSubmoduleNameToModuleCtx.class, stmt.getStatementArgument(),
+                    includedSubModuleContext);
+            }
+
+            @Override
+            public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
+                InferenceException.throwIf(failed.contains(requiresCtxPrerequisite),
+                    stmt.getStatementSourceReference(),
+                    "Included submodule '%s' was not found: ", stmt.getStatementArgument());
+            }
+        });
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/IncludeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/IncludeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..28b33eb
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.include;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.IncludeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IncludeStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class IncludeEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, IncludeStatement>
+        implements IncludeEffectiveStatement {
+    IncludeEffectiveStatementImpl(final StmtContext<String, IncludeStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/IncludeStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/IncludeStatementImpl.java
new file mode 100644 (file)
index 0000000..128c882
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.include;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.IncludeStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class IncludeStatementImpl extends AbstractDeclaredStatement<String> implements IncludeStatement {
+    IncludeStatementImpl(final StmtContext<String, IncludeStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/IncludeStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/IncludeStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..5ecf061
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.include;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class IncludeStatementRFC6020Support extends AbstractIncludeStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.INCLUDE).addOptional(YangStmtMapping.REVISION_DATE).build();
+    private static final IncludeStatementRFC6020Support INSTANCE = new IncludeStatementRFC6020Support();
+
+    private IncludeStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static IncludeStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/IncludeStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/include/IncludeStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..4c0d1e2
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 Inocybe Technologies 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.rfc7950.stmt.include;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Implementation of include statement definition for Yang 1.1 (RFC 7950) to
+ * allow include statement to have "description" and "reference" as substatements.
+ */
+@Beta
+public final class IncludeStatementRFC7950Support extends AbstractIncludeStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator
+            .builder(YangStmtMapping.INCLUDE)
+            .addOptional(YangStmtMapping.REVISION_DATE)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addOptional(YangStmtMapping.REFERENCE).build();
+    private static final IncludeStatementRFC7950Support INSTANCE = new IncludeStatementRFC7950Support();
+
+    private IncludeStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static IncludeStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/AbstractInputStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/AbstractInputStatementSupport.java
new file mode 100644 (file)
index 0000000..2ba945b
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.input;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.InputStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+abstract class AbstractInputStatementSupport
+        extends AbstractQNameStatementSupport<InputStatement, EffectiveStatement<QName, InputStatement>> {
+    AbstractInputStatementSupport() {
+        super(YangStmtMapping.INPUT);
+    }
+
+    @Override
+    public final QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return YangConstants.operationInputQName(StmtContextUtils.getRootModuleQName(ctx));
+    }
+
+    @Override
+    public final void onStatementAdded(final Mutable<QName, InputStatement,
+            EffectiveStatement<QName, InputStatement>> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public final InputStatement createDeclared(final StmtContext<QName, InputStatement, ?> ctx) {
+        return new InputStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<QName, InputStatement> createEffective(
+            final StmtContext<QName, InputStatement, EffectiveStatement<QName, InputStatement>> ctx) {
+        return new InputEffectiveStatementImpl(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/InputEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/InputEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..2e75ffd
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.input;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.InputStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveOperationContainerSchemaNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class InputEffectiveStatementImpl extends AbstractEffectiveOperationContainerSchemaNode<InputStatement>
+        implements InputEffectiveStatement {
+    InputEffectiveStatementImpl(
+            final StmtContext<QName, InputStatement, EffectiveStatement<QName, InputStatement>> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/InputStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/InputStatementImpl.java
new file mode 100644 (file)
index 0000000..870a006
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.input;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.InputStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class InputStatementImpl extends AbstractDeclaredStatement<QName> implements InputStatement {
+    InputStatementImpl(final StmtContext<QName, InputStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/InputStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/InputStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..a16f8b4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.input;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class InputStatementRFC6020Support extends AbstractInputStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .INPUT)
+        .addAny(YangStmtMapping.ANYXML)
+        .addAny(YangStmtMapping.CHOICE)
+        .addAny(YangStmtMapping.CONTAINER)
+        .addAny(YangStmtMapping.GROUPING)
+        .addAny(YangStmtMapping.LEAF)
+        .addAny(YangStmtMapping.LEAF_LIST)
+        .addAny(YangStmtMapping.LIST)
+        .addAny(YangStmtMapping.TYPEDEF)
+        .addAny(YangStmtMapping.USES)
+        .build();
+    private static final InputStatementRFC6020Support INSTANCE = new InputStatementRFC6020Support();
+
+    private InputStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static InputStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/InputStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/input/InputStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..381d87c
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.input;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Input statement.
+ */
+@Beta
+public final class InputStatementRFC7950Support extends AbstractInputStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .INPUT)
+            .addAny(YangStmtMapping.ANYDATA)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.CHOICE)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addAny(YangStmtMapping.GROUPING)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addAny(YangStmtMapping.MUST)
+            .addAny(YangStmtMapping.TYPEDEF)
+            .addAny(YangStmtMapping.USES)
+            .build();
+    private static final InputStatementRFC7950Support INSTANCE = new InputStatementRFC7950Support();
+
+    private InputStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static InputStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..a66b789
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.key;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.stmt.KeyEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.KeyStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class KeyEffectiveStatementImpl
+        extends DeclaredEffectiveStatementBase<Collection<SchemaNodeIdentifier>, KeyStatement>
+        implements KeyEffectiveStatement {
+    KeyEffectiveStatementImpl(final StmtContext<Collection<SchemaNodeIdentifier>, KeyStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyStatementImpl.java
new file mode 100644 (file)
index 0000000..99648fd
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.key;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.stmt.KeyStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class KeyStatementImpl extends AbstractDeclaredStatement<Collection<SchemaNodeIdentifier>>
+        implements KeyStatement {
+    KeyStatementImpl(final StmtContext<Collection<SchemaNodeIdentifier>, KeyStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/key/KeyStatementSupport.java
new file mode 100644 (file)
index 0000000..9b5f6f7
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.key;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.KeyStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class KeyStatementSupport
+        extends AbstractStatementSupport<Collection<SchemaNodeIdentifier>, KeyStatement,
+                EffectiveStatement<Collection<SchemaNodeIdentifier>, KeyStatement>> {
+    private static final Splitter LIST_KEY_SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.KEY)
+        .build();
+    private static final KeyStatementSupport INSTANCE = new KeyStatementSupport();
+
+    private KeyStatementSupport() {
+        super(YangStmtMapping.KEY);
+    }
+
+    public static KeyStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Collection<SchemaNodeIdentifier> parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        final Builder<SchemaNodeIdentifier> builder = ImmutableSet.builder();
+        int tokens = 0;
+        for (String keyToken : LIST_KEY_SPLITTER.split(value)) {
+            builder.add(SchemaNodeIdentifier.SAME.createChild(StmtContextUtils.parseNodeIdentifier(ctx, keyToken)));
+            tokens++;
+        }
+
+        // Throws NPE on nulls, retains first inserted value, cannot be modified
+        final Collection<SchemaNodeIdentifier> ret = builder.build();
+        SourceException.throwIf(ret.size() != tokens, ctx.getStatementSourceReference(),
+                "Key argument '%s' contains duplicates", value);
+        return ret;
+    }
+
+    @Override
+    public Collection<SchemaNodeIdentifier> adaptArgumentValue(
+            final StmtContext<Collection<SchemaNodeIdentifier>, KeyStatement,
+                EffectiveStatement<Collection<SchemaNodeIdentifier>, KeyStatement>> ctx,
+            final QNameModule targetModule) {
+        final Builder<SchemaNodeIdentifier> builder = ImmutableSet.builder();
+        boolean replaced = false;
+        for (final SchemaNodeIdentifier arg : ctx.coerceStatementArgument()) {
+            final QName qname = arg.getLastComponent();
+            if (!targetModule.equals(qname.getModule())) {
+                final QName newQname = ctx.getFromNamespace(QNameCacheNamespace.class, qname.withModule(targetModule));
+                builder.add(SchemaNodeIdentifier.SAME.createChild(newQname));
+                replaced = true;
+            } else {
+                builder.add(arg);
+            }
+        }
+
+        // This makes sure we reuse the collection when a grouping is
+        // instantiated in the same module
+        return replaced ? builder.build() : ctx.getStatementArgument();
+    }
+
+    @Override
+    public KeyStatement createDeclared(final StmtContext<Collection<SchemaNodeIdentifier>, KeyStatement, ?> ctx) {
+        return new KeyStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<Collection<SchemaNodeIdentifier>, KeyStatement> createEffective(
+            final StmtContext<Collection<SchemaNodeIdentifier>, KeyStatement,
+                    EffectiveStatement<Collection<SchemaNodeIdentifier>, KeyStatement>> ctx) {
+        return new KeyEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf/LeafEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf/LeafEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..72922df
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.leaf;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DefaultEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnitsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.util.type.ConcreteTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.ConcreteTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDataSchemaNode;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStmtUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+final class LeafEffectiveStatementImpl extends AbstractEffectiveDataSchemaNode<LeafStatement>
+        implements LeafEffectiveStatement, LeafSchemaNode, DerivableSchemaNode {
+    private final ImmutableSet<MustDefinition> mustConstraints;
+    private final LeafSchemaNode original;
+    private final TypeDefinition<?> type;
+    private final String defaultStr;
+    private final String unitsStr;
+    private final boolean mandatory;
+
+    LeafEffectiveStatementImpl(final StmtContext<QName, LeafStatement, EffectiveStatement<QName, LeafStatement>> ctx) {
+        super(ctx);
+        this.original = (LeafSchemaNode) ctx.getOriginalCtx().map(StmtContext::buildEffective).orElse(null);
+
+        final TypeEffectiveStatement<?> typeStmt = SourceException.throwIfNull(
+                firstSubstatementOfType(TypeEffectiveStatement.class), ctx.getStatementSourceReference(),
+                "Leaf is missing a 'type' statement");
+
+        String dflt = null;
+        String units = null;
+        final ConcreteTypeBuilder<?> builder = ConcreteTypes.concreteTypeBuilder(typeStmt.getTypeDefinition(),
+            ctx.getSchemaPath().get());
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof DefaultEffectiveStatement) {
+                dflt = ((DefaultEffectiveStatement)stmt).argument();
+                builder.setDefaultValue(stmt.argument());
+            } else if (stmt instanceof DescriptionEffectiveStatement) {
+                builder.setDescription(((DescriptionEffectiveStatement)stmt).argument());
+            } else if (stmt instanceof ReferenceEffectiveStatement) {
+                builder.setReference(((ReferenceEffectiveStatement)stmt).argument());
+            } else if (stmt instanceof StatusEffectiveStatement) {
+                builder.setStatus(((StatusEffectiveStatement)stmt).argument());
+            } else if (stmt instanceof UnitsEffectiveStatement) {
+                units = ((UnitsEffectiveStatement)stmt).argument();
+                builder.setUnits(units);
+            }
+        }
+
+        SourceException.throwIf(
+            EffectiveStmtUtils.hasDefaultValueMarkedWithIfFeature(ctx.getRootVersion(), typeStmt, dflt),
+            ctx.getStatementSourceReference(),
+            "Leaf '%s' has default value '%s' marked with an if-feature statement.", ctx.getStatementArgument(), dflt);
+
+        defaultStr = dflt;
+        unitsStr = units;
+        type = builder.build();
+        mandatory = findFirstEffectiveSubstatementArgument(MandatoryEffectiveStatement.class).orElse(Boolean.FALSE)
+                .booleanValue();
+        mustConstraints = ImmutableSet.copyOf(allSubstatementsOfType(MustDefinition.class));
+    }
+
+    @Override
+    public boolean isMandatory() {
+        return mandatory;
+    }
+
+    @Override
+    public Optional<LeafSchemaNode> getOriginal() {
+        return Optional.ofNullable(original);
+    }
+
+    @Override
+    public TypeDefinition<?> getType() {
+        return type;
+    }
+
+    @Override
+    public Collection<MustDefinition> getMustConstraints() {
+        return mustConstraints;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getQName());
+        result = prime * result + Objects.hashCode(getPath());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof LeafEffectiveStatementImpl)) {
+            return false;
+        }
+        final LeafEffectiveStatementImpl other = (LeafEffectiveStatementImpl) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return LeafEffectiveStatementImpl.class.getSimpleName() + "["
+                + "qname=" + getQName()
+                + ", path=" + getPath()
+                + "]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf/LeafStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf/LeafStatementImpl.java
new file mode 100644 (file)
index 0000000..e88bf2d
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.leaf;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class LeafStatementImpl extends AbstractDeclaredStatement<QName> implements LeafStatement {
+    LeafStatementImpl(final StmtContext<QName, LeafStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf/LeafStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf/LeafStatementSupport.java
new file mode 100644 (file)
index 0000000..488009e
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.leaf;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class LeafStatementSupport
+        extends AbstractQNameStatementSupport<LeafStatement, EffectiveStatement<QName, LeafStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .LEAF)
+        .addOptional(YangStmtMapping.CONFIG)
+        .addOptional(YangStmtMapping.DEFAULT)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addOptional(YangStmtMapping.MANDATORY)
+        .addAny(YangStmtMapping.MUST)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addMandatory(YangStmtMapping.TYPE)
+        .addOptional(YangStmtMapping.UNITS)
+        .addOptional(YangStmtMapping.WHEN)
+        .build();
+    private static final LeafStatementSupport INSTANCE = new LeafStatementSupport();
+
+    private LeafStatementSupport() {
+        super(YangStmtMapping.LEAF);
+    }
+
+    public static LeafStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx,value);
+    }
+
+    @Override
+    public void onStatementAdded(final Mutable<QName, LeafStatement, EffectiveStatement<QName, LeafStatement>> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public void onFullDefinitionDeclared(
+            final Mutable<QName, LeafStatement, EffectiveStatement<QName, LeafStatement>> ctx) {
+        super.onFullDefinitionDeclared(ctx);
+        StmtContextUtils.validateIfFeatureAndWhenOnListKeys(ctx);
+    }
+
+    @Override
+    public LeafStatement createDeclared(final StmtContext<QName, LeafStatement, ?> ctx) {
+        return new LeafStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<QName, LeafStatement> createEffective(
+            final StmtContext<QName, LeafStatement, EffectiveStatement<QName, LeafStatement>> ctx) {
+        return new LeafEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/AbstractLeafListStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/AbstractLeafListStatementSupport.java
new file mode 100644 (file)
index 0000000..9ecb77f
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.leaf_list;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafListStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+abstract class AbstractLeafListStatementSupport
+        extends AbstractQNameStatementSupport<LeafListStatement, EffectiveStatement<QName, LeafListStatement>> {
+
+    AbstractLeafListStatementSupport() {
+        super(YangStmtMapping.LEAF_LIST);
+    }
+
+    @Override
+    public final void onStatementAdded(
+            final Mutable<QName, LeafListStatement, EffectiveStatement<QName, LeafListStatement>> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public final QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public final LeafListStatement createDeclared(final StmtContext<QName, LeafListStatement, ?> ctx) {
+        return new LeafListStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<QName, LeafListStatement> createEffective(
+            final StmtContext<QName, LeafListStatement, EffectiveStatement<QName, LeafListStatement>> ctx) {
+        return new LeafListEffectiveStatementImpl(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/LeafListEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/LeafListEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..0aa7f1f
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.leaf_list;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DefaultEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafListEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafListStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OrderedByEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnitsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.util.type.ConcreteTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.ConcreteTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDataSchemaNode;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStmtUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+final class LeafListEffectiveStatementImpl extends AbstractEffectiveDataSchemaNode<LeafListStatement>
+        implements LeafListEffectiveStatement, LeafListSchemaNode, DerivableSchemaNode {
+
+    private static final String ORDER_BY_USER_KEYWORD = "user";
+
+    private final TypeDefinition<?> type;
+    private final LeafListSchemaNode original;
+    private final boolean userOrdered;
+    private final @NonNull ImmutableSet<String> defaultValues;
+    private final ImmutableSet<MustDefinition> mustConstraints;
+    private final ElementCountConstraint elementCountConstraint;
+
+    LeafListEffectiveStatementImpl(
+            final StmtContext<QName, LeafListStatement, EffectiveStatement<QName, LeafListStatement>> ctx) {
+        super(ctx);
+        this.original = (LeafListSchemaNode) ctx.getOriginalCtx().map(StmtContext::buildEffective).orElse(null);
+
+        final TypeEffectiveStatement<?> typeStmt = SourceException.throwIfNull(
+            firstSubstatementOfType(TypeEffectiveStatement.class), ctx.getStatementSourceReference(),
+            "Leaf-list is missing a 'type' statement");
+
+        final ConcreteTypeBuilder<?> builder = ConcreteTypes.concreteTypeBuilder(typeStmt.getTypeDefinition(),
+            ctx.getSchemaPath().get());
+        final ImmutableSet.Builder<String> defaultValuesBuilder = ImmutableSet.builder();
+        boolean isUserOrdered = false;
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof OrderedByEffectiveStatement) {
+                isUserOrdered = ORDER_BY_USER_KEYWORD.equals(stmt.argument());
+            }
+
+            if (stmt instanceof DefaultEffectiveStatement) {
+                defaultValuesBuilder.add(((DefaultEffectiveStatement) stmt).argument());
+            } else if (stmt instanceof DescriptionEffectiveStatement) {
+                builder.setDescription(((DescriptionEffectiveStatement)stmt).argument());
+            } else if (stmt instanceof ReferenceEffectiveStatement) {
+                builder.setReference(((ReferenceEffectiveStatement)stmt).argument());
+            } else if (stmt instanceof StatusEffectiveStatement) {
+                builder.setStatus(((StatusEffectiveStatement)stmt).argument());
+            } else if (stmt instanceof UnitsEffectiveStatement) {
+                builder.setUnits(((UnitsEffectiveStatement)stmt).argument());
+            }
+        }
+
+        // FIXME: We need to interpret the default value in terms of supplied element type
+        defaultValues = defaultValuesBuilder.build();
+        SourceException.throwIf(
+            EffectiveStmtUtils.hasDefaultValueMarkedWithIfFeature(ctx.getRootVersion(), typeStmt, defaultValues),
+            ctx.getStatementSourceReference(),
+            "Leaf-list '%s' has one of its default values '%s' marked with an if-feature statement.",
+            ctx.getStatementArgument(), defaultValues);
+
+        // FIXME: RFC7950 section 7.7.4: we need to check for min-elements and defaultValues conflict
+
+        type = builder.build();
+        userOrdered = isUserOrdered;
+        elementCountConstraint = EffectiveStmtUtils.createElementCountConstraint(this).orElse(null);
+        mustConstraints = ImmutableSet.copyOf(allSubstatementsOfType(MustDefinition.class));
+    }
+
+    @Override
+    public Collection<String> getDefaults() {
+        return defaultValues;
+    }
+
+    @Override
+    public Optional<LeafListSchemaNode> getOriginal() {
+        return Optional.ofNullable(original);
+    }
+
+    @Override
+    public TypeDefinition<?> getType() {
+        return type;
+    }
+
+    @Override
+    public boolean isUserOrdered() {
+        return userOrdered;
+    }
+
+    @Override
+    public Optional<ElementCountConstraint> getElementCountConstraint() {
+        return Optional.ofNullable(elementCountConstraint);
+    }
+
+    @Override
+    public Collection<MustDefinition> getMustConstraints() {
+        return mustConstraints;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getQName());
+        result = prime * result + Objects.hashCode(getPath());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final LeafListEffectiveStatementImpl other = (LeafListEffectiveStatementImpl) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return LeafListEffectiveStatementImpl.class.getSimpleName() + "[" + getQName() + "]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/LeafListStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/LeafListStatementImpl.java
new file mode 100644 (file)
index 0000000..2063141
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.leaf_list;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafListStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class LeafListStatementImpl extends AbstractDeclaredStatement<QName> implements LeafListStatement {
+    LeafListStatementImpl(final StmtContext<QName, LeafListStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/LeafListStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/LeafListStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..dbee6de
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.leaf_list;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class LeafListStatementRFC6020Support extends AbstractLeafListStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .LEAF_LIST)
+        .addOptional(YangStmtMapping.CONFIG)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addOptional(YangStmtMapping.MIN_ELEMENTS)
+        .addOptional(YangStmtMapping.MAX_ELEMENTS)
+        .addAny(YangStmtMapping.MUST)
+        .addOptional(YangStmtMapping.ORDERED_BY)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addMandatory(YangStmtMapping.TYPE)
+        .addOptional(YangStmtMapping.UNITS)
+        .addOptional(YangStmtMapping.WHEN)
+        .build();
+    private static final LeafListStatementRFC6020Support INSTANCE = new LeafListStatementRFC6020Support();
+
+    private LeafListStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static LeafListStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/LeafListStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/leaf_list/LeafListStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..2c68953
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.leaf_list;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 leaf-list statement.
+ */
+public final class LeafListStatementRFC7950Support extends AbstractLeafListStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .LEAF_LIST)
+            .addOptional(YangStmtMapping.CONFIG)
+            .addAny(YangStmtMapping.DEFAULT)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.IF_FEATURE)
+            .addOptional(YangStmtMapping.MIN_ELEMENTS)
+            .addOptional(YangStmtMapping.MAX_ELEMENTS)
+            .addAny(YangStmtMapping.MUST)
+            .addOptional(YangStmtMapping.ORDERED_BY)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.STATUS)
+            .addMandatory(YangStmtMapping.TYPE)
+            .addOptional(YangStmtMapping.UNITS)
+            .addOptional(YangStmtMapping.WHEN)
+            .build();
+    private static final LeafListStatementRFC7950Support INSTANCE = new LeafListStatementRFC7950Support();
+
+    private LeafListStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static LeafListStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/length/LengthEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/length/LengthEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..e7f4195
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.length;
+
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.stmt.LengthEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LengthStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractListConstraintEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+// FIXME: hide this type
+public final class LengthEffectiveStatementImpl
+        extends AbstractListConstraintEffectiveStatement<ValueRange, LengthStatement>
+        implements LengthEffectiveStatement {
+    LengthEffectiveStatementImpl(final StmtContext<List<ValueRange>, LengthStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/length/LengthStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/length/LengthStatementImpl.java
new file mode 100644 (file)
index 0000000..fdcaef6
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.length;
+
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.stmt.LengthStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class LengthStatementImpl extends AbstractDeclaredStatement<List<ValueRange>> implements LengthStatement {
+    LengthStatementImpl(final StmtContext<List<ValueRange>, LengthStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/length/LengthStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/length/LengthStatementSupport.java
new file mode 100644 (file)
index 0000000..556da96
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.length;
+
+import com.google.common.collect.Iterables;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LengthStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class LengthStatementSupport extends AbstractStatementSupport<List<ValueRange>, LengthStatement,
+        EffectiveStatement<List<ValueRange>, LengthStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .LENGTH)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addOptional(YangStmtMapping.ERROR_APP_TAG)
+        .addOptional(YangStmtMapping.ERROR_MESSAGE)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .build();
+    private static final LengthStatementSupport INSTANCE = new LengthStatementSupport();
+
+    private LengthStatementSupport() {
+        super(YangStmtMapping.LENGTH);
+    }
+
+    public static LengthStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public List<ValueRange> parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        final List<ValueRange> ranges = new ArrayList<>();
+
+        for (final String singleRange : ArgumentUtils.PIPE_SPLITTER.split(value)) {
+            final Iterator<String> boundaries = ArgumentUtils.TWO_DOTS_SPLITTER.split(singleRange).iterator();
+            final Number min = parseIntegerConstraintValue(ctx, boundaries.next());
+
+            final Number max;
+            if (boundaries.hasNext()) {
+                max = parseIntegerConstraintValue(ctx, boundaries.next());
+
+                // if min larger than max then error
+                SourceException.throwIf(ArgumentUtils.compareNumbers(min, max) == 1, ctx.getStatementSourceReference(),
+                        "Length constraint %s has descending order of boundaries; should be ascending.", singleRange);
+                SourceException.throwIf(boundaries.hasNext(), ctx.getStatementSourceReference(),
+                        "Wrong number of boundaries in length constraint %s.", singleRange);
+            } else {
+                max = min;
+            }
+
+            // some of intervals overlapping
+            InferenceException.throwIf(ranges.size() > 1
+                && ArgumentUtils.compareNumbers(min, Iterables.getLast(ranges).upperBound()) != 1,
+                        ctx.getStatementSourceReference(),  "Some of the length ranges in %s are not disjoint", value);
+            ranges.add(ValueRange.of(min, max));
+        }
+
+        return ranges;
+    }
+
+    @Override
+    public LengthStatement createDeclared(final StmtContext<List<ValueRange>, LengthStatement, ?> ctx) {
+        return new LengthStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<List<ValueRange>, LengthStatement> createEffective(
+            final StmtContext<List<ValueRange>, LengthStatement, EffectiveStatement<List<ValueRange>,
+                    LengthStatement>> ctx) {
+        return new LengthEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+
+    private static Number parseIntegerConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        if ("max".equals(value)) {
+            return UnresolvedNumber.max();
+        }
+        if ("min".equals(value)) {
+            return UnresolvedNumber.min();
+        }
+
+        try {
+            return new BigInteger(value);
+        } catch (final NumberFormatException e) {
+            throw new SourceException(ctx.getStatementSourceReference(), e, "Value %s is not a valid integer", value);
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/AbstractListStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/AbstractListStatementSupport.java
new file mode 100644 (file)
index 0000000..f6d460f
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.list;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+abstract class AbstractListStatementSupport
+        extends AbstractQNameStatementSupport<ListStatement, EffectiveStatement<QName, ListStatement>> {
+
+    AbstractListStatementSupport() {
+        super(YangStmtMapping.LIST);
+    }
+
+    @Override
+    public final QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public final void onStatementAdded(
+            final Mutable<QName, ListStatement, EffectiveStatement<QName, ListStatement>> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public final ListStatement createDeclared(final StmtContext<QName, ListStatement, ?> ctx) {
+        return new ListStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<QName, ListStatement> createEffective(
+            final StmtContext<QName, ListStatement, EffectiveStatement<QName, ListStatement>> ctx) {
+        return new ListEffectiveStatementImpl(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..e96b42c
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.list;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.UniqueConstraint;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.KeyEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OrderedByEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.compat.ActionNodeContainerCompat;
+import org.opendaylight.yangtools.yang.model.api.stmt.compat.NotificationNodeContainerCompat;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveSimpleDataNodeContainer;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStmtUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ListEffectiveStatementImpl extends AbstractEffectiveSimpleDataNodeContainer<ListStatement>
+        implements ListEffectiveStatement, ListSchemaNode, DerivableSchemaNode,
+            ActionNodeContainerCompat<QName, ListStatement>, NotificationNodeContainerCompat<QName, ListStatement> {
+    private static final String ORDER_BY_USER_KEYWORD = "user";
+
+    private final boolean userOrdered;
+    private final ImmutableList<QName> keyDefinition;
+    private final ListSchemaNode original;
+    private final @NonNull ImmutableSet<ActionDefinition> actions;
+    private final @NonNull ImmutableSet<NotificationDefinition> notifications;
+    private final @NonNull ImmutableList<UniqueConstraint> uniqueConstraints;
+    private final ElementCountConstraint elementCountConstraint;
+    private final ImmutableSet<MustDefinition> mustConstraints;
+
+    ListEffectiveStatementImpl(
+            final StmtContext<QName, ListStatement, EffectiveStatement<QName, ListStatement>> ctx) {
+        super(ctx);
+
+        this.original = (ListSchemaNode) ctx.getOriginalCtx().map(StmtContext::buildEffective).orElse(null);
+        this.userOrdered = findFirstEffectiveSubstatementArgument(OrderedByEffectiveStatement.class)
+                .map(ORDER_BY_USER_KEYWORD::equals).orElse(Boolean.FALSE).booleanValue();
+
+        // initKeyDefinition
+        final Optional<KeyEffectiveStatement> optKeyStmt = findFirstEffectiveSubstatement(KeyEffectiveStatement.class);
+        if (optKeyStmt.isPresent()) {
+            final KeyEffectiveStatement keyStmt = optKeyStmt.get();
+            final List<QName> keyDefinitionInit = new ArrayList<>(keyStmt.argument().size());
+            final Set<QName> possibleLeafQNamesForKey = new HashSet<>();
+            for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+                if (effectiveStatement instanceof LeafSchemaNode) {
+                    possibleLeafQNamesForKey.add(((LeafSchemaNode) effectiveStatement).getQName());
+                }
+            }
+            for (final SchemaNodeIdentifier key : keyStmt.argument()) {
+                final QName keyQName = key.getLastComponent();
+
+                if (!possibleLeafQNamesForKey.contains(keyQName)) {
+                    throw new InferenceException(ctx.getStatementSourceReference(),
+                            "Key '%s' misses node '%s' in list '%s'", keyStmt.getDeclared().rawArgument(),
+                            keyQName.getLocalName(), ctx.getStatementArgument());
+                }
+                keyDefinitionInit.add(keyQName);
+            }
+
+            this.keyDefinition = ImmutableList.copyOf(keyDefinitionInit);
+        } else {
+            this.keyDefinition = ImmutableList.of();
+        }
+
+        this.uniqueConstraints = ImmutableList.copyOf(allSubstatementsOfType(UniqueConstraint.class));
+
+        final Builder<ActionDefinition> actionsBuilder = ImmutableSet.builder();
+        final Builder<NotificationDefinition> notificationsBuilder = ImmutableSet.builder();
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof ActionDefinition) {
+                actionsBuilder.add((ActionDefinition) effectiveStatement);
+            }
+
+            if (effectiveStatement instanceof NotificationDefinition) {
+                notificationsBuilder.add((NotificationDefinition) effectiveStatement);
+            }
+        }
+
+        this.actions = actionsBuilder.build();
+        this.notifications = notificationsBuilder.build();
+        elementCountConstraint = EffectiveStmtUtils.createElementCountConstraint(this).orElse(null);
+        mustConstraints = ImmutableSet.copyOf(allSubstatementsOfType(MustDefinition.class));
+    }
+
+    @Override
+    public Optional<ListSchemaNode> getOriginal() {
+        return Optional.ofNullable(original);
+    }
+
+    @Override
+    public List<QName> getKeyDefinition() {
+        return keyDefinition;
+    }
+
+    @Override
+    public Set<ActionDefinition> getActions() {
+        return actions;
+    }
+
+    @Override
+    public Set<NotificationDefinition> getNotifications() {
+        return notifications;
+    }
+
+    @Override
+    public Collection<UniqueConstraint> getUniqueConstraints() {
+        return uniqueConstraints;
+    }
+
+    @Override
+    public boolean isUserOrdered() {
+        return userOrdered;
+    }
+
+    @Override
+    public Optional<ElementCountConstraint> getElementCountConstraint() {
+        return Optional.ofNullable(elementCountConstraint);
+    }
+
+    @Override
+    public Collection<MustDefinition> getMustConstraints() {
+        return mustConstraints;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(getQName());
+        result = prime * result + Objects.hashCode(getPath());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ListEffectiveStatementImpl other = (ListEffectiveStatementImpl) obj;
+        return Objects.equals(getQName(), other.getQName()) && Objects.equals(getPath(), other.getPath());
+    }
+
+    @Override
+    public String toString() {
+        return "list " + getQName().getLocalName();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListStatementImpl.java
new file mode 100644 (file)
index 0000000..7d2662e
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.list;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.ListStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ListStatementImpl extends AbstractDeclaredStatement<QName> implements ListStatement {
+    ListStatementImpl(final StmtContext<QName, ListStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..cced709
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.list;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ListStatementRFC6020Support extends AbstractListStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .LIST)
+        .addAny(YangStmtMapping.ANYXML)
+        .addAny(YangStmtMapping.CHOICE)
+        .addOptional(YangStmtMapping.CONFIG)
+        .addAny(YangStmtMapping.CONTAINER)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.GROUPING)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addOptional(YangStmtMapping.KEY)
+        .addAny(YangStmtMapping.LEAF)
+        .addAny(YangStmtMapping.LEAF_LIST)
+        .addAny(YangStmtMapping.LIST)
+        .addOptional(YangStmtMapping.MAX_ELEMENTS)
+        .addOptional(YangStmtMapping.MIN_ELEMENTS)
+        .addAny(YangStmtMapping.MUST)
+        .addOptional(YangStmtMapping.ORDERED_BY)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addAny(YangStmtMapping.TYPEDEF)
+        .addAny(YangStmtMapping.UNIQUE)
+        .addAny(YangStmtMapping.USES)
+        .addOptional(YangStmtMapping.WHEN)
+        .build();
+    private static final ListStatementRFC6020Support INSTANCE = new ListStatementRFC6020Support();
+
+    private ListStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static ListStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/list/ListStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..76d612c
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.list;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 List statement.
+ */
+@Beta
+public final class ListStatementRFC7950Support extends AbstractListStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .LIST)
+            .addAny(YangStmtMapping.ACTION)
+            .addAny(YangStmtMapping.ANYDATA)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.CHOICE)
+            .addOptional(YangStmtMapping.CONFIG)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.GROUPING)
+            .addAny(YangStmtMapping.IF_FEATURE)
+            .addOptional(YangStmtMapping.KEY)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addOptional(YangStmtMapping.MAX_ELEMENTS)
+            .addOptional(YangStmtMapping.MIN_ELEMENTS)
+            .addAny(YangStmtMapping.MUST)
+            .addAny(YangStmtMapping.NOTIFICATION)
+            .addOptional(YangStmtMapping.ORDERED_BY)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.STATUS)
+            .addAny(YangStmtMapping.TYPEDEF)
+            .addAny(YangStmtMapping.UNIQUE)
+            .addAny(YangStmtMapping.USES)
+            .addOptional(YangStmtMapping.WHEN)
+            .build();
+    private static final ListStatementRFC7950Support INSTANCE = new ListStatementRFC7950Support();
+
+    private ListStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static ListStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/EmptyMandatoryEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/EmptyMandatoryEffectiveStatement.java
new file mode 100644 (file)
index 0000000..448c96c
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.mandatory;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
+
+abstract class EmptyMandatoryEffectiveStatement implements MandatoryEffectiveStatement {
+    static final EmptyMandatoryEffectiveStatement FALSE = new EmptyMandatoryEffectiveStatement() {
+        @Override
+        public MandatoryStatement getDeclared() {
+            return EmptyMandatoryStatement.FALSE;
+        }
+    };
+
+    static final EmptyMandatoryEffectiveStatement TRUE = new EmptyMandatoryEffectiveStatement() {
+        @Override
+        public MandatoryStatement getDeclared() {
+            return EmptyMandatoryStatement.TRUE;
+        }
+    };
+
+    @Override
+    public final StatementDefinition statementDefinition() {
+        return getDeclared().statementDefinition();
+    }
+
+    @Override
+    public final Boolean argument() {
+        return getDeclared().argument();
+    }
+
+    @Override
+    public final StatementSource getStatementSource() {
+        return getDeclared().getStatementSource();
+    }
+
+    @Override
+    public final <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends V> get(final Class<N> namespace,
+            final K identifier) {
+        throw new UnsupportedOperationException("Not implemented yet.");
+    }
+
+    @Override
+    public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAll(final Class<N> namespace) {
+        throw new UnsupportedOperationException("Not implemented yet.");
+    }
+
+    @Override
+    public final Collection<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public abstract @NonNull MandatoryStatement getDeclared();
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/EmptyMandatoryStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/EmptyMandatoryStatement.java
new file mode 100644 (file)
index 0000000..ae7915a
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.mandatory;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
+
+abstract class EmptyMandatoryStatement implements MandatoryStatement {
+    static final @NonNull MandatoryStatement FALSE = new EmptyMandatoryStatement() {
+        @Override
+        public Boolean argument() {
+            return Boolean.FALSE;
+        }
+
+        @Override
+        EffectiveStatement<Boolean, MandatoryStatement> toEffective() {
+            return EmptyMandatoryEffectiveStatement.FALSE;
+        }
+    };
+
+    static final @NonNull MandatoryStatement TRUE = new EmptyMandatoryStatement() {
+        @Override
+        public Boolean argument() {
+            return Boolean.TRUE;
+        }
+
+        @Override
+        EffectiveStatement<Boolean, MandatoryStatement> toEffective() {
+            return EmptyMandatoryEffectiveStatement.TRUE;
+        }
+    };
+
+    abstract EffectiveStatement<Boolean, MandatoryStatement> toEffective();
+
+    @Override
+    public final Collection<? extends DeclaredStatement<?>> declaredSubstatements() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public final StatementDefinition statementDefinition() {
+        return YangStmtMapping.MANDATORY;
+    }
+
+    @Override
+    public final String rawArgument() {
+        return getValue().toString();
+    }
+
+    @Override
+    public final StatementSource getStatementSource() {
+        return StatementSource.DECLARATION;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(statementDefinition(), getStatementSource(), argument(),
+            rawArgument(), declaredSubstatements(), getValue());
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof MandatoryStatement)) {
+            return false;
+        }
+
+        final MandatoryStatement other = (MandatoryStatement) obj;
+        return argument().equals(other.argument())
+                && rawArgument().equals(other.rawArgument())
+                && getValue().equals(other.getValue())
+                && statementDefinition().equals(other.statementDefinition())
+                && getStatementSource().equals(other.getStatementSource())
+                && declaredSubstatements().equals(other.declaredSubstatements());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/MandatoryEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/MandatoryEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..428a0c0
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.mandatory;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class MandatoryEffectiveStatementImpl extends DeclaredEffectiveStatementBase<Boolean, MandatoryStatement>
+    implements MandatoryEffectiveStatement {
+    MandatoryEffectiveStatementImpl(final StmtContext<Boolean, MandatoryStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/MandatoryStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/MandatoryStatementImpl.java
new file mode 100644 (file)
index 0000000..68be40f
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.mandatory;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class MandatoryStatementImpl extends AbstractDeclaredStatement<Boolean> implements MandatoryStatement {
+    MandatoryStatementImpl(final StmtContext<Boolean, MandatoryStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/MandatoryStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/mandatory/MandatoryStatementSupport.java
new file mode 100644 (file)
index 0000000..5564ff3
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.mandatory;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class MandatoryStatementSupport extends
+        AbstractStatementSupport<Boolean, MandatoryStatement, EffectiveStatement<Boolean, MandatoryStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.MANDATORY).build();
+    private static final MandatoryStatementSupport INSTANCE = new MandatoryStatementSupport();
+
+    private MandatoryStatementSupport() {
+        super(YangStmtMapping.MANDATORY);
+    }
+
+    public static MandatoryStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Boolean parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return ArgumentUtils.parseBoolean(ctx, value);
+    }
+
+    @Override
+    public MandatoryStatement createDeclared(final StmtContext<Boolean, MandatoryStatement, ?> ctx) {
+        final MandatoryStatement ret = new MandatoryStatementImpl(ctx);
+        if (EmptyMandatoryStatement.FALSE.equals(ret)) {
+            return EmptyMandatoryStatement.FALSE;
+        } else if (EmptyMandatoryStatement.TRUE.equals(ret)) {
+            return EmptyMandatoryStatement.TRUE;
+        } else {
+            return ret;
+        }
+    }
+
+    @Override
+    public EffectiveStatement<Boolean, MandatoryStatement> createEffective(
+            final StmtContext<Boolean, MandatoryStatement, EffectiveStatement<Boolean, MandatoryStatement>> ctx) {
+        final EffectiveStatement<Boolean, MandatoryStatement> ret = new MandatoryEffectiveStatementImpl(ctx);
+        final MandatoryStatement declared = ret.getDeclared();
+        if (declared instanceof EmptyMandatoryStatement && ret.effectiveSubstatements().isEmpty()) {
+            return ((EmptyMandatoryStatement)declared).toEffective();
+        }
+        return ret;
+    }
+
+    @Override
+    public String internArgument(final String rawArgument) {
+        return ArgumentUtils.internBoolean(rawArgument);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/max_elements/MaxElementsEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/max_elements/MaxElementsEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..00a5f53
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.max_elements;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.MaxElementsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MaxElementsStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class MaxElementsEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, MaxElementsStatement>
+        implements MaxElementsEffectiveStatement {
+    MaxElementsEffectiveStatementImpl(final StmtContext<String, MaxElementsStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/max_elements/MaxElementsStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/max_elements/MaxElementsStatementImpl.java
new file mode 100644 (file)
index 0000000..59077dc
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.max_elements;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.MaxElementsStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class MaxElementsStatementImpl extends AbstractDeclaredStatement<String> implements MaxElementsStatement {
+    MaxElementsStatementImpl(final StmtContext<String, MaxElementsStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/max_elements/MaxElementsStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/max_elements/MaxElementsStatementSupport.java
new file mode 100644 (file)
index 0000000..0da052f
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.max_elements;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MaxElementsStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class MaxElementsStatementSupport extends
+        AbstractStatementSupport<String, MaxElementsStatement, EffectiveStatement<String, MaxElementsStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.MAX_ELEMENTS)
+        .build();
+    private static final MaxElementsStatementSupport INSTANCE = new MaxElementsStatementSupport();
+
+    private MaxElementsStatementSupport() {
+        super(YangStmtMapping.MAX_ELEMENTS);
+    }
+
+    public static MaxElementsStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public MaxElementsStatement createDeclared(
+            final StmtContext<String, MaxElementsStatement, ?> ctx) {
+        return new MaxElementsStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, MaxElementsStatement> createEffective(
+            final StmtContext<String, MaxElementsStatement, EffectiveStatement<String, MaxElementsStatement>> ctx) {
+        return new MaxElementsEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+
+    @Override
+    public String internArgument(final String rawArgument) {
+        return "unbounded".equals(rawArgument) ? "unbounded" : rawArgument;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/min_elements/MinElementsEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/min_elements/MinElementsEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..f2b5153
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.min_elements;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class MinElementsEffectiveStatementImpl extends DeclaredEffectiveStatementBase<Integer, MinElementsStatement>
+        implements MinElementsEffectiveStatement {
+    MinElementsEffectiveStatementImpl(final StmtContext<Integer, MinElementsStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/min_elements/MinElementsStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/min_elements/MinElementsStatementImpl.java
new file mode 100644 (file)
index 0000000..cfbb2dc
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.min_elements;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class MinElementsStatementImpl extends AbstractDeclaredStatement<Integer> implements MinElementsStatement {
+    MinElementsStatementImpl(final StmtContext<Integer, MinElementsStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/min_elements/MinElementsStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/min_elements/MinElementsStatementSupport.java
new file mode 100644 (file)
index 0000000..8a913b6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.min_elements;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class MinElementsStatementSupport extends
+        AbstractStatementSupport<Integer, MinElementsStatement, EffectiveStatement<Integer, MinElementsStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.MIN_ELEMENTS)
+        .build();
+    private static final MinElementsStatementSupport INSTANCE = new MinElementsStatementSupport();
+
+    private MinElementsStatementSupport() {
+        super(YangStmtMapping.MIN_ELEMENTS);
+    }
+
+    public static MinElementsStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Integer parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return Integer.parseInt(value);
+    }
+
+    @Override
+    public MinElementsStatement createDeclared(
+            final StmtContext<Integer, MinElementsStatement, ?> ctx) {
+        return new MinElementsStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<Integer, MinElementsStatement> createEffective(
+            final StmtContext<Integer, MinElementsStatement,
+            EffectiveStatement<Integer, MinElementsStatement>> ctx) {
+        return new MinElementsEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/modifier/ModifierEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/modifier/ModifierEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..b6f4200
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.modifier;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModifierEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModifierStatement;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * YANG 1.1 Modifier effective statement implementation.
+ */
+@Beta
+final class ModifierEffectiveStatementImpl extends DeclaredEffectiveStatementBase<ModifierKind, ModifierStatement>
+        implements ModifierEffectiveStatement {
+    ModifierEffectiveStatementImpl(final StmtContext<ModifierKind, ModifierStatement, ?> ctx) {
+        super(ctx);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("argument", argument()).toString();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/modifier/ModifierStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/modifier/ModifierStatementImpl.java
new file mode 100644 (file)
index 0000000..99e381a
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.modifier;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModifierStatement;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Modifier statement.
+ */
+@Beta
+final class ModifierStatementImpl extends AbstractDeclaredStatement<ModifierKind> implements ModifierStatement {
+    ModifierStatementImpl(final StmtContext<ModifierKind, ModifierStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/modifier/ModifierStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/modifier/ModifierStatementSupport.java
new file mode 100644 (file)
index 0000000..ff3a9bd
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.modifier;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModifierStatement;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class ModifierStatementSupport extends AbstractStatementSupport<ModifierKind, ModifierStatement,
+        EffectiveStatement<ModifierKind, ModifierStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.MODIFIER).build();
+    private static final ModifierStatementSupport INSTANCE = new ModifierStatementSupport();
+
+    private ModifierStatementSupport() {
+        super(YangStmtMapping.MODIFIER);
+    }
+
+    public static ModifierStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public ModifierKind parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return SourceException.unwrap(ModifierKind.parse(value), ctx.getStatementSourceReference(),
+            "'%s' is not valid argument of modifier statement", value);
+    }
+
+    @Override
+    public ModifierStatement createDeclared(final StmtContext<ModifierKind, ModifierStatement, ?> ctx) {
+        return new ModifierStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<ModifierKind, ModifierStatement> createEffective(
+            final StmtContext<ModifierKind, ModifierStatement,
+            EffectiveStatement<ModifierKind, ModifierStatement>> ctx) {
+        return new ModifierEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+
+    @Override
+    public String internArgument(final String rawArgument) {
+        return "invert-match".equals(rawArgument) ? "invert-match" : rawArgument;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/AbstractModuleStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/AbstractModuleStatementSupport.java
new file mode 100644 (file)
index 0000000..09d0ce8
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.module;
+
+import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
+
+import java.net.URI;
+import java.util.Optional;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.NamespaceToModule;
+import org.opendaylight.yangtools.yang.parser.spi.PreLinkageModuleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionModuleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToSourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNamespaceForBelongsTo;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleQNameToModuleName;
+import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+abstract class AbstractModuleStatementSupport
+        extends AbstractStatementSupport<String, ModuleStatement, ModuleEffectiveStatement> {
+    AbstractModuleStatementSupport() {
+        super(YangStmtMapping.MODULE);
+    }
+
+    @Override
+    public final String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public final ModuleStatement createDeclared(final StmtContext<String, ModuleStatement, ?> ctx) {
+        return new ModuleStatementImpl(ctx);
+    }
+
+    @Override
+    public final ModuleEffectiveStatement createEffective(
+            final StmtContext<String, ModuleStatement, ModuleEffectiveStatement> ctx) {
+        return new ModuleEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public final void onPreLinkageDeclared(final Mutable<String, ModuleStatement, ModuleEffectiveStatement> stmt) {
+        final String moduleName = stmt.getStatementArgument();
+
+        final URI moduleNs = firstAttributeOf(stmt.declaredSubstatements(), NamespaceStatement.class);
+        SourceException.throwIfNull(moduleNs, stmt.getStatementSourceReference(),
+            "Namespace of the module [%s] is missing", stmt.getStatementArgument());
+        stmt.addToNs(ModuleNameToNamespace.class, moduleName, moduleNs);
+
+        final String modulePrefix = firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class);
+        SourceException.throwIfNull(modulePrefix, stmt.getStatementSourceReference(),
+            "Prefix of the module [%s] is missing", stmt.getStatementArgument());
+        stmt.addToNs(ImpPrefixToNamespace.class, modulePrefix, moduleNs);
+
+        stmt.addContext(PreLinkageModuleNamespace.class, moduleName, stmt);
+
+        final Optional<Revision> revisionDate = StmtContextUtils.getLatestRevision(stmt.declaredSubstatements());
+        final QNameModule qNameModule = QNameModule.create(moduleNs, revisionDate.orElse(null)).intern();
+
+        stmt.addToNs(ModuleCtxToModuleQName.class, stmt, qNameModule);
+        stmt.setRootIdentifier(RevisionSourceIdentifier.create(stmt.getStatementArgument(), revisionDate));
+    }
+
+    @Override
+    public final void onLinkageDeclared(final Mutable<String, ModuleStatement, ModuleEffectiveStatement> stmt) {
+
+        final Optional<URI> moduleNs = Optional.ofNullable(firstAttributeOf(stmt.declaredSubstatements(),
+                NamespaceStatement.class));
+        SourceException.throwIf(!moduleNs.isPresent(), stmt.getStatementSourceReference(),
+            "Namespace of the module [%s] is missing", stmt.getStatementArgument());
+
+        final Optional<Revision> revisionDate = StmtContextUtils.getLatestRevision(stmt.declaredSubstatements());
+        final QNameModule qNameModule = QNameModule.create(moduleNs.get(), revisionDate.orElse(null)).intern();
+        final StmtContext<?, ModuleStatement, ModuleEffectiveStatement> possibleDuplicateModule =
+                stmt.getFromNamespace(NamespaceToModule.class, qNameModule);
+        if (possibleDuplicateModule != null && possibleDuplicateModule != stmt) {
+            throw new SourceException(stmt.getStatementSourceReference(), "Module namespace collision: %s. At %s",
+                    qNameModule.getNamespace(), possibleDuplicateModule.getStatementSourceReference());
+        }
+
+        final SourceIdentifier moduleIdentifier = RevisionSourceIdentifier.create(stmt.getStatementArgument(),
+                revisionDate);
+
+        stmt.addContext(ModuleNamespace.class, moduleIdentifier, stmt);
+        stmt.addContext(ModuleNamespaceForBelongsTo.class, moduleIdentifier.getName(), stmt);
+        stmt.addContext(NamespaceToModule.class, qNameModule, stmt);
+
+        final String modulePrefix = firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class);
+        SourceException.throwIfNull(modulePrefix, stmt.getStatementSourceReference(),
+            "Prefix of the module [%s] is missing", stmt.getStatementArgument());
+
+        stmt.addToNs(PrefixToModule.class, modulePrefix, qNameModule);
+        stmt.addToNs(ModuleNameToModuleQName.class, stmt.getStatementArgument(), qNameModule);
+        stmt.addToNs(ModuleCtxToModuleQName.class, stmt, qNameModule);
+        stmt.addToNs(ModuleCtxToSourceIdentifier.class, stmt, moduleIdentifier);
+        stmt.addToNs(ModuleQNameToModuleName.class, qNameModule, stmt.getStatementArgument());
+        stmt.addToNs(ImportPrefixToModuleCtx.class, modulePrefix, stmt);
+
+        if (stmt.isEnabledSemanticVersioning()) {
+            addToSemVerModuleNamespace(stmt, moduleIdentifier);
+        }
+    }
+
+    private static void addToSemVerModuleNamespace(
+            final Mutable<String, ModuleStatement, ModuleEffectiveStatement> stmt,
+            final SourceIdentifier moduleIdentifier) {
+        final String moduleName = stmt.coerceStatementArgument();
+        final SemVer moduleSemVer = stmt.getFromNamespace(SemanticVersionNamespace.class, stmt);
+        final SemVerSourceIdentifier id = SemVerSourceIdentifier.create(moduleName, moduleSemVer);
+        stmt.addToNs(SemanticVersionModuleNamespace.class, id, stmt);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/ModuleEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/ModuleEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..eb0810d
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.module;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.Maps;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionEffectiveStatementNamespace;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureEffectiveStatementNamespace;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatementNamespace;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ImportEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveModule;
+import org.opendaylight.yangtools.yang.parser.spi.ExtensionNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.FeatureNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.IdentityNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.IncludedSubmoduleNameToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
+
+final class ModuleEffectiveStatementImpl extends AbstractEffectiveModule<ModuleStatement>
+        implements ModuleEffectiveStatement {
+    private final ImmutableMap<String, SubmoduleEffectiveStatement> nameToSubmodule;
+    private final ImmutableMap<QName, ExtensionEffectiveStatement> qnameToExtension;
+    private final ImmutableMap<QName, FeatureEffectiveStatement> qnameToFeature;
+    private final ImmutableMap<QName, IdentityEffectiveStatement> qnameToIdentity;
+    private final ImmutableMap<String, ModuleEffectiveStatement> prefixToModule;
+    private final ImmutableMap<QNameModule, String> namespaceToPrefix;
+    private final @NonNull QNameModule qnameModule;
+
+    ModuleEffectiveStatementImpl(final StmtContext<String, ModuleStatement, ModuleEffectiveStatement> ctx) {
+        super(ctx);
+        qnameModule = verifyNotNull(ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx));
+
+        final String localPrefix = findFirstEffectiveSubstatementArgument(PrefixEffectiveStatement.class).get();
+        final Builder<String, ModuleEffectiveStatement> prefixToModuleBuilder = ImmutableMap.builder();
+        prefixToModuleBuilder.put(localPrefix, this);
+
+        streamEffectiveSubstatements(ImportEffectiveStatement.class)
+                .map(imp -> imp.findFirstEffectiveSubstatementArgument(PrefixEffectiveStatement.class).get())
+                .forEach(prefix -> {
+                    final StmtContext<?, ?, ?> importedCtx =
+                            verifyNotNull(ctx.getFromNamespace(ImportPrefixToModuleCtx.class, prefix),
+                                "Failed to resolve prefix %s", prefix);
+                    prefixToModuleBuilder.put(prefix, (ModuleEffectiveStatement) importedCtx.buildEffective());
+                });
+        prefixToModule = prefixToModuleBuilder.build();
+
+        final Map<QNameModule, String> tmp = Maps.newLinkedHashMapWithExpectedSize(prefixToModule.size() + 1);
+        tmp.put(qnameModule, localPrefix);
+        for (Entry<String, ModuleEffectiveStatement> e : prefixToModule.entrySet()) {
+            tmp.putIfAbsent(e.getValue().localQNameModule(), e.getKey());
+        }
+        namespaceToPrefix = ImmutableMap.copyOf(tmp);
+
+        final Map<String, StmtContext<?, ?, ?>> submodules =
+                ctx.getAllFromCurrentStmtCtxNamespace(IncludedSubmoduleNameToModuleCtx.class);
+        nameToSubmodule = submodules == null ? ImmutableMap.of()
+                : ImmutableMap.copyOf(Maps.transformValues(submodules,
+                    submodule -> (SubmoduleEffectiveStatement) submodule.buildEffective()));
+
+        final Map<QName, StmtContext<?, ExtensionStatement, ExtensionEffectiveStatement>> extensions =
+                ctx.getAllFromCurrentStmtCtxNamespace(ExtensionNamespace.class);
+        qnameToExtension = extensions == null ? ImmutableMap.of()
+                : ImmutableMap.copyOf(Maps.transformValues(extensions, StmtContext::buildEffective));
+        final Map<QName, StmtContext<?, FeatureStatement, FeatureEffectiveStatement>> features =
+                ctx.getAllFromCurrentStmtCtxNamespace(FeatureNamespace.class);
+        qnameToFeature = features == null ? ImmutableMap.of()
+                : ImmutableMap.copyOf(Maps.transformValues(features, StmtContext::buildEffective));
+        final Map<QName, StmtContext<?, IdentityStatement, IdentityEffectiveStatement>> identities =
+                ctx.getAllFromCurrentStmtCtxNamespace(IdentityNamespace.class);
+        qnameToIdentity = identities == null ? ImmutableMap.of()
+                : ImmutableMap.copyOf(Maps.transformValues(identities, StmtContext::buildEffective));
+    }
+
+    @Override
+    public @NonNull QNameModule localQNameModule() {
+        return qnameModule;
+    }
+
+    @Override
+    public @NonNull QNameModule getQNameModule() {
+        return qnameModule;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
+            final @NonNull Class<N> namespace) {
+        if (PrefixToEffectiveModuleNamespace.class.equals(namespace)) {
+            return Optional.of((Map<K, V>) prefixToModule);
+        }
+        if (QNameModuleToPrefixNamespace.class.equals(namespace)) {
+            return Optional.of((Map<K, V>) namespaceToPrefix);
+        }
+        if (NameToEffectiveSubmoduleNamespace.class.equals(namespace)) {
+            return Optional.of((Map<K, V>) nameToSubmodule);
+        }
+        if (ExtensionEffectiveStatementNamespace.class.equals(namespace)) {
+            return Optional.of((Map<K, V>) qnameToExtension);
+        }
+        if (FeatureEffectiveStatementNamespace.class.equals(namespace)) {
+            return Optional.of((Map<K, V>) qnameToFeature);
+        }
+        if (IdentityEffectiveStatementNamespace.class.equals(namespace)) {
+            return Optional.of((Map<K, V>) qnameToIdentity);
+        }
+        return super.getNamespaceContents(namespace);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getName(), getYangVersion(), qnameModule);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof ModuleEffectiveStatementImpl)) {
+            return false;
+        }
+        ModuleEffectiveStatementImpl other = (ModuleEffectiveStatementImpl) obj;
+        return Objects.equals(getName(), other.getName()) && qnameModule.equals(other.qnameModule)
+                && Objects.equals(getYangVersion(), other.getYangVersion());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/ModuleStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/ModuleStatementImpl.java
new file mode 100644 (file)
index 0000000..e358e6b
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.module;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractRootStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ModuleStatementImpl extends AbstractRootStatement<ModuleStatement> implements ModuleStatement {
+    ModuleStatementImpl(final StmtContext<String, ModuleStatement,?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/ModuleStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/ModuleStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..d616e5c
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.module;
+
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ModuleStatementRFC6020Support extends AbstractModuleStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .MODULE)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.AUGMENT)
+            .addAny(YangStmtMapping.CHOICE)
+            .addOptional(YangStmtMapping.CONTACT)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.DEVIATION)
+            .addAny(YangStmtMapping.EXTENSION)
+            .addAny(YangStmtMapping.FEATURE)
+            .addAny(YangStmtMapping.GROUPING)
+            .addAny(YangStmtMapping.IDENTITY)
+            .addAny(YangStmtMapping.IMPORT)
+            .addAny(YangStmtMapping.INCLUDE)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addMandatory(YangStmtMapping.NAMESPACE)
+            .addAny(YangStmtMapping.NOTIFICATION)
+            .addOptional(YangStmtMapping.ORGANIZATION)
+            .addMandatory(YangStmtMapping.PREFIX)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addAny(YangStmtMapping.REVISION)
+            .addAny(YangStmtMapping.RPC)
+            .addAny(YangStmtMapping.TYPEDEF)
+            .addAny(YangStmtMapping.USES)
+            .addOptional(YangStmtMapping.YANG_VERSION)
+            .addOptional(OpenConfigStatements.OPENCONFIG_VERSION)
+            .build();
+    private static final ModuleStatementRFC6020Support INSTANCE = new ModuleStatementRFC6020Support();
+
+    private ModuleStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static ModuleStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/ModuleStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/module/ModuleStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..80f77cc
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.module;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+@Beta
+public final class ModuleStatementRFC7950Support extends AbstractModuleStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .MODULE)
+            .addAny(YangStmtMapping.ANYDATA)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.AUGMENT)
+            .addAny(YangStmtMapping.CHOICE)
+            .addOptional(YangStmtMapping.CONTACT)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.DEVIATION)
+            .addAny(YangStmtMapping.EXTENSION)
+            .addAny(YangStmtMapping.FEATURE)
+            .addAny(YangStmtMapping.GROUPING)
+            .addAny(YangStmtMapping.IDENTITY)
+            .addAny(YangStmtMapping.IMPORT)
+            .addAny(YangStmtMapping.INCLUDE)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addMandatory(YangStmtMapping.NAMESPACE)
+            .addAny(YangStmtMapping.NOTIFICATION)
+            .addOptional(YangStmtMapping.ORGANIZATION)
+            .addMandatory(YangStmtMapping.PREFIX)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addAny(YangStmtMapping.REVISION)
+            .addAny(YangStmtMapping.RPC)
+            .addAny(YangStmtMapping.TYPEDEF)
+            .addAny(YangStmtMapping.USES)
+            .addMandatory(YangStmtMapping.YANG_VERSION)
+            .addOptional(OpenConfigStatements.OPENCONFIG_VERSION)
+            .build();
+    private static final ModuleStatementRFC7950Support INSTANCE = new ModuleStatementRFC7950Support();
+
+    private ModuleStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static ModuleStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/must/MustEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/must/MustEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..d5fdbe8
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.must;
+
+import java.util.Objects;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorAppTagEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ErrorMessageEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MustEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MustStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class MustEffectiveStatementImpl extends DeclaredEffectiveStatementBase<RevisionAwareXPath, MustStatement>
+        implements MustDefinition, MustEffectiveStatement {
+
+    private final RevisionAwareXPath xpath;
+    private final String description;
+    private final String errorAppTag;
+    private final String errorMessage;
+    private final String reference;
+
+    MustEffectiveStatementImpl(final StmtContext<RevisionAwareXPath, MustStatement, ?> ctx) {
+        super(ctx);
+        xpath = ctx.getStatementArgument();
+        description = findFirstEffectiveSubstatementArgument(DescriptionEffectiveStatement.class).orElse(null);
+        errorAppTag = findFirstEffectiveSubstatementArgument(ErrorAppTagEffectiveStatement.class).orElse(null);
+        errorMessage = findFirstEffectiveSubstatementArgument(ErrorMessageEffectiveStatement.class).orElse(null);
+        reference = findFirstEffectiveSubstatementArgument(ReferenceEffectiveStatement.class).orElse(null);
+    }
+
+    @Override
+    public RevisionAwareXPath getXpath() {
+        return xpath;
+    }
+
+    @Override
+    public Optional<String> getDescription() {
+        return Optional.ofNullable(description);
+    }
+
+    @Override
+    public Optional<String> getErrorAppTag() {
+        return Optional.ofNullable(errorAppTag);
+    }
+
+    @Override
+    public Optional<String> getErrorMessage() {
+        return Optional.ofNullable(errorMessage);
+    }
+
+    @Override
+    public Optional<String> getReference() {
+        return Optional.ofNullable(reference);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(xpath, description, reference);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof MustEffectiveStatementImpl)) {
+            return false;
+        }
+        final MustEffectiveStatementImpl other = (MustEffectiveStatementImpl) obj;
+        return Objects.equals(xpath, other.xpath) && Objects.equals(description, other.description)
+                && Objects.equals(reference, other.reference);
+    }
+
+    @Override
+    public String toString() {
+        return xpath.getOriginalString();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/must/MustStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/must/MustStatementImpl.java
new file mode 100644 (file)
index 0000000..31c6e3c
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.must;
+
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.MustStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class MustStatementImpl extends AbstractDeclaredStatement<RevisionAwareXPath> implements MustStatement {
+    MustStatementImpl(final StmtContext<RevisionAwareXPath, MustStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/must/MustStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/must/MustStatementSupport.java
new file mode 100644 (file)
index 0000000..ab48a17
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.must;
+
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MustStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class MustStatementSupport extends AbstractStatementSupport<RevisionAwareXPath, MustStatement,
+        EffectiveStatement<RevisionAwareXPath, MustStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .MUST)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addOptional(YangStmtMapping.ERROR_APP_TAG)
+        .addOptional(YangStmtMapping.ERROR_MESSAGE)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .build();
+    private static final MustStatementSupport INSTANCE = new MustStatementSupport();
+
+    private MustStatementSupport() {
+        super(YangStmtMapping.MUST);
+    }
+
+    public static MustStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public RevisionAwareXPath parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return ArgumentUtils.parseXPath(ctx, value);
+    }
+
+    @Override
+    public MustStatement createDeclared(final StmtContext<RevisionAwareXPath, MustStatement, ?> ctx) {
+        return new MustStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<RevisionAwareXPath, MustStatement> createEffective(
+            final StmtContext<RevisionAwareXPath, MustStatement,
+            EffectiveStatement<RevisionAwareXPath, MustStatement>> ctx) {
+        return new MustEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/namespace/NamespaceEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/namespace/NamespaceEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..de89394
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.namespace;
+
+import java.net.URI;
+import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class NamespaceEffectiveStatementImpl extends DeclaredEffectiveStatementBase<URI, NamespaceStatement>
+        implements NamespaceEffectiveStatement {
+    NamespaceEffectiveStatementImpl(final StmtContext<URI, NamespaceStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/namespace/NamespaceStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/namespace/NamespaceStatementImpl.java
new file mode 100644 (file)
index 0000000..414213e
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.namespace;
+
+import java.net.URI;
+import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class NamespaceStatementImpl extends AbstractDeclaredStatement<URI> implements NamespaceStatement {
+    NamespaceStatementImpl(final StmtContext<URI, NamespaceStatement,?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/namespace/NamespaceStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/namespace/NamespaceStatementSupport.java
new file mode 100644 (file)
index 0000000..6ff02e6
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.namespace;
+
+import java.net.URI;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class NamespaceStatementSupport extends AbstractStatementSupport<URI, NamespaceStatement,
+        EffectiveStatement<URI, NamespaceStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .NAMESPACE)
+        .build();
+    private static final NamespaceStatementSupport INSTANCE = new NamespaceStatementSupport();
+
+    private NamespaceStatementSupport() {
+        super(YangStmtMapping.NAMESPACE);
+    }
+
+    public static NamespaceStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public URI parseArgumentValue(final StmtContext<?, ?,?> ctx, final String value) {
+        return URI.create(value);
+    }
+
+    @Override
+    public NamespaceStatement createDeclared(final StmtContext<URI, NamespaceStatement,?> ctx) {
+        return new NamespaceStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<URI,NamespaceStatement> createEffective(
+            final StmtContext<URI, NamespaceStatement, EffectiveStatement<URI, NamespaceStatement>> ctx) {
+        return new NamespaceEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/AbstractNotificationStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/AbstractNotificationStatementSupport.java
new file mode 100644 (file)
index 0000000..e11a152
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.notification;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NotificationStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+abstract class AbstractNotificationStatementSupport
+        extends AbstractQNameStatementSupport<NotificationStatement, EffectiveStatement<QName, NotificationStatement>> {
+    AbstractNotificationStatementSupport() {
+        super(YangStmtMapping.NOTIFICATION);
+    }
+
+    @Override
+    public final QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public final void onStatementAdded(
+            final Mutable<QName, NotificationStatement, EffectiveStatement<QName, NotificationStatement>> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public final NotificationStatement createDeclared(final StmtContext<QName, NotificationStatement, ?> ctx) {
+        return new NotificationStatementImpl(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/NotificationEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/NotificationEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..687c693
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.notification;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NotificationStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDocumentedDataNodeContainer;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class NotificationEffectiveStatementImpl
+        extends AbstractEffectiveDocumentedDataNodeContainer<QName, NotificationStatement>
+        implements NotificationDefinition, NotificationEffectiveStatement {
+    private final @NonNull QName qname;
+    private final @NonNull SchemaPath path;
+    private final ImmutableSet<AugmentationSchemaNode> augmentations;
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
+    private final boolean augmenting;
+    private final boolean addedByUses;
+    private final ImmutableSet<MustDefinition> mustConstraints;
+
+    NotificationEffectiveStatementImpl(
+            final StmtContext<QName, NotificationStatement, EffectiveStatement<QName, NotificationStatement>> ctx) {
+        super(ctx);
+        this.qname = ctx.coerceStatementArgument();
+        this.path = ctx.getSchemaPath().get();
+
+        // initSubstatementCollections
+        final List<UnknownSchemaNode> unknownNodesInit = new ArrayList<>();
+        final Set<AugmentationSchemaNode> augmentationsInit = new LinkedHashSet<>();
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof UnknownSchemaNode) {
+                final UnknownSchemaNode unknownNode = (UnknownSchemaNode) effectiveStatement;
+                unknownNodesInit.add(unknownNode);
+            }
+            if (effectiveStatement instanceof AugmentationSchemaNode) {
+                final AugmentationSchemaNode augmentationSchema = (AugmentationSchemaNode) effectiveStatement;
+                augmentationsInit.add(augmentationSchema);
+            }
+        }
+        this.unknownNodes = ImmutableList.copyOf(unknownNodesInit);
+        this.augmentations = ImmutableSet.copyOf(augmentationsInit);
+        this.mustConstraints = ImmutableSet.copyOf(this.allSubstatementsOfType(MustDefinition.class));
+
+        // initCopyType
+        final CopyHistory copyTypesFromOriginal = ctx.getCopyHistory();
+        if (copyTypesFromOriginal.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) {
+            this.augmenting = true;
+            this.addedByUses = true;
+        } else {
+            this.augmenting = copyTypesFromOriginal.contains(CopyType.ADDED_BY_AUGMENTATION);
+            this.addedByUses = copyTypesFromOriginal.contains(CopyType.ADDED_BY_USES);
+        }
+    }
+
+    @Override
+    public QName getQName() {
+        return qname;
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return path;
+    }
+
+    @Override
+    public Collection<MustDefinition> getMustConstraints() {
+        return mustConstraints;
+    }
+
+    @Override
+    public Set<AugmentationSchemaNode> getAvailableAugmentations() {
+        return augmentations;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownNodes;
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAugmenting() {
+        return augmenting;
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAddedByUses() {
+        return addedByUses;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(qname);
+        result = prime * result + Objects.hashCode(path);
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final NotificationEffectiveStatementImpl other = (NotificationEffectiveStatementImpl) obj;
+        return Objects.equals(qname, other.qname) && Objects.equals(path, other.path);
+    }
+
+    @Override
+    public String toString() {
+        return NotificationEffectiveStatementImpl.class.getSimpleName() + "[qname=" + qname + ", path=" + path + "]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/NotificationStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/NotificationStatementImpl.java
new file mode 100644 (file)
index 0000000..c25a595
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.notification;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.NotificationStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class NotificationStatementImpl extends AbstractDeclaredStatement<QName> implements NotificationStatement {
+    NotificationStatementImpl(final StmtContext<QName, NotificationStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/NotificationStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/NotificationStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..b8f3eeb
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.notification;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NotificationStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class NotificationStatementRFC6020Support extends AbstractNotificationStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.NOTIFICATION)
+        .addAny(YangStmtMapping.ANYXML)
+        .addAny(YangStmtMapping.CHOICE)
+        .addAny(YangStmtMapping.CONTAINER)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.GROUPING)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addAny(YangStmtMapping.LEAF)
+        .addAny(YangStmtMapping.LEAF_LIST)
+        .addAny(YangStmtMapping.LIST)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addAny(YangStmtMapping.TYPEDEF)
+        .addAny(YangStmtMapping.USES)
+        .build();
+    private static final NotificationStatementRFC6020Support INSTANCE = new NotificationStatementRFC6020Support();
+
+    private NotificationStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static NotificationStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public EffectiveStatement<QName, NotificationStatement> createEffective(
+            final StmtContext<QName, NotificationStatement, EffectiveStatement<QName, NotificationStatement>> ctx) {
+        return new NotificationEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/NotificationStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/notification/NotificationStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..6125160
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.notification;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.NotificationStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Notification
+ * statement.
+ */
+@Beta
+public final class NotificationStatementRFC7950Support extends AbstractNotificationStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .NOTIFICATION)
+            .addAny(YangStmtMapping.ANYDATA)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.CHOICE)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.GROUPING)
+            .addAny(YangStmtMapping.IF_FEATURE)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addAny(YangStmtMapping.MUST)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.STATUS)
+            .addAny(YangStmtMapping.TYPEDEF)
+            .addAny(YangStmtMapping.USES)
+            .build();
+
+    private static final ImmutableSet<StatementDefinition> ILLEGAL_PARENTS = ImmutableSet.of(
+            YangStmtMapping.NOTIFICATION, YangStmtMapping.RPC, YangStmtMapping.ACTION);
+    private static final NotificationStatementRFC7950Support INSTANCE = new NotificationStatementRFC7950Support();
+
+    private NotificationStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static NotificationStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public EffectiveStatement<QName, NotificationStatement> createEffective(
+            final StmtContext<QName, NotificationStatement, EffectiveStatement<QName, NotificationStatement>> ctx) {
+        SourceException.throwIf(StmtContextUtils.hasAncestorOfType(ctx, ILLEGAL_PARENTS),
+            ctx.getStatementSourceReference(),
+            "Notification %s is defined within an rpc, action, or another notification",
+            ctx.getStatementArgument());
+        SourceException.throwIf(!StmtContextUtils.hasAncestorOfTypeWithChildOfType(ctx, YangStmtMapping.LIST,
+            YangStmtMapping.KEY), ctx.getStatementSourceReference(),
+            "Notification %s is defined within a list that has no key statement", ctx.getStatementArgument());
+        SourceException.throwIf(StmtContextUtils.hasParentOfType(ctx, YangStmtMapping.CASE),
+            ctx.getStatementSourceReference(), "Notification %s is defined within a case statement",
+            ctx.getStatementArgument());
+        return new NotificationEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ordered_by/OrderedByEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ordered_by/OrderedByEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..c3fdef5
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.ordered_by;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.OrderedByEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OrderedByStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class OrderedByEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, OrderedByStatement>
+        implements OrderedByEffectiveStatement {
+    OrderedByEffectiveStatementImpl(final StmtContext<String, OrderedByStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ordered_by/OrderedByStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ordered_by/OrderedByStatementImpl.java
new file mode 100644 (file)
index 0000000..266b255
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.ordered_by;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.OrderedByStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class OrderedByStatementImpl extends AbstractDeclaredStatement<String> implements OrderedByStatement {
+    OrderedByStatementImpl(final StmtContext<String, OrderedByStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ordered_by/OrderedByStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ordered_by/OrderedByStatementSupport.java
new file mode 100644 (file)
index 0000000..87513e2
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.ordered_by;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OrderedByStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class OrderedByStatementSupport
+        extends AbstractStatementSupport<String, OrderedByStatement, EffectiveStatement<String, OrderedByStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.ORDERED_BY)
+        .build();
+    private static final OrderedByStatementSupport INSTANCE = new OrderedByStatementSupport();
+
+    private OrderedByStatementSupport() {
+        super(YangStmtMapping.ORDERED_BY);
+    }
+
+    public static OrderedByStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public OrderedByStatement createDeclared(final StmtContext<String, OrderedByStatement, ?> ctx) {
+        return new OrderedByStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, OrderedByStatement> createEffective(
+            final StmtContext<String, OrderedByStatement, EffectiveStatement<String, OrderedByStatement>> ctx) {
+        return new OrderedByEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+
+    @Override
+    public String internArgument(final String rawArgument) {
+        if ("user".equals(rawArgument)) {
+            return "user";
+        } else if ("system".equals(rawArgument)) {
+            return "system";
+        } else {
+            return rawArgument;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/organization/OrganizationEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/organization/OrganizationEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..2029a16
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.organization;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.OrganizationEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OrganizationStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class OrganizationEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, OrganizationStatement>
+        implements OrganizationEffectiveStatement {
+    OrganizationEffectiveStatementImpl(final StmtContext<String, OrganizationStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/organization/OrganizationStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/organization/OrganizationStatementImpl.java
new file mode 100644 (file)
index 0000000..75738c5
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.organization;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.OrganizationStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class OrganizationStatementImpl extends AbstractDeclaredStatement<String> implements OrganizationStatement {
+    OrganizationStatementImpl(final StmtContext<String, OrganizationStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/organization/OrganizationStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/organization/OrganizationStatementSupport.java
new file mode 100644 (file)
index 0000000..400ab7f
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.organization;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OrganizationStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class OrganizationStatementSupport extends
+        AbstractStatementSupport<String, OrganizationStatement, EffectiveStatement<String, OrganizationStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.ORGANIZATION)
+        .build();
+    private static final OrganizationStatementSupport INSTANCE = new OrganizationStatementSupport();
+
+    private OrganizationStatementSupport() {
+        super(YangStmtMapping.ORGANIZATION);
+    }
+
+    public static OrganizationStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public OrganizationStatement createDeclared(
+            final StmtContext<String, OrganizationStatement, ?> ctx) {
+        return new OrganizationStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, OrganizationStatement> createEffective(
+            final StmtContext<String, OrganizationStatement,
+            EffectiveStatement<String, OrganizationStatement>> ctx) {
+        return new OrganizationEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/AbstractOutputStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/AbstractOutputStatementSupport.java
new file mode 100644 (file)
index 0000000..57c1b78
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.output;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OutputStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+abstract class AbstractOutputStatementSupport extends
+        AbstractQNameStatementSupport<OutputStatement, EffectiveStatement<QName, OutputStatement>> {
+    AbstractOutputStatementSupport() {
+        super(YangStmtMapping.OUTPUT);
+    }
+
+    @Override
+    public final QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return YangConstants.operationOutputQName(StmtContextUtils.getRootModuleQName(ctx));
+    }
+
+    @Override
+    public final void onStatementAdded(final Mutable<QName, OutputStatement,
+            EffectiveStatement<QName, OutputStatement>> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public final OutputStatement createDeclared(final StmtContext<QName, OutputStatement, ?> ctx) {
+        return new OutputStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<QName, OutputStatement> createEffective(
+            final StmtContext<QName, OutputStatement, EffectiveStatement<QName, OutputStatement>> ctx) {
+        return new OutputEffectiveStatementImpl(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/OutputEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/OutputEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..2a4cae2
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.output;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OutputStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveOperationContainerSchemaNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class OutputEffectiveStatementImpl extends AbstractEffectiveOperationContainerSchemaNode<OutputStatement>
+        implements OutputEffectiveStatement {
+    OutputEffectiveStatementImpl(
+            final StmtContext<QName, OutputStatement, EffectiveStatement<QName, OutputStatement>> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/OutputStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/OutputStatementImpl.java
new file mode 100644 (file)
index 0000000..b1b8fcc
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.output;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.OutputStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class OutputStatementImpl extends AbstractDeclaredStatement<QName> implements OutputStatement {
+    OutputStatementImpl(final StmtContext<QName, OutputStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/OutputStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/OutputStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..01d9cd7
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.output;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class OutputStatementRFC6020Support extends AbstractOutputStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .OUTPUT)
+        .addAny(YangStmtMapping.ANYXML)
+        .addAny(YangStmtMapping.CHOICE)
+        .addAny(YangStmtMapping.CONTAINER)
+        .addAny(YangStmtMapping.GROUPING)
+        .addAny(YangStmtMapping.LEAF)
+        .addAny(YangStmtMapping.LEAF_LIST)
+        .addAny(YangStmtMapping.LIST)
+        .addAny(YangStmtMapping.TYPEDEF)
+        .addAny(YangStmtMapping.USES)
+        .build();
+    private static final OutputStatementRFC6020Support INSTANCE = new OutputStatementRFC6020Support();
+
+    private OutputStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static OutputStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/OutputStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/output/OutputStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..83a9e72
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.output;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Output statement.
+ */
+@Beta
+public final class OutputStatementRFC7950Support extends AbstractOutputStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .OUTPUT)
+            .addAny(YangStmtMapping.ANYDATA)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.CHOICE)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addAny(YangStmtMapping.GROUPING)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addAny(YangStmtMapping.MUST)
+            .addAny(YangStmtMapping.TYPEDEF)
+            .addAny(YangStmtMapping.USES)
+            .build();
+    private static final OutputStatementRFC7950Support INSTANCE = new OutputStatementRFC7950Support();
+
+    private OutputStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static OutputStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
+
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/package-info.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/package-info.java
new file mode 100644 (file)
index 0000000..5e7209d
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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
+ */
+/**
+ * Statement library for YANG version 1 and 1.1, as defined in RFC6020 and RFC7950. Since YANG 1.1 is built on top
+ * of YANG 1 and our semantic model follows version 1.1, base statements are organized in a single package hierarchy.
+ *
+ * <p>
+ * Each statement has its own package underneath this package, from which it exports the StatementSupport instance,
+ * which can be wired into the statement reactor. Other classes should be kept package-private, so inter-statement
+ * interactions follow properly-exposed API interfaces.
+ *
+ * <p>
+ * Common base and utility classes for individual statement implementations are maintained in this package. This package
+ * is not meant for consumption by end users and should be used only by parser extensions.
+ *
+ * <p>
+ * Do not use bare implementation classes exposed from statement sub-packages, as their details are explicitly outside
+ * of API contract. The only exception to this rule are the StatementSupport classes, which allow for correlation and
+ * creation of known YANG statements.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/ParsedPathExpression.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/ParsedPathExpression.java
new file mode 100644 (file)
index 0000000..21ee7f7
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.rfc7950.stmt.path;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.util.AbstractPathExpression;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+
+final class ParsedPathExpression extends AbstractPathExpression {
+    private final @NonNull YangLocationPath location;
+
+    ParsedPathExpression(final YangLocationPath location, final String originalString) {
+        super(originalString);
+        this.location = requireNonNull(location);
+    }
+
+    @Override
+    public YangLocationPath getLocation() {
+        return location;
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper.add("location", location));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..01fe490
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.path;
+
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.stmt.PathEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PathStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class PathEffectiveStatementImpl extends DeclaredEffectiveStatementBase<PathExpression, PathStatement>
+        implements PathEffectiveStatement {
+    PathEffectiveStatementImpl(final StmtContext<PathExpression, PathStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathExpressionParser.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathExpressionParser.java
new file mode 100644 (file)
index 0000000..e02e6fc
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.rfc7950.stmt.path;
+
+import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.TerminalNode;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathLexer;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Absolute_pathContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Descendant_pathContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Node_identifierContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_argContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_equality_exprContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_key_exprContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_predicateContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Rel_path_keyexprContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Relative_pathContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.parser.rfc7950.antlr.SourceExceptionParser;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.xpath.api.YangBinaryExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
+import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
+import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Absolute;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Relative;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
+import org.opendaylight.yangtools.yang.xpath.api.YangPathExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class PathExpressionParser {
+    static final class Lenient extends PathExpressionParser {
+        private static final Logger LOG = LoggerFactory.getLogger(Lenient.class);
+
+        @Override
+        PathExpression parseExpression(final StmtContext<?, ?, ?> ctx, final String pathArg) {
+            try {
+                return super.parseExpression(ctx, pathArg);
+            } catch (IllegalStateException | SourceException e) {
+                LOG.warn("Failed to parse expression '{}'", pathArg, e);
+                return new UnparsedPathExpression(pathArg, e);
+            }
+        }
+    }
+
+    private static final YangFunctionCallExpr CURRENT_CALL = YangFunctionCallExpr.of(
+        YangFunction.CURRENT.getIdentifier());
+
+    PathExpression parseExpression(final StmtContext<?, ?, ?> ctx, final String pathArg) {
+        final LeafRefPathLexer lexer = new LeafRefPathLexer(CharStreams.fromString(pathArg));
+        final LeafRefPathParser parser = new LeafRefPathParser(new CommonTokenStream(lexer));
+        final Path_argContext path = SourceExceptionParser.parse(lexer, parser, parser::path_arg,
+            ctx.getStatementSourceReference());
+
+        final ParseTree childPath = path.getChild(0);
+        final YangLocationPath location;
+        if (childPath instanceof Absolute_pathContext) {
+            location = parseAbsolute(ctx, pathArg, (Absolute_pathContext) childPath);
+        } else if (childPath instanceof Relative_pathContext) {
+            location = parseRelative(ctx, pathArg, (Relative_pathContext) childPath);
+        } else {
+            throw new IllegalStateException("Unsupported child " + childPath);
+        }
+
+        return new ParsedPathExpression(location, pathArg);
+    }
+
+    private static Absolute parseAbsolute(final StmtContext<?, ?, ?> ctx, final String pathArg,
+            final Absolute_pathContext absolute) {
+        final List<Step> steps = new ArrayList<>();
+        fillSteps(ctx, pathArg, absolute, steps);
+        return YangLocationPath.absolute(steps);
+    }
+
+    private static Relative parseRelative(final StmtContext<?, ?, ?> ctx, final String pathArg,
+            final Relative_pathContext relative) {
+        final List<Step> steps = new ArrayList<>();
+
+        final int relativeChildren = relative.getChildCount();
+        verify(relativeChildren % 2 != 0, "Unexpected child count %s", relativeChildren);
+        for (int i = 0; i < relativeChildren / 2; ++i) {
+            steps.add(YangXPathAxis.PARENT.asStep());
+        }
+
+        final Descendant_pathContext descendant = getChild(relative, relativeChildren - 1,
+            Descendant_pathContext.class);
+        final Node_identifierContext qname = getChild(descendant, 0, Node_identifierContext.class);
+        final int descandantChildren = descendant.getChildCount();
+        if (descandantChildren > 1) {
+            final List<YangExpr> predicates = new ArrayList<>(descandantChildren);
+            for (int i = 1; i < descandantChildren - 1; ++i) {
+                predicates.add(parsePathPredicate(ctx, getChild(descendant, i, Path_predicateContext.class)));
+            }
+            steps.add(createChildStep(ctx, qname, predicates));
+            fillSteps(ctx, pathArg, getChild(descendant, descandantChildren - 1, Absolute_pathContext.class), steps);
+        } else {
+            steps.add(createChildStep(ctx, qname, ImmutableList.of()));
+        }
+
+        return YangLocationPath.relative(steps);
+    }
+
+    private static void fillSteps(final StmtContext<?, ?, ?> ctx, final String pathArg,
+            final Absolute_pathContext absolute, final List<Step> output) {
+
+        final List<YangExpr> predicates = new ArrayList<>();
+        Node_identifierContext qname = getChild(absolute, 1, Node_identifierContext.class);
+
+        final int children = absolute.getChildCount();
+        for (int i = 2; i < children; ++i) {
+            final ParseTree child = absolute.getChild(i);
+            if (child instanceof Node_identifierContext) {
+                output.add(createChildStep(ctx, qname, predicates));
+                predicates.clear();
+                qname = (Node_identifierContext) child;
+            } else if (child instanceof Path_predicateContext) {
+                predicates.add(parsePathPredicate(ctx, (Path_predicateContext) child));
+            }
+        }
+
+        output.add(createChildStep(ctx, qname, predicates));
+    }
+
+    private static <T> T getChild(final ParseTree parent, final int offset, final Class<T> clazz) {
+        final ParseTree child = parent.getChild(offset);
+        verify(clazz.isInstance(child), "Unexpected child %s at offset %s of %s when expecting %s", child, offset,
+            parent, clazz);
+        return clazz.cast(child);
+    }
+
+    private static YangBinaryExpr parsePathPredicate(final StmtContext<?, ?, ?> ctx,
+            final Path_predicateContext predicate) {
+        final Path_equality_exprContext eqExpr = verifyNotNull(predicate.path_equality_expr());
+        return YangBinaryOperator.EQUALS.exprWith(
+            createChildExpr(ctx, getChild(eqExpr, 0, Node_identifierContext.class)),
+            parsePathKeyExpr(ctx, verifyNotNull(eqExpr.path_key_expr())));
+    }
+
+    private static YangExpr parsePathKeyExpr(final StmtContext<?, ?, ?> ctx, final Path_key_exprContext expr) {
+        final Rel_path_keyexprContext relPath = verifyNotNull(expr.rel_path_keyexpr());
+        final int children = relPath.getChildCount();
+
+        // Process dots first
+        final List<Step> steps = new ArrayList<>();
+        int offset = 0;
+        while (offset < children - 1) {
+            final ParseTree child = expr.getChild(offset);
+            if (child instanceof Node_identifierContext) {
+                break;
+            }
+            if (child instanceof TerminalNode
+                    && ((TerminalNode) child).getSymbol().getType() == LeafRefPathLexer.DOTS) {
+                steps.add(YangXPathAxis.PARENT.asStep());
+            }
+
+            ++offset;
+        }
+
+        // Process node identifiers
+        while (offset < children) {
+            final ParseTree child = expr.getChild(offset);
+            if (child instanceof Node_identifierContext) {
+                steps.add(createChildStep(ctx, (Node_identifierContext) child, ImmutableList.of()));
+            }
+            ++offset;
+        }
+
+        return steps.isEmpty() ? CURRENT_CALL : YangPathExpr.of(CURRENT_CALL, YangLocationPath.relative(steps));
+    }
+
+    private static YangQNameExpr createChildExpr(final StmtContext<?, ?, ?> ctx, final Node_identifierContext qname) {
+        switch (qname.getChildCount()) {
+            case 1:
+                return YangQNameExpr.of(UnqualifiedQName.of(qname.getText()).intern());
+            case 3:
+                return YangQNameExpr.of(parseQName(ctx, qname));
+            default:
+                throw new IllegalStateException("Unexpected shape " + qname.getText());
+        }
+    }
+
+    private static QNameStep createChildStep(final StmtContext<?, ?, ?> ctx, final Node_identifierContext qname,
+            final List<YangExpr> predicates) {
+        switch (qname.getChildCount()) {
+            case 1:
+                return YangXPathAxis.CHILD.asStep(UnqualifiedQName.of(qname.getText()).intern(), predicates);
+            case 3:
+                return YangXPathAxis.CHILD.asStep(parseQName(ctx, qname), predicates);
+            default:
+                throw new IllegalStateException("Unexpected shape " + qname.getText());
+        }
+    }
+
+    private static QName parseQName(final StmtContext<?, ?, ?> ctx, final Node_identifierContext qname) {
+        return StmtContextUtils.parseNodeIdentifier(ctx, qname.getChild(0).getText(), qname.getChild(2).getText());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathStatementImpl.java
new file mode 100644 (file)
index 0000000..9e6be68
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.path;
+
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.stmt.PathStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class PathStatementImpl extends AbstractDeclaredStatement<PathExpression> implements PathStatement {
+    PathStatementImpl(final StmtContext<PathExpression, PathStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathStatementSupport.java
new file mode 100644 (file)
index 0000000..2bf55f7
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.path;
+
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PathStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class PathStatementSupport extends AbstractStatementSupport<PathExpression, PathStatement,
+        EffectiveStatement<PathExpression, PathStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.PATH).build();
+    private static final PathStatementSupport LENIENT_INSTANCE = new PathStatementSupport(
+        new PathExpressionParser.Lenient());
+    private static final PathStatementSupport STRICT_INSTANCE = new PathStatementSupport(
+        new PathExpressionParser());
+
+    private final PathExpressionParser parser;
+
+    private PathStatementSupport(final PathExpressionParser parser) {
+        super(YangStmtMapping.PATH);
+        this.parser = requireNonNull(parser);
+    }
+
+    public static PathStatementSupport lenientInstance() {
+        return LENIENT_INSTANCE;
+    }
+
+    public static PathStatementSupport strictInstance() {
+        return STRICT_INSTANCE;
+    }
+
+    @Override
+    public PathExpression parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return parser.parseExpression(ctx, value);
+    }
+
+    @Override
+    public PathStatement createDeclared(final StmtContext<PathExpression, PathStatement, ?> ctx) {
+        return new PathStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<PathExpression, PathStatement> createEffective(
+            final StmtContext<PathExpression, PathStatement, EffectiveStatement<PathExpression, PathStatement>> ctx) {
+        return new PathEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/UnparsedPathExpression.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/UnparsedPathExpression.java
new file mode 100644 (file)
index 0000000..643691d
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.rfc7950.stmt.path;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.opendaylight.yangtools.yang.model.util.AbstractPathExpression;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+
+final class UnparsedPathExpression extends AbstractPathExpression {
+    private final RuntimeException cause;
+    private final boolean absolute;
+
+    UnparsedPathExpression(final String originalString, final RuntimeException cause) {
+        super(originalString);
+        this.cause = requireNonNull(cause);
+        absolute = ArgumentUtils.isAbsoluteXPath(originalString);
+    }
+
+    @Override
+    public boolean isAbsolute() {
+        return absolute;
+    }
+
+    @Override
+    public YangLocationPath getLocation() {
+        throw new UnsupportedOperationException("Expression '" + getOriginalString() + "' was not parsed", cause);
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper.add("absolute", absolute)).add("cause", cause);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/AbstractPatternStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/AbstractPatternStatementSupport.java
new file mode 100644 (file)
index 0000000..814fbaf
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.pattern;
+
+import java.util.Optional;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PatternStatement;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.util.RegexUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractPatternStatementSupport extends AbstractStatementSupport<PatternConstraint, PatternStatement,
+        EffectiveStatement<PatternConstraint, PatternStatement>> {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractPatternStatementSupport.class);
+
+    AbstractPatternStatementSupport() {
+        super(YangStmtMapping.PATTERN);
+    }
+
+    @Override
+    public final PatternConstraint parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        final String pattern = RegexUtils.getJavaRegexFromXSD(value);
+
+        try {
+            Pattern.compile(pattern);
+        } catch (final PatternSyntaxException e) {
+            LOG.debug("Pattern \"{}\" failed to compile at {}", pattern, ctx.getStatementSourceReference(), e);
+            return null;
+        }
+
+        return new PatternConstraintImpl(pattern, value, Optional.empty(), Optional.empty());
+    }
+
+    @Override
+    public final PatternStatement createDeclared(final StmtContext<PatternConstraint, PatternStatement, ?> ctx) {
+        return new PatternStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<PatternConstraint, PatternStatement> createEffective(
+            final StmtContext<PatternConstraint, PatternStatement,
+            EffectiveStatement<PatternConstraint, PatternStatement>> ctx) {
+        return new PatternEffectiveStatementImpl(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternConstraintImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternConstraintImpl.java
new file mode 100644 (file)
index 0000000..2bb4fef
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.pattern;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import java.util.Objects;
+import java.util.Optional;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+
+final class PatternConstraintImpl implements PatternConstraint, Immutable {
+
+    private final String regEx;
+    private final String rawRegEx;
+    private final String description;
+    private final String reference;
+    private final String errorAppTag;
+    private final String errorMessage;
+    private final ModifierKind modifier;
+
+    PatternConstraintImpl(final String regex, final String rawRegex,
+            final Optional<String> description, final Optional<String> reference) {
+        this(regex, rawRegex, description.orElse(null), reference.orElse(null), null, null, null);
+    }
+
+    PatternConstraintImpl(final String regex, final String rawRegex, final String description,
+            final String reference, final String errorAppTag, final String errorMessage, final ModifierKind modifier) {
+        this.regEx = requireNonNull(regex, "regex must not be null");
+        this.rawRegEx = requireNonNull(rawRegex, "raw regex must not be null");
+        this.description = description;
+        this.reference = reference;
+        this.errorAppTag = errorAppTag != null ? errorAppTag : "invalid-regular-expression";
+        this.errorMessage = errorMessage;
+        this.modifier = modifier;
+    }
+
+    @Override
+    public String getJavaPatternString() {
+        return regEx;
+    }
+
+    @Override
+    public String getRegularExpressionString() {
+        return rawRegEx;
+    }
+
+    @Override
+    public Optional<String> getDescription() {
+        return Optional.ofNullable(description);
+    }
+
+    @Override
+    public Optional<String> getErrorAppTag() {
+        return Optional.ofNullable(errorAppTag);
+    }
+
+    @Override
+    public Optional<String> getErrorMessage() {
+        return Optional.ofNullable(errorMessage);
+    }
+
+    @Override
+    public Optional<ModifierKind> getModifier() {
+        return Optional.ofNullable(modifier);
+    }
+
+    @Override
+    public Optional<String> getReference() {
+        return Optional.ofNullable(reference);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(description, errorAppTag, errorMessage, reference, regEx, modifier);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final PatternConstraintImpl other = (PatternConstraintImpl) obj;
+        return Objects.equals(description, other.description) && Objects.equals(errorAppTag, other.errorAppTag)
+                && Objects.equals(errorMessage, other.errorMessage) && Objects.equals(reference, other.reference)
+                && Objects.equals(regEx, other.regEx) && Objects.equals(modifier, other.modifier);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).omitNullValues().add("regex", regEx).add("description", description)
+                .add("reference", reference).add("errorAppTag", errorAppTag).add("errorMessage", errorMessage)
+                .add("modifier", modifier).toString();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..747fb8d
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.pattern;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.PatternEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PatternStatement;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractConstraintEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class PatternEffectiveStatementImpl
+        extends AbstractConstraintEffectiveStatement<PatternConstraint, PatternStatement>
+        implements PatternEffectiveStatement {
+    PatternEffectiveStatementImpl(final StmtContext<PatternConstraint, PatternStatement, ?> ctx) {
+        super(ctx);
+    }
+
+    @Override
+    protected PatternConstraint createConstraints(final PatternConstraint argument) {
+        if (!isCustomizedStatement()) {
+            return argument;
+        }
+
+        return new PatternConstraintImpl(argument.getJavaPatternString(),
+            argument.getRegularExpressionString(),
+            getDescription().orElse(null), getReference().orElse(null), getErrorAppTag().orElse(null),
+            getErrorMessage().orElse(null), getModifier());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternStatementImpl.java
new file mode 100644 (file)
index 0000000..27f8aa2
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.pattern;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.PatternStatement;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public class PatternStatementImpl extends AbstractDeclaredStatement<PatternConstraint> implements PatternStatement {
+    protected PatternStatementImpl(final StmtContext<PatternConstraint, PatternStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..5c38a77
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.pattern;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class PatternStatementRFC6020Support extends AbstractPatternStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .PATTERN)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addOptional(YangStmtMapping.ERROR_APP_TAG)
+        .addOptional(YangStmtMapping.ERROR_MESSAGE)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .build();
+    private static final PatternStatementRFC6020Support INSTANCE = new PatternStatementRFC6020Support();
+
+    private PatternStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static PatternStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/pattern/PatternStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..d3f315c
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.pattern;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Pattern statement.
+ */
+@Beta
+public final class PatternStatementRFC7950Support extends AbstractPatternStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .PATTERN)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addOptional(YangStmtMapping.ERROR_APP_TAG)
+            .addOptional(YangStmtMapping.ERROR_MESSAGE)
+            .addOptional(YangStmtMapping.MODIFIER)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .build();
+    private static final PatternStatementRFC7950Support INSTANCE = new PatternStatementRFC7950Support();
+
+    private PatternStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static PatternStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/position/PositionEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/position/PositionEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..e1719bc
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.position;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.PositionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PositionStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class PositionEffectiveStatementImpl extends DeclaredEffectiveStatementBase<Long, PositionStatement>
+        implements PositionEffectiveStatement {
+    PositionEffectiveStatementImpl(final StmtContext<Long, PositionStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/position/PositionStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/position/PositionStatementImpl.java
new file mode 100644 (file)
index 0000000..d03de21
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.position;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.PositionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class PositionStatementImpl extends AbstractDeclaredStatement<Long> implements PositionStatement {
+    PositionStatementImpl(final StmtContext<Long, PositionStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/position/PositionStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/position/PositionStatementSupport.java
new file mode 100644 (file)
index 0000000..c8f33e4
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.position;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PositionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class PositionStatementSupport
+        extends AbstractStatementSupport<Long, PositionStatement, EffectiveStatement<Long, PositionStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.POSITION).build();
+    private static final PositionStatementSupport INSTANCE = new PositionStatementSupport();
+
+    private PositionStatementSupport() {
+        super(YangStmtMapping.POSITION);
+    }
+
+    public static PositionStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Long parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        try {
+            return Long.parseLong(value);
+        } catch (NumberFormatException e) {
+            throw new SourceException(String.format("Bit position value %s is not valid integer", value),
+                    ctx.getStatementSourceReference(), e);
+        }
+    }
+
+    @Override
+    public PositionStatement createDeclared(final StmtContext<Long, PositionStatement, ?> ctx) {
+        return new PositionStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<Long, PositionStatement> createEffective(
+            final StmtContext<Long, PositionStatement, EffectiveStatement<Long, PositionStatement>> ctx) {
+        return new PositionEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/prefix/PrefixEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/prefix/PrefixEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..b772ed1
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.prefix;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class PrefixEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, PrefixStatement>
+        implements PrefixEffectiveStatement {
+    PrefixEffectiveStatementImpl(final StmtContext<String, PrefixStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/prefix/PrefixStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/prefix/PrefixStatementImpl.java
new file mode 100644 (file)
index 0000000..4c1816e
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.prefix;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class PrefixStatementImpl extends AbstractDeclaredStatement<String> implements PrefixStatement {
+    PrefixStatementImpl(final StmtContext<String, PrefixStatement,?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/prefix/PrefixStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/prefix/PrefixStatementSupport.java
new file mode 100644 (file)
index 0000000..0eed9af
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.prefix;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class PrefixStatementSupport
+    extends AbstractStatementSupport<String, PrefixStatement, EffectiveStatement<String, PrefixStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .PREFIX)
+        .build();
+    private static final PrefixStatementSupport INSTANCE = new PrefixStatementSupport();
+
+    private PrefixStatementSupport() {
+        super(YangStmtMapping.PREFIX);
+    }
+
+    public static PrefixStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?,?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public PrefixStatement createDeclared(final StmtContext<String, PrefixStatement,?> ctx) {
+        return new PrefixStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String,PrefixStatement> createEffective(
+            final StmtContext<String, PrefixStatement, EffectiveStatement<String, PrefixStatement>> ctx) {
+        return new PrefixEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/presence/PresenceEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/presence/PresenceEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..31d427c
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.presence;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.PresenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PresenceStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class PresenceEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, PresenceStatement>
+        implements PresenceEffectiveStatement {
+    PresenceEffectiveStatementImpl(final StmtContext<String, PresenceStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/presence/PresenceStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/presence/PresenceStatementImpl.java
new file mode 100644 (file)
index 0000000..12e06f0
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.presence;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.PresenceStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class PresenceStatementImpl extends AbstractDeclaredStatement<String> implements PresenceStatement {
+    PresenceStatementImpl(final StmtContext<String, PresenceStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/presence/PresenceStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/presence/PresenceStatementSupport.java
new file mode 100644 (file)
index 0000000..72f77a4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.presence;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PresenceStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class PresenceStatementSupport
+        extends AbstractStatementSupport<String, PresenceStatement, EffectiveStatement<String, PresenceStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .PRESENCE)
+        .build();
+    private static final PresenceStatementSupport INSTANCE = new PresenceStatementSupport();
+
+    private PresenceStatementSupport() {
+        super(YangStmtMapping.PRESENCE);
+    }
+
+    public static PresenceStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public PresenceStatement createDeclared(final StmtContext<String, PresenceStatement, ?> ctx) {
+        return new PresenceStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, PresenceStatement> createEffective(
+            final StmtContext<String, PresenceStatement, EffectiveStatement<String, PresenceStatement>> ctx) {
+        return new PresenceEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/range/RangeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/range/RangeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..4a24b53
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.range;
+
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.stmt.RangeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RangeStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractListConstraintEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+// FIXME: hide this class
+public final class RangeEffectiveStatementImpl
+        extends AbstractListConstraintEffectiveStatement<ValueRange, RangeStatement>
+        implements RangeEffectiveStatement {
+    RangeEffectiveStatementImpl(final StmtContext<List<ValueRange>, RangeStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/range/RangeStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/range/RangeStatementImpl.java
new file mode 100644 (file)
index 0000000..c715067
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.range;
+
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.stmt.RangeStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class RangeStatementImpl extends AbstractDeclaredStatement<List<ValueRange>> implements RangeStatement {
+    RangeStatementImpl(final StmtContext<List<ValueRange>, RangeStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/range/RangeStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/range/RangeStatementSupport.java
new file mode 100644 (file)
index 0000000..fa8e3b1
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.range;
+
+import com.google.common.collect.Iterables;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RangeStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class RangeStatementSupport extends AbstractStatementSupport<List<ValueRange>, RangeStatement,
+        EffectiveStatement<List<ValueRange>, RangeStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .RANGE)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addOptional(YangStmtMapping.ERROR_APP_TAG)
+        .addOptional(YangStmtMapping.ERROR_MESSAGE)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .build();
+    private static final RangeStatementSupport INSTANCE = new RangeStatementSupport();
+
+    private RangeStatementSupport() {
+        super(YangStmtMapping.RANGE);
+    }
+
+    public static RangeStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public List<ValueRange> parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String rangeArgument) {
+        final List<ValueRange> ranges = new ArrayList<>();
+
+        for (final String singleRange : ArgumentUtils.PIPE_SPLITTER.split(rangeArgument)) {
+            final Iterator<String> boundaries = ArgumentUtils.TWO_DOTS_SPLITTER.split(singleRange).iterator();
+            final Number min = parseDecimalConstraintValue(ctx, boundaries.next());
+
+            final Number max;
+            if (boundaries.hasNext()) {
+                max = parseDecimalConstraintValue(ctx, boundaries.next());
+
+                // if min larger than max then error
+                SourceException.throwIf(ArgumentUtils.compareNumbers(min, max) == 1, ctx.getStatementSourceReference(),
+                        "Range constraint %s has descending order of boundaries; should be ascending", singleRange);
+                SourceException.throwIf(boundaries.hasNext(), ctx.getStatementSourceReference(),
+                    "Wrong number of boundaries in range constraint %s", singleRange);
+            } else {
+                max = min;
+            }
+
+            // some of intervals overlapping
+            InferenceException.throwIf(ranges.size() > 1
+                && ArgumentUtils.compareNumbers(min, Iterables.getLast(ranges).upperBound()) != 1,
+                ctx.getStatementSourceReference(),  "Some of the value ranges in %s are not disjoint", rangeArgument);
+            ranges.add(ValueRange.of(min, max));
+        }
+
+        return ranges;
+    }
+
+    @Override
+    public RangeStatement createDeclared(final StmtContext<List<ValueRange>, RangeStatement, ?> ctx) {
+        return new RangeStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<List<ValueRange>, RangeStatement> createEffective(
+            final StmtContext<List<ValueRange>, RangeStatement, EffectiveStatement<List<ValueRange>,
+                    RangeStatement>> ctx) {
+        return new RangeEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+
+    private static Number parseDecimalConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        if ("max".equals(value)) {
+            return UnresolvedNumber.max();
+        }
+        if ("min".equals(value)) {
+            return UnresolvedNumber.min();
+        }
+
+        try {
+            return value.indexOf('.') != -1 ? new BigDecimal(value) : new BigInteger(value);
+        } catch (final NumberFormatException e) {
+            throw new SourceException(String.format("Value %s is not a valid decimal number", value),
+                    ctx.getStatementSourceReference(), e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/reference/ReferenceEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/reference/ReferenceEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..6a87987
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.reference;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ReferenceEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, ReferenceStatement>
+        implements ReferenceEffectiveStatement {
+    ReferenceEffectiveStatementImpl(final StmtContext<String, ReferenceStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/reference/ReferenceStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/reference/ReferenceStatementImpl.java
new file mode 100644 (file)
index 0000000..6e19e7c
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.reference;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ReferenceStatementImpl extends AbstractDeclaredStatement<String> implements ReferenceStatement {
+    ReferenceStatementImpl(final StmtContext<String, ReferenceStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/reference/ReferenceStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/reference/ReferenceStatementSupport.java
new file mode 100644 (file)
index 0000000..f502a06
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.reference;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ReferenceStatementSupport extends AbstractStatementSupport<String, ReferenceStatement,
+        EffectiveStatement<String, ReferenceStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.REFERENCE)
+        .build();
+    private static final ReferenceStatementSupport INSTANCE = new ReferenceStatementSupport();
+
+    private ReferenceStatementSupport() {
+        super(YangStmtMapping.REFERENCE);
+    }
+
+    public static ReferenceStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public ReferenceStatement createDeclared(final StmtContext<String, ReferenceStatement, ?> ctx) {
+        return new ReferenceStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, ReferenceStatement> createEffective(
+            final StmtContext<String, ReferenceStatement, EffectiveStatement<String, ReferenceStatement>> ctx) {
+        return new ReferenceEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/AbstractRefineStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/AbstractRefineStatementSupport.java
new file mode 100644 (file)
index 0000000..c914b20
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.refine;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+abstract class AbstractRefineStatementSupport extends AbstractStatementSupport<SchemaNodeIdentifier, RefineStatement,
+        EffectiveStatement<SchemaNodeIdentifier, RefineStatement>> {
+
+    AbstractRefineStatementSupport() {
+        super(YangStmtMapping.REFINE);
+    }
+
+    @Override
+    public final SchemaNodeIdentifier parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return ArgumentUtils.nodeIdentifierFromPath(ctx, value);
+    }
+
+    @Override
+    public final RefineStatement createDeclared(final StmtContext<SchemaNodeIdentifier, RefineStatement, ?> ctx) {
+        return new RefineStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<SchemaNodeIdentifier, RefineStatement> createEffective(
+            final StmtContext<SchemaNodeIdentifier, RefineStatement,
+            EffectiveStatement<SchemaNodeIdentifier, RefineStatement>> ctx) {
+        return new RefineEffectiveStatementImpl(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/RefineEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/RefineEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..6b050bd
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.refine;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.stmt.RefineEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDocumentedNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+// FIXME: hide this class
+public final class RefineEffectiveStatementImpl
+        extends AbstractEffectiveDocumentedNode<SchemaNodeIdentifier, RefineStatement>
+        implements RefineEffectiveStatement, SchemaNode {
+
+    private final @NonNull QName qname;
+    private final @NonNull SchemaPath path;
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
+    private final SchemaNode refineTargetNode;
+
+    RefineEffectiveStatementImpl(final StmtContext<SchemaNodeIdentifier, RefineStatement, ?> ctx) {
+        super(ctx);
+        qname = verifyNotNull(ctx.coerceStatementArgument().getLastComponent());
+        path = ctx.getSchemaPath().get();
+        refineTargetNode = (SchemaNode) ctx.getEffectOfStatement().iterator().next().buildEffective();
+
+        // initSubstatementCollectionsAndFields
+        this.unknownNodes = ImmutableList.copyOf(effectiveSubstatements().stream()
+            .filter(UnknownSchemaNode.class::isInstance)
+            .map(UnknownSchemaNode.class::cast)
+            .collect(Collectors.toList()));
+    }
+
+    public SchemaNode getRefineTargetNode() {
+        return refineTargetNode;
+    }
+
+    @Override
+    public QName getQName() {
+        return qname;
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return path;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownNodes;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/RefineStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/RefineStatementImpl.java
new file mode 100644 (file)
index 0000000..6f8ee8e
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.refine;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class RefineStatementImpl extends AbstractDeclaredStatement<SchemaNodeIdentifier> implements RefineStatement {
+    RefineStatementImpl(final StmtContext<SchemaNodeIdentifier, RefineStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/RefineStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/RefineStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..28562d7
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.refine;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class RefineStatementRFC6020Support extends AbstractRefineStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.REFINE)
+        .addOptional(YangStmtMapping.DEFAULT)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.CONFIG)
+        .addOptional(YangStmtMapping.MANDATORY)
+        .addOptional(YangStmtMapping.PRESENCE)
+        .addAny(YangStmtMapping.MUST)
+        .addOptional(YangStmtMapping.MIN_ELEMENTS)
+        .addOptional(YangStmtMapping.MAX_ELEMENTS)
+        .build();
+    private static final RefineStatementRFC6020Support INSTANCE = new RefineStatementRFC6020Support();
+
+    private RefineStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static RefineStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/RefineStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/refine/RefineStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..8502b00
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.refine;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Refine statement.
+ */
+@Beta
+public final class RefineStatementRFC7950Support extends AbstractRefineStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .REFINE)
+            .addOptional(YangStmtMapping.DEFAULT)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addOptional(YangStmtMapping.CONFIG)
+            .addAny(YangStmtMapping.IF_FEATURE)
+            .addOptional(YangStmtMapping.MANDATORY)
+            .addOptional(YangStmtMapping.PRESENCE)
+            .addAny(YangStmtMapping.MUST)
+            .addOptional(YangStmtMapping.MIN_ELEMENTS)
+            .addOptional(YangStmtMapping.MAX_ELEMENTS)
+            .build();
+    private static final RefineStatementRFC7950Support INSTANCE = new RefineStatementRFC7950Support();
+
+    private RefineStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static RefineStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/require_instance/RequireInstanceEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/require_instance/RequireInstanceEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..0dd3aa2
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.require_instance;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.RequireInstanceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RequireInstanceStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class RequireInstanceEffectiveStatementImpl
+        extends DeclaredEffectiveStatementBase<Boolean, RequireInstanceStatement>
+        implements RequireInstanceEffectiveStatement {
+    RequireInstanceEffectiveStatementImpl(final StmtContext<Boolean, RequireInstanceStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/require_instance/RequireInstanceStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/require_instance/RequireInstanceStatementImpl.java
new file mode 100644 (file)
index 0000000..ff08ebc
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.require_instance;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.RequireInstanceStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class RequireInstanceStatementImpl extends AbstractDeclaredStatement<Boolean>
+        implements RequireInstanceStatement {
+    RequireInstanceStatementImpl(final StmtContext<Boolean, RequireInstanceStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/require_instance/RequireInstanceStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/require_instance/RequireInstanceStatementSupport.java
new file mode 100644 (file)
index 0000000..7394423
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.require_instance;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RequireInstanceStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class RequireInstanceStatementSupport extends AbstractStatementSupport<Boolean, RequireInstanceStatement,
+        EffectiveStatement<Boolean, RequireInstanceStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.REQUIRE_INSTANCE).build();
+    private static final RequireInstanceStatementSupport INSTANCE = new RequireInstanceStatementSupport();
+
+    private RequireInstanceStatementSupport() {
+        super(YangStmtMapping.REQUIRE_INSTANCE);
+    }
+
+    public static RequireInstanceStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Boolean parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return ArgumentUtils.parseBoolean(ctx, value);
+    }
+
+    @Override
+    public RequireInstanceStatement createDeclared(final StmtContext<Boolean, RequireInstanceStatement, ?> ctx) {
+        return new RequireInstanceStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<Boolean, RequireInstanceStatement> createEffective(
+            final StmtContext<Boolean, RequireInstanceStatement,
+            EffectiveStatement<Boolean, RequireInstanceStatement>> ctx) {
+        return new RequireInstanceEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public String internArgument(final String rawArgument) {
+        return ArgumentUtils.internBoolean(rawArgument);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision/RevisionEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision/RevisionEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..df9ec74
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.revision;
+
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class RevisionEffectiveStatementImpl extends DeclaredEffectiveStatementBase<Revision, RevisionStatement>
+        implements DocumentedNode, RevisionEffectiveStatement {
+
+    private final String reference;
+    private final String description;
+
+    RevisionEffectiveStatementImpl(final StmtContext<Revision, RevisionStatement, ?> ctx) {
+        super(ctx);
+        description = findFirstEffectiveSubstatementArgument(DescriptionEffectiveStatement.class).orElse(null);
+        reference = findFirstEffectiveSubstatementArgument(ReferenceEffectiveStatement.class).orElse(null);
+    }
+
+    @Override
+    public Optional<String> getDescription() {
+        return Optional.ofNullable(description);
+    }
+
+    @Override
+    public Optional<String> getReference() {
+        return Optional.ofNullable(reference);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision/RevisionStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision/RevisionStatementImpl.java
new file mode 100644 (file)
index 0000000..17da111
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.revision;
+
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class RevisionStatementImpl extends AbstractDeclaredStatement<Revision> implements RevisionStatement {
+    RevisionStatementImpl(final StmtContext<Revision, RevisionStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision/RevisionStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision/RevisionStatementSupport.java
new file mode 100644 (file)
index 0000000..8cf4c1c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.revision;
+
+import java.time.format.DateTimeParseException;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class RevisionStatementSupport extends
+        AbstractStatementSupport<Revision, RevisionStatement, EffectiveStatement<Revision, RevisionStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.REVISION)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .build();
+    private static final RevisionStatementSupport INSTANCE = new RevisionStatementSupport();
+
+    private RevisionStatementSupport() {
+        super(YangStmtMapping.REVISION);
+    }
+
+    public static RevisionStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Revision parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        try {
+            return Revision.of(value);
+        } catch (DateTimeParseException e) {
+            throw new SourceException(ctx.getStatementSourceReference(), e,
+                "Revision value %s is not in required format yyyy-MM-dd", value);
+        }
+    }
+
+    @Override
+    public RevisionStatement createDeclared(final StmtContext<Revision, RevisionStatement, ?> ctx) {
+        return new RevisionStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<Revision, RevisionStatement> createEffective(
+            final StmtContext<Revision, RevisionStatement, EffectiveStatement<Revision, RevisionStatement>> ctx) {
+        return new RevisionEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision_date/RevisionDateEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision_date/RevisionDateEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..13799eb
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.revision_date;
+
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class RevisionDateEffectiveStatementImpl extends DeclaredEffectiveStatementBase<Revision, RevisionDateStatement>
+        implements RevisionDateEffectiveStatement {
+    RevisionDateEffectiveStatementImpl(final StmtContext<Revision, RevisionDateStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision_date/RevisionDateStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision_date/RevisionDateStatementImpl.java
new file mode 100644 (file)
index 0000000..4b6d0cf
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.revision_date;
+
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class RevisionDateStatementImpl extends AbstractDeclaredStatement<Revision> implements RevisionDateStatement {
+    RevisionDateStatementImpl(final StmtContext<Revision, RevisionDateStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision_date/RevisionDateStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/revision_date/RevisionDateStatementSupport.java
new file mode 100644 (file)
index 0000000..dc4948f
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.revision_date;
+
+import java.time.format.DateTimeParseException;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class RevisionDateStatementSupport extends AbstractStatementSupport<Revision, RevisionDateStatement,
+        EffectiveStatement<Revision, RevisionDateStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR =
+            SubstatementValidator.builder(YangStmtMapping.REVISION_DATE).build();
+    private static final RevisionDateStatementSupport INSTANCE = new RevisionDateStatementSupport();
+
+    private RevisionDateStatementSupport() {
+        super(YangStmtMapping.REVISION_DATE);
+    }
+
+    public static RevisionDateStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Revision parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        try {
+            return Revision.of(value);
+        } catch (DateTimeParseException e) {
+            throw new SourceException(ctx.getStatementSourceReference(), e,
+                "Revision value %s is not in required format yyyy-MM-dd", value);
+        }
+    }
+
+    @Override
+    public RevisionDateStatement createDeclared(final StmtContext<Revision, RevisionDateStatement, ?> ctx) {
+        return new RevisionDateStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<Revision, RevisionDateStatement> createEffective(final StmtContext<Revision,
+            RevisionDateStatement, EffectiveStatement<Revision, RevisionDateStatement>> ctx) {
+        return new RevisionDateEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/AbstractRpcStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/AbstractRpcStatementSupport.java
new file mode 100644 (file)
index 0000000..13cead2
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.rpc;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.InputStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.OutputStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RpcStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
+
+abstract class AbstractRpcStatementSupport
+        extends AbstractQNameStatementSupport<RpcStatement, EffectiveStatement<QName, RpcStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.RPC)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.GROUPING)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addOptional(YangStmtMapping.INPUT)
+        .addOptional(YangStmtMapping.OUTPUT)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addAny(YangStmtMapping.TYPEDEF)
+        .build();
+
+    AbstractRpcStatementSupport() {
+        super(YangStmtMapping.RPC);
+    }
+
+    @Override
+    public final QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public final void onStatementAdded(
+            final Mutable<QName, RpcStatement, EffectiveStatement<QName, RpcStatement>> stmt) {
+        stmt.coerceParentContext().addToNs(ChildSchemaNodeNamespace.class, stmt.coerceStatementArgument(), stmt);
+    }
+
+    @Override
+    public final RpcStatement createDeclared(final StmtContext<QName, RpcStatement, ?> ctx) {
+        return new RpcStatementImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<QName, RpcStatement> createEffective(
+            final StmtContext<QName, RpcStatement, EffectiveStatement<QName, RpcStatement>> ctx) {
+        return new RpcEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public final void onFullDefinitionDeclared(
+            final Mutable<QName, RpcStatement, EffectiveStatement<QName, RpcStatement>> stmt) {
+        super.onFullDefinitionDeclared(stmt);
+
+        if (StmtContextUtils.findFirstDeclaredSubstatement(stmt, InputStatement.class) == null) {
+            ((StatementContextBase<?, ?, ?>) stmt).appendImplicitStatement(implictInput());
+        }
+
+        if (StmtContextUtils.findFirstDeclaredSubstatement(stmt, OutputStatement.class) == null) {
+            ((StatementContextBase<?, ?, ?>) stmt).appendImplicitStatement(implictOutput());
+        }
+    }
+
+
+    @Override
+    protected final SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+
+    abstract StatementSupport<?, ?, ?> implictInput();
+
+    abstract StatementSupport<?, ?, ?> implictOutput();
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/RpcEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/RpcEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..3b96314
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.rpc;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RpcStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveOperationDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class RpcEffectiveStatementImpl extends AbstractEffectiveOperationDefinition<RpcStatement>
+        implements RpcDefinition, RpcEffectiveStatement {
+    RpcEffectiveStatementImpl(final StmtContext<QName, RpcStatement,
+            EffectiveStatement<QName, RpcStatement>> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/RpcStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/RpcStatementImpl.java
new file mode 100644 (file)
index 0000000..139ff27
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.rpc;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.RpcStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class RpcStatementImpl extends AbstractDeclaredStatement<QName> implements RpcStatement {
+    RpcStatementImpl(final StmtContext<QName, RpcStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/RpcStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/RpcStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..7fdb97a
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.rpc;
+
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.input.InputStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.output.OutputStatementRFC6020Support;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+
+public final class RpcStatementRFC6020Support extends AbstractRpcStatementSupport {
+    private static final RpcStatementRFC6020Support INSTANCE = new RpcStatementRFC6020Support();
+
+    private RpcStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static RpcStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    StatementSupport<?, ?, ?> implictInput() {
+        return InputStatementRFC6020Support.getInstance();
+    }
+
+    @Override
+    StatementSupport<?, ?, ?> implictOutput() {
+        return OutputStatementRFC6020Support.getInstance();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/RpcStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/rpc/RpcStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..fe52e8e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2017 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.rfc7950.stmt.rpc;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.input.InputStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.output.OutputStatementRFC7950Support;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+
+/**
+ * Bridge class for RFC7950 RPCs. Specializes implicit input/output statements.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class RpcStatementRFC7950Support extends AbstractRpcStatementSupport {
+    private static final RpcStatementRFC7950Support INSTANCE = new RpcStatementRFC7950Support();
+
+    private RpcStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static RpcStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    StatementSupport<?, ?, ?> implictInput() {
+        return InputStatementRFC7950Support.getInstance();
+    }
+
+    @Override
+    StatementSupport<?, ?, ?> implictOutput() {
+        return OutputStatementRFC7950Support.getInstance();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/status/StatusEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/status/StatusEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..f293c95
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.status;
+
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class StatusEffectiveStatementImpl extends DeclaredEffectiveStatementBase<Status, StatusStatement>
+        implements StatusEffectiveStatement {
+    StatusEffectiveStatementImpl(final StmtContext<Status, StatusStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/status/StatusStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/status/StatusStatementImpl.java
new file mode 100644 (file)
index 0000000..59ad68c
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.status;
+
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class StatusStatementImpl extends AbstractDeclaredStatement<Status> implements StatusStatement {
+    StatusStatementImpl(final StmtContext<Status, StatusStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/status/StatusStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/status/StatusStatementSupport.java
new file mode 100644 (file)
index 0000000..b63f94d
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.status;
+
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class StatusStatementSupport
+        extends AbstractStatementSupport<Status, StatusStatement, EffectiveStatement<Status, StatusStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .STATUS)
+        .build();
+    private static final StatusStatementSupport INSTANCE = new StatusStatementSupport();
+
+    private StatusStatementSupport() {
+        super(YangStmtMapping.STATUS);
+    }
+
+    public static StatusStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Status parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        switch (value) {
+            case "current":
+                return Status.CURRENT;
+            case "deprecated":
+                return Status.DEPRECATED;
+            case "obsolete":
+                return Status.OBSOLETE;
+            default:
+                throw new SourceException(ctx.getStatementSourceReference(),
+                    "Invalid status '%s', must be one of 'current', 'deprecated' or 'obsolete'", value);
+        }
+    }
+
+    @Override
+    public StatusStatement createDeclared(
+            final StmtContext<Status, StatusStatement, ?> ctx) {
+        return new StatusStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<Status, StatusStatement> createEffective(
+            final StmtContext<Status, StatusStatement, EffectiveStatement<Status, StatusStatement>> ctx) {
+        return new StatusEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public String internArgument(final String rawArgument) {
+        if ("current".equals(rawArgument)) {
+            return "current";
+        } else if ("deprecated".equals(rawArgument)) {
+            return "deprecated";
+        } else if ("obsolete".equals(rawArgument)) {
+            return "obsolete";
+        } else {
+            return rawArgument;
+        }
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/AbstractSubmoduleStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/AbstractSubmoduleStatementSupport.java
new file mode 100644 (file)
index 0000000..db6523f
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.submodule;
+
+import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.findFirstDeclaredSubstatement;
+import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.SubmoduleNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleName;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+abstract class AbstractSubmoduleStatementSupport
+        extends AbstractStatementSupport<String, SubmoduleStatement, SubmoduleEffectiveStatement> {
+    AbstractSubmoduleStatementSupport() {
+        super(YangStmtMapping.SUBMODULE);
+    }
+
+    @Override
+    public final String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public final SubmoduleStatement createDeclared(final StmtContext<String, SubmoduleStatement, ?> ctx) {
+        return new SubmoduleStatementImpl(ctx);
+    }
+
+    @Override
+    public final SubmoduleEffectiveStatement createEffective(
+            final StmtContext<String, SubmoduleStatement, SubmoduleEffectiveStatement> ctx) {
+        return new SubmoduleEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public final void onPreLinkageDeclared(
+            final Mutable<String, SubmoduleStatement, SubmoduleEffectiveStatement> stmt) {
+        stmt.setRootIdentifier(RevisionSourceIdentifier.create(stmt.getStatementArgument(),
+            StmtContextUtils.getLatestRevision(stmt.declaredSubstatements())));
+    }
+
+    @Override
+    public final void onLinkageDeclared(final Mutable<String, SubmoduleStatement, SubmoduleEffectiveStatement> stmt) {
+        final SourceIdentifier submoduleIdentifier = RevisionSourceIdentifier.create(stmt.coerceStatementArgument(),
+            StmtContextUtils.getLatestRevision(stmt.declaredSubstatements()));
+
+        final StmtContext<?, SubmoduleStatement, SubmoduleEffectiveStatement>
+            possibleDuplicateSubmodule = stmt.getFromNamespace(SubmoduleNamespace.class, submoduleIdentifier);
+        if (possibleDuplicateSubmodule != null && possibleDuplicateSubmodule != stmt) {
+            throw new SourceException(stmt.getStatementSourceReference(), "Submodule name collision: %s. At %s",
+                    stmt.getStatementArgument(), possibleDuplicateSubmodule.getStatementSourceReference());
+        }
+
+        stmt.addContext(SubmoduleNamespace.class, submoduleIdentifier, stmt);
+
+        final String belongsToModuleName = firstAttributeOf(stmt.declaredSubstatements(), BelongsToStatement.class);
+        final StmtContext<?, ?, ?> prefixSubStmtCtx = findFirstDeclaredSubstatement(stmt, 0,
+                BelongsToStatement.class, PrefixStatement.class);
+        SourceException.throwIfNull(prefixSubStmtCtx, stmt.getStatementSourceReference(),
+                "Prefix of belongsTo statement is missing in submodule [%s]", stmt.getStatementArgument());
+
+        final String prefix = (String) prefixSubStmtCtx.getStatementArgument();
+
+        stmt.addToNs(BelongsToPrefixToModuleName.class, prefix, belongsToModuleName);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/SubmoduleEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/SubmoduleEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..a531553
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.submodule;
+
+import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
+
+import java.util.Objects;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveModule;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
+
+final class SubmoduleEffectiveStatementImpl extends AbstractEffectiveModule<SubmoduleStatement>
+        implements SubmoduleEffectiveStatement {
+
+    private final QNameModule qnameModule;
+
+    SubmoduleEffectiveStatementImpl(final StmtContext<String, SubmoduleStatement, SubmoduleEffectiveStatement> ctx) {
+        super(ctx);
+
+        final String belongsToModuleName = firstAttributeOf(ctx.declaredSubstatements(), BelongsToStatement.class);
+        final QNameModule belongsToModuleQName = ctx.getFromNamespace(ModuleNameToModuleQName.class,
+                belongsToModuleName);
+
+        final Optional<Revision> submoduleRevision = findFirstEffectiveSubstatementArgument(
+            RevisionEffectiveStatement.class);
+        this.qnameModule = QNameModule.create(belongsToModuleQName.getNamespace(), submoduleRevision).intern();
+    }
+
+    @Override
+    public QNameModule getQNameModule() {
+        return qnameModule;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getName(), getYangVersion(), qnameModule);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof SubmoduleEffectiveStatementImpl)) {
+            return false;
+        }
+        final SubmoduleEffectiveStatementImpl other = (SubmoduleEffectiveStatementImpl) obj;
+        return Objects.equals(getName(), other.getName()) && qnameModule.equals(other.qnameModule)
+                && Objects.equals(getYangVersion(), other.getYangVersion());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/SubmoduleStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/SubmoduleStatementImpl.java
new file mode 100644 (file)
index 0000000..514f57c
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.submodule;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractRootStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class SubmoduleStatementImpl extends AbstractRootStatement<SubmoduleStatement> implements SubmoduleStatement {
+    SubmoduleStatementImpl(final StmtContext<String, SubmoduleStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/SubmoduleStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/SubmoduleStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..df4b31f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.submodule;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class SubmoduleStatementRFC6020Support extends AbstractSubmoduleStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .SUBMODULE)
+        .addAny(YangStmtMapping.ANYXML)
+        .addAny(YangStmtMapping.AUGMENT)
+        .addMandatory(YangStmtMapping.BELONGS_TO)
+        .addAny(YangStmtMapping.CHOICE)
+        .addOptional(YangStmtMapping.CONTACT)
+        .addAny(YangStmtMapping.CONTAINER)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.DEVIATION)
+        .addAny(YangStmtMapping.EXTENSION)
+        .addAny(YangStmtMapping.FEATURE)
+        .addAny(YangStmtMapping.GROUPING)
+        .addAny(YangStmtMapping.IDENTITY)
+        .addAny(YangStmtMapping.IMPORT)
+        .addAny(YangStmtMapping.INCLUDE)
+        .addAny(YangStmtMapping.LEAF)
+        .addAny(YangStmtMapping.LEAF_LIST)
+        .addAny(YangStmtMapping.LIST)
+        .addAny(YangStmtMapping.NOTIFICATION)
+        .addOptional(YangStmtMapping.ORGANIZATION)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addAny(YangStmtMapping.REVISION)
+        .addAny(YangStmtMapping.RPC)
+        .addAny(YangStmtMapping.TYPEDEF)
+        .addAny(YangStmtMapping.USES)
+        .addOptional(YangStmtMapping.YANG_VERSION)
+        .build();
+    private static final SubmoduleStatementRFC6020Support INSTANCE = new SubmoduleStatementRFC6020Support();
+
+    private SubmoduleStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static SubmoduleStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/SubmoduleStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/submodule/SubmoduleStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..3fc2eb4
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 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.rfc7950.stmt.submodule;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Submodule statement.
+ */
+@Beta
+public final class SubmoduleStatementRFC7950Support extends AbstractSubmoduleStatementSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .SUBMODULE)
+            .addAny(YangStmtMapping.ANYDATA)
+            .addAny(YangStmtMapping.ANYXML)
+            .addAny(YangStmtMapping.AUGMENT)
+            .addMandatory(YangStmtMapping.BELONGS_TO)
+            .addAny(YangStmtMapping.CHOICE)
+            .addOptional(YangStmtMapping.CONTACT)
+            .addAny(YangStmtMapping.CONTAINER)
+            .addOptional(YangStmtMapping.DESCRIPTION)
+            .addAny(YangStmtMapping.DEVIATION)
+            .addAny(YangStmtMapping.EXTENSION)
+            .addAny(YangStmtMapping.FEATURE)
+            .addAny(YangStmtMapping.GROUPING)
+            .addAny(YangStmtMapping.IDENTITY)
+            .addAny(YangStmtMapping.IMPORT)
+            .addAny(YangStmtMapping.INCLUDE)
+            .addAny(YangStmtMapping.LEAF)
+            .addAny(YangStmtMapping.LEAF_LIST)
+            .addAny(YangStmtMapping.LIST)
+            .addAny(YangStmtMapping.NOTIFICATION)
+            .addOptional(YangStmtMapping.ORGANIZATION)
+            .addOptional(YangStmtMapping.REFERENCE)
+            .addAny(YangStmtMapping.REVISION)
+            .addAny(YangStmtMapping.RPC)
+            .addAny(YangStmtMapping.TYPEDEF)
+            .addAny(YangStmtMapping.USES)
+            .addOptional(YangStmtMapping.YANG_VERSION)
+            .build();
+    private static final SubmoduleStatementRFC7950Support INSTANCE = new SubmoduleStatementRFC7950Support();
+
+    private SubmoduleStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static SubmoduleStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractIdentityRefSpecificationSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractIdentityRefSpecificationSupport.java
new file mode 100644 (file)
index 0000000..91ea402
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BaseStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.IdentityRefSpecification;
+import org.opendaylight.yangtools.yang.parser.spi.IdentityNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+
+abstract class AbstractIdentityRefSpecificationSupport
+        extends AbstractStatementSupport<String, IdentityRefSpecification,
+            EffectiveStatement<String, IdentityRefSpecification>> {
+    AbstractIdentityRefSpecificationSupport() {
+        super(YangStmtMapping.TYPE);
+    }
+
+    @Override
+    public final String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public final IdentityRefSpecification createDeclared(final StmtContext<String, IdentityRefSpecification, ?> ctx) {
+        return new IdentityRefSpecificationImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<String, IdentityRefSpecification> createEffective(
+            final StmtContext<String, IdentityRefSpecification,
+            EffectiveStatement<String, IdentityRefSpecification>> ctx) {
+        return new IdentityRefSpecificationEffectiveStatement(ctx);
+    }
+
+    @Override
+    public final void onFullDefinitionDeclared(final Mutable<String, IdentityRefSpecification,
+            EffectiveStatement<String, IdentityRefSpecification>> stmt) {
+        super.onFullDefinitionDeclared(stmt);
+
+        final Collection<StmtContext<QName, BaseStatement, ?>> baseStatements =
+                StmtContextUtils.findAllDeclaredSubstatements(stmt, BaseStatement.class);
+        for (StmtContext<QName, BaseStatement, ?> baseStmt : baseStatements) {
+            final QName baseIdentity = baseStmt.coerceStatementArgument();
+            final StmtContext<?, ?, ?> stmtCtx = stmt.getFromNamespace(IdentityNamespace.class, baseIdentity);
+            InferenceException.throwIfNull(stmtCtx, stmt.getStatementSourceReference(),
+                "Referenced base identity '%s' doesn't exist in given scope (module, imported modules, submodules)",
+                    baseIdentity.getLocalName());
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractLeafrefSpecificationSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractLeafrefSpecificationSupport.java
new file mode 100644 (file)
index 0000000..52649ad
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.LeafrefSpecification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+abstract class AbstractLeafrefSpecificationSupport
+        extends
+        AbstractStatementSupport<String, LeafrefSpecification, EffectiveStatement<String, LeafrefSpecification>> {
+    AbstractLeafrefSpecificationSupport() {
+        super(YangStmtMapping.TYPE);
+    }
+
+    @Override
+    public final String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public final LeafrefSpecification createDeclared(final StmtContext<String, LeafrefSpecification, ?> ctx) {
+        return new LeafrefSpecificationImpl(ctx);
+    }
+
+    @Override
+    public final EffectiveStatement<String, LeafrefSpecification> createEffective(
+            final StmtContext<String, LeafrefSpecification, EffectiveStatement<String, LeafrefSpecification>> ctx) {
+        return new LeafrefSpecificationEffectiveStatement(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractTypeEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractTypeEffectiveStatement.java
new file mode 100644 (file)
index 0000000..54e9e11
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.util.type.TypeBuilder;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+abstract class AbstractTypeEffectiveStatement<T extends TypeDefinition<T>> extends
+        DeclaredEffectiveStatementBase<String, TypeStatement> implements TypeEffectiveStatement<TypeStatement> {
+    private final @NonNull T typeDefinition;
+
+    AbstractTypeEffectiveStatement(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final TypeBuilder<T> builder) {
+        super(ctx);
+
+        for (EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public final T getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractTypeStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/AbstractTypeStatementSupport.java
new file mode 100644 (file)
index 0000000..77be43c
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefStatement;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.parser.spi.TypeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+abstract class AbstractTypeStatementSupport
+        extends AbstractStatementSupport<String, TypeStatement, EffectiveStatement<String, TypeStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.TYPE)
+        .addOptional(YangStmtMapping.BASE)
+        .addAny(YangStmtMapping.BIT)
+        .addAny(YangStmtMapping.ENUM)
+        .addOptional(YangStmtMapping.FRACTION_DIGITS)
+        .addOptional(YangStmtMapping.LENGTH)
+        .addOptional(YangStmtMapping.PATH)
+        .addAny(YangStmtMapping.PATTERN)
+        .addOptional(YangStmtMapping.RANGE)
+        .addOptional(YangStmtMapping.REQUIRE_INSTANCE)
+        .addAny(YangStmtMapping.TYPE)
+        .build();
+
+    static final String BINARY = "binary";
+    static final String BITS = "bits";
+    static final String BOOLEAN = "boolean";
+    static final String DECIMAL64 = "decimal64";
+    static final String EMPTY = "empty";
+    static final String ENUMERATION = "enumeration";
+    static final String IDENTITY_REF = "identityref";
+    static final String INSTANCE_IDENTIFIER = "instance-identifier";
+    static final String INT8 = "int8";
+    static final String INT16 = "int16";
+    static final String INT32 = "int32";
+    static final String INT64 = "int64";
+    static final String LEAF_REF = "leafref";
+    static final String STRING = "string";
+    static final String UINT8 = "uint8";
+    static final String UINT16 = "uint16";
+    static final String UINT32 = "uint32";
+    static final String UINT64 = "uint64";
+    static final String UNION = "union";
+
+    private static final ImmutableMap<String, String> BUILT_IN_TYPES = ImmutableMap.<String, String>builder()
+        .put(BINARY, BINARY)
+        .put(BITS, BITS)
+        .put(BOOLEAN, BOOLEAN)
+        .put(DECIMAL64, DECIMAL64)
+        .put(EMPTY, EMPTY)
+        .put(ENUMERATION, ENUMERATION)
+        .put(IDENTITY_REF,IDENTITY_REF)
+        .put(INSTANCE_IDENTIFIER, INSTANCE_IDENTIFIER)
+        .put(INT8, INT8)
+        .put(INT16, INT16)
+        .put(INT32, INT32)
+        .put(INT64, INT64)
+        .put(LEAF_REF, LEAF_REF)
+        .put(STRING, STRING)
+        .put(UINT8, UINT8)
+        .put(UINT16, UINT16)
+        .put(UINT32, UINT32)
+        .put(UINT64, UINT64)
+        .put(UNION, UNION)
+        .build();
+
+    private static final ImmutableMap<String, StatementSupport<?, ?, ?>> ARGUMENT_SPECIFIC_SUPPORTS =
+            ImmutableMap.<String, StatementSupport<?, ?, ?>>builder()
+            .put(BITS, new BitsSpecificationSupport())
+            .put(DECIMAL64, new Decimal64SpecificationSupport())
+            .put(ENUMERATION, new EnumSpecificationSupport())
+            .put(IDENTITY_REF, new IdentityRefSpecificationRFC6020Support())
+            .put(INSTANCE_IDENTIFIER, new InstanceIdentifierSpecificationSupport())
+            .put(LEAF_REF, new LeafrefSpecificationRFC6020Support())
+            .put(UNION, new UnionSpecificationSupport())
+            .build();
+
+    AbstractTypeStatementSupport() {
+        super(YangStmtMapping.TYPE);
+    }
+
+    @Override
+    public final String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public final TypeStatement createDeclared(final StmtContext<String, TypeStatement, ?> ctx) {
+        return BuiltinTypeStatement.maybeReplace(new TypeStatementImpl(ctx));
+    }
+
+    @Override
+    public final TypeEffectiveStatement<TypeStatement> createEffective(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx) {
+
+        // First look up the proper base type
+        final TypeEffectiveStatement<TypeStatement> typeStmt;
+        switch (ctx.coerceStatementArgument()) {
+            case BINARY:
+                typeStmt = BuiltinEffectiveStatement.BINARY;
+                break;
+            case BOOLEAN:
+                typeStmt = BuiltinEffectiveStatement.BOOLEAN;
+                break;
+            case EMPTY:
+                typeStmt = BuiltinEffectiveStatement.EMPTY;
+                break;
+            case INSTANCE_IDENTIFIER:
+                typeStmt = BuiltinEffectiveStatement.INSTANCE_IDENTIFIER;
+                break;
+            case INT8:
+                typeStmt = BuiltinEffectiveStatement.INT8;
+                break;
+            case INT16:
+                typeStmt = BuiltinEffectiveStatement.INT16;
+                break;
+            case INT32:
+                typeStmt = BuiltinEffectiveStatement.INT32;
+                break;
+            case INT64:
+                typeStmt = BuiltinEffectiveStatement.INT64;
+                break;
+            case STRING:
+                typeStmt = BuiltinEffectiveStatement.STRING;
+                break;
+            case UINT8:
+                typeStmt = BuiltinEffectiveStatement.UINT8;
+                break;
+            case UINT16:
+                typeStmt = BuiltinEffectiveStatement.UINT16;
+                break;
+            case UINT32:
+                typeStmt = BuiltinEffectiveStatement.UINT32;
+                break;
+            case UINT64:
+                typeStmt = BuiltinEffectiveStatement.UINT64;
+                break;
+            default:
+                final QName qname = StmtContextUtils.parseNodeIdentifier(ctx, ctx.getStatementArgument());
+                final StmtContext<?, TypedefStatement, TypedefEffectiveStatement> typedef =
+                        SourceException.throwIfNull(ctx.getFromNamespace(TypeNamespace.class, qname),
+                            ctx.getStatementSourceReference(), "Type '%s' not found", qname);
+                typeStmt = typedef.buildEffective().asTypeEffectiveStatement();
+        }
+
+        if (ctx.declaredSubstatements().isEmpty() && ctx.effectiveSubstatements().isEmpty()) {
+            return typeStmt;
+        }
+
+        // Now instantiate the proper effective statement for that type
+        final TypeDefinition<?> baseType = typeStmt.getTypeDefinition();
+        if (baseType instanceof BinaryTypeDefinition) {
+            return new BinaryTypeEffectiveStatementImpl(ctx, (BinaryTypeDefinition) baseType);
+        } else if (baseType instanceof BitsTypeDefinition) {
+            return new BitsTypeEffectiveStatementImpl(ctx, (BitsTypeDefinition) baseType);
+        } else if (baseType instanceof BooleanTypeDefinition) {
+            return new BooleanTypeEffectiveStatementImpl(ctx, (BooleanTypeDefinition) baseType);
+        } else if (baseType instanceof DecimalTypeDefinition) {
+            return new DecimalTypeEffectiveStatementImpl(ctx, (DecimalTypeDefinition) baseType);
+        } else if (baseType instanceof EmptyTypeDefinition) {
+            return new EmptyTypeEffectiveStatementImpl(ctx, (EmptyTypeDefinition) baseType);
+        } else if (baseType instanceof EnumTypeDefinition) {
+            return new EnumTypeEffectiveStatementImpl(ctx, (EnumTypeDefinition) baseType);
+        } else if (baseType instanceof IdentityrefTypeDefinition) {
+            return new IdentityrefTypeEffectiveStatementImpl(ctx, (IdentityrefTypeDefinition) baseType);
+        } else if (baseType instanceof InstanceIdentifierTypeDefinition) {
+            return new InstanceIdentifierTypeEffectiveStatementImpl(ctx,
+                (InstanceIdentifierTypeDefinition) baseType);
+        } else if (baseType instanceof Int8TypeDefinition) {
+            return new IntegralTypeEffectiveStatementImpl<>(ctx,
+                    RestrictedTypes.newInt8Builder((Int8TypeDefinition) baseType, typeEffectiveSchemaPath(ctx)));
+        } else if (baseType instanceof Int16TypeDefinition) {
+            return new IntegralTypeEffectiveStatementImpl<>(ctx,
+                    RestrictedTypes.newInt16Builder((Int16TypeDefinition) baseType, typeEffectiveSchemaPath(ctx)));
+        } else if (baseType instanceof Int32TypeDefinition) {
+            return new IntegralTypeEffectiveStatementImpl<>(ctx,
+                    RestrictedTypes.newInt32Builder((Int32TypeDefinition) baseType, typeEffectiveSchemaPath(ctx)));
+        } else if (baseType instanceof Int64TypeDefinition) {
+            return new IntegralTypeEffectiveStatementImpl<>(ctx,
+                    RestrictedTypes.newInt64Builder((Int64TypeDefinition) baseType, typeEffectiveSchemaPath(ctx)));
+        } else if (baseType instanceof LeafrefTypeDefinition) {
+            return new LeafrefTypeEffectiveStatementImpl(ctx, (LeafrefTypeDefinition) baseType);
+        } else if (baseType instanceof StringTypeDefinition) {
+            return new StringTypeEffectiveStatementImpl(ctx, (StringTypeDefinition) baseType);
+        } else if (baseType instanceof Uint8TypeDefinition) {
+            return new IntegralTypeEffectiveStatementImpl<>(ctx,
+                    RestrictedTypes.newUint8Builder((Uint8TypeDefinition) baseType, typeEffectiveSchemaPath(ctx)));
+        } else if (baseType instanceof Uint16TypeDefinition) {
+            return new IntegralTypeEffectiveStatementImpl<>(ctx,
+                    RestrictedTypes.newUint16Builder((Uint16TypeDefinition) baseType, typeEffectiveSchemaPath(ctx)));
+        } else if (baseType instanceof Uint32TypeDefinition) {
+            return new IntegralTypeEffectiveStatementImpl<>(ctx,
+                    RestrictedTypes.newUint32Builder((Uint32TypeDefinition) baseType, typeEffectiveSchemaPath(ctx)));
+        } else if (baseType instanceof Uint64TypeDefinition) {
+            return new IntegralTypeEffectiveStatementImpl<>(ctx,
+                    RestrictedTypes.newUint64Builder((Uint64TypeDefinition) baseType, typeEffectiveSchemaPath(ctx)));
+        } else if (baseType instanceof UnionTypeDefinition) {
+            return new UnionTypeEffectiveStatementImpl(ctx, (UnionTypeDefinition) baseType);
+        } else {
+            throw new IllegalStateException("Unhandled base type " + baseType);
+        }
+    }
+
+    @Override
+    public final void onFullDefinitionDeclared(
+            final Mutable<String, TypeStatement, EffectiveStatement<String, TypeStatement>> stmt) {
+        super.onFullDefinitionDeclared(stmt);
+
+        // if it is yang built-in type, no prerequisite is needed, so simply return
+        if (BUILT_IN_TYPES.containsKey(stmt.getStatementArgument())) {
+            return;
+        }
+
+        final QName typeQName = StmtContextUtils.parseNodeIdentifier(stmt, stmt.getStatementArgument());
+        final ModelActionBuilder typeAction = stmt.newInferenceAction(ModelProcessingPhase.EFFECTIVE_MODEL);
+        final Prerequisite<StmtContext<?, ?, ?>> typePrereq = typeAction.requiresCtx(stmt, TypeNamespace.class,
+                typeQName, ModelProcessingPhase.EFFECTIVE_MODEL);
+        typeAction.mutatesEffectiveCtx(stmt.getParentContext());
+
+        /*
+         * If the type does not exist, throw new InferenceException.
+         * Otherwise perform no operation.
+         */
+        typeAction.apply(new InferenceAction() {
+            @Override
+            public void apply(final InferenceContext ctx) {
+                // Intentional NOOP
+            }
+
+            @Override
+            public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
+                InferenceException.throwIf(failed.contains(typePrereq), stmt.getStatementSourceReference(),
+                    "Type [%s] was not found.", typeQName);
+            }
+        });
+    }
+
+    @Override
+    protected final SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+
+    @Override
+    public final String internArgument(final String rawArgument) {
+        final String found = BUILT_IN_TYPES.get(rawArgument);
+        return found != null ? found : rawArgument;
+    }
+
+    @Override
+    public boolean hasArgumentSpecificSupports() {
+        return !ARGUMENT_SPECIFIC_SUPPORTS.isEmpty();
+    }
+
+    @Override
+    public StatementSupport<?, ?, ?> getSupportSpecificForArgument(final String argument) {
+        return ARGUMENT_SPECIFIC_SUPPORTS.get(argument);
+    }
+
+    static final SchemaPath typeEffectiveSchemaPath(final StmtContext<?, ?, ?> stmtCtx) {
+        final SchemaPath path = stmtCtx.getSchemaPath().get();
+        final SchemaPath parent = path.getParent();
+        final QName parentQName = parent.getLastComponent();
+        Preconditions.checkArgument(parentQName != null, "Path %s has an empty parent", path);
+
+        final QName qname = stmtCtx.getFromNamespace(QNameCacheNamespace.class,
+            QName.create(parentQName, path.getLastComponent().getLocalName()));
+        return parent.createChild(qname);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BinaryTypeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BinaryTypeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..002a5ff
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.InvalidLengthConstraintException;
+import org.opendaylight.yangtools.yang.model.util.type.LengthRestrictedTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.length.LengthEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+final class BinaryTypeEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, TypeStatement>
+        implements TypeEffectiveStatement<TypeStatement> {
+    private final @NonNull BinaryTypeDefinition typeDefinition;
+
+    BinaryTypeEffectiveStatementImpl(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final BinaryTypeDefinition baseType) {
+        super(ctx);
+
+        final LengthRestrictedTypeBuilder<BinaryTypeDefinition> builder =
+                RestrictedTypes.newBinaryBuilder(baseType, AbstractTypeStatementSupport.typeEffectiveSchemaPath(ctx));
+
+        for (EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof LengthEffectiveStatementImpl) {
+                final LengthEffectiveStatementImpl length = (LengthEffectiveStatementImpl)stmt;
+
+                try {
+                    builder.setLengthConstraint(length, length.argument());
+                } catch (IllegalStateException e) {
+                    throw new SourceException(ctx.getStatementSourceReference(), e,
+                        "Multiple length constraints encountered");
+                } catch (InvalidLengthConstraintException e) {
+                    throw new SourceException(ctx.getStatementSourceReference(), e, "Invalid length constraint %s",
+                        length.argument());
+                }
+            }
+
+            if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public BinaryTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BitsSpecificationEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BitsSpecificationEffectiveStatement.java
new file mode 100644 (file)
index 0000000..7764b19
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.BitsSpecification;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.model.util.type.BitsTypeBuilder;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.bit.BitEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+@VisibleForTesting
+// FIXME: hide this class
+public final class BitsSpecificationEffectiveStatement extends
+        DeclaredEffectiveStatementBase<String, BitsSpecification> implements TypeEffectiveStatement<BitsSpecification> {
+
+    private final @NonNull BitsTypeDefinition typeDefinition;
+
+    BitsSpecificationEffectiveStatement(
+            final StmtContext<String, BitsSpecification, EffectiveStatement<String, BitsSpecification>> ctx) {
+        super(ctx);
+
+        final BitsTypeBuilder builder = BaseTypes.bitsTypeBuilder(ctx.getSchemaPath().get());
+        Long highestPosition = null;
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof BitEffectiveStatementImpl) {
+                final BitEffectiveStatementImpl bitSubStmt = (BitEffectiveStatementImpl) stmt;
+
+                final long effectivePos;
+                if (bitSubStmt.getDeclaredPosition() == null) {
+                    if (highestPosition != null) {
+                        SourceException.throwIf(highestPosition == 4294967295L, ctx.getStatementSourceReference(),
+                                "Bit %s must have a position statement", bitSubStmt);
+                        effectivePos = highestPosition + 1;
+                    } else {
+                        effectivePos = 0L;
+                    }
+                } else {
+                    effectivePos = bitSubStmt.getDeclaredPosition();
+                }
+
+                final Bit bit = EffectiveTypeUtil.buildBit(bitSubStmt, effectivePos);
+                SourceException.throwIf(bit.getPosition() < 0L && bit.getPosition() > 4294967295L,
+                        ctx.getStatementSourceReference(), "Bit %s has illegal position", bit);
+
+                if (highestPosition == null || highestPosition < bit.getPosition()) {
+                    highestPosition = bit.getPosition();
+                }
+
+                builder.addBit(bit);
+            }
+            if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode) stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public BitsTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BitsSpecificationImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BitsSpecificationImpl.java
new file mode 100644 (file)
index 0000000..b771f3f
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.BitsSpecification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public class BitsSpecificationImpl extends AbstractDeclaredStatement<String> implements BitsSpecification {
+    protected BitsSpecificationImpl(final StmtContext<String, BitsSpecification, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BitsSpecificationSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BitsSpecificationSupport.java
new file mode 100644 (file)
index 0000000..6a21582
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.BitsSpecification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+final class BitsSpecificationSupport
+        extends AbstractStatementSupport<String, BitsSpecification, EffectiveStatement<String, BitsSpecification>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.TYPE)
+        .addMultiple(YangStmtMapping.BIT)
+        .build();
+
+    BitsSpecificationSupport() {
+        super(YangStmtMapping.TYPE);
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public BitsSpecification createDeclared(final StmtContext<String, BitsSpecification, ?> ctx) {
+        return new BitsSpecificationImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, BitsSpecification> createEffective(
+            final StmtContext<String, BitsSpecification, EffectiveStatement<String, BitsSpecification>> ctx) {
+        return new BitsSpecificationEffectiveStatement(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BitsTypeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BitsTypeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..b331ab6
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+import org.opendaylight.yangtools.yang.model.util.type.BitsTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.bit.BitEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+final class BitsTypeEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, TypeStatement>
+        implements TypeEffectiveStatement<TypeStatement> {
+
+    private final @NonNull BitsTypeDefinition typeDefinition;
+
+    BitsTypeEffectiveStatementImpl(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final BitsTypeDefinition baseType) {
+        super(ctx);
+
+        final BitsTypeBuilder builder = RestrictedTypes.newBitsBuilder(baseType, ctx.getSchemaPath().get());
+
+        final YangVersion yangVersion = ctx.getRootVersion();
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof BitEffectiveStatementImpl) {
+                SourceException.throwIf(yangVersion != YangVersion.VERSION_1_1, ctx.getStatementSourceReference(),
+                        "Restricted bits type is allowed only in YANG 1.1 version.");
+                final BitEffectiveStatementImpl bitSubStmt = (BitEffectiveStatementImpl) stmt;
+
+                final long effectivePos;
+                if (bitSubStmt.getDeclaredPosition() == null) {
+                    effectivePos = getBaseTypeBitPosition(bitSubStmt.getName(), baseType, ctx);
+                } else {
+                    effectivePos = bitSubStmt.getDeclaredPosition();
+                }
+
+                builder.addBit(EffectiveTypeUtil.buildBit(bitSubStmt, effectivePos));
+            } else if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode) stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    private static long getBaseTypeBitPosition(final String bitName, final BitsTypeDefinition baseType,
+            final StmtContext<?, ?, ?> ctx) {
+        for (Bit baseTypeBit : baseType.getBits()) {
+            if (bitName.equals(baseTypeBit.getName())) {
+                return baseTypeBit.getPosition();
+            }
+        }
+
+        throw new SourceException(ctx.getStatementSourceReference(),
+                "Bit '%s' is not a subset of its base bits type %s.", bitName, baseType.getQName());
+    }
+
+    @Override
+    public BitsTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BooleanTypeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BooleanTypeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..179dfee
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class BooleanTypeEffectiveStatementImpl extends AbstractTypeEffectiveStatement<BooleanTypeDefinition> {
+    BooleanTypeEffectiveStatementImpl(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final BooleanTypeDefinition baseType) {
+        super(ctx, RestrictedTypes.newBooleanBuilder(baseType,
+            AbstractTypeStatementSupport.typeEffectiveSchemaPath(ctx)));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BuiltinEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BuiltinEffectiveStatement.java
new file mode 100644 (file)
index 0000000..c8d4386
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.rfc7950.stmt.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Common shared effective statements for built-in types.
+ */
+enum BuiltinEffectiveStatement implements TypeEffectiveStatement<TypeStatement> {
+    BINARY(BaseTypes.binaryType()),
+    BOOLEAN(BaseTypes.booleanType()),
+    EMPTY(BaseTypes.emptyType()),
+    INSTANCE_IDENTIFIER(BaseTypes.instanceIdentifierType()),
+    INT8(BaseTypes.int8Type()),
+    INT16(BaseTypes.int16Type()),
+    INT32(BaseTypes.int32Type()),
+    INT64(BaseTypes.int64Type()),
+    STRING(BaseTypes.stringType()),
+    UINT8(BaseTypes.uint8Type()),
+    UINT16(BaseTypes.uint16Type()),
+    UINT32(BaseTypes.uint32Type()),
+    UINT64(BaseTypes.uint64Type());
+
+    private final @NonNull TypeDefinition<?> typedef;
+
+    BuiltinEffectiveStatement(final TypeDefinition<?> typedef) {
+        this.typedef = requireNonNull(typedef);
+    }
+
+    @Override
+    public TypeDefinition<?> getTypeDefinition() {
+        return typedef;
+    }
+
+    @Override
+    public final TypeStatement getDeclared() {
+        return null;
+    }
+
+    @Override
+    public final <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends V> get(final Class<N> namespace,
+            final K identifier) {
+        // FIXME: 5.0.0: implement this
+        return Optional.empty();
+    }
+
+    @Override
+    public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAll(final Class<N> namespace) {
+        // FIXME: 5.0.0: implement this
+        return ImmutableMap.of();
+    }
+
+    @Override
+    public final Collection<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public final StatementDefinition statementDefinition() {
+        return YangStmtMapping.TYPE;
+    }
+
+    @Override
+    public final String argument() {
+        return getTypeDefinition().getQName().getLocalName();
+    }
+
+    @Override
+    public final StatementSource getStatementSource() {
+        return StatementSource.CONTEXT;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BuiltinTypeStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/BuiltinTypeStatement.java
new file mode 100644 (file)
index 0000000..3645272
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. 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.rfc7950.stmt.type;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+
+final class BuiltinTypeStatement implements TypeStatement {
+    private static final ImmutableMap<String, BuiltinTypeStatement> BUILTINS;
+
+    static {
+        final Builder<String, BuiltinTypeStatement> builder = ImmutableMap.builder();
+        putBuiltin(builder, AbstractTypeStatementSupport.BINARY);
+        putBuiltin(builder, AbstractTypeStatementSupport.BOOLEAN);
+        putBuiltin(builder, AbstractTypeStatementSupport.EMPTY);
+        putBuiltin(builder, AbstractTypeStatementSupport.INSTANCE_IDENTIFIER);
+        putBuiltin(builder, AbstractTypeStatementSupport.INT8);
+        putBuiltin(builder, AbstractTypeStatementSupport.INT16);
+        putBuiltin(builder, AbstractTypeStatementSupport.INT32);
+        putBuiltin(builder, AbstractTypeStatementSupport.INT64);
+        putBuiltin(builder, AbstractTypeStatementSupport.STRING);
+        putBuiltin(builder, AbstractTypeStatementSupport.UINT8);
+        putBuiltin(builder, AbstractTypeStatementSupport.UINT16);
+        putBuiltin(builder, AbstractTypeStatementSupport.UINT32);
+        putBuiltin(builder, AbstractTypeStatementSupport.UINT64);
+        BUILTINS = builder.build();
+    }
+
+    private static void putBuiltin(final Builder<String, BuiltinTypeStatement> builder, final String argument) {
+        builder.put(argument, new BuiltinTypeStatement(argument));
+    }
+
+    private final String argument;
+
+    private BuiltinTypeStatement(final String argument) {
+        this.argument = requireNonNull(argument);
+    }
+
+    static TypeStatement maybeReplace(final TypeStatementImpl orig) {
+        if (orig.declaredSubstatements().isEmpty() && orig.getStatementSource() == StatementSource.DECLARATION
+                && orig.statementDefinition() == YangStmtMapping.TYPE) {
+            final BuiltinTypeStatement builtin = BUILTINS.get(orig.argument());
+            if (builtin != null) {
+                return builtin;
+            }
+        }
+
+        return orig;
+    }
+
+    @Override
+    public String argument() {
+        return argument;
+    }
+
+    @Override
+    public String rawArgument() {
+        return argument;
+    }
+
+    @Override
+    public Collection<? extends DeclaredStatement<?>> declaredSubstatements() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public StatementDefinition statementDefinition() {
+        return YangStmtMapping.TYPE;
+    }
+
+    @Override
+    public StatementSource getStatementSource() {
+        return StatementSource.DECLARATION;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/Decimal64SpecificationEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/Decimal64SpecificationEffectiveStatement.java
new file mode 100644 (file)
index 0000000..cf14cc0
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FractionDigitsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.Decimal64Specification;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.model.util.type.DecimalTypeBuilder;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.range.RangeEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+// FIXME: hide this class
+@VisibleForTesting
+public final class Decimal64SpecificationEffectiveStatement
+        extends DeclaredEffectiveStatementBase<String, Decimal64Specification>
+        implements TypeEffectiveStatement<Decimal64Specification> {
+
+    private final @NonNull DecimalTypeDefinition typeDefinition;
+
+    Decimal64SpecificationEffectiveStatement(
+            final StmtContext<String, Decimal64Specification, EffectiveStatement<String, Decimal64Specification>> ctx) {
+        super(ctx);
+
+        final DecimalTypeBuilder builder = BaseTypes.decimalTypeBuilder(ctx.getSchemaPath().get());
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof FractionDigitsEffectiveStatement) {
+                builder.setFractionDigits(((FractionDigitsEffectiveStatement) stmt).argument());
+            }
+            if (stmt instanceof RangeEffectiveStatementImpl) {
+                final RangeEffectiveStatementImpl range = (RangeEffectiveStatementImpl) stmt;
+                builder.setRangeConstraint(range, range.argument());
+            }
+            if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public DecimalTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/Decimal64SpecificationImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/Decimal64SpecificationImpl.java
new file mode 100644 (file)
index 0000000..d5d0506
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.Decimal64Specification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class Decimal64SpecificationImpl extends AbstractDeclaredStatement<String> implements Decimal64Specification {
+    Decimal64SpecificationImpl(final StmtContext<String, Decimal64Specification, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/Decimal64SpecificationSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/Decimal64SpecificationSupport.java
new file mode 100644 (file)
index 0000000..59163ad
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.Decimal64Specification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+final class Decimal64SpecificationSupport extends AbstractStatementSupport<String, Decimal64Specification,
+        EffectiveStatement<String, Decimal64Specification>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.TYPE)
+        .addMandatory(YangStmtMapping.FRACTION_DIGITS)
+        .addOptional(YangStmtMapping.RANGE)
+        .build();
+
+    Decimal64SpecificationSupport() {
+        super(YangStmtMapping.TYPE);
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public Decimal64Specification createDeclared(final StmtContext<String, Decimal64Specification, ?> ctx) {
+        return new Decimal64SpecificationImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, Decimal64Specification> createEffective(final StmtContext<String,
+            Decimal64Specification, EffectiveStatement<String, Decimal64Specification>> ctx) {
+        return new Decimal64SpecificationEffectiveStatement(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/DecimalTypeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/DecimalTypeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..b5f8c4c
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import java.math.BigDecimal;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FractionDigitsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.RangeRestrictedTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.range.RangeEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+final class DecimalTypeEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, TypeStatement>
+        implements TypeEffectiveStatement<TypeStatement> {
+    private final @NonNull DecimalTypeDefinition typeDefinition;
+
+    DecimalTypeEffectiveStatementImpl(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final DecimalTypeDefinition baseType) {
+        super(ctx);
+
+        final RangeRestrictedTypeBuilder<DecimalTypeDefinition, BigDecimal> builder =
+                RestrictedTypes.newDecima64Builder(baseType, AbstractTypeStatementSupport.typeEffectiveSchemaPath(ctx));
+
+        for (EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof RangeEffectiveStatementImpl) {
+                final RangeEffectiveStatementImpl range = (RangeEffectiveStatementImpl) stmt;
+                builder.setRangeConstraint(range, range.argument());
+            }
+            if (stmt instanceof FractionDigitsEffectiveStatement) {
+                final Integer digits = ((FractionDigitsEffectiveStatement)stmt).argument();
+                SourceException.throwIf(baseType.getFractionDigits() != digits, ctx.getStatementSourceReference(),
+                    "Cannot override fraction-digits from base type %s to %s", baseType, digits);
+            }
+            if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public DecimalTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EffectiveTypeUtil.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EffectiveTypeUtil.java
new file mode 100644 (file)
index 0000000..072e687
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+import org.opendaylight.yangtools.yang.model.util.type.BitBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.EnumPairBuilder;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.bit.BitEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.enum_.EnumEffectiveStatementImpl;
+
+@Beta
+final class EffectiveTypeUtil {
+    private EffectiveTypeUtil() {
+        throw new UnsupportedOperationException();
+    }
+
+    static @NonNull Bit buildBit(final @NonNull BitEffectiveStatementImpl stmt, final long effectivePos) {
+        // TODO: code duplication with EnumPairBuilder is indicating we could use a common Builder<?> interface
+        final BitBuilder builder = BitBuilder.create(stmt.getPath(), effectivePos).setStatus(stmt.getStatus());
+        stmt.getDescription().ifPresent(builder::setDescription);
+        stmt.getReference().ifPresent(builder::setReference);
+
+        return builder.build();
+    }
+
+    static @NonNull EnumPair buildEnumPair(final @NonNull EnumEffectiveStatementImpl stmt, final int effectiveValue) {
+        final EnumPairBuilder builder = EnumPairBuilder.create(stmt.getName(), effectiveValue)
+                .setStatus(stmt.getStatus()).setUnknownSchemaNodes(stmt.getUnknownSchemaNodes());
+        stmt.getDescription().ifPresent(builder::setDescription);
+        stmt.getReference().ifPresent(builder::setReference);
+
+        return builder.build();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EmptyTypeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EmptyTypeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..488b9dc
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class EmptyTypeEffectiveStatementImpl extends AbstractTypeEffectiveStatement<EmptyTypeDefinition> {
+    EmptyTypeEffectiveStatementImpl(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final EmptyTypeDefinition baseType) {
+        super(ctx, RestrictedTypes.newEmptyBuilder(baseType,
+            AbstractTypeStatementSupport.typeEffectiveSchemaPath(ctx)));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EnumSpecificationEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EnumSpecificationEffectiveStatement.java
new file mode 100644 (file)
index 0000000..e2bc0b8
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.EnumSpecification;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.model.util.type.EnumerationTypeBuilder;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.enum_.EnumEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+@VisibleForTesting
+// FIXME: hide this class
+public final class EnumSpecificationEffectiveStatement extends
+        DeclaredEffectiveStatementBase<String, EnumSpecification> implements
+        TypeEffectiveStatement<EnumSpecification> {
+
+    private final @NonNull EnumTypeDefinition typeDefinition;
+
+    EnumSpecificationEffectiveStatement(
+            final StmtContext<String, EnumSpecification, EffectiveStatement<String, EnumSpecification>> ctx) {
+        super(ctx);
+
+        final EnumerationTypeBuilder builder = BaseTypes.enumerationTypeBuilder(ctx.getSchemaPath().get());
+        Integer highestValue = null;
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof EnumEffectiveStatementImpl) {
+                final EnumEffectiveStatementImpl enumSubStmt = (EnumEffectiveStatementImpl) stmt;
+
+                final int effectiveValue;
+                if (enumSubStmt.getDeclaredValue() == null) {
+                    if (highestValue != null) {
+                        SourceException.throwIf(highestValue == 2147483647, ctx.getStatementSourceReference(),
+                                "Enum '%s' must have a value statement", enumSubStmt);
+                        effectiveValue = highestValue + 1;
+                    } else {
+                        effectiveValue = 0;
+                    }
+                } else {
+                    effectiveValue = enumSubStmt.getDeclaredValue();
+                }
+
+                final EnumPair pair = EffectiveTypeUtil.buildEnumPair(enumSubStmt, effectiveValue);
+                if (highestValue == null || highestValue < pair.getValue()) {
+                    highestValue = pair.getValue();
+                }
+
+                builder.addEnum(pair);
+            }
+            if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode) stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public EnumTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EnumSpecificationImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EnumSpecificationImpl.java
new file mode 100644 (file)
index 0000000..2d760f1
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.EnumSpecification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class EnumSpecificationImpl extends AbstractDeclaredStatement<String> implements EnumSpecification {
+    EnumSpecificationImpl(final StmtContext<String, EnumSpecification, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EnumSpecificationSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EnumSpecificationSupport.java
new file mode 100644 (file)
index 0000000..01aea39
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.EnumSpecification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+final class EnumSpecificationSupport
+        extends AbstractStatementSupport<String, EnumSpecification, EffectiveStatement<String, EnumSpecification>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .TYPE)
+        .addMultiple(YangStmtMapping.ENUM)
+        .build();
+
+    EnumSpecificationSupport() {
+        super(YangStmtMapping.TYPE);
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public EnumSpecification createDeclared(final StmtContext<String, EnumSpecification, ?> ctx) {
+        return new EnumSpecificationImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, EnumSpecification> createEffective(
+            final StmtContext<String, EnumSpecification, EffectiveStatement<String, EnumSpecification>> ctx) {
+        return new EnumSpecificationEffectiveStatement(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EnumTypeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/EnumTypeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..8c8e930
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+import org.opendaylight.yangtools.yang.model.util.type.EnumerationTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.enum_.EnumEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+final class EnumTypeEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, TypeStatement>
+        implements TypeEffectiveStatement<TypeStatement> {
+
+    private final @NonNull EnumTypeDefinition typeDefinition;
+
+    EnumTypeEffectiveStatementImpl(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final EnumTypeDefinition baseType) {
+        super(ctx);
+
+        final EnumerationTypeBuilder builder = RestrictedTypes.newEnumerationBuilder(baseType,
+                ctx.getSchemaPath().get());
+
+        final YangVersion yangVersion = ctx.getRootVersion();
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof EnumEffectiveStatementImpl) {
+                SourceException.throwIf(yangVersion != YangVersion.VERSION_1_1, ctx.getStatementSourceReference(),
+                        "Restricted enumeration type is allowed only in YANG 1.1 version.");
+
+                final EnumEffectiveStatementImpl enumSubStmt = (EnumEffectiveStatementImpl) stmt;
+
+                final int effectiveValue;
+                if (enumSubStmt.getDeclaredValue() == null) {
+                    effectiveValue = getBaseTypeEnumValue(enumSubStmt.getName(), baseType, ctx);
+                } else {
+                    effectiveValue = enumSubStmt.getDeclaredValue();
+                }
+
+                builder.addEnum(EffectiveTypeUtil.buildEnumPair(enumSubStmt, effectiveValue));
+            } else if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode) stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    private static int getBaseTypeEnumValue(final String enumName, final EnumTypeDefinition baseType,
+            final StmtContext<?, ?, ?> ctx) {
+        for (EnumPair baseTypeEnumPair : baseType.getValues()) {
+            if (enumName.equals(baseTypeEnumPair.getName())) {
+                return baseTypeEnumPair.getValue();
+            }
+        }
+
+        throw new SourceException(ctx.getStatementSourceReference(),
+                "Enum '%s' is not a subset of its base enumeration type %s.", enumName, baseType.getQName());
+    }
+
+    @Override
+    public EnumTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityRefSpecificationEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityRefSpecificationEffectiveStatement.java
new file mode 100644 (file)
index 0000000..a0e0334
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BaseEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.IdentityRefSpecification;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.model.util.type.IdentityrefTypeBuilder;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.IdentityNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+// FIXME: hide this class
+public final class IdentityRefSpecificationEffectiveStatement extends
+        DeclaredEffectiveStatementBase<String, IdentityRefSpecification> implements
+        TypeEffectiveStatement<IdentityRefSpecification> {
+
+    private final @NonNull IdentityrefTypeDefinition typeDefinition;
+
+    IdentityRefSpecificationEffectiveStatement(final StmtContext<String, IdentityRefSpecification,
+            EffectiveStatement<String, IdentityRefSpecification>> ctx) {
+        super(ctx);
+
+        final IdentityrefTypeBuilder builder = BaseTypes.identityrefTypeBuilder(ctx.getSchemaPath().get());
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof BaseEffectiveStatement) {
+                final QName identityQName = ((BaseEffectiveStatement) stmt).argument();
+                final StmtContext<?, IdentityStatement, IdentityEffectiveStatement> identityCtx =
+                        ctx.getFromNamespace(IdentityNamespace.class, identityQName);
+                builder.addIdentity((IdentitySchemaNode) identityCtx.buildEffective());
+            }
+            if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public IdentityrefTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityRefSpecificationImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityRefSpecificationImpl.java
new file mode 100644 (file)
index 0000000..02ba0bb
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.IdentityRefSpecification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class IdentityRefSpecificationImpl extends AbstractDeclaredStatement<String> implements IdentityRefSpecification {
+    IdentityRefSpecificationImpl(final StmtContext<String, IdentityRefSpecification, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityRefSpecificationRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityRefSpecificationRFC6020Support.java
new file mode 100644 (file)
index 0000000..bc2eb81
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+final class IdentityRefSpecificationRFC6020Support extends AbstractIdentityRefSpecificationSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .TYPE)
+        .addMandatory(YangStmtMapping.BASE)
+        .build();
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityrefSpecificationRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityrefSpecificationRFC7950Support.java
new file mode 100644 (file)
index 0000000..0323dd4
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2017 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 identityref statement.
+ */
+final class IdentityrefSpecificationRFC7950Support extends AbstractIdentityRefSpecificationSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .TYPE)
+            .addMultiple(YangStmtMapping.BASE)
+            .build();
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityrefTypeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IdentityrefTypeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..5101590
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class IdentityrefTypeEffectiveStatementImpl extends AbstractTypeEffectiveStatement<IdentityrefTypeDefinition> {
+    IdentityrefTypeEffectiveStatementImpl(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final IdentityrefTypeDefinition baseType) {
+        super(ctx, RestrictedTypes.newIdentityrefBuilder(baseType,
+            AbstractTypeStatementSupport.typeEffectiveSchemaPath(ctx)));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/InstanceIdentifierSpecificationEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/InstanceIdentifierSpecificationEffectiveStatement.java
new file mode 100644 (file)
index 0000000..0dfbf4f
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RequireInstanceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.InstanceIdentifierSpecification;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.model.util.type.InstanceIdentifierTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class InstanceIdentifierSpecificationEffectiveStatement
+        extends DeclaredEffectiveStatementBase<String, InstanceIdentifierSpecification>
+        implements TypeEffectiveStatement<InstanceIdentifierSpecification> {
+
+    private final @NonNull InstanceIdentifierTypeDefinition typeDefinition;
+
+    InstanceIdentifierSpecificationEffectiveStatement(final StmtContext<String,
+            InstanceIdentifierSpecification, EffectiveStatement<String, InstanceIdentifierSpecification>> ctx) {
+        super(ctx);
+
+        final InstanceIdentifierTypeBuilder builder = RestrictedTypes.newInstanceIdentifierBuilder(
+            BaseTypes.instanceIdentifierType(), ctx.getSchemaPath().get());
+
+        for (EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof RequireInstanceEffectiveStatement) {
+                builder.setRequireInstance(((RequireInstanceEffectiveStatement)stmt).argument());
+            }
+            if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public InstanceIdentifierTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/InstanceIdentifierSpecificationImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/InstanceIdentifierSpecificationImpl.java
new file mode 100644 (file)
index 0000000..b6c9f02
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.InstanceIdentifierSpecification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class InstanceIdentifierSpecificationImpl extends AbstractDeclaredStatement<String>
+        implements InstanceIdentifierSpecification {
+    InstanceIdentifierSpecificationImpl(final StmtContext<String, InstanceIdentifierSpecification, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/InstanceIdentifierSpecificationSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/InstanceIdentifierSpecificationSupport.java
new file mode 100644 (file)
index 0000000..be40ac3
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.InstanceIdentifierSpecification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+final class InstanceIdentifierSpecificationSupport extends AbstractStatementSupport<String,
+        InstanceIdentifierSpecification, EffectiveStatement<String, InstanceIdentifierSpecification>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.TYPE)
+        .addOptional(YangStmtMapping.REQUIRE_INSTANCE)
+        .build();
+
+    InstanceIdentifierSpecificationSupport() {
+        super(YangStmtMapping.TYPE);
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public InstanceIdentifierSpecification createDeclared(
+            final StmtContext<String, InstanceIdentifierSpecification, ?> ctx) {
+        return new InstanceIdentifierSpecificationImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, InstanceIdentifierSpecification> createEffective(
+            final StmtContext<String, InstanceIdentifierSpecification,
+            EffectiveStatement<String, InstanceIdentifierSpecification>> ctx) {
+        return new InstanceIdentifierSpecificationEffectiveStatement(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/InstanceIdentifierTypeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/InstanceIdentifierTypeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..67f03e6
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RequireInstanceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.InstanceIdentifierTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class InstanceIdentifierTypeEffectiveStatementImpl
+        extends DeclaredEffectiveStatementBase<String, TypeStatement> implements TypeEffectiveStatement<TypeStatement> {
+    private final @NonNull InstanceIdentifierTypeDefinition typeDefinition;
+
+    InstanceIdentifierTypeEffectiveStatementImpl(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final InstanceIdentifierTypeDefinition baseType) {
+        super(ctx);
+
+        final InstanceIdentifierTypeBuilder builder =
+                RestrictedTypes.newInstanceIdentifierBuilder(baseType,
+                    AbstractTypeStatementSupport.typeEffectiveSchemaPath(ctx));
+
+        for (EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof RequireInstanceEffectiveStatement) {
+                builder.setRequireInstance(((RequireInstanceEffectiveStatement)stmt).argument());
+            }
+            if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public InstanceIdentifierTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IntegralTypeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/IntegralTypeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..f214389
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.InvalidRangeConstraintException;
+import org.opendaylight.yangtools.yang.model.util.type.RangeRestrictedTypeBuilder;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.range.RangeEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+final class IntegralTypeEffectiveStatementImpl<T extends RangeRestrictedTypeDefinition<T, N>,
+        N extends Number & Comparable<N>> extends DeclaredEffectiveStatementBase<String, TypeStatement>
+        implements TypeEffectiveStatement<TypeStatement> {
+
+    private final @NonNull T typeDefinition;
+
+    IntegralTypeEffectiveStatementImpl(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final RangeRestrictedTypeBuilder<T, N> builder) {
+        super(ctx);
+
+        for (EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof RangeEffectiveStatementImpl) {
+                final RangeEffectiveStatementImpl rangeStmt = (RangeEffectiveStatementImpl)stmt;
+                builder.setRangeConstraint(rangeStmt, rangeStmt.argument());
+            }
+            if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            }
+        }
+
+        try {
+            typeDefinition = builder.build();
+        } catch (InvalidRangeConstraintException e) {
+            throw new SourceException(ctx.getStatementSourceReference(), e, "Invalid range constraint: %s",
+                e.getOffendingRanges());
+        }
+    }
+
+    @Override
+    public T getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefSpecificationEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefSpecificationEffectiveStatement.java
new file mode 100644 (file)
index 0000000..8281148
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PathEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RequireInstanceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.LeafrefSpecification;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.model.util.type.LeafrefTypeBuilder;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+@VisibleForTesting
+// FIXME: hide this class
+public final class LeafrefSpecificationEffectiveStatement
+        extends DeclaredEffectiveStatementBase<String, LeafrefSpecification>
+        implements TypeEffectiveStatement<LeafrefSpecification> {
+    private final @NonNull LeafrefTypeDefinition typeDefinition;
+
+    LeafrefSpecificationEffectiveStatement(final StmtContext<String, LeafrefSpecification,
+            EffectiveStatement<String, LeafrefSpecification>> ctx) {
+        super(ctx);
+
+        final LeafrefTypeBuilder builder = BaseTypes.leafrefTypeBuilder(ctx.getSchemaPath().get());
+
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof PathEffectiveStatement) {
+                builder.setPathStatement(((PathEffectiveStatement) stmt).argument());
+            } else if (stmt instanceof RequireInstanceEffectiveStatement) {
+                builder.setRequireInstance(((RequireInstanceEffectiveStatement)stmt).argument());
+            } else if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public LeafrefTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefSpecificationImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefSpecificationImpl.java
new file mode 100644 (file)
index 0000000..27dd08f
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.LeafrefSpecification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class LeafrefSpecificationImpl extends AbstractDeclaredStatement<String> implements LeafrefSpecification {
+    LeafrefSpecificationImpl(final StmtContext<String, LeafrefSpecification, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefSpecificationRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefSpecificationRFC6020Support.java
new file mode 100644 (file)
index 0000000..699a3fe
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+final class LeafrefSpecificationRFC6020Support extends AbstractLeafrefSpecificationSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .TYPE)
+        .addMandatory(YangStmtMapping.PATH)
+        .build();
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefSpecificationRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefSpecificationRFC7950Support.java
new file mode 100644 (file)
index 0000000..24d9f8e
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 leafref statement.
+ */
+final class LeafrefSpecificationRFC7950Support extends AbstractLeafrefSpecificationSupport {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+            .TYPE)
+            .addMandatory(YangStmtMapping.PATH)
+            .addOptional(YangStmtMapping.REQUIRE_INSTANCE)
+            .build();
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefTypeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/LeafrefTypeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..f5c9a67
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RequireInstanceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.RequireInstanceRestrictedTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class LeafrefTypeEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, TypeStatement>
+        implements TypeEffectiveStatement<TypeStatement> {
+
+    private final @NonNull LeafrefTypeDefinition typeDefinition;
+
+    LeafrefTypeEffectiveStatementImpl(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final LeafrefTypeDefinition baseType) {
+        super(ctx);
+
+        final RequireInstanceRestrictedTypeBuilder<LeafrefTypeDefinition> builder =
+                RestrictedTypes.newLeafrefBuilder(baseType, AbstractTypeStatementSupport.typeEffectiveSchemaPath(ctx));
+
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof RequireInstanceEffectiveStatement) {
+                builder.setRequireInstance(((RequireInstanceEffectiveStatement) stmt).argument());
+            } else if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public LeafrefTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/NumericalRestrictionsEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/NumericalRestrictionsEffectiveStatement.java
new file mode 100644 (file)
index 0000000..f81d370
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.NumericalRestrictions;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+// FIXME: this class is not used anywhere, decide its future
+final class NumericalRestrictionsEffectiveStatement
+        extends DeclaredEffectiveStatementBase<String, NumericalRestrictions> {
+
+    NumericalRestrictionsEffectiveStatement(final StmtContext<String, NumericalRestrictions, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/NumericalRestrictionsImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/NumericalRestrictionsImpl.java
new file mode 100644 (file)
index 0000000..5de2822
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.NumericalRestrictions;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+// FIXME: this class is not used anywhere, decide its future
+final class NumericalRestrictionsImpl extends AbstractDeclaredStatement<String> implements NumericalRestrictions {
+    NumericalRestrictionsImpl(final StmtContext<String, NumericalRestrictions, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/NumericalRestrictionsSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/NumericalRestrictionsSupport.java
new file mode 100644 (file)
index 0000000..cc63247
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.NumericalRestrictions;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+// FIXME: this class is not used anywhere, decide its future
+final class NumericalRestrictionsSupport extends
+        AbstractStatementSupport<String, NumericalRestrictions, EffectiveStatement<String, NumericalRestrictions>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .TYPE)
+        .addMandatory(YangStmtMapping.RANGE)
+        .build();
+
+    NumericalRestrictionsSupport() {
+        super(YangStmtMapping.TYPE);
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public NumericalRestrictions createDeclared(final StmtContext<String, NumericalRestrictions, ?> ctx) {
+        return new NumericalRestrictionsImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, NumericalRestrictions> createEffective(
+            final StmtContext<String, NumericalRestrictions,
+            EffectiveStatement<String, NumericalRestrictions>> ctx) {
+        return new NumericalRestrictionsEffectiveStatement(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/StringRestrictionsEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/StringRestrictionsEffectiveStatement.java
new file mode 100644 (file)
index 0000000..eba180e
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.StringRestrictions;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+// FIXME: this class is not used anywhere, decide its future
+final class StringRestrictionsEffectiveStatement
+        extends DeclaredEffectiveStatementBase<String, StringRestrictions> {
+    StringRestrictionsEffectiveStatement(final StmtContext<String, StringRestrictions, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/StringRestrictionsImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/StringRestrictionsImpl.java
new file mode 100644 (file)
index 0000000..ef749e4
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+// FIXME: this class is not used anywhere, decide its future
+final class StringRestrictionsImpl extends AbstractDeclaredStatement<String> implements
+        TypeStatement.StringRestrictions {
+    StringRestrictionsImpl(final StmtContext<String, StringRestrictions, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/StringRestrictionsSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/StringRestrictionsSupport.java
new file mode 100644 (file)
index 0000000..15f6761
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.StringRestrictions;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+//FIXME: this class is not used anywhere, decide its future
+final class StringRestrictionsSupport
+        extends AbstractStatementSupport<String, StringRestrictions, EffectiveStatement<String, StringRestrictions>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .TYPE)
+        .addOptional(YangStmtMapping.LENGTH)
+        .addAny(YangStmtMapping.PATTERN)
+        .build();
+
+    StringRestrictionsSupport() {
+        super(YangStmtMapping.TYPE);
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public StringRestrictions createDeclared(final StmtContext<String, StringRestrictions, ?> ctx) {
+        return new StringRestrictionsImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, StringRestrictions> createEffective(
+            final StmtContext<String, StringRestrictions, EffectiveStatement<String, StringRestrictions>> ctx) {
+        return new StringRestrictionsEffectiveStatement(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/StringTypeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/StringTypeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..d25880f
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PatternEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.InvalidLengthConstraintException;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.model.util.type.StringTypeBuilder;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.length.LengthEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class StringTypeEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, TypeStatement>
+        implements TypeEffectiveStatement<TypeStatement> {
+    private static final Logger LOG = LoggerFactory.getLogger(StringTypeEffectiveStatementImpl.class);
+
+    private final @NonNull StringTypeDefinition typeDefinition;
+
+    StringTypeEffectiveStatementImpl(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final StringTypeDefinition baseType) {
+        super(ctx);
+
+        final StringTypeBuilder builder = RestrictedTypes.newStringBuilder(baseType,
+            AbstractTypeStatementSupport.typeEffectiveSchemaPath(ctx));
+
+        for (EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof LengthEffectiveStatementImpl) {
+                final LengthEffectiveStatementImpl length = (LengthEffectiveStatementImpl)stmt;
+
+                try {
+                    builder.setLengthConstraint(length, length.argument());
+                } catch (IllegalStateException e) {
+                    throw new SourceException(ctx.getStatementSourceReference(), e,
+                            "Multiple length constraints encountered");
+                } catch (InvalidLengthConstraintException e) {
+                    throw new SourceException(ctx.getStatementSourceReference(), e, "Invalid length constraint %s",
+                        length.argument());
+                }
+            }
+            if (stmt instanceof PatternEffectiveStatement) {
+                final PatternConstraint pattern = ((PatternEffectiveStatement)stmt).argument();
+                if (pattern != null) {
+                    builder.addPatternConstraint(pattern);
+                } else {
+                    LOG.debug("Ignoring empty pattern statement {}", stmt);
+                }
+            }
+            if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public StringTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/TypeStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/TypeStatementImpl.java
new file mode 100644 (file)
index 0000000..091208c
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class TypeStatementImpl extends AbstractDeclaredStatement<String> implements TypeStatement {
+    TypeStatementImpl(final StmtContext<String, TypeStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/TypeStatementRFC6020Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/TypeStatementRFC6020Support.java
new file mode 100644 (file)
index 0000000..22a61b4
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+public final class TypeStatementRFC6020Support extends AbstractTypeStatementSupport {
+    private static final TypeStatementRFC6020Support INSTANCE = new TypeStatementRFC6020Support();
+
+    private TypeStatementRFC6020Support() {
+        // Hidden
+    }
+
+    public static TypeStatementRFC6020Support getInstance() {
+        return INSTANCE;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/TypeStatementRFC7950Support.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/TypeStatementRFC7950Support.java
new file mode 100644 (file)
index 0000000..ba61b92
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017 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.rfc7950.stmt.type;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+
+/**
+ * Class providing necessary support for processing YANG 1.1 Type statement.
+ */
+@Beta
+public final class TypeStatementRFC7950Support extends AbstractTypeStatementSupport {
+    private static final ImmutableMap<String, StatementSupport<?, ?, ?>> ARGUMENT_SPECIFIC_SUPPORTS = ImmutableMap.of(
+        LEAF_REF, new LeafrefSpecificationRFC7950Support(),
+        IDENTITY_REF, new IdentityrefSpecificationRFC7950Support());
+    private static final TypeStatementRFC7950Support INSTANCE = new TypeStatementRFC7950Support();
+
+    private TypeStatementRFC7950Support() {
+        // Hidden
+    }
+
+    public static TypeStatementRFC7950Support getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public boolean hasArgumentSpecificSupports() {
+        return !ARGUMENT_SPECIFIC_SUPPORTS.isEmpty() || super.hasArgumentSpecificSupports();
+    }
+
+    @Override
+    public StatementSupport<?, ?, ?> getSupportSpecificForArgument(final String argument) {
+        final StatementSupport<?, ?, ?> potential = ARGUMENT_SPECIFIC_SUPPORTS.get(argument);
+        return potential != null ? potential : super.getSupportSpecificForArgument(argument);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/UnionSpecificationEffectiveStatement.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/UnionSpecificationEffectiveStatement.java
new file mode 100644 (file)
index 0000000..37c7803
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.UnionSpecification;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.model.util.type.UnionTypeBuilder;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+@VisibleForTesting
+// FIXME: hide this class
+public final class UnionSpecificationEffectiveStatement extends
+        DeclaredEffectiveStatementBase<String, UnionSpecification> implements
+        TypeEffectiveStatement<UnionSpecification> {
+
+    private final @NonNull UnionTypeDefinition typeDefinition;
+
+    UnionSpecificationEffectiveStatement(
+            final StmtContext<String, UnionSpecification, EffectiveStatement<String, UnionSpecification>> ctx) {
+        super(ctx);
+
+        final UnionTypeBuilder builder = BaseTypes.unionTypeBuilder(ctx.getSchemaPath().get());
+
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof TypeEffectiveStatement) {
+                builder.addType(((TypeEffectiveStatement<?>)stmt).getTypeDefinition());
+            }
+            if (stmt instanceof UnknownSchemaNode) {
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            }
+        }
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public UnionTypeDefinition getTypeDefinition() {
+        return typeDefinition;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/UnionSpecificationImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/UnionSpecificationImpl.java
new file mode 100644 (file)
index 0000000..833dd04
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.UnionSpecification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class UnionSpecificationImpl extends AbstractDeclaredStatement<String> implements UnionSpecification {
+    UnionSpecificationImpl(final StmtContext<String, UnionSpecification, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/UnionSpecificationSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/UnionSpecificationSupport.java
new file mode 100644 (file)
index 0000000..3fcc5f7
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement.UnionSpecification;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+final class UnionSpecificationSupport extends
+    AbstractStatementSupport<String, UnionSpecification, EffectiveStatement<String, UnionSpecification>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .TYPE)
+        .addMultiple(YangStmtMapping.TYPE)
+        .build();
+
+    UnionSpecificationSupport() {
+        super(YangStmtMapping.TYPE);
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public UnionSpecification createDeclared(final StmtContext<String, UnionSpecification, ?> ctx) {
+        return new UnionSpecificationImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, UnionSpecification> createEffective(
+            final StmtContext<String, UnionSpecification, EffectiveStatement<String, UnionSpecification>> ctx) {
+        return new UnionSpecificationEffectiveStatement(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/UnionTypeEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/type/UnionTypeEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..d98465b
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.type;
+
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.RestrictedTypes;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class UnionTypeEffectiveStatementImpl extends AbstractTypeEffectiveStatement<UnionTypeDefinition> {
+    UnionTypeEffectiveStatementImpl(
+            final StmtContext<String, TypeStatement, EffectiveStatement<String, TypeStatement>> ctx,
+            final UnionTypeDefinition baseType) {
+        super(ctx, RestrictedTypes.newUnionBuilder(baseType,
+            AbstractTypeStatementSupport.typeEffectiveSchemaPath(ctx)));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/typedef/TypedefEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/typedef/TypedefEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..8ab0ae3
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.typedef;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.model.api.stmt.DefaultEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnitsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.util.type.DerivedTypeBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.DerivedTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveSchemaNode;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.EffectiveStmtUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class TypedefEffectiveStatementImpl extends AbstractEffectiveSchemaNode<TypedefStatement> implements
+        TypedefEffectiveStatement {
+    private static final Logger LOG = LoggerFactory.getLogger(TypedefEffectiveStatementImpl.class);
+
+    private final @NonNull TypeDefinition<?> typeDefinition;
+
+    private volatile TypeEffectiveStatement<TypeStatement> typeStatement;
+
+    TypedefEffectiveStatementImpl(final StmtContext<QName, TypedefStatement, ?> ctx) {
+        super(ctx);
+
+        final TypeEffectiveStatement<?> typeEffectiveStmt = firstSubstatementOfType(TypeEffectiveStatement.class);
+        final DerivedTypeBuilder<?> builder = DerivedTypes.derivedTypeBuilder(typeEffectiveStmt.getTypeDefinition(),
+            ctx.getSchemaPath().get());
+        String dflt = null;
+        for (final EffectiveStatement<?, ?> stmt : effectiveSubstatements()) {
+            if (stmt instanceof DefaultEffectiveStatement) {
+                dflt = ((DefaultEffectiveStatement) stmt).argument();
+                builder.setDefaultValue(dflt);
+            } else if (stmt instanceof DescriptionEffectiveStatement) {
+                builder.setDescription(((DescriptionEffectiveStatement)stmt).argument());
+            } else if (stmt instanceof ReferenceEffectiveStatement) {
+                builder.setReference(((ReferenceEffectiveStatement)stmt).argument());
+            } else if (stmt instanceof StatusEffectiveStatement) {
+                builder.setStatus(((StatusEffectiveStatement)stmt).argument());
+            } else if (stmt instanceof UnitsEffectiveStatement) {
+                builder.setUnits(((UnitsEffectiveStatement)stmt).argument());
+            } else if (stmt instanceof UnknownSchemaNode) {
+                // FIXME: should not directly implement, I think
+                builder.addUnknownSchemaNode((UnknownSchemaNode)stmt);
+            } else {
+                if (!(stmt instanceof TypeEffectiveStatement)) {
+                    LOG.debug("Ignoring statement {}", stmt);
+                }
+            }
+        }
+
+        SourceException.throwIf(
+            EffectiveStmtUtils.hasDefaultValueMarkedWithIfFeature(ctx.getRootVersion(), typeEffectiveStmt, dflt),
+            ctx.getStatementSourceReference(),
+            "Typedef '%s' has default value '%s' marked with an if-feature statement.", ctx.getStatementArgument(),
+            dflt);
+
+        typeDefinition = builder.build();
+    }
+
+    @Override
+    public TypeDefinition<?> getTypeDefinition() {
+        return typeDefinition;
+    }
+
+    @Override
+    public TypeEffectiveStatement<TypeStatement> asTypeEffectiveStatement() {
+        TypeEffectiveStatement<TypeStatement> ret = typeStatement;
+        if (ret == null) {
+            synchronized (this) {
+                ret = typeStatement;
+                if (ret == null) {
+                    typeStatement = ret = new ProxyTypeEffectiveStatement();
+                }
+            }
+        }
+
+        return ret;
+    }
+
+    private final class ProxyTypeEffectiveStatement implements TypeEffectiveStatement<TypeStatement> {
+        @Override
+        public TypeStatement getDeclared() {
+            return null;
+        }
+
+        @Override
+        public <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends V> get(final Class<N> namespace,
+                final K identifier) {
+            return TypedefEffectiveStatementImpl.this.get(namespace, identifier);
+        }
+
+        @Override
+        public <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAll(final Class<N> namespace) {
+            return TypedefEffectiveStatementImpl.this.getAll(namespace);
+        }
+
+        @Override
+        public Collection<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
+            return TypedefEffectiveStatementImpl.this.effectiveSubstatements();
+        }
+
+        @Override
+        public StatementDefinition statementDefinition() {
+            return YangStmtMapping.TYPE;
+        }
+
+        @Override
+        public String argument() {
+            return getQName().getLocalName();
+        }
+
+        @Override
+        public StatementSource getStatementSource() {
+            return StatementSource.CONTEXT;
+        }
+
+        @Override
+        public TypeDefinition<?> getTypeDefinition() {
+            return TypedefEffectiveStatementImpl.this.getTypeDefinition();
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/typedef/TypedefStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/typedef/TypedefStatementImpl.java
new file mode 100644 (file)
index 0000000..59bb56a
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.typedef;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class TypedefStatementImpl extends AbstractDeclaredStatement<QName> implements TypedefStatement {
+    TypedefStatementImpl(final StmtContext<QName, TypedefStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/typedef/TypedefStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/typedef/TypedefStatementSupport.java
new file mode 100644 (file)
index 0000000..5c10bc4
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.typedef;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefStatement;
+import org.opendaylight.yangtools.yang.parser.spi.TypeNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class TypedefStatementSupport extends
+        AbstractQNameStatementSupport<TypedefStatement, EffectiveStatement<QName, TypedefStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.TYPEDEF)
+        .addOptional(YangStmtMapping.DEFAULT)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addMandatory(YangStmtMapping.TYPE)
+        .addOptional(YangStmtMapping.UNITS)
+        .build();
+    private static final TypedefStatementSupport INSTANCE = new TypedefStatementSupport();
+
+    private TypedefStatementSupport() {
+        super(YangStmtMapping.TYPEDEF);
+    }
+
+    public static TypedefStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseIdentifier(ctx, value);
+    }
+
+    @Override
+    public TypedefStatement createDeclared(final StmtContext<QName, TypedefStatement, ?> ctx) {
+        // Shadowing check: make sure grandparent does not see a conflicting definition. This is required to ensure
+        // that a typedef in child scope does not shadow a typedef in parent scope which occurs later in the text.
+        final StmtContext<?, ?, ?> parent = ctx.getParentContext();
+        if (parent != null) {
+            final StmtContext<?, ?, ?> grandParent = parent.getParentContext();
+            if (grandParent != null) {
+                checkConflict(grandParent, ctx);
+            }
+        }
+
+        return new TypedefStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<QName, TypedefStatement> createEffective(
+            final StmtContext<QName, TypedefStatement, EffectiveStatement<QName, TypedefStatement>> ctx) {
+        return new TypedefEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public void onFullDefinitionDeclared(final Mutable<QName, TypedefStatement,
+            EffectiveStatement<QName, TypedefStatement>> stmt) {
+        super.onFullDefinitionDeclared(stmt);
+
+        if (stmt != null) {
+            final Mutable<?, ?, ?> parent = stmt.getParentContext();
+            if (parent != null) {
+                // Shadowing check: make sure we do not trample on pre-existing definitions. This catches sibling
+                // declarations and parent declarations which have already been declared.
+                checkConflict(parent, stmt);
+                parent.addContext(TypeNamespace.class, stmt.coerceStatementArgument(), stmt);
+            }
+        }
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+
+    private static void checkConflict(final StmtContext<?, ?, ?> parent, final StmtContext<QName, ?, ?> stmt) {
+        final QName arg = stmt.coerceStatementArgument();
+        final StmtContext<?, ?, ?> existing = parent.getFromNamespace(TypeNamespace.class, arg);
+        // RFC7950 sections 5.5 and 6.2.1: identifiers must not be shadowed
+        SourceException.throwIf(existing != null, stmt.getStatementSourceReference(), "Duplicate name for typedef %s",
+                arg);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/unique/UniqueEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/unique/UniqueEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..ddab93d
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.unique;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.UniqueConstraint;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Relative;
+import org.opendaylight.yangtools.yang.model.api.stmt.UniqueEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UniqueStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class UniqueEffectiveStatementImpl extends DeclaredEffectiveStatementBase<Set<Relative>, UniqueStatement>
+        implements UniqueConstraint, UniqueEffectiveStatement {
+    UniqueEffectiveStatementImpl(final StmtContext<Set<Relative>, UniqueStatement, ?> ctx) {
+        super(ctx);
+    }
+
+    @Override
+    public Set<Relative> getTag() {
+        // FIXME: YANGTOOLS-908: verifyNotNull() should not be needed here
+        return verifyNotNull(argument());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/unique/UniqueStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/unique/UniqueStatementImpl.java
new file mode 100644 (file)
index 0000000..06bf082
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.unique;
+
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Relative;
+import org.opendaylight.yangtools.yang.model.api.stmt.UniqueStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class UniqueStatementImpl extends AbstractDeclaredStatement<Set<Relative>> implements UniqueStatement {
+    UniqueStatementImpl(final StmtContext<Set<Relative>, UniqueStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/unique/UniqueStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/unique/UniqueStatementSupport.java
new file mode 100644 (file)
index 0000000..73b54d7
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.unique;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Relative;
+import org.opendaylight.yangtools.yang.model.api.stmt.UniqueStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class UniqueStatementSupport extends AbstractStatementSupport<Set<Relative>, UniqueStatement,
+        EffectiveStatement<Set<Relative>, UniqueStatement>> {
+    /**
+     * Support 'sep' ABNF rule in RFC7950 section 14. CRLF pattern is used to squash line-break from CRLF to LF form
+     * and then we use SEP_SPLITTER, which can operate on single characters.
+     */
+    private static final Pattern CRLF_PATTERN = Pattern.compile("\r\n", Pattern.LITERAL);
+    private static final Splitter SEP_SPLITTER = Splitter.on(CharMatcher.anyOf(" \t\n").precomputed())
+            .omitEmptyStrings();
+
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.UNIQUE)
+        .build();
+    private static final UniqueStatementSupport INSTANCE = new UniqueStatementSupport();
+
+    private UniqueStatementSupport() {
+        super(YangStmtMapping.UNIQUE);
+    }
+
+    public static UniqueStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Set<Relative> parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        final Set<Relative> uniqueConstraints = parseUniqueConstraintArgument(ctx, value);
+        SourceException.throwIf(uniqueConstraints.isEmpty(), ctx.getStatementSourceReference(),
+                "Invalid argument value '%s' of unique statement. The value must contains at least "
+                        + "one descendant schema node identifier.", value);
+        return uniqueConstraints;
+    }
+
+    @Override
+    public UniqueStatement createDeclared(final StmtContext<Set<Relative>, UniqueStatement, ?> ctx) {
+        return new UniqueStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<Set<Relative>, UniqueStatement> createEffective(
+            final StmtContext<Set<Relative>, UniqueStatement, EffectiveStatement<Set<Relative>, UniqueStatement>> ctx) {
+        return new UniqueEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+
+    private static Set<Relative> parseUniqueConstraintArgument(final StmtContext<?, ?, ?> ctx,
+            final String argumentValue) {
+        // deal with 'line-break' rule, which is either "\n" or "\r\n", but not "\r"
+        final String nocrlf = CRLF_PATTERN.matcher(argumentValue).replaceAll("\n");
+
+        final Set<Relative> uniqueConstraintNodes = new HashSet<>();
+        for (final String uniqueArgToken : SEP_SPLITTER.split(nocrlf)) {
+            final SchemaNodeIdentifier nodeIdentifier = ArgumentUtils.nodeIdentifierFromPath(ctx, uniqueArgToken);
+            SourceException.throwIf(nodeIdentifier.isAbsolute(), ctx.getStatementSourceReference(),
+                    "Unique statement argument '%s' contains schema node identifier '%s' "
+                            + "which is not in the descendant node identifier form.", argumentValue, uniqueArgToken);
+            uniqueConstraintNodes.add((Relative) nodeIdentifier);
+        }
+        return ImmutableSet.copyOf(uniqueConstraintNodes);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/units/UnitsEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/units/UnitsEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..4485fda
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.units;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.UnitsEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnitsStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class UnitsEffectiveStatementImpl extends DeclaredEffectiveStatementBase<String, UnitsStatement>
+        implements UnitsEffectiveStatement {
+    UnitsEffectiveStatementImpl(final StmtContext<String, UnitsStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/units/UnitsStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/units/UnitsStatementImpl.java
new file mode 100644 (file)
index 0000000..b0ca5d7
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.units;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.UnitsStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class UnitsStatementImpl extends AbstractDeclaredStatement<String> implements UnitsStatement {
+    UnitsStatementImpl(final StmtContext<String, UnitsStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/units/UnitsStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/units/UnitsStatementSupport.java
new file mode 100644 (file)
index 0000000..7cd5cb0
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.units;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnitsStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class UnitsStatementSupport
+        extends AbstractStatementSupport<String, UnitsStatement, EffectiveStatement<String, UnitsStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.UNITS)
+        .build();
+    private static final UnitsStatementSupport INSTANCE = new UnitsStatementSupport();
+
+    private UnitsStatementSupport() {
+        super(YangStmtMapping.UNITS);
+    }
+
+    public static UnitsStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public UnitsStatement createDeclared(
+            final StmtContext<String, UnitsStatement, ?> ctx) {
+        return new UnitsStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, UnitsStatement> createEffective(
+            final StmtContext<String, UnitsStatement, EffectiveStatement<String, UnitsStatement>> ctx) {
+        return new UnitsEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/uses/UsesEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/uses/UsesEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..8ccf4fc
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.uses;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.GroupingEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.GroupingStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.UsesEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.AbstractEffectiveDocumentedNode;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.refine.RefineEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.spi.GroupingNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class UsesEffectiveStatementImpl extends AbstractEffectiveDocumentedNode<QName, UsesStatement>
+        implements UsesEffectiveStatement, UsesNode {
+    private final @NonNull SchemaPath groupingPath;
+    private final boolean addedByUses;
+    private final @NonNull ImmutableMap<SchemaPath, SchemaNode> refines;
+    private final @NonNull ImmutableSet<AugmentationSchemaNode> augmentations;
+    private final @NonNull ImmutableList<UnknownSchemaNode> unknownNodes;
+    private final @Nullable RevisionAwareXPath whenCondition;
+
+    UsesEffectiveStatementImpl(final StmtContext<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> ctx) {
+        super(ctx);
+
+        // initGroupingPath
+        final StmtContext<?, GroupingStatement, GroupingEffectiveStatement> grpCtx =
+                ctx.getFromNamespace(GroupingNamespace.class, ctx.coerceStatementArgument());
+        this.groupingPath = grpCtx.getSchemaPath().get();
+
+        // initCopyType
+        addedByUses = ctx.getCopyHistory().contains(CopyType.ADDED_BY_USES);
+
+        // initSubstatementCollections
+        final List<UnknownSchemaNode> unknownNodesInit = new ArrayList<>();
+        final Set<AugmentationSchemaNode> augmentationsInit = new LinkedHashSet<>();
+        final Map<SchemaPath, SchemaNode> refinesInit = new HashMap<>();
+        for (final EffectiveStatement<?, ?> effectiveStatement : effectiveSubstatements()) {
+            if (effectiveStatement instanceof UnknownSchemaNode) {
+                final UnknownSchemaNode unknownNode = (UnknownSchemaNode) effectiveStatement;
+                unknownNodesInit.add(unknownNode);
+            }
+            if (effectiveStatement instanceof AugmentationSchemaNode) {
+                final AugmentationSchemaNode augmentationSchema = (AugmentationSchemaNode) effectiveStatement;
+                augmentationsInit.add(augmentationSchema);
+            }
+            if (effectiveStatement instanceof RefineEffectiveStatementImpl) {
+                final RefineEffectiveStatementImpl refineStmt = (RefineEffectiveStatementImpl) effectiveStatement;
+                final SchemaNodeIdentifier identifier = refineStmt.argument();
+                refinesInit.put(identifier.asSchemaPath(), refineStmt.getRefineTargetNode());
+            }
+        }
+        this.unknownNodes = ImmutableList.copyOf(unknownNodesInit);
+        this.augmentations = ImmutableSet.copyOf(augmentationsInit);
+        this.refines = ImmutableMap.copyOf(refinesInit);
+
+        whenCondition = findFirstEffectiveSubstatementArgument(WhenEffectiveStatement.class).orElse(null);
+    }
+
+    @Override
+    public SchemaPath getGroupingPath() {
+        return groupingPath;
+    }
+
+    @Override
+    public Set<AugmentationSchemaNode> getAugmentations() {
+        return augmentations;
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAugmenting() {
+        return false;
+    }
+
+    @Deprecated
+    @Override
+    public boolean isAddedByUses() {
+        return addedByUses;
+    }
+
+    @Override
+    public Map<SchemaPath, SchemaNode> getRefines() {
+        return refines;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return unknownNodes;
+    }
+
+    @Override
+    public Optional<RevisionAwareXPath> getWhenCondition() {
+        return Optional.ofNullable(whenCondition);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Objects.hashCode(groupingPath);
+        result = prime * result + Objects.hashCode(augmentations);
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final UsesEffectiveStatementImpl other = (UsesEffectiveStatementImpl) obj;
+        return Objects.equals(groupingPath, other.groupingPath) && Objects.equals(augmentations, other.augmentations);
+    }
+
+    @Override
+    public String toString() {
+        return UsesEffectiveStatementImpl.class.getSimpleName() + "[groupingPath=" + groupingPath + "]";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/uses/UsesStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/uses/UsesStatementImpl.java
new file mode 100644 (file)
index 0000000..992944f
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.uses;
+
+import com.google.common.base.Verify;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.YangValidationBundles;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class UsesStatementImpl extends AbstractDeclaredStatement<QName> implements UsesStatement {
+    private static final Logger LOG = LoggerFactory.getLogger(UsesStatementImpl.class);
+
+    UsesStatementImpl(final StmtContext<QName, UsesStatement, ?> context) {
+        super(context);
+    }
+
+    /**
+     * Copy statements from a grouping to a target node.
+     *
+     * @param sourceGrpStmtCtx
+     *            source grouping statement context
+     * @param targetCtx
+     *            target context
+     * @param usesNode
+     *            uses node
+     * @throws SourceException
+     *             instance of SourceException
+     */
+    static void copyFromSourceToTarget(final Mutable<?, ?, ?> sourceGrpStmtCtx,
+            final StatementContextBase<?, ?, ?> targetCtx,
+            final Mutable<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> usesNode) {
+        final Collection<? extends Mutable<?, ?, ?>> declared = sourceGrpStmtCtx.mutableDeclaredSubstatements();
+        final Collection<? extends Mutable<?, ?, ?>> effective = sourceGrpStmtCtx.mutableEffectiveSubstatements();
+        final Collection<Mutable<?, ?, ?>> buffer = new ArrayList<>(declared.size() + effective.size());
+        final QNameModule newQNameModule = getNewQNameModule(targetCtx, sourceGrpStmtCtx);
+
+        for (final Mutable<?, ?, ?> original : declared) {
+            if (original.isSupportedByFeatures()) {
+                copyStatement(original, targetCtx, newQNameModule, buffer);
+            }
+        }
+
+        for (final Mutable<?, ?, ?> original : effective) {
+            copyStatement(original, targetCtx, newQNameModule, buffer);
+        }
+
+        targetCtx.addEffectiveSubstatements(buffer);
+        usesNode.addAsEffectOfStatement(buffer);
+    }
+
+    private static void copyStatement(final Mutable<?, ?, ?> original,
+            final StatementContextBase<?, ?, ?> targetCtx, final QNameModule targetModule,
+            final Collection<Mutable<?, ?, ?>> buffer) {
+        if (needToCopyByUses(original)) {
+            final Mutable<?, ?, ?> copy = targetCtx.childCopyOf(original, CopyType.ADDED_BY_USES, targetModule);
+            buffer.add(copy);
+        } else if (isReusedByUsesOnTop(original)) {
+            buffer.add(original);
+        }
+    }
+
+    private static final ImmutableSet<YangStmtMapping> TOP_REUSED_DEF_SET = ImmutableSet.of(
+        YangStmtMapping.TYPE,
+        YangStmtMapping.TYPEDEF);
+
+    private static boolean isReusedByUsesOnTop(final StmtContext<?, ?, ?> stmtContext) {
+        return TOP_REUSED_DEF_SET.contains(stmtContext.getPublicDefinition());
+    }
+
+    private static final ImmutableSet<YangStmtMapping> NOCOPY_FROM_GROUPING_SET = ImmutableSet.of(
+        YangStmtMapping.DESCRIPTION,
+        YangStmtMapping.REFERENCE,
+        YangStmtMapping.STATUS);
+    private static final ImmutableSet<YangStmtMapping> REUSED_DEF_SET = ImmutableSet.of(
+        YangStmtMapping.TYPE,
+        YangStmtMapping.TYPEDEF,
+        YangStmtMapping.USES);
+
+    public static boolean needToCopyByUses(final StmtContext<?, ?, ?> stmtContext) {
+        final StatementDefinition def = stmtContext.getPublicDefinition();
+        if (REUSED_DEF_SET.contains(def)) {
+            LOG.trace("Will reuse {} statement {}", def, stmtContext);
+            return false;
+        }
+        if (NOCOPY_FROM_GROUPING_SET.contains(def)) {
+            return !YangStmtMapping.GROUPING.equals(stmtContext.coerceParentContext().getPublicDefinition());
+        }
+
+        LOG.trace("Will copy {} statement {}", def, stmtContext);
+        return true;
+    }
+
+    public static void resolveUsesNode(
+            final Mutable<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> usesNode,
+            final StmtContext<?, ?, ?> targetNodeStmtCtx) {
+        for (final Mutable<?, ?, ?> subStmtCtx : usesNode.mutableDeclaredSubstatements()) {
+            if (StmtContextUtils.producesDeclared(subStmtCtx, RefineStatement.class)
+                    && areFeaturesSupported(subStmtCtx)) {
+                performRefine(subStmtCtx, targetNodeStmtCtx);
+            }
+        }
+    }
+
+    private static boolean areFeaturesSupported(final StmtContext<?, ?, ?> subStmtCtx) {
+        /*
+         * In case of Yang 1.1, checks whether features are supported.
+         */
+        return !YangVersion.VERSION_1_1.equals(subStmtCtx.getRootVersion()) || subStmtCtx.isSupportedByFeatures();
+    }
+
+    private static void performRefine(final Mutable<?, ?, ?> subStmtCtx, final StmtContext<?, ?, ?> usesParentCtx) {
+        final Object refineArgument = subStmtCtx.getStatementArgument();
+        InferenceException.throwIf(!(refineArgument instanceof SchemaNodeIdentifier),
+            subStmtCtx.getStatementSourceReference(),
+            "Invalid refine argument %s. It must be instance of SchemaNodeIdentifier.", refineArgument);
+
+        final Optional<StmtContext<?, ?, ?>> optRefineTargetCtx = ChildSchemaNodeNamespace.findNode(
+            usesParentCtx, (SchemaNodeIdentifier) refineArgument);
+        InferenceException.throwIf(!optRefineTargetCtx.isPresent(), subStmtCtx.getStatementSourceReference(),
+            "Refine target node %s not found.", refineArgument);
+
+        final StmtContext<?, ?, ?> refineTargetNodeCtx = optRefineTargetCtx.get();
+        if (StmtContextUtils.isUnknownStatement(refineTargetNodeCtx)) {
+            LOG.trace("Refine node '{}' in uses '{}' has target node unknown statement '{}'. "
+                + "Refine has been skipped. At line: {}", subStmtCtx.getStatementArgument(),
+                subStmtCtx.coerceParentContext().getStatementArgument(),
+                refineTargetNodeCtx.getStatementArgument(), subStmtCtx.getStatementSourceReference());
+            subStmtCtx.addAsEffectOfStatement(refineTargetNodeCtx);
+            return;
+        }
+
+        Verify.verify(refineTargetNodeCtx instanceof StatementContextBase);
+        addOrReplaceNodes(subStmtCtx, (StatementContextBase<?, ?, ?>) refineTargetNodeCtx);
+        subStmtCtx.addAsEffectOfStatement(refineTargetNodeCtx);
+    }
+
+    private static void addOrReplaceNodes(final Mutable<?, ?, ?> subStmtCtx,
+            final StatementContextBase<?, ?, ?> refineTargetNodeCtx) {
+        for (final Mutable<?, ?, ?> refineSubstatementCtx : subStmtCtx.mutableDeclaredSubstatements()) {
+            if (isSupportedRefineSubstatement(refineSubstatementCtx)) {
+                addOrReplaceNode(refineSubstatementCtx, refineTargetNodeCtx);
+            }
+        }
+    }
+
+    private static void addOrReplaceNode(final Mutable<?, ?, ?> refineSubstatementCtx,
+            final StatementContextBase<?, ?, ?> refineTargetNodeCtx) {
+
+        final StatementDefinition refineSubstatementDef = refineSubstatementCtx.getPublicDefinition();
+
+        SourceException.throwIf(!isSupportedRefineTarget(refineSubstatementCtx, refineTargetNodeCtx),
+                refineSubstatementCtx.getStatementSourceReference(),
+                "Error in module '%s' in the refine of uses '%s': can not perform refine of '%s' for the target '%s'.",
+                refineSubstatementCtx.getRoot().getStatementArgument(),
+                refineSubstatementCtx.coerceParentContext().getStatementArgument(),
+                refineSubstatementCtx.getPublicDefinition(), refineTargetNodeCtx.getPublicDefinition());
+
+        if (isAllowedToAddByRefine(refineSubstatementDef)) {
+            refineTargetNodeCtx.addEffectiveSubstatement(refineSubstatementCtx);
+        } else {
+            refineTargetNodeCtx.removeStatementFromEffectiveSubstatements(refineSubstatementDef);
+            refineTargetNodeCtx.addEffectiveSubstatement(refineSubstatementCtx);
+        }
+    }
+
+    private static final ImmutableSet<YangStmtMapping> ALLOWED_TO_ADD_BY_REFINE_DEF_SET =
+            ImmutableSet.of(YangStmtMapping.MUST);
+
+    private static boolean isAllowedToAddByRefine(final StatementDefinition publicDefinition) {
+        return ALLOWED_TO_ADD_BY_REFINE_DEF_SET.contains(publicDefinition);
+    }
+
+    private static boolean isSupportedRefineSubstatement(final StmtContext<?, ?, ?> refineSubstatementCtx) {
+        final Collection<?> supportedRefineSubstatements = refineSubstatementCtx.getFromNamespace(
+                ValidationBundlesNamespace.class, ValidationBundleType.SUPPORTED_REFINE_SUBSTATEMENTS);
+
+        return supportedRefineSubstatements == null || supportedRefineSubstatements.isEmpty()
+                || supportedRefineSubstatements.contains(refineSubstatementCtx.getPublicDefinition())
+                || StmtContextUtils.isUnknownStatement(refineSubstatementCtx);
+    }
+
+    private static boolean isSupportedRefineTarget(final StmtContext<?, ?, ?> refineSubstatementCtx,
+            final StmtContext<?, ?, ?> refineTargetNodeCtx) {
+        final Collection<?> supportedRefineTargets = YangValidationBundles.SUPPORTED_REFINE_TARGETS
+                .get(refineSubstatementCtx.getPublicDefinition());
+
+        return supportedRefineTargets == null || supportedRefineTargets.isEmpty()
+                || supportedRefineTargets.contains(refineTargetNodeCtx.getPublicDefinition());
+    }
+
+
+    private static QNameModule getNewQNameModule(final StmtContext<?, ?, ?> targetCtx,
+            final StmtContext<?, ?, ?> stmtContext) {
+        if (targetCtx.getParentContext() == null) {
+            return targetCtx.getFromNamespace(ModuleCtxToModuleQName.class, targetCtx);
+        }
+        if (targetCtx.getPublicDefinition() == YangStmtMapping.AUGMENT) {
+            return StmtContextUtils.getRootModuleQName(targetCtx);
+        }
+
+        final Object targetStmtArgument = targetCtx.getStatementArgument();
+        final Object sourceStmtArgument = stmtContext.getStatementArgument();
+        if (targetStmtArgument instanceof QName && sourceStmtArgument instanceof QName) {
+            return ((QName) targetStmtArgument).getModule();
+        }
+
+        return null;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/uses/UsesStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/uses/UsesStatementSupport.java
new file mode 100644 (file)
index 0000000..7f28b90
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.uses;
+
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
+import org.opendaylight.yangtools.yang.parser.spi.GroupingNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractQNameStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
+
+public final class UsesStatementSupport extends
+        AbstractQNameStatementSupport<UsesStatement, EffectiveStatement<QName, UsesStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .USES)
+        .addAny(YangStmtMapping.AUGMENT)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addAny(YangStmtMapping.IF_FEATURE)
+        .addAny(YangStmtMapping.REFINE)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .addOptional(YangStmtMapping.STATUS)
+        .addOptional(YangStmtMapping.WHEN)
+        .build();
+    private static final UsesStatementSupport INSTANCE = new UsesStatementSupport();
+
+    private UsesStatementSupport() {
+        super(YangStmtMapping.USES);
+    }
+
+    public static UsesStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public QName parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return StmtContextUtils.parseNodeIdentifier(ctx, value);
+    }
+
+    @Override
+    public void onFullDefinitionDeclared(
+            final Mutable<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> usesNode) {
+        if (!usesNode.isSupportedByFeatures()) {
+            return;
+        }
+        super.onFullDefinitionDeclared(usesNode);
+
+        final ModelActionBuilder usesAction = usesNode.newInferenceAction(ModelProcessingPhase.EFFECTIVE_MODEL);
+        final QName groupingName = usesNode.getStatementArgument();
+
+        final Prerequisite<StmtContext<?, ?, ?>> sourceGroupingPre = usesAction.requiresCtx(usesNode,
+                GroupingNamespace.class, groupingName, ModelProcessingPhase.EFFECTIVE_MODEL);
+        final Prerequisite<? extends StmtContext.Mutable<?, ?, ?>> targetNodePre = usesAction.mutatesEffectiveCtx(
+                usesNode.getParentContext());
+
+        usesAction.apply(new InferenceAction() {
+
+            @Override
+            public void apply(final InferenceContext ctx) {
+                final StatementContextBase<?, ?, ?> targetNodeStmtCtx =
+                        (StatementContextBase<?, ?, ?>) targetNodePre.resolve(ctx);
+                final StatementContextBase<?, ?, ?> sourceGrpStmtCtx =
+                        (StatementContextBase<?, ?, ?>) sourceGroupingPre.resolve(ctx);
+
+                UsesStatementImpl.copyFromSourceToTarget(sourceGrpStmtCtx, targetNodeStmtCtx, usesNode);
+                UsesStatementImpl.resolveUsesNode(usesNode, targetNodeStmtCtx);
+                StmtContextUtils.validateIfFeatureAndWhenOnListKeys(usesNode);
+            }
+
+            @Override
+            public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
+                InferenceException.throwIf(failed.contains(sourceGroupingPre),
+                        usesNode.getStatementSourceReference(), "Grouping '%s' was not resolved.", groupingName);
+                throw new InferenceException("Unknown error occurred.", usesNode.getStatementSourceReference());
+            }
+        });
+    }
+
+    @Override
+    public UsesStatement createDeclared(final StmtContext<QName, UsesStatement, ?> ctx) {
+        return new UsesStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<QName, UsesStatement> createEffective(
+            final StmtContext<QName, UsesStatement, EffectiveStatement<QName, UsesStatement>> ctx) {
+        return new UsesEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/value/ValueEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/value/ValueEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..aa000a9
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.value;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ValueEffectiveStatementImpl extends DeclaredEffectiveStatementBase<Integer, ValueStatement>
+        implements ValueEffectiveStatement {
+    ValueEffectiveStatementImpl(final StmtContext<Integer, ValueStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/value/ValueStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/value/ValueStatementImpl.java
new file mode 100644 (file)
index 0000000..e9d23d1
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.value;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ValueStatementImpl extends AbstractDeclaredStatement<Integer> implements ValueStatement {
+    ValueStatementImpl(final StmtContext<Integer, ValueStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/value/ValueStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/value/ValueStatementSupport.java
new file mode 100644 (file)
index 0000000..c03c96c
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.value;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ValueStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class ValueStatementSupport extends
+        AbstractStatementSupport<Integer, ValueStatement, EffectiveStatement<Integer, ValueStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.VALUE).build();
+    private static final ValueStatementSupport INSTANCE = new ValueStatementSupport();
+
+    private ValueStatementSupport() {
+        super(YangStmtMapping.VALUE);
+    }
+
+    public static ValueStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Integer parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        try {
+            return Integer.valueOf(value);
+        } catch (NumberFormatException e) {
+            throw new SourceException(ctx.getStatementSourceReference(), e,
+                "%s is not valid value statement integer argument in a range of -2147483648..2147483647", value);
+        }
+    }
+
+    @Override
+    public ValueStatement createDeclared(final StmtContext<Integer, ValueStatement, ?> ctx) {
+        return new ValueStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<Integer, ValueStatement> createEffective(
+            final StmtContext<Integer, ValueStatement, EffectiveStatement<Integer, ValueStatement>> ctx) {
+        return new ValueEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/when/WhenEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/when/WhenEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..177cbc0
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.when;
+
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.WhenEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class WhenEffectiveStatementImpl extends DeclaredEffectiveStatementBase<RevisionAwareXPath, WhenStatement>
+        implements WhenEffectiveStatement {
+    WhenEffectiveStatementImpl(final StmtContext<RevisionAwareXPath, WhenStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/when/WhenStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/when/WhenStatementImpl.java
new file mode 100644 (file)
index 0000000..d58699e
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.when;
+
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class WhenStatementImpl extends AbstractDeclaredStatement<RevisionAwareXPath> implements WhenStatement {
+    WhenStatementImpl(final StmtContext<RevisionAwareXPath, WhenStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/when/WhenStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/when/WhenStatementSupport.java
new file mode 100644 (file)
index 0000000..1d55917
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.when;
+
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class WhenStatementSupport extends AbstractStatementSupport<RevisionAwareXPath, WhenStatement,
+        EffectiveStatement<RevisionAwareXPath, WhenStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.WHEN)
+        .addOptional(YangStmtMapping.DESCRIPTION)
+        .addOptional(YangStmtMapping.REFERENCE)
+        .build();
+    private static final WhenStatementSupport INSTANCE = new WhenStatementSupport();
+
+    private WhenStatementSupport() {
+        super(YangStmtMapping.WHEN);
+    }
+
+    public static WhenStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public RevisionAwareXPath parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return ArgumentUtils.parseXPath(ctx, value);
+    }
+
+    @Override
+    public WhenStatement createDeclared(final StmtContext<RevisionAwareXPath, WhenStatement, ?> ctx) {
+        return new WhenStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<RevisionAwareXPath, WhenStatement> createEffective(
+            final StmtContext<RevisionAwareXPath, WhenStatement,
+            EffectiveStatement<RevisionAwareXPath, WhenStatement>> ctx) {
+        return new WhenEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yang_version/YangVersionEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yang_version/YangVersionEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..f513785
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.yang_version;
+
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.stmt.YangVersionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YangVersionStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class YangVersionEffectiveStatementImpl extends DeclaredEffectiveStatementBase<YangVersion, YangVersionStatement>
+        implements YangVersionEffectiveStatement {
+    YangVersionEffectiveStatementImpl(final StmtContext<YangVersion, YangVersionStatement, ?> ctx) {
+        super(ctx);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yang_version/YangVersionStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yang_version/YangVersionStatementImpl.java
new file mode 100644 (file)
index 0000000..eefb012
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.yang_version;
+
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.stmt.YangVersionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class YangVersionStatementImpl extends AbstractDeclaredStatement<YangVersion> implements YangVersionStatement {
+    YangVersionStatementImpl(final StmtContext<YangVersion, YangVersionStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yang_version/YangVersionStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yang_version/YangVersionStatementSupport.java
new file mode 100644 (file)
index 0000000..483e639
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.yang_version;
+
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YangVersionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class YangVersionStatementSupport extends AbstractStatementSupport<YangVersion, YangVersionStatement,
+        EffectiveStatement<YangVersion, YangVersionStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
+        .YANG_VERSION)
+        .build();
+    private static final YangVersionStatementSupport INSTANCE = new YangVersionStatementSupport();
+
+    private YangVersionStatementSupport() {
+        super(YangStmtMapping.YANG_VERSION);
+    }
+
+    public static YangVersionStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public YangVersion parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return SourceException.unwrap(YangVersion.parse(value), ctx.getStatementSourceReference(),
+            "Unsupported YANG version %s", value);
+    }
+
+    @Override
+    public YangVersionStatement createDeclared(final StmtContext<YangVersion, YangVersionStatement, ?> ctx) {
+        return new YangVersionStatementImpl(ctx);
+    }
+
+    @Override
+    public void onPreLinkageDeclared(final Mutable<YangVersion, YangVersionStatement,
+            EffectiveStatement<YangVersion, YangVersionStatement>> stmt) {
+        stmt.setRootVersion(stmt.getStatementArgument());
+    }
+
+    @Override
+    public EffectiveStatement<YangVersion, YangVersionStatement> createEffective(final StmtContext<YangVersion,
+            YangVersionStatement, EffectiveStatement<YangVersion, YangVersionStatement>> ctx) {
+        return new YangVersionEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yin_element/YinElementEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yin_element/YinElementEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..116b471
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.yin_element;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.YinElementEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YinElementStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.DeclaredEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class YinElementEffectiveStatementImpl extends DeclaredEffectiveStatementBase<Boolean, YinElementStatement>
+        implements YinElementEffectiveStatement {
+    YinElementEffectiveStatementImpl(final StmtContext<Boolean, YinElementStatement, ?> ctx) {
+        super(ctx);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yin_element/YinElementStatementImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yin_element/YinElementStatementImpl.java
new file mode 100644 (file)
index 0000000..fae4b1f
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.rfc7950.stmt.yin_element;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.YinElementStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class YinElementStatementImpl extends AbstractDeclaredStatement<Boolean> implements YinElementStatement {
+    YinElementStatementImpl(final StmtContext<Boolean, YinElementStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yin_element/YinElementStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/yin_element/YinElementStatementSupport.java
new file mode 100644 (file)
index 0000000..ffff495
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.rfc7950.stmt.yin_element;
+
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YinElementStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class YinElementStatementSupport extends
+        AbstractStatementSupport<Boolean, YinElementStatement, EffectiveStatement<Boolean, YinElementStatement>> {
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.YIN_ELEMENT).build();
+    private static final YinElementStatementSupport INSTANCE = new YinElementStatementSupport();
+
+    private YinElementStatementSupport() {
+        super(YangStmtMapping.YIN_ELEMENT);
+    }
+
+    public static YinElementStatementSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Boolean parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return ArgumentUtils.parseBoolean(ctx, value);
+    }
+
+    @Override
+    public YinElementStatement createDeclared(final StmtContext<Boolean, YinElementStatement, ?> ctx) {
+        return new YinElementStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<Boolean, YinElementStatement> createEffective(
+            final StmtContext<Boolean, YinElementStatement, EffectiveStatement<Boolean, YinElementStatement>> ctx) {
+        return new YinElementEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    public String internArgument(final String rawArgument) {
+        return ArgumentUtils.internBoolean(rawArgument);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return SUBSTATEMENT_VALIDATOR;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/AugmentContextUtilsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/AugmentContextUtilsTest.java
new file mode 100644 (file)
index 0000000..381e57d
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.rfc7950.repo;
+
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.yangtools.yang.parser.rfc7950.repo.ArgumentContextUtils.trimWhitespace;
+
+import org.junit.Test;
+
+public class AugmentContextUtilsTest {
+
+    @Test
+    public void testTrimWhitespace() {
+        assertEquals("\n", trimWhitespace("\n", 0));
+        assertEquals("\n", trimWhitespace("\n", 5));
+        assertEquals("\n\n\n\n", trimWhitespace("\n\n\n\n", 0));
+        assertEquals("\n\n\n\n", trimWhitespace("\n\n\n\n", 5));
+        assertEquals("abc\n\n", trimWhitespace("abc \n  \n", 0));
+        assertEquals("abc\n\n", trimWhitespace("abc \n  \n", 1));
+        assertEquals("abc\n  ", trimWhitespace("abc\n   ", 0));
+        assertEquals("abc\n", trimWhitespace("abc\n   ", 2));
+        assertEquals("abc\n\n", trimWhitespace("abc\n   \n", 2));
+        assertEquals("abc\n        ", trimWhitespace("abc\n\t ", 0));
+        assertEquals("abc\n      ", trimWhitespace("abc\n\t ", 2));
+        assertEquals("abc\n    ", trimWhitespace("abc\n\t ", 4));
+        assertEquals("abc\n    ", trimWhitespace("abc\n \t", 4));
+        assertEquals("abc\n   a\n    a\n", trimWhitespace("abc\n\ta\n\t a\n", 4));
+        assertEquals("abc\n\n    a\n", trimWhitespace("abc\n\t\n\t a\n", 4));
+        assertEquals("   \ta\n", trimWhitespace("   \ta\n", 3));
+        assertEquals("   \ta\n", trimWhitespace("   \ta\n  ", 3));
+        assertEquals("   \ta\n", trimWhitespace("   \ta\n   ", 3));
+        assertEquals("   \ta\n", trimWhitespace("   \ta\n    ", 3));
+        assertEquals("   \ta\n ", trimWhitespace("   \ta\n     ", 3));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/SchemaContextUtilTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/SchemaContextUtilTest.java
new file mode 100644 (file)
index 0000000..e071f11
--- /dev/null
@@ -0,0 +1,856 @@
+/*
+ * 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.rfc7950.repo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.Optional;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.util.PathExpressionImpl;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+@RunWith(MockitoJUnitRunner.class)
+public class SchemaContextUtilTest {
+    @Mock
+    private SchemaContext mockSchemaContext;
+    @Mock
+    private Module mockModule;
+
+    @Test
+    public void testFindDummyData() {
+        doReturn(Optional.empty()).when(mockSchemaContext).findModule(any(QNameModule.class));
+        doReturn(Optional.empty()).when(mockSchemaContext).findDataTreeChild(any(Iterable.class));
+        doReturn(URI.create("dummy")).when(mockModule).getNamespace();
+        doReturn(Optional.empty()).when(mockModule).getRevision();
+
+        final QName qName = QName.create("dummy", "TestQName");
+        final SchemaPath schemaPath = SchemaPath.create(Collections.singletonList(qName), true);
+        assertEquals("Should be null. Module TestQName not found", null,
+                SchemaContextUtil.findDataSchemaNode(mockSchemaContext, schemaPath));
+
+        final PathExpression xPath = new PathExpressionImpl("/bookstore/book/title", true);
+        assertEquals("Should be null. Module bookstore not found", null,
+                SchemaContextUtil.findDataSchemaNode(mockSchemaContext, mockModule, xPath));
+
+        final SchemaNode schemaNode = BaseTypes.int32Type();
+        final PathExpression xPathRelative = new PathExpressionImpl("../prefix", false);
+        assertEquals("Should be null, Module prefix not found", null,
+                SchemaContextUtil.findDataSchemaNodeForRelativeXPath(mockSchemaContext, mockModule, schemaNode,
+                        xPathRelative));
+
+        assertEquals("Should be null. Module TestQName not found", null,
+                SchemaContextUtil.findNodeInSchemaContext(mockSchemaContext, Collections.singleton(qName)));
+
+        assertEquals("Should be null.", null, SchemaContextUtil.findParentModule(mockSchemaContext, schemaNode));
+    }
+
+    @Test
+    public void findNodeInSchemaContextTest() throws URISyntaxException, IOException, YangSyntaxErrorException,
+            ReactorException {
+
+        final SchemaContext context = TestUtils.parseYangSources("/schema-context-util-test");
+
+        final Module myModule = context.findModule(URI.create("uri:my-module"), Revision.of("2014-10-07")).get();
+
+        SchemaNode testNode = ((ContainerSchemaNode) myModule.getDataChildByName(QName.create(
+                myModule.getQNameModule(), "my-container"))).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-leaf-in-container"));
+
+        SchemaPath path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-container"),
+                QName.create(myModule.getQNameModule(), "my-leaf-in-container"));
+        SchemaNode foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        RpcDefinition rpc = getRpcByName(myModule, "my-rpc");
+        testNode = rpc.getInput().getDataChildByName(QName.create(myModule.getQNameModule(), "my-input-leaf"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-rpc"),
+                QName.create(myModule.getQNameModule(), "input"),
+                QName.create(myModule.getQNameModule(), "my-input-leaf"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        rpc = getRpcByName(myModule, "my-rpc");
+        testNode = rpc.getOutput().getDataChildByName(QName.create(myModule.getQNameModule(), "my-output-leaf"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-rpc"),
+                QName.create(myModule.getQNameModule(), "output"),
+                QName.create(myModule.getQNameModule(), "my-output-leaf"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        final NotificationDefinition notification = myModule.getNotifications().iterator().next();
+        testNode = notification.getDataChildByName(QName.create(myModule.getQNameModule(), "my-notification-leaf"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-notification"),
+                QName.create(myModule.getQNameModule(), "my-notification-leaf"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        final GroupingDefinition grouping = getGroupingByName(myModule, "my-grouping");
+        testNode = ((ContainerSchemaNode) grouping.getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-container-in-grouping"))).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-leaf-in-grouping"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-grouping"),
+                QName.create(myModule.getQNameModule(), "my-container-in-grouping"),
+                QName.create(myModule.getQNameModule(), "my-leaf-in-grouping"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = ((ChoiceSchemaNode) myModule
+                .getDataChildByName(QName.create(myModule.getQNameModule(), "my-choice")))
+                .findCaseNodes("one").iterator().next()
+                .getDataChildByName(QName.create(myModule.getQNameModule(), "my-choice-leaf-one"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-choice"),
+                QName.create(myModule.getQNameModule(), "one"),
+                QName.create(myModule.getQNameModule(), "my-choice-leaf-one"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        ListSchemaNode listNode = (ListSchemaNode) ((ContainerSchemaNode) myModule.getDataChildByName(QName.create(
+                myModule.getQNameModule(), "my-container"))).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-list"));
+
+        testNode = listNode.getDataChildByName(QName.create(myModule.getQNameModule(), "my-leaf-in-list"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-container"),
+                QName.create(myModule.getQNameModule(), "my-list"),
+                QName.create(myModule.getQNameModule(), "my-leaf-in-list"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        listNode = (ListSchemaNode) ((ContainerSchemaNode) myModule.getDataChildByName(QName.create(
+                myModule.getQNameModule(), "my-container"))).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-list"));
+
+        testNode = listNode.getDataChildByName(QName.create(myModule.getQNameModule(), "my-leaf-list-in-list"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-container"),
+                QName.create(myModule.getQNameModule(), "my-list"),
+                QName.create(myModule.getQNameModule(), "my-leaf-list-in-list"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+    }
+
+    @Test
+    public void findNodeInSchemaContextTest2() throws URISyntaxException, IOException, YangSyntaxErrorException,
+            ReactorException {
+
+        final SchemaContext context = TestUtils.parseYangSources("/schema-context-util-test");
+
+        final Module myModule = context.findModule(URI.create("uri:my-module"), Revision.of("2014-10-07")).get();
+
+        SchemaNode testNode = ((ContainerSchemaNode) myModule.getDataChildByName(QName.create(
+                myModule.getQNameModule(), "my-container"))).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-leaf-not-in-container"));
+
+        SchemaPath path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-container"),
+                QName.create(myModule.getQNameModule(), "my-leaf-not-in-container"));
+        SchemaNode foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        final RpcDefinition rpc = getRpcByName(myModule, "my-rpc");
+        testNode = rpc.getInput().getDataChildByName(QName.create(myModule.getQNameModule(), "no-input-leaf"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-rpc"),
+                QName.create(myModule.getQNameModule(), "input"),
+                QName.create(myModule.getQNameModule(), "no-input-leaf"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        final NotificationDefinition notification = myModule.getNotifications().iterator().next();
+        testNode = notification.getDataChildByName(QName.create(myModule.getQNameModule(), "no-notification-leaf"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-notification"),
+                QName.create(myModule.getQNameModule(), "no-notification-leaf"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        final GroupingDefinition grouping = getGroupingByName(myModule, "my-grouping");
+        testNode = ((ContainerSchemaNode) grouping.getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-container-in-grouping"))).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "no-leaf-in-grouping"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-grouping"),
+                QName.create(myModule.getQNameModule(), "my-container-in-grouping"),
+                QName.create(myModule.getQNameModule(), "no-leaf-in-grouping"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        testNode = ((ChoiceSchemaNode) myModule
+                .getDataChildByName(QName.create(myModule.getQNameModule(), "my-choice")))
+                .findCaseNodes("one").iterator().next()
+                .getDataChildByName(QName.create(myModule.getQNameModule(), "no-choice-leaf"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-choice"),
+                QName.create(myModule.getQNameModule(), "one"),
+                QName.create(myModule.getQNameModule(), "no-choice-leaf"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        ListSchemaNode listNode = (ListSchemaNode) ((ContainerSchemaNode) myModule.getDataChildByName(QName.create(
+                myModule.getQNameModule(), "my-container"))).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-list"));
+
+        testNode = listNode.getDataChildByName(QName.create(myModule.getQNameModule(), "no-leaf-in-list"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-container"),
+                QName.create(myModule.getQNameModule(), "my-list"),
+                QName.create(myModule.getQNameModule(), "no-leaf-in-list"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        listNode = (ListSchemaNode) ((ContainerSchemaNode) myModule.getDataChildByName(QName.create(
+                myModule.getQNameModule(), "my-container"))).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-list"));
+
+        testNode = listNode.getDataChildByName(QName.create(myModule.getQNameModule(), "no-leaf-list-in-list"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-container"),
+                QName.create(myModule.getQNameModule(), "my-list"),
+                QName.create(myModule.getQNameModule(), "no-leaf-list-in-list"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+    }
+
+    @Test
+    public void findNodeInSchemaContextTest3() throws URISyntaxException, IOException, YangSyntaxErrorException,
+            ReactorException {
+
+        final SchemaContext context = TestUtils.parseYangSources("/schema-context-util-test");
+
+        final Module myModule = context.findModule(URI.create("uri:my-module"), Revision.of("2014-10-07")).get();
+
+        SchemaNode testNode = myModule.getDataChildByName(QName.create(myModule.getQNameModule(), "my-container"));
+
+        SchemaPath path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-container"));
+        SchemaNode foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = getRpcByName(myModule, "my-rpc");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-rpc"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = myModule.getNotifications().iterator().next();
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-notification"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = getGroupingByName(myModule, "my-grouping");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-grouping"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = myModule.getDataChildByName(QName.create(myModule.getQNameModule(), "my-choice"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-choice"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = ((ContainerSchemaNode) myModule.getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-container"))).getDataChildByName(QName.create(myModule.getQNameModule(), "my-list"));
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-container"),
+                QName.create(myModule.getQNameModule(), "my-list"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+    }
+
+    @Test
+    public void findParentModuleTest() throws URISyntaxException, IOException, YangSyntaxErrorException,
+            ReactorException {
+
+        final SchemaContext context = TestUtils.parseYangSources("/schema-context-util-test");
+
+        final Module myModule = context.findModule(URI.create("uri:my-module"), Revision.of("2014-10-07")).get();
+
+        final DataSchemaNode node = myModule.getDataChildByName(QName.create(myModule.getQNameModule(),
+            "my-container"));
+
+        final Module foundModule = SchemaContextUtil.findParentModule(context, node);
+
+        assertEquals(myModule, foundModule);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void findParentModuleIllegalArgumentTest() {
+        SchemaContextUtil.findParentModule(mock(SchemaContext.class), null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void findParentModuleIllegalArgumentTest2() {
+        SchemaContextUtil.findParentModule(null, mock(SchemaNode.class));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void findDataSchemaNodeIllegalArgumentTest() {
+        SchemaContextUtil.findDataSchemaNode(mock(SchemaContext.class), null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void findDataSchemaNodeIllegalArgumentTest2() {
+        SchemaContextUtil.findDataSchemaNode(null, SchemaPath.create(true,
+            QName.create(URI.create("uri:my-module"), Revision.of("2014-10-07"), "foo")));
+    }
+
+    @Test
+    public void findDataSchemaNodeTest() throws URISyntaxException, IOException, YangSyntaxErrorException,
+            ReactorException {
+        final SchemaContext context = TestUtils.parseYangSources("/schema-context-util-test");
+        final Module module = context.findModule(URI.create("uri:my-module"), Revision.of("2014-10-07")).get();
+        final Module importedModule = context.findModule(URI.create("uri:imported-module"),
+            Revision.of("2014-10-07")).get();
+
+        final SchemaNode testNode = ((ContainerSchemaNode) importedModule.getDataChildByName(QName.create(
+                importedModule.getQNameModule(), "my-imported-container"))).getDataChildByName(QName.create(
+                importedModule.getQNameModule(), "my-imported-leaf"));
+
+        final PathExpression xpath = new PathExpressionImpl("imp:my-imported-container/imp:my-imported-leaf", true);
+
+        final SchemaNode foundNode = SchemaContextUtil.findDataSchemaNode(context, module, xpath);
+
+        assertNotNull(foundNode);
+        assertNotNull(testNode);
+        assertEquals(testNode, foundNode);
+    }
+
+    @Test
+    public void findDataSchemaNodeTest2() throws URISyntaxException, IOException, YangSyntaxErrorException,
+            ReactorException {
+        // findDataSchemaNode(final SchemaContext context, final Module module,
+        // final RevisionAwareXPath nonCondXPath) {
+
+        final SchemaContext context = TestUtils.parseYangSources("/schema-context-util-test");
+        final Module module = context.findModule(URI.create("uri:my-module"), Revision.of("2014-10-07")).get();
+
+        final GroupingDefinition grouping = getGroupingByName(module, "my-grouping");
+        final SchemaNode testNode = grouping.getDataChildByName(QName.create(module.getQNameModule(),
+                "my-leaf-in-gouping2"));
+
+        final PathExpression xpath = new PathExpressionImpl("my:my-grouping/my:my-leaf-in-gouping2", true);
+
+        final SchemaNode foundNode = SchemaContextUtil.findDataSchemaNode(context, module, xpath);
+
+        assertNotNull(foundNode);
+        assertNotNull(testNode);
+        assertEquals(testNode, foundNode);
+
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void findDataSchemaNodeFromXPathIllegalArgumentTest() {
+        SchemaContextUtil.findDataSchemaNode(mock(SchemaContext.class), mock(Module.class), null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void findDataSchemaNodeFromXPathIllegalArgumentTest2() {
+        final SchemaContext mockContext = mock(SchemaContext.class);
+        final PathExpression xpath = new PathExpressionImpl("my:my-grouping/my:my-leaf-in-gouping2", true);
+
+        SchemaContextUtil.findDataSchemaNode(mockContext, null, xpath);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void findDataSchemaNodeFromXPathIllegalArgumentTest3() {
+        final Module module = mock(Module.class);
+        final PathExpression xpath = new PathExpressionImpl("my:my-grouping/my:my-leaf-in-gouping2", true);
+
+        SchemaContextUtil.findDataSchemaNode(null, module, xpath);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void findDataSchemaNodeFromXPathIllegalArgumentTest4() {
+        final SchemaContext mockContext = mock(SchemaContext.class);
+        final Module module = mock(Module.class);
+        final PathExpression xpath = new PathExpressionImpl("my:my-grouping[@con='NULL']/my:my-leaf-in-gouping2", true);
+
+        SchemaContextUtil.findDataSchemaNode(mockContext, module, xpath);
+    }
+
+    @Test
+    public void findDataSchemaNodeFromXPathNullTest() {
+        final SchemaContext mockContext = mock(SchemaContext.class);
+        final Module module = mock(Module.class);
+        final PathExpression xpath = mock(PathExpression.class);
+
+        when(xpath.getOriginalString()).thenReturn("");
+        assertNull(SchemaContextUtil.findDataSchemaNode(mockContext, module, xpath));
+    }
+
+    @Test
+    public void findDataSchemaNodeFromXPathNullTest2() {
+        final SchemaContext mockContext = mock(SchemaContext.class);
+        final Module module = mock(Module.class);
+        final PathExpression xpath = new PathExpressionImpl("my:my-grouping/my:my-leaf-in-gouping2", false);
+
+        assertNull(SchemaContextUtil.findDataSchemaNode(mockContext, module, xpath));
+    }
+
+    @Test
+    public void findNodeInSchemaContextGroupingsTest() throws URISyntaxException, IOException,
+            YangSyntaxErrorException, ReactorException {
+        final SchemaContext context = TestUtils.parseYangSources("/schema-context-util-test");
+        final Module myModule = context.findModule(URI.create("uri:my-module"), Revision.of("2014-10-07")).get();
+
+        // find grouping in container
+        DataNodeContainer dataContainer = (DataNodeContainer) myModule.getDataChildByName(QName.create(
+                myModule.getQNameModule(), "my-container"));
+        SchemaNode testNode = getGroupingByName(dataContainer, "my-grouping-in-container");
+
+        SchemaPath path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-container"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-container"));
+        SchemaNode foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = ((GroupingDefinition) testNode).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-leaf-in-grouping-in-container"));
+        path = path.createChild(QName.create(myModule.getQNameModule(), "my-leaf-in-grouping-in-container"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        // find grouping in list
+        dataContainer = (DataNodeContainer) ((DataNodeContainer) myModule.getDataChildByName(QName.create(
+                myModule.getQNameModule(), "my-container"))).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-list"));
+        testNode = getGroupingByName(dataContainer, "my-grouping-in-list");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-container"),
+                QName.create(myModule.getQNameModule(), "my-list"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-list"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = ((GroupingDefinition) testNode).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-leaf-in-grouping-in-list"));
+        path = path.createChild(QName.create(myModule.getQNameModule(), "my-leaf-in-grouping-in-list"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        // find grouping in grouping
+        dataContainer = getGroupingByName(myModule, "my-grouping");
+        testNode = getGroupingByName(dataContainer, "my-grouping-in-grouping");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-grouping"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-grouping"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = ((GroupingDefinition) testNode).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-leaf-in-grouping-in-grouping"));
+        path = path.createChild(QName.create(myModule.getQNameModule(), "my-leaf-in-grouping-in-grouping"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        // find grouping in rpc
+        final RpcDefinition rpc = getRpcByName(myModule, "my-rpc");
+        for (final GroupingDefinition grouping : rpc.getGroupings()) {
+            if (grouping.getQName().getLocalName().equals("my-grouping-in-rpc")) {
+                testNode = grouping;
+            }
+        }
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-rpc"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-rpc"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = ((GroupingDefinition) testNode).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-leaf-in-grouping-in-rpc"));
+        path = path.createChild(QName.create(myModule.getQNameModule(), "my-leaf-in-grouping-in-rpc"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        // find grouping in output
+        dataContainer = getRpcByName(myModule, "my-rpc").getOutput();
+        testNode = getGroupingByName(dataContainer, "my-grouping-in-output");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-rpc"),
+                QName.create(myModule.getQNameModule(), "output"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-output"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = ((GroupingDefinition) testNode).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-leaf-in-grouping-in-output"));
+        path = path.createChild(QName.create(myModule.getQNameModule(), "my-leaf-in-grouping-in-output"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        // find grouping in input
+        dataContainer = getRpcByName(myModule, "my-rpc").getInput();
+        testNode = getGroupingByName(dataContainer, "my-grouping-in-input");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-rpc"),
+                QName.create(myModule.getQNameModule(), "input"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-input"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = ((GroupingDefinition) testNode).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-leaf-in-grouping-in-input"));
+        path = path.createChild(QName.create(myModule.getQNameModule(), "my-leaf-in-grouping-in-input"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        // find grouping in notification
+        dataContainer = getNotificationByName(myModule, "my-notification");
+        testNode = getGroupingByName(dataContainer, "my-grouping-in-notification");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-notification"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-notification"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = ((GroupingDefinition) testNode).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-leaf-in-grouping-in-notification"));
+        path = path.createChild(QName.create(myModule.getQNameModule(), "my-leaf-in-grouping-in-notification"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        // find grouping in case
+        dataContainer = (DataNodeContainer) ((ChoiceSchemaNode) myModule.getDataChildByName(
+            QName.create(myModule.getQNameModule(), "my-choice")))
+                .findCaseNodes("one").iterator().next()
+                .getDataChildByName(QName.create(myModule.getQNameModule(), "my-container-in-case"));
+        testNode = getGroupingByName(dataContainer, "my-grouping-in-case");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-choice"),
+                QName.create(myModule.getQNameModule(), "one"),
+                QName.create(myModule.getQNameModule(), "my-container-in-case"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-case"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+
+        testNode = ((GroupingDefinition) testNode).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-leaf-in-grouping-in-case"));
+        path = path.createChild(QName.create(myModule.getQNameModule(), "my-leaf-in-grouping-in-case"));
+
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+    }
+
+    @Test
+    public void findNodeInSchemaContextGroupingsTest2() throws URISyntaxException, IOException,
+            YangSyntaxErrorException, ReactorException {
+
+        final SchemaContext context = TestUtils.parseYangSources("/schema-context-util-test");
+        final Module myModule = context.findModule(URI.create("uri:my-module"), Revision.of("2014-10-07")).get();
+
+        // find grouping in container
+        DataNodeContainer dataContainer = (DataNodeContainer) myModule.getDataChildByName(QName.create(
+                myModule.getQNameModule(), "my-container"));
+        SchemaNode testNode = getGroupingByName(dataContainer, "my-grouping-in-container2");
+
+        SchemaPath path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-container"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-container2"));
+        SchemaNode foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        // find grouping in list
+        dataContainer = (DataNodeContainer) ((DataNodeContainer) myModule.getDataChildByName(QName.create(
+                myModule.getQNameModule(), "my-container"))).getDataChildByName(QName.create(myModule.getQNameModule(),
+                "my-list"));
+        testNode = getGroupingByName(dataContainer, "my-grouping-in-list2");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-container"),
+                QName.create(myModule.getQNameModule(), "my-list"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-list2"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        // find grouping in grouping
+        dataContainer = getGroupingByName(myModule, "my-grouping");
+        testNode = getGroupingByName(dataContainer, "my-grouping-in-grouping2");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-grouping"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-grouping2"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        // find grouping in rpc
+        final RpcDefinition rpc = getRpcByName(myModule, "my-rpc");
+        for (final GroupingDefinition grouping : rpc.getGroupings()) {
+            if (grouping.getQName().getLocalName().equals("my-grouping-in-rpc2")) {
+                testNode = grouping;
+            }
+        }
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-rpc"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-rpc2"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        // find grouping in output
+        dataContainer = getRpcByName(myModule, "my-rpc").getOutput();
+        testNode = getGroupingByName(dataContainer, "my-grouping-in-output2");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-rpc"),
+                QName.create(myModule.getQNameModule(), "output"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-output2"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        // find grouping in input
+        dataContainer = getRpcByName(myModule, "my-rpc").getInput();
+        testNode = getGroupingByName(dataContainer, "my-grouping-in-input2");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-rpc"),
+                QName.create(myModule.getQNameModule(), "input"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-input2"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        // find grouping in notification
+        dataContainer = getNotificationByName(myModule, "my-notification");
+        testNode = getGroupingByName(dataContainer, "my-grouping-in-notification2");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-notification"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-notification2"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+
+        // find grouping in case
+        dataContainer = (DataNodeContainer) ((ChoiceSchemaNode) myModule.getDataChildByName(
+            QName.create(myModule.getQNameModule(), "my-choice")))
+                .findCaseNodes("one").iterator().next()
+                .getDataChildByName(QName.create(myModule.getQNameModule(), "my-container-in-case"));
+        testNode = getGroupingByName(dataContainer, "my-grouping-in-case2");
+
+        path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-choice"),
+                QName.create(myModule.getQNameModule(), "one"),
+                QName.create(myModule.getQNameModule(), "my-container-in-case"),
+                QName.create(myModule.getQNameModule(), "my-grouping-in-case2"));
+        foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNull(testNode);
+        assertNull(foundNode);
+    }
+
+    private static GroupingDefinition getGroupingByName(final DataNodeContainer dataNodeContainer, final String name) {
+        for (final GroupingDefinition grouping : dataNodeContainer.getGroupings()) {
+            if (grouping.getQName().getLocalName().equals(name)) {
+                return grouping;
+            }
+        }
+        return null;
+    }
+
+    private static RpcDefinition getRpcByName(final Module module, final String name) {
+        for (final RpcDefinition rpc : module.getRpcs()) {
+            if (rpc.getQName().getLocalName().equals(name)) {
+                return rpc;
+            }
+        }
+        return null;
+    }
+
+    private static NotificationDefinition getNotificationByName(final Module module, final String name) {
+        for (final NotificationDefinition notification : module.getNotifications()) {
+            if (notification.getQName().getLocalName().equals(name)) {
+                return notification;
+            }
+        }
+        return null;
+    }
+
+    @Test
+    public void findNodeInSchemaContextTheSameNameOfSiblingsTest() throws URISyntaxException, IOException,
+            YangSyntaxErrorException, ReactorException {
+
+        final SchemaContext context = TestUtils.parseYangSources("/schema-context-util-test");
+
+        final Module myModule = context.findModule(URI.create("uri:my-module"), Revision.of("2014-10-07")).get();
+        final ChoiceSchemaNode choice = (ChoiceSchemaNode) getRpcByName(myModule, "my-name").getInput()
+                .getDataChildByName(QName.create(myModule.getQNameModule(), "my-choice"));
+        final SchemaNode testNode = choice.findCaseNodes("case-two").iterator().next()
+                .getDataChildByName(QName.create(myModule.getQNameModule(), "two"));
+
+        final SchemaPath path = SchemaPath.create(true, QName.create(myModule.getQNameModule(), "my-name"),
+                QName.create(myModule.getQNameModule(), "input"), QName.create(myModule.getQNameModule(), "my-choice"),
+                QName.create(myModule.getQNameModule(), "case-two"), QName.create(myModule.getQNameModule(), "two"));
+        final SchemaNode foundNode = SchemaContextUtil.findNodeInSchemaContext(context, path.getPathFromRoot());
+
+        assertNotNull(testNode);
+        assertNotNull(foundNode);
+        assertEquals(testNode, foundNode);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangModelDependencyInfoTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangModelDependencyInfoTest.java
new file mode 100644 (file)
index 0000000..71fd9a6
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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.rfc7950.repo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+
+public class YangModelDependencyInfoTest {
+
+    @Test
+    public void testModuleWithNoImports() throws IOException, YangSyntaxErrorException {
+        YangModelDependencyInfo info = YangModelDependencyInfo.forResource(getClass(),
+            "/ietf/ietf-inet-types@2010-09-24.yang");
+        assertNotNull(info);
+        assertEquals("ietf-inet-types", info.getName());
+        assertEquals("2010-09-24", info.getFormattedRevision());
+        assertNotNull(info.getDependencies());
+
+        assertTrue(info.equals(info));
+    }
+
+    @Test
+    public void testModuleWithImports() throws IOException, YangSyntaxErrorException {
+        YangModelDependencyInfo info = YangModelDependencyInfo.forResource(getClass(),
+                "/parse-methods/dependencies/m2@2013-09-30.yang");
+        assertNotNull(info);
+        assertEquals("m2", info.getName());
+        assertEquals("2013-09-30", info.getFormattedRevision());
+        assertNotNull(info.getDependencies());
+        assertEquals(2, info.getDependencies().size());
+    }
+
+    @Test
+    public void testModuleWithoutRevision() throws IOException, YangSyntaxErrorException {
+        YangModelDependencyInfo info = YangModelDependencyInfo.forResource(getClass(),
+                "/no-revision/module-without-revision.yang");
+        assertNotNull(info);
+        assertEquals("module-without-revision", info.getName());
+        assertNull(info.getFormattedRevision());
+    }
+
+    @Test
+    public void testEquals() throws IOException, YangSyntaxErrorException {
+        YangModelDependencyInfo info1 = YangModelDependencyInfo.forResource(getClass(),
+            "/ietf/ietf-inet-types@2010-09-24.yang");
+        YangModelDependencyInfo info2 = YangModelDependencyInfo.forResource(getClass(),
+            "/no-revision/module-without-revision.yang");
+
+        assertTrue(info1.equals(info1));
+        assertFalse(info1.equals(null));
+        assertFalse(info1.equals(info2));
+    }
+
+    @Test
+    public void testYangtools827() throws IOException, YangSyntaxErrorException {
+        // Latest revision needs to be picked up irrespective of ordering
+        YangModelDependencyInfo info = YangModelDependencyInfo.forResource(getClass(),
+            "/bugs/YT827/foo.yang");
+        assertEquals("2014-12-24", info.getFormattedRevision());
+    }
+
+    @Test
+    public void testHashcode() throws IOException, YangSyntaxErrorException {
+        YangModelDependencyInfo info = YangModelDependencyInfo.forResource(getClass(),
+                "/no-revision/module-without-revision.yang");
+        assertNotEquals("hashcode", 31, info.hashCode());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testMalformedImport() throws IOException, YangSyntaxErrorException {
+        YangModelDependencyInfo.forResource(getClass(), "/depinfo-malformed/malformed-import.yang");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testMalformedImportRev() throws IOException, YangSyntaxErrorException {
+        YangModelDependencyInfo.forResource(getClass(), "/depinfo-malformed/malformed-import-rev.yang");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testMalformedModule() throws IOException, YangSyntaxErrorException {
+        YangModelDependencyInfo.forResource(getClass(), "/depinfo-malformed/malformed-module.yang");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testMalformedRev() throws IOException, YangSyntaxErrorException {
+        YangModelDependencyInfo.forResource(getClass(), "/depinfo-malformed/malformed-rev.yang");
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/Bug5410Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/Bug5410Test.java
new file mode 100644 (file)
index 0000000..8e4903b
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017 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.stmt.rfc6020;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug5410Test {
+    private static final String FOO_NS = "foo";
+
+    @Test
+    public void testYangPattern() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5410");
+        assertNotNull(context);
+
+        final PatternConstraint pattern = getPatternConstraintOf(context, "leaf-with-pattern");
+
+        final String rawRegex = pattern.getRegularExpressionString();
+        final String expectedYangRegex = "$0$.*|$1$[a-zA-Z0-9./]{1,8}$[a-zA-Z0-9./]{22}|$5$(rounds=\\d+$)?"
+                + "[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{43}|$6$(rounds=\\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{86}";
+        assertEquals(expectedYangRegex, rawRegex);
+
+        final String javaRegexFromYang = pattern.getJavaPatternString();
+        final String expectedJavaRegex = "^(?:\\$0\\$.*|\\$1\\$[a-zA-Z0-9./]{1,8}\\$[a-zA-Z0-9./]{22}|\\$5\\$"
+                + "(rounds=\\d+\\$)?[a-zA-Z0-9./]{1,16}\\$[a-zA-Z0-9./]{43}|\\$6\\$(rounds=\\d+\\$)?"
+                + "[a-zA-Z0-9./]{1,16}\\$[a-zA-Z0-9./]{86})$";
+        assertEquals(expectedJavaRegex, javaRegexFromYang);
+
+        final String value = "$6$AnrKGc0V$B/0/A.pWg4HrrA6YiEJOtFGibQ9Fmm5.4rI/"
+                + "00gEz3QeB7joSxBU3YtbHDm6NSkS1dKTQy3BWhwKKDS8nB5S//";
+        testPattern(javaRegexFromYang, ImmutableList.of(value), ImmutableList.of());
+    }
+
+    private static PatternConstraint getPatternConstraintOf(final SchemaContext context, final String leafName) {
+        final DataSchemaNode dataChildByName = context.getDataChildByName(foo(leafName));
+        assertTrue(dataChildByName instanceof LeafSchemaNode);
+        final LeafSchemaNode leaf = (LeafSchemaNode) dataChildByName;
+        final TypeDefinition<? extends TypeDefinition<?>> type = leaf.getType();
+        assertTrue(type instanceof StringTypeDefinition);
+        final StringTypeDefinition strType = (StringTypeDefinition) type;
+        return strType.getPatternConstraints().iterator().next();
+    }
+
+
+    private static void testPattern(final String javaRegex, final List<String> positiveMatches,
+            final List<String> negativeMatches) {
+        for (final String value : positiveMatches) {
+            assertTrue("Value '" + value + "' does not match java regex '" + javaRegex + "'", value.matches(javaRegex));
+        }
+        for (final String value : negativeMatches) {
+            assertFalse("Value '" + value + "' matches java regex '" + javaRegex + "'", value.matches(javaRegex));
+        }
+    }
+
+    private static QName foo(final String localName) {
+        return QName.create(FOO_NS, localName);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/YT826Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/YT826Test.java
new file mode 100644 (file)
index 0000000..b574ab0
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.stmt.rfc6020;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class YT826Test {
+
+    @Test
+    public void testWhenExpressionWhitespace() throws ReactorException, URISyntaxException, IOException,
+            YangSyntaxErrorException {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/bugs/yangtools826/example.yang");
+        assertNotNull(schemaContext);
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/ElementCountConstraintsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/ElementCountConstraintsTest.java
new file mode 100644 (file)
index 0000000..9b2e7a9
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016 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.stmt.rfc6020.effective;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+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.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class ElementCountConstraintsTest {
+
+    @Test
+    public void testElementCountConstraints() throws ParseException, ReactorException, URISyntaxException, IOException,
+            YangSyntaxErrorException {
+        final SchemaContext schemaContext = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(YangStatementStreamSource.create(
+                    YangTextSchemaSource.forResource("/constraint-definitions-test/foo.yang")))
+                .buildEffective();
+        assertNotNull(schemaContext);
+
+        final Module testModule = schemaContext.findModule("foo", Revision.of("2016-09-20")).get();
+        final LeafListSchemaNode constrainedLeafList1 = (LeafListSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "constrained-leaf-list-1"));
+        assertNotNull(constrainedLeafList1);
+        ElementCountConstraint constraints1 = constrainedLeafList1.getElementCountConstraint().get();
+
+        final LeafListSchemaNode constrainedLeafList2 = (LeafListSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "constrained-leaf-list-2"));
+        assertNotNull(constrainedLeafList2);
+        ElementCountConstraint constraints2 = constrainedLeafList2.getElementCountConstraint().get();
+
+        assertEquals(constraints1.hashCode(), constraints2.hashCode());
+        assertEquals(constraints1, constraints2);
+
+        final LeafListSchemaNode constrainedLeafList3 = (LeafListSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "constrained-leaf-list-3"));
+        assertNotNull(constrainedLeafList3);
+        ElementCountConstraint constraints3 = constrainedLeafList3.getElementCountConstraint().get();
+
+        assertNotEquals(constraints2.hashCode(), constraints3.hashCode());
+        assertNotEquals(constraints2, constraints3);
+
+        final LeafListSchemaNode constrainedLeafList4 = (LeafListSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "constrained-leaf-list-4"));
+        assertNotNull(constrainedLeafList4);
+        ElementCountConstraint constraints4 = constrainedLeafList4.getElementCountConstraint().get();
+
+        assertNotEquals(constraints3.hashCode(), constraints4.hashCode());
+        assertNotEquals(constraints3, constraints4);
+
+        assertEquals("ElementCountConstraint{minElements=50, maxElements=100}", constraints4.toString());
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/ActionStatementTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/ActionStatementTest.java
new file mode 100644 (file)
index 0000000..6c09db4
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class ActionStatementTest {
+
+    private static final String FOO_NS = "foo-namespace";
+    private static final String FOO_REV = "2016-12-13";
+
+    @Test
+    public void testActionStatementInDataContainers() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/action-stmt/foo.yang");
+        assertNotNull(schemaContext);
+
+        assertContainsActions(schemaContext, "root", "grp-action", "aug-action");
+        assertContainsActions(schemaContext, "top-list", "top-list-action");
+        assertContainsActions(schemaContext, "top", "top-action");
+
+        final Set<GroupingDefinition> groupings = schemaContext.getGroupings();
+        assertEquals(1, groupings.size());
+        assertContainsActions(groupings.iterator().next(), "grp-action");
+
+        final Set<Module> modules = schemaContext.getModules();
+        assertEquals(1, modules.size());
+        final Module foo = modules.iterator().next();
+        final Set<AugmentationSchemaNode> augmentations = foo.getAugmentations();
+        assertEquals(1, augmentations.size());
+        assertContainsActions(augmentations.iterator().next(), "aug-action", "grp-action");
+    }
+
+    private static void assertContainsActions(final SchemaContext schemaContext, final String dataContainerName,
+            final String... actionNames) {
+        final DataSchemaNode dataChildByName = schemaContext.getDataChildByName(QName.create(FOO_NS, FOO_REV,
+                dataContainerName));
+        assertTrue(dataChildByName instanceof ActionNodeContainer);
+        assertContainsActions((ActionNodeContainer) dataChildByName, actionNames);
+    }
+
+    private static void assertContainsActions(final ActionNodeContainer actionContainer,
+            final String... actionNames) {
+        final Set<ActionDefinition> actions = actionContainer.getActions();
+        assertEquals(actionNames.length, actions.size());
+
+        final Set<QName> actionQNames = new HashSet<>();
+        actions.forEach(n -> actionQNames.add(n.getQName()));
+
+        for (final String actionName : actionNames) {
+            assertTrue(actionQNames.contains(QName.create(FOO_NS, FOO_REV, actionName)));
+        }
+    }
+
+    @Test
+    public void testActionUnsupportedInYang10() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/action-stmt/foo10.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith("action is not a YANG statement or use of extension"));
+        }
+    }
+
+    @Test
+    public void testActionWithinIllegalAncestor() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/action-stmt/foo-invalid.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith("Action (foo-namespace?revision=2016-12-13)"
+                    + "action-in-grouping is defined within a notification, rpc or another action"));
+        }
+    }
+
+    @Test
+    public void testActionWithinListWithoutKey() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/action-stmt/bar-invalid.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                    "Action (bar-namespace?revision=2016-12-13)my-action is defined within a list"
+                            + " that has no key statement"));
+        }
+    }
+
+    @Test
+    public void testActionInUsedGroupingWithinCase() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/action-stmt/baz-invalid.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                    "Action (baz-namespace?revision=2016-12-13)action-in-grouping is defined within a "
+                            + "case statement"));
+        }
+    }
+
+    @Test
+    public void testActionInUsedGroupingAtTopLevelOfModule() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/action-stmt/foobar-invalid.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                    "Action (foobar-namespace?revision=2016-12-13)my-action is defined at the top level of a module"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6867BasicTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6867BasicTest.java
new file mode 100644 (file)
index 0000000..a154d46
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6867BasicTest {
+
+    @Test
+    public void valid10Test() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/basic-test/valid-10.yang");
+        assertNotNull(schemaContext);
+    }
+
+    @Test
+    public void valid11Test() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/basic-test/valid-11.yang");
+        assertNotNull(schemaContext);
+    }
+
+    @Test
+    public void invalid10Test() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/basic-test/invalid-10.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith("NOTIFICATION is not valid for CONTAINER"));
+        }
+    }
+
+    @Test
+    public void invalid11Test() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/basic-test/invalid-11.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith("RPC is not valid for CONTAINER"));
+        }
+    }
+
+    @Test
+    public void anyData11Test() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/basic-test/anydata-11.yang");
+        assertNotNull(schemaContext);
+    }
+
+    @Test
+    public void anyData10Test() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/basic-test/anydata-10.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith("anydata is not a YANG statement or use of extension"));
+        }
+    }
+
+    @Test
+    public void yangModelTest() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources("/rfc7950/model");
+        assertNotNull(schemaContext);
+    }
+
+    @Test
+    public void unsupportedVersionTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/basic-test/unsupported-version.yang");
+            fail("Test should fail due to unsupported Yang version");
+        } catch (final SomeModifiersUnresolvedException e) {
+            final Throwable cause = e.getCause();
+            assertNotNull(cause);
+            assertTrue(cause.getMessage().startsWith("Unsupported YANG version 2.3"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6868Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6868Test.java
new file mode 100644 (file)
index 0000000..7b6d585
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2017 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6868Test {
+    private static final String FOO_NS = "foo";
+    private static final String IMP_NS = "imp";
+    private static final String IMP_REV = "2017-01-09";
+    private static final Set<String> ALL_CONTAINERS = ImmutableSet.of("my-container-1", "my-container-2",
+            "my-container-3", "foo", "not-foo", "imp-bar", "imp-bar-2");
+
+    @Test
+    public void ifFeatureYang11ResolutionTest() throws Exception {
+        assertSchemaContextFor(null, ALL_CONTAINERS);
+        assertSchemaContextFor(ImmutableSet.of(), ImmutableSet.of("my-container-1", "my-container-2", "not-foo"));
+        assertSchemaContextFor(ImmutableSet.of("foo"), ImmutableSet.of("foo"));
+        assertSchemaContextFor(ImmutableSet.of("baz"),
+                ImmutableSet.of("my-container-1", "my-container-2", "my-container-3", "not-foo"));
+        assertSchemaContextFor(ImmutableSet.of("bar", "baz"),
+                ImmutableSet.of("my-container-1", "my-container-2", "my-container-3", "not-foo"));
+        assertSchemaContextFor(ImmutableSet.of("foo", "bar", "baz"),
+                ImmutableSet.of("my-container-1", "my-container-2", "my-container-3", "foo"));
+        assertSchemaContextFor(ImmutableSet.of("foo", "bar", "baz", "imp:bar"),
+                ImmutableSet.of("my-container-1", "my-container-2", "my-container-3", "foo", "imp-bar"));
+        assertSchemaContextFor(ImmutableSet.of("foo", "baz", "imp:bar"),
+            ImmutableSet.of("foo", "imp-bar", "imp-bar-2"));
+    }
+
+    private static void assertSchemaContextFor(final Set<String> supportedFeatures,
+            final Set<String> expectedContainers) throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources("/rfc7950/bug6868/yang11",
+                supportedFeatures != null ? createFeaturesSet(supportedFeatures) : null,
+                StatementParserMode.DEFAULT_MODE);
+        assertNotNull(schemaContext);
+
+        for (final String expectedContainer : expectedContainers) {
+            assertTrue(String.format("Expected container %s not found.", expectedContainer),
+                    findNode(schemaContext, expectedContainer) instanceof ContainerSchemaNode);
+        }
+
+        final Set<String> unexpectedContainers = Sets.difference(ALL_CONTAINERS, expectedContainers);
+        for (final String unexpectedContainer : unexpectedContainers) {
+            assertNull(String.format("Unexpected container %s.", unexpectedContainer),
+                    findNode(schemaContext, unexpectedContainer));
+        }
+    }
+
+    private static Set<QName> createFeaturesSet(final Set<String> featureNames) {
+        final Set<QName> supportedFeatures = new HashSet<>();
+        for (final String featureName : featureNames) {
+            if (featureName.indexOf(':') == -1) {
+                supportedFeatures.add(QName.create(FOO_NS, featureName));
+            } else {
+                supportedFeatures
+                        .add(QName.create(IMP_NS, IMP_REV, featureName.substring(featureName.indexOf(':') + 1)));
+            }
+        }
+
+        return ImmutableSet.copyOf(supportedFeatures);
+    }
+
+    private static SchemaNode findNode(final SchemaContext context, final String localName) {
+        return SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, QName.create(FOO_NS, localName)));
+    }
+
+    @Test
+    public void invalidYang10Test() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6868/invalid10.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage()
+                    .startsWith("Invalid identifier '(not foo) or (bar and baz)' [at "));
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6869Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6869Test.java
new file mode 100644 (file)
index 0000000..e8c22ab
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2016 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6869Test {
+    private static final String FOO_NS = "foo";
+
+    @Test
+    public void identityNoFeaureTest() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6869/foo.yang",
+                ImmutableSet.of());
+        assertNotNull(schemaContext);
+
+        final Set<IdentitySchemaNode> identities = getIdentities(schemaContext);
+        assertEquals(0, identities.size());
+
+        final SchemaNode findNode = findNode(schemaContext, ImmutableList.of("root", "grp-leaf"));
+        assertTrue(findNode instanceof LeafSchemaNode);
+        final LeafSchemaNode grpLeaf = (LeafSchemaNode) findNode;
+        assertFalse(grpLeaf.isMandatory());
+    }
+
+    @Test
+    public void identityAllFeauresTest() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6869/foo.yang",
+                createFeaturesSet("identity-feature", "mandatory-leaf", "tls", "ssh", "two", "three"));
+        assertNotNull(schemaContext);
+
+        final Set<IdentitySchemaNode> identities = getIdentities(schemaContext);
+        assertEquals(1, identities.size());
+
+        final SchemaNode findNode = findNode(schemaContext, ImmutableList.of("root", "grp-leaf"));
+        assertTrue(findNode instanceof LeafSchemaNode);
+        final LeafSchemaNode grpLeaf = (LeafSchemaNode) findNode;
+        assertTrue(grpLeaf.isMandatory());
+    }
+
+    private static Set<IdentitySchemaNode> getIdentities(final SchemaContext schemaContext) {
+        final Set<Module> modules = schemaContext.getModules();
+        assertEquals(1, modules.size());
+        final Module module = modules.iterator().next();
+        return module.getIdentities();
+    }
+
+    private static Set<QName> createFeaturesSet(final String... featureNames) {
+        final Set<QName> supportedFeatures = new HashSet<>();
+        for (final String featureName : featureNames) {
+            supportedFeatures.add(QName.create(FOO_NS, featureName));
+        }
+
+        return ImmutableSet.copyOf(supportedFeatures);
+    }
+
+    private static SchemaNode findNode(final SchemaContext context, final Iterable<String> localNamesPath) {
+        final Iterable<QName> qNames = Iterables.transform(localNamesPath,
+            localName -> QName.create(FOO_NS, localName));
+        return SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(qNames, true));
+    }
+
+    @Test
+    public void invalidYang10Test() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6869/invalid10.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith("IF_FEATURE is not valid for IDENTITY"));
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6870Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6870Test.java
new file mode 100644 (file)
index 0000000..bd3e176
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2016 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.List;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6870Test {
+    private static final String FOO_NS = "foo";
+
+    @Test
+    public void valid11Test() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6870/foo.yang");
+        assertNotNull(schemaContext);
+
+        assertModifier(schemaContext, ModifierKind.INVERT_MATCH, ImmutableList.of("root", "my-leaf"));
+        assertModifier(schemaContext, null, ImmutableList.of("root", "my-leaf-2"));
+    }
+
+    private static void assertModifier(final SchemaContext schemaContext, final ModifierKind expectedModifierKind,
+            final List<String> localNamePath) {
+        final SchemaNode findNode = findNode(schemaContext, localNamePath);
+        assertTrue(findNode instanceof LeafSchemaNode);
+        final LeafSchemaNode myLeaf = (LeafSchemaNode) findNode;
+
+        final TypeDefinition<? extends TypeDefinition<?>> type = myLeaf.getType();
+        assertTrue(type instanceof StringTypeDefinition);
+        final StringTypeDefinition stringType = (StringTypeDefinition) type;
+
+        final List<PatternConstraint> patternConstraints = stringType.getPatternConstraints();
+        assertEquals(1, patternConstraints.size());
+
+        final PatternConstraint patternConstraint = patternConstraints.iterator().next();
+        assertEquals(Optional.ofNullable(expectedModifierKind), patternConstraint.getModifier());
+    }
+
+    private static SchemaNode findNode(final SchemaContext context, final Iterable<String> localNamesPath) {
+        final Iterable<QName> qNames = Iterables.transform(localNamesPath,
+            localName -> QName.create(FOO_NS, localName));
+        return SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(qNames, true));
+    }
+
+    @Test
+    public void invalid11Test() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6870/invalid11.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage()
+                    .startsWith("'Invert-match' is not valid argument of modifier statement"));
+        }
+    }
+
+    @Test
+    public void invalid10Test() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6870/invalid10.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith("modifier is not a YANG statement or use of extension"));
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6871Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6871Test.java
new file mode 100644 (file)
index 0000000..197e47a
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Collection;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6871Test {
+
+    @Test
+    public void testValidYang11Model() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6871/foo.yang");
+        assertNotNull(schemaContext);
+
+        final Module foo = schemaContext.findModule("foo", Revision.of("2016-12-14")).get();
+
+        final Set<NotificationDefinition> notifications = foo.getNotifications();
+        assertEquals(1, notifications.size());
+        final NotificationDefinition myNotification = notifications.iterator().next();
+        Collection<MustDefinition> mustConstraints = myNotification.getMustConstraints();
+        assertEquals(2, mustConstraints.size());
+
+        final Set<RpcDefinition> rpcs = foo.getRpcs();
+        assertEquals(1, rpcs.size());
+        final RpcDefinition myRpc = rpcs.iterator().next();
+
+        final ContainerSchemaNode input = myRpc.getInput();
+        assertNotNull(input);
+        mustConstraints = input.getMustConstraints();
+        assertEquals(2, mustConstraints.size());
+
+        final ContainerSchemaNode output = myRpc.getOutput();
+        assertNotNull(output);
+        mustConstraints = output.getMustConstraints();
+        assertEquals(2, mustConstraints.size());
+    }
+
+    @Test
+    public void testInvalidYang10Model() throws Exception {
+        assertException("/rfc7950/bug6871/foo10.yang", "MUST is not valid for NOTIFICATION");
+        assertException("/rfc7950/bug6871/bar10.yang", "MUST is not valid for INPUT");
+        assertException("/rfc7950/bug6871/baz10.yang", "MUST is not valid for OUTPUT");
+    }
+
+    private static void assertException(final String sourcePath, final String exceptionMessage) throws Exception {
+        try {
+            StmtTestUtils.parseYangSource(sourcePath);
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final ReactorException ex) {
+            assertTrue(ex.getCause().getMessage().startsWith(exceptionMessage));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6874Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6874Test.java
new file mode 100644 (file)
index 0000000..05ec866
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2016 Inocybe Technologies 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IncludeStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6874Test {
+
+    private static final StatementStreamSource ROOT_MODULE = sourceForResource(
+        "/rfc7950/include-import-stmt-test/valid-11/root-module.yang");
+    private static final StatementStreamSource CHILD_MODULE = sourceForResource(
+        "/rfc7950/include-import-stmt-test/valid-11/child-module.yang");
+    private static final StatementStreamSource CHILD_MODULE_1 = sourceForResource(
+        "/rfc7950/include-import-stmt-test/valid-11/child-module-1.yang");
+    private static final StatementStreamSource IMPORTED_MODULE = sourceForResource(
+        "/rfc7950/include-import-stmt-test/valid-11/imported-module.yang");
+
+    @Test
+    public void valid11Test() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(
+            "/rfc7950/include-import-stmt-test/valid-11");
+        assertNotNull(schemaContext);
+
+        // Test for valid include statement
+        final Module testModule = schemaContext.findModules("root-module").iterator().next();
+        assertNotNull(testModule);
+
+        // Test for valid import statement
+        ModuleImport importStmt = testModule.getImports().iterator().next();
+        assertEquals(Optional.of("Yang 1.1: Allow description and reference in include and import."),
+            importStmt.getDescription());
+        assertEquals(Optional.of("https://tools.ietf.org/html/rfc7950 section-7.1.5/6"),
+            importStmt.getReference());
+    }
+
+    @Test
+    public void invalid10IncludeStmtTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/rfc7950/include-import-stmt-test/invalid-include-10");
+            fail("Test must fail: DESCRIPTION/REFERENCE are not valid for INCLUDE in Yang 1.0");
+        } catch (final SomeModifiersUnresolvedException e) {
+            final String msg = e.getCause().getMessage();
+            assertTrue(msg.startsWith("DESCRIPTION is not valid for INCLUDE")
+                || msg.startsWith("REFERENCE is not valid for INCLUDE"));
+        }
+    }
+
+    @Test
+    public void invalid10ImportStmtTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/rfc7950/include-import-stmt-test/invalid-import-10");
+            fail("Test must fail: DESCRIPTION/REFERENCE are not valid for IMPORT in Yang 1.0");
+        } catch (final SomeModifiersUnresolvedException e) {
+            final String msg = e.getCause().getMessage();
+            assertTrue(msg.startsWith("DESCRIPTION is not valid for IMPORT")
+                || msg.startsWith("REFERENCE is not valid for IMPORT"));
+        }
+    }
+
+    @Test
+    public void descriptionAndReferenceTest11() throws ReactorException {
+        RFC7950Reactors.defaultReactor().newBuild()
+            .addSources(ROOT_MODULE, CHILD_MODULE, CHILD_MODULE_1, IMPORTED_MODULE)
+            .build().getRootStatements().forEach(declaredStmt -> {
+                if (declaredStmt instanceof ModuleStatement) {
+                    declaredStmt.declaredSubstatements().forEach(subStmt -> {
+                        if (subStmt instanceof IncludeStatement && subStmt.rawArgument().equals("child-module")) {
+                            subStmt.declaredSubstatements().forEach(Bug6874Test::verifyDescAndRef);
+                        }
+                    });
+                }
+            });
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static void verifyDescAndRef(final DeclaredStatement stmt) {
+        if (stmt instanceof DescriptionStatement) {
+            assertEquals("Yang 1.1: Allow description and reference in include and import.",
+                ((DescriptionStatement) stmt).argument());
+        }
+        if (stmt instanceof ReferenceStatement) {
+            assertEquals("https://tools.ietf.org/html/rfc7950 section-7.1.5/6", ((ReferenceStatement) stmt).argument());
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6876Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6876Test.java
new file mode 100644 (file)
index 0000000..b74618d
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6876Test {
+    private static final String BAR_NS = "bar";
+    private static final String BAR_REV = "2017-01-11";
+    private static final String FOO_NS = "foo";
+
+    @Test
+    public void yang11Test() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/rfc7950/bug6876/yang11");
+        assertNotNull(context);
+
+        assertTrue(findNode(context, ImmutableList.of(bar("augment-target"),
+            bar("my-leaf"))) instanceof LeafSchemaNode);
+        assertTrue(findNode(context, ImmutableList.of(bar("augment-target"),
+            foo("mandatory-leaf"))) instanceof LeafSchemaNode);
+    }
+
+    @Test
+    public void yang10Test() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/rfc7950/bug6876/yang10");
+        assertNotNull(context);
+
+        assertTrue(findNode(context, ImmutableList.of(bar("augment-target"),
+            bar("my-leaf"))) instanceof LeafSchemaNode);
+        assertNull(findNode(context, ImmutableList.of(bar("augment-target"), foo("mandatory-leaf"))));
+    }
+
+    private static SchemaNode findNode(final SchemaContext context, final Iterable<QName> qnames) {
+        return SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(qnames, true));
+    }
+
+    private static QName foo(final String localName) {
+        return QName.create(FOO_NS, localName);
+    }
+
+    private static QName bar(final String localName) {
+        return QName.create(BAR_NS, BAR_REV, localName);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6878Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6878Test.java
new file mode 100644 (file)
index 0000000..7f7140b
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017 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.stmt.rfc7950;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6878Test {
+
+    @Test
+    public void testParsingXPathWithYang11Functions() throws Exception {
+        final String testLog = parseAndcaptureLog("/rfc7950/bug6878/foo.yang");
+        assertFalse(testLog.contains("Could not find function: "));
+    }
+
+    @Test
+    public void shouldLogInvalidYang10XPath() throws Exception {
+        final String testLog = parseAndcaptureLog("/rfc7950/bug6878/foo10-invalid.yang");
+        assertThat(testLog, containsString("RFC7950 features required in RFC6020 context to parse expression "));
+    }
+
+    @Test
+    public void shouldLogInvalidYang10XPath2() throws Exception {
+        final String testLog = parseAndcaptureLog("/rfc7950/bug6878/foo10-invalid-2.yang");
+        assertThat(testLog, containsString("RFC7950 features required in RFC6020 context to parse expression "));
+    }
+
+    @SuppressWarnings("checkstyle:regexpSinglelineJava")
+    private static String parseAndcaptureLog(final String yangFile) throws Exception {
+        final PrintStream stdout = System.out;
+        final ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+        try (PrintStream out = new PrintStream(output, true, "UTF-8")) {
+            System.setOut(out);
+            StmtTestUtils.parseYangSource(yangFile);
+        } finally {
+            System.setOut(stdout);
+        }
+
+        return output.toString();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6880Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6880Test.java
new file mode 100644 (file)
index 0000000..a44bde1
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Collection;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6880Test {
+    private static final String FOO_NS = "foo";
+
+    @Test
+    public void valid10Test() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6880/foo.yang");
+        assertNotNull(schemaContext);
+
+        final SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(schemaContext,
+                SchemaPath.create(true, QName.create(FOO_NS, "my-leaf-list")));
+        assertTrue(findDataSchemaNode instanceof LeafListSchemaNode);
+        final LeafListSchemaNode myLeafList = (LeafListSchemaNode) findDataSchemaNode;
+
+        final Collection<? extends Object> defaults = myLeafList.getDefaults();
+        assertEquals(2, defaults.size());
+        assertTrue(defaults.contains("my-default-value-1") && defaults.contains("my-default-value-2"));
+    }
+
+    @Test
+    public void invalid10Test() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6880/invalid10.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith("DEFAULT is not valid for LEAF_LIST"));
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6883Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6883Test.java
new file mode 100644 (file)
index 0000000..8891459
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6883Test {
+    private static final String FOO_NS = "foo";
+
+    @Test
+    public void test() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources("/rfc7950/bug6883");
+        assertNotNull(schemaContext);
+
+        final AnydataSchemaNode topAnyData = assertAnyData(schemaContext, ImmutableList.of("top"));
+        assertEquals(Status.DEPRECATED, topAnyData.getStatus());
+        assertEquals(Optional.of("top anydata"), topAnyData.getDescription());
+
+        assertAnyData(schemaContext, ImmutableList.of("root", "root-anydata"));
+        assertAnyData(schemaContext, ImmutableList.of("root", "aug-anydata"));
+        assertAnyData(schemaContext, ImmutableList.of("root", "grp-anydata"));
+        assertAnyData(schemaContext, ImmutableList.of("my-list", "list-anydata"));
+        assertAnyData(schemaContext, ImmutableList.of("sub-data"));
+
+        assertAnyData(schemaContext, ImmutableList.of("my-rpc", "input", "input-anydata"));
+        assertAnyData(schemaContext, ImmutableList.of("my-rpc", "output", "output-anydata"));
+        assertAnyData(schemaContext, ImmutableList.of("my-notification", "notification-anydata"));
+
+        assertAnyData(schemaContext, ImmutableList.of("my-choice", "one", "case-anydata"));
+        assertAnyData(schemaContext, ImmutableList.of("my-choice", "case-shorthand-anydata", "case-shorthand-anydata"));
+    }
+
+    private static AnydataSchemaNode assertAnyData(final SchemaContext context, final Iterable<String> localNamesPath) {
+        final Iterable<QName> qNames = Iterables.transform(localNamesPath,
+            localName -> QName.create(FOO_NS, localName));
+        final SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(qNames, true));
+        assertTrue(findDataSchemaNode instanceof AnydataSchemaNode);
+        return (AnydataSchemaNode) findDataSchemaNode;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6884Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6884Test.java
new file mode 100644 (file)
index 0000000..ce98c6d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6884Test {
+    private static final String FOO_NS = "foo";
+
+    @Test
+    public void testYang11() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources("/rfc7950/bug6884/yang1-1");
+        assertNotNull(schemaContext);
+
+        assertTrue(findNode(schemaContext, ImmutableList.of(foo("sub-root"), foo("sub-foo-2-con")))
+            instanceof ContainerSchemaNode);
+    }
+
+    @Test
+    public void testCircularIncludesYang10() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources("/rfc7950/bug6884/circular-includes");
+
+        assertNotNull(schemaContext);
+        assertTrue(findNode(schemaContext, ImmutableList.of(foo("sub-root"), foo("sub-foo-2-con")))
+            instanceof ContainerSchemaNode);
+        assertTrue(findNode(schemaContext, ImmutableList.of(foo("sub-root-2"), foo("sub-foo-con")))
+            instanceof ContainerSchemaNode);
+    }
+
+    private static SchemaNode findNode(final SchemaContext context, final Iterable<QName> qnames) {
+        return SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(qnames, true));
+    }
+
+    private static QName foo(final String localName) {
+        return QName.create(FOO_NS, localName);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6885Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6885Test.java
new file mode 100644 (file)
index 0000000..d6d8fdd
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2017 Opendaylight.  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.stmt.rfc7950;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6885Test {
+
+    @Test
+    public void validYang10Test() throws Exception {
+        // Yang 1.0 allows "if-feature" and "when" on list keys
+        final SchemaContext schemaContext =
+                StmtTestUtils.parseYangSource("/rfc7950/list-keys-test/correct-list-keys-test.yang");
+        assertNotNull(schemaContext);
+    }
+
+    @Test
+    public void invalidListLeafKeyTest1() throws Exception {
+        final String exceptionMessage = "(urn:ietf:params:xml:ns:yang:yin:1)when statement is not allowed in "
+                + "(incorrect-list-keys-test?revision=2017-02-06)a2 leaf statement which is specified as a list key.";
+        testForWhen("/rfc7950/list-keys-test/incorrect-list-keys-test.yang", exceptionMessage);
+    }
+
+    @Test
+    public void invalidListLeafKeyTest2() throws Exception {
+        final String exceptionMessage = "(urn:ietf:params:xml:ns:yang:yin:1)if-feature statement is not allowed in "
+                + "(incorrect-list-keys-test1?revision=2017-02-06)b leaf statement which is specified as a list key.";
+        testForIfFeature("/rfc7950/list-keys-test/incorrect-list-keys-test1.yang", exceptionMessage);
+    }
+
+    @Test
+    public void invalidListUsesLeafKeyTest() throws Exception {
+        final String exceptionMessage = "(urn:ietf:params:xml:ns:yang:yin:1)if-feature statement is not allowed in "
+                + "(incorrect-list-keys-test2?revision=2017-02-06)a1 leaf statement which is specified as a list key.";
+        testForIfFeature("/rfc7950/list-keys-test/incorrect-list-keys-test2.yang", exceptionMessage);
+    }
+
+    @Test
+    public void invalidListUsesLeafKeyTest1() throws Exception {
+        final String exceptionMessage = "(urn:ietf:params:xml:ns:yang:yin:1)when statement is not allowed in "
+                + "(incorrect-list-keys-test3?revision=2017-02-06)a2 leaf statement which is specified as a list key.";
+        testForWhen("/rfc7950/list-keys-test/incorrect-list-keys-test3.yang", exceptionMessage);
+    }
+
+    @Test
+    public void invalidListUsesLeafKeyTest2() throws Exception {
+        final String exceptionMessage = "(urn:ietf:params:xml:ns:yang:yin:1)if-feature statement is not allowed in "
+                + "(incorrect-list-keys-test4?revision=2017-02-06)a1 leaf statement which is specified as a list key.";
+        testForIfFeature("/rfc7950/list-keys-test/incorrect-list-keys-test4.yang", exceptionMessage);
+    }
+
+    @Test
+    public void invalidListUsesRefineLeafKeyTest() throws Exception {
+        final String exceptionMessage = "(urn:ietf:params:xml:ns:yang:yin:1)if-feature statement is not allowed in "
+                + "(incorrect-list-keys-test5?revision=2017-02-06)a1 leaf statement which is specified as a list key.";
+        testForIfFeature("/rfc7950/list-keys-test/incorrect-list-keys-test5.yang", exceptionMessage);
+    }
+
+    private static void testForIfFeature(final String yangSrcPath, final String exMsg) throws URISyntaxException,
+            SourceException, IOException, YangSyntaxErrorException {
+        try {
+            StmtTestUtils.parseYangSource(yangSrcPath);
+            fail("Test must fail: IF-FEATURE substatement is not allowed in LIST keys");
+        } catch (final ReactorException e) {
+            final Throwable cause = e.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith(exMsg));
+        }
+    }
+
+    private static void testForWhen(final String yangSrcPath, final String exMsg) throws URISyntaxException,
+            SourceException, IOException, YangSyntaxErrorException {
+        try {
+            StmtTestUtils.parseYangSource(yangSrcPath);
+            fail("Test must fail: WHEN substatement is not allowed in LIST keys");
+        } catch (final ReactorException e) {
+            final Throwable cause = e.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith(exMsg));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6886Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6886Test.java
new file mode 100644 (file)
index 0000000..f20b1ae
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2017 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6886Test {
+
+    @Test
+    public void yang11UnquotedStrTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6886/yang11/foo.yang");
+            fail("Test should fail due to invalid yang");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage()
+                    .startsWith("YANG 1.1: unquoted string (illegalchars\"test1) contains illegal characters"));
+        }
+    }
+
+    @Test
+    public void yang11UnquotedStrTest2() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6886/yang11/foo2.yang");
+            fail("Test should fail due to invalid yang");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage()
+                    .startsWith("YANG 1.1: unquoted string (illegalchars'test2) contains illegal characters"));
+        }
+    }
+
+    @Test
+    public void yang11DoubleQuotedStrTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6886/yang11/foo3.yang");
+            fail("Test should fail due to invalid yang");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith("YANG 1.1: illegal double quoted string "
+                    + "(i\\\\\\\\l\\nl\\te\\\"\\galcharstest1). In double quoted string the backslash must be followed "
+                    + "by one of the following character [n,t,\",\\], but was 'g'."));
+        }
+    }
+
+    @Test
+    public void yang10UnquotedStrTest() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6886/yang10/foo.yang");
+        assertNotNull(schemaContext);
+    }
+
+    @Test
+    public void yang10UnquotedStrTest2() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6886/yang10/foo2.yang");
+        assertNotNull(schemaContext);
+    }
+
+    @Test
+    public void yang10DoubleQuotedStrTest() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6886/yang10/foo3.yang");
+        assertNotNull(schemaContext);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6887Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6887Test.java
new file mode 100644 (file)
index 0000000..232f506
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2017 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+import org.opendaylight.yangtools.yang.model.util.type.BitBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.EnumPairBuilder;
+import org.opendaylight.yangtools.yang.model.util.type.InvalidBitDefinitionException;
+import org.opendaylight.yangtools.yang.model.util.type.InvalidEnumDefinitionException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6887Test {
+
+    @Test
+    public void testRestrictedEnumeration() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6887/foo.yang");
+        assertNotNull(schemaContext);
+
+        final Module foo = schemaContext.findModule("foo", Revision.of("2017-01-26")).get();
+        final LeafSchemaNode myEnumerationLeaf = (LeafSchemaNode) foo.getDataChildByName(
+                QName.create(foo.getQNameModule(), "my-enumeration-leaf"));
+        assertNotNull(myEnumerationLeaf);
+
+        EnumTypeDefinition enumerationType = (EnumTypeDefinition) myEnumerationLeaf.getType();
+
+        List<EnumPair> enums = enumerationType.getValues();
+        assertEquals(2, enums.size());
+        final EnumPair yellowEnum = createEnumPair("yellow", 2);
+        final EnumPair redEnum = createEnumPair("red", 3);
+        assertContainsEnums(enums, yellowEnum, redEnum);
+
+        enumerationType = enumerationType.getBaseType();
+        enums = enumerationType.getValues();
+        assertEquals(3, enums.size());
+        final EnumPair blackEnum = createEnumPair("black", 4);
+        assertContainsEnums(enums, yellowEnum, redEnum, blackEnum);
+
+        enumerationType = enumerationType.getBaseType();
+        enums = enumerationType.getValues();
+        assertEquals(4, enums.size());
+        final EnumPair whiteEnum = createEnumPair("white", 1);
+        assertContainsEnums(enums, whiteEnum, yellowEnum, redEnum, blackEnum);
+
+        final LeafSchemaNode myEnumerationLeaf2 = (LeafSchemaNode) foo.getDataChildByName(
+                QName.create(foo.getQNameModule(), "my-enumeration-leaf-2"));
+        assertNotNull(myEnumerationLeaf2);
+
+        enumerationType = (EnumTypeDefinition) myEnumerationLeaf2.getType();
+        enums = enumerationType.getValues();
+        assertEquals(3, enums.size());
+        assertContainsEnums(enums, yellowEnum, redEnum, blackEnum);
+    }
+
+    @Test
+    public void testInvalidRestrictedEnumeration() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6887/foo-invalid.yang");
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith("Enum 'purple' is not a subset of its base enumeration type "
+                    + "(foo?revision=2017-02-02)my-derived-enumeration-type."));
+        }
+    }
+
+    @Test
+    public void testInvalidRestrictedEnumeration2() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6887/foo-invalid-2.yang");
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidEnumDefinitionException);
+            assertTrue(cause.getMessage().startsWith("Enum 'magenta' is not a subset of its base enumeration type "
+                    + "(foo?revision=2017-02-02)my-base-enumeration-type."));
+        }
+    }
+
+    @Test
+    public void testInvalidRestrictedEnumeration3() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6887/foo-invalid-3.yang");
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidEnumDefinitionException);
+            assertTrue(cause.getMessage().startsWith("Value of enum 'red' must be the same as the value of "
+                    + "corresponding enum in the base enumeration type (foo?revision=2017-02-02)"
+                    + "my-derived-enumeration-type."));
+        }
+    }
+
+    @Test
+    public void testInvalidRestrictedEnumeration4() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6887/foo-invalid-4.yang");
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidEnumDefinitionException);
+            assertTrue(cause.getMessage().startsWith("Value of enum 'black' must be the same as the value of "
+                    + "corresponding enum in the base enumeration type (foo?revision=2017-02-02)"
+                    + "my-base-enumeration-type."));
+        }
+    }
+
+    @Test
+    public void testValidYang10EnumerationWithUnknownStatements() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6887/foo10-valid.yang");
+        assertNotNull(schemaContext);
+    }
+
+    @Test
+    public void testInvalidYang10RestrictedEnumeration() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6887/foo10-invalid.yang");
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith(
+                "Restricted enumeration type is allowed only in YANG 1.1 version."));
+        }
+    }
+
+    @Test
+    public void testInvalidYang10RestrictedEnumeration2() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6887/foo10-invalid-2.yang");
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith(
+                "Restricted enumeration type is allowed only in YANG 1.1 version."));
+        }
+    }
+
+    @Test
+    public void testRestrictedBits() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6887/bar.yang");
+        assertNotNull(schemaContext);
+
+        final Module bar = schemaContext.findModule("bar", Revision.of("2017-02-02")).get();
+        final LeafSchemaNode myBitsLeaf = (LeafSchemaNode) bar.getDataChildByName(
+                QName.create(bar.getQNameModule(), "my-bits-leaf"));
+        assertNotNull(myBitsLeaf);
+
+        BitsTypeDefinition bitsType = (BitsTypeDefinition) myBitsLeaf.getType();
+
+        List<Bit> bits = bitsType.getBits();
+        assertEquals(2, bits.size());
+        Bit bitB = createBit(createSchemaPath(true, bar.getQNameModule(), "my-bits-leaf", "my-derived-bits-type",
+            "bit-b"), 2);
+        Bit bitC = createBit(createSchemaPath(true, bar.getQNameModule(), "my-bits-leaf", "my-derived-bits-type",
+            "bit-c"), 3);
+        assertContainsBits(bits, bitB, bitC);
+
+        bitsType = bitsType.getBaseType();
+        bits = bitsType.getBits();
+        assertEquals(3, bits.size());
+        bitB = createBit(createSchemaPath(true, bar.getQNameModule(), "my-derived-bits-type", "my-base-bits-type",
+            "bit-b"), 2);
+        bitC = createBit(createSchemaPath(true, bar.getQNameModule(), "my-derived-bits-type", "my-base-bits-type",
+            "bit-c"), 3);
+        Bit bitD = createBit(createSchemaPath(true, bar.getQNameModule(), "my-derived-bits-type", "my-base-bits-type",
+            "bit-d"), 4);
+        assertContainsBits(bits, bitB, bitC, bitD);
+
+        bitsType = bitsType.getBaseType();
+        bits = bitsType.getBits();
+        assertEquals(4, bits.size());
+        final Bit bitA = createBit(createSchemaPath(true, bar.getQNameModule(), "my-base-bits-type", "bits", "bit-a"),
+            1);
+        bitB = createBit(createSchemaPath(true, bar.getQNameModule(), "my-base-bits-type", "bits", "bit-b"), 2);
+        bitC = createBit(createSchemaPath(true, bar.getQNameModule(), "my-base-bits-type", "bits", "bit-c"), 3);
+        bitD = createBit(createSchemaPath(true, bar.getQNameModule(), "my-base-bits-type", "bits", "bit-d"), 4);
+        assertContainsBits(bits, bitA, bitB, bitC, bitD);
+
+        final LeafSchemaNode myBitsLeaf2 = (LeafSchemaNode) bar.getDataChildByName(
+                QName.create(bar.getQNameModule(), "my-bits-leaf-2"));
+        assertNotNull(myBitsLeaf2);
+
+        bitsType = (BitsTypeDefinition) myBitsLeaf2.getType();
+        bits = bitsType.getBits();
+        assertEquals(3, bits.size());
+        bitB = createBit(createSchemaPath(true, bar.getQNameModule(), "my-derived-bits-type", "my-base-bits-type",
+            "bit-b"), 2);
+        bitC = createBit(createSchemaPath(true, bar.getQNameModule(), "my-derived-bits-type", "my-base-bits-type",
+            "bit-c"), 3);
+        bitD = createBit(createSchemaPath(true, bar.getQNameModule(), "my-derived-bits-type", "my-base-bits-type",
+            "bit-d"), 4);
+        assertContainsBits(bits, bitB, bitC, bitD);
+    }
+
+    @Test
+    public void testInvalidRestrictedBits() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6887/bar-invalid.yang");
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith("Bit 'bit-w' is not a subset of its base bits type "
+                    + "(bar?revision=2017-02-02)my-derived-bits-type."));
+        }
+    }
+
+    @Test
+    public void testInvalidRestrictedBits2() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6887/bar-invalid-2.yang");
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidBitDefinitionException);
+            assertTrue(cause.getMessage().startsWith("Bit 'bit-x' is not a subset of its base bits type "
+                    + "(bar?revision=2017-02-02)my-base-bits-type."));
+        }
+    }
+
+    @Test
+    public void testInvalidRestrictedBits3() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6887/bar-invalid-3.yang");
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidBitDefinitionException);
+            assertTrue(cause.getMessage().startsWith("Position of bit 'bit-c' must be the same as the position of "
+                    + "corresponding bit in the base bits type (bar?revision=2017-02-02)my-derived-bits-type."));
+        }
+    }
+
+    @Test
+    public void testInvalidRestrictedBits4() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6887/bar-invalid-4.yang");
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidBitDefinitionException);
+            assertTrue(cause.getMessage().startsWith("Position of bit 'bit-d' must be the same as the position of "
+                    + "corresponding bit in the base bits type (bar?revision=2017-02-02)my-base-bits-type."));
+        }
+    }
+
+    @Test
+    public void testValidYang10BitsWithUnknownStatements() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6887/bar10-valid.yang");
+        assertNotNull(schemaContext);
+    }
+
+    @Test
+    public void testInvalidYang10RestrictedBits() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6887/bar10-invalid.yang");
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith("Restricted bits type is allowed only in YANG 1.1 version."));
+        }
+    }
+
+    @Test
+    public void testInvalidYang10RestrictedBits2() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6887/bar10-invalid-2.yang");
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith("Restricted bits type is allowed only in YANG 1.1 version."));
+        }
+    }
+
+    private static EnumPair createEnumPair(final String name, final int value) {
+        return EnumPairBuilder.create(name, value).build();
+    }
+
+    private static void assertContainsEnums(final List<EnumPair> enumList, final EnumPair... enumPairs) {
+        for (final EnumPair enumPair : enumPairs) {
+            assertTrue(enumList.contains(enumPair));
+        }
+    }
+
+    private static Bit createBit(final SchemaPath path, final long position) {
+        return BitBuilder.create(path, position).build();
+    }
+
+    private static void assertContainsBits(final List<Bit> bitList, final Bit... bits) {
+        for (final Bit bit : bits) {
+            assertTrue(bitList.contains(bit));
+        }
+    }
+
+    private static SchemaPath createSchemaPath(final boolean absolute, final QNameModule qnameModule,
+            final String... localNames) {
+        return SchemaPath.create(Arrays.stream(localNames).map(localName -> QName.create(qnameModule, localName))
+            .collect(Collectors.toList()), true);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6897Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6897Test.java
new file mode 100644 (file)
index 0000000..8377d12
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2016 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6897Test {
+    private static final String FOO_NS = "foo";
+
+    @Test
+    public void notificationsInDataContainersTest() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils
+                .parseYangSource("/rfc7950/notifications-in-data-nodes/foo.yang");
+        assertNotNull(schemaContext);
+
+        assertContainsNotifications(schemaContext, "root", "grp-notification", "aug-notification");
+        assertContainsNotifications(schemaContext, "top-list", "top-list-notification");
+        assertContainsNotifications(schemaContext, "top", "top-notification");
+
+        final Set<GroupingDefinition> groupings = schemaContext.getGroupings();
+        assertEquals(1, groupings.size());
+        assertContainsNotifications(groupings.iterator().next(), "grp-notification");
+
+        final Set<Module> modules = schemaContext.getModules();
+        assertEquals(1, modules.size());
+        final Module foo = modules.iterator().next();
+        final Set<AugmentationSchemaNode> augmentations = foo.getAugmentations();
+        assertEquals(1, augmentations.size());
+        assertContainsNotifications(augmentations.iterator().next(), "aug-notification", "grp-notification");
+    }
+
+    private static void assertContainsNotifications(final SchemaContext schemaContext, final String dataContainerName,
+            final String... notificationNames) {
+        final DataSchemaNode dataChildByName = schemaContext.getDataChildByName(
+            QName.create(FOO_NS, dataContainerName));
+        assertTrue(dataChildByName instanceof NotificationNodeContainer);
+        assertContainsNotifications((NotificationNodeContainer) dataChildByName, notificationNames);
+    }
+
+    private static void assertContainsNotifications(final NotificationNodeContainer notificationContainer,
+            final String... notificationNames) {
+        final Set<NotificationDefinition> notifications = notificationContainer.getNotifications();
+        assertEquals(notificationNames.length, notifications.size());
+
+        final Set<QName> notificationQNames = new HashSet<>();
+        notifications.forEach(n -> notificationQNames.add(n.getQName()));
+
+        for (final String notificationName : notificationNames) {
+            assertTrue(notificationQNames.contains(QName.create(FOO_NS, notificationName)));
+        }
+    }
+
+    @Test
+    public void invalid10Test() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/notifications-in-data-nodes/foo10.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith("NOTIFICATION is not valid for"));
+        }
+    }
+
+    @Test
+    public void invalid11Test() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/notifications-in-data-nodes/foo-invalid.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                "Notification (foo)grp-notification is defined within an rpc, action, or another notification"));
+        }
+    }
+
+    @Test
+    public void testNotificationWithinListWithoutKey() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/notifications-in-data-nodes/bar-invalid.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                "Notification (bar-namespace?revision=2016-12-08)my-notification is defined within a list "
+                        + "that has no key statement"));
+        }
+    }
+
+    @Test
+    public void testNotificationInUsedGroupingWithinCase() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/notifications-in-data-nodes/baz-invalid.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                "Notification (baz-namespace?revision=2016-12-08)notification-in-grouping is defined within a case "
+                        + "statement"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6901Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6901Test.java
new file mode 100644 (file)
index 0000000..33ad7db
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2017 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug6901Test {
+
+    @Test
+    public void ifFeature11EnumBitTest() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6901/foo.yang");
+        assertNotNull(schemaContext);
+    }
+
+    @Test
+    public void ifFeatureOnDefaultValueEnumTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6901/invalid-foo-enum.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                    "Leaf '(foo)enum-leaf' has default value 'two' marked with an if-feature statement."));
+        }
+    }
+
+    @Test
+    public void ifFeatureOnDefaultValueEnumTest2() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6901/invalid-foo-enum-2.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                    "Leaf '(foo)enum-leaf' has default value 'two' marked with an if-feature statement."));
+        }
+    }
+
+    @Test
+    public void ifFeatureOnDefaultValueEnumTest3() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6901/invalid-foo-enum-3.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                    "Leaf '(foo)enum-leaf' has default value 'two' marked with an if-feature statement."));
+        }
+    }
+
+    @Test
+    public void ifFeatureOnDefaultValueBitTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6901/invalid-foo-bit.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                    "Typedef '(foo)bits-typedef-2' has default value 'two' marked with an if-feature statement."));
+        }
+    }
+
+    @Test
+    public void ifFeatureOnDefaultValueUnionTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6901/invalid-foo-union.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                    "Leaf '(foo)union-leaf' has default value 'two' marked with an if-feature statement."));
+        }
+    }
+
+    @Test
+    public void unsupportedFeatureTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6901/invalid-foo-enum.yang");
+            fail("Test should fail due to invalid Yang 1.1");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(
+                    e.getCause().getMessage().contains("has default value 'two' marked with an if-feature statement"));
+        }
+    }
+
+    @Test
+    public void ifFeature10EnumTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6901/invalid-foo-10-enum.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith("IF_FEATURE is not valid for ENUM"));
+        }
+    }
+
+    @Test
+    public void ifFeature10BitTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/bug6901/invalid-foo-10-bit.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith("IF_FEATURE is not valid for BIT"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug9241Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug9241Test.java
new file mode 100644 (file)
index 0000000..63a72fc
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug9241Test {
+
+    @Test
+    public void testImplicitInputAndOutputInAction() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug9241/foo.yang");
+        assertNotNull(schemaContext);
+
+        final Module fooModule = schemaContext.findModule("foo", Revision.of("2017-10-13")).get();
+
+        final ContainerSchemaNode actionCont = (ContainerSchemaNode) fooModule.getDataChildByName(QName.create(
+                fooModule.getQNameModule(), "action-cont"));
+        assertNotNull(actionCont);
+
+        final ActionDefinition actionInCont = actionCont.getActions().iterator().next();
+
+        final ContainerSchemaNode input = actionInCont.getInput();
+        assertNotNull(input);
+        assertEquals(1, input.getChildNodes().size());
+        assertEquals(StatementSource.CONTEXT, ((EffectiveStatement<?, ?>) input).getDeclared().getStatementSource());
+
+        final ContainerSchemaNode output = actionInCont.getOutput();
+        assertNotNull(output);
+        assertEquals(1, output.getChildNodes().size());
+        assertEquals(StatementSource.CONTEXT, ((EffectiveStatement<?, ?>) output).getDeclared().getStatementSource());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/IdentityStatementTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/IdentityStatementTest.java
new file mode 100644 (file)
index 0000000..00a7651
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class IdentityStatementTest {
+
+    @Test
+    public void testMultipleBaseIdentities() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/identity-stmt/foo.yang");
+        assertNotNull(schemaContext);
+
+        final Module foo = schemaContext.findModule("foo", Revision.of("2016-12-21")).get();
+        final Set<IdentitySchemaNode> identities = foo.getIdentities();
+        for (final IdentitySchemaNode identity : identities) {
+            if ("derived-id".equals(identity.getQName().getLocalName())) {
+                final Set<IdentitySchemaNode> baseIdentities = identity.getBaseIdentities();
+                assertEquals(3, baseIdentities.size());
+            }
+        }
+    }
+
+    @Test
+    public void testInvalidYang10() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/identity-stmt/foo10.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith("Maximal count of BASE for IDENTITY is 1, detected 3."));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/IdentityrefStatementTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/IdentityrefStatementTest.java
new file mode 100644 (file)
index 0000000..8b8d2f0
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class IdentityrefStatementTest {
+
+    @Test
+    public void testIdentityrefWithMultipleBaseIdentities() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/identityref-stmt/foo.yang");
+        assertNotNull(schemaContext);
+
+        final Module foo = schemaContext.findModule("foo", Revision.of("2017-01-11")).get();
+        final Set<IdentitySchemaNode> identities = foo.getIdentities();
+        assertEquals(3, identities.size());
+
+        final LeafSchemaNode idrefLeaf = (LeafSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+                "idref-leaf"));
+        assertNotNull(idrefLeaf);
+
+        final IdentityrefTypeDefinition idrefType = (IdentityrefTypeDefinition) idrefLeaf.getType();
+        final Set<IdentitySchemaNode> referencedIdentities = idrefType.getIdentities();
+        assertEquals(3, referencedIdentities.size());
+        assertEquals(identities, referencedIdentities);
+        assertEquals("id-a", idrefType.getIdentities().iterator().next().getQName().getLocalName());
+    }
+
+    @Test
+    public void testInvalidYang10() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/identityref-stmt/foo10.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith("Maximal count of BASE for TYPE is 1, detected 3."));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/LeafrefStatementTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/LeafrefStatementTest.java
new file mode 100644 (file)
index 0000000..da05fe5
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2017 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.stmt.rfc7950;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class LeafrefStatementTest {
+
+    @Test
+    public void testRequireInstanceInLeafrefs() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/leafref-stmt/foo.yang");
+        assertNotNull(schemaContext);
+
+        final Module foo = schemaContext.findModule("foo", Revision.of("2016-12-20")).get();
+        final Set<TypeDefinition<?>> typeDefinitions = foo.getTypeDefinitions();
+        assertEquals(1, typeDefinitions.size());
+
+        final TypeDefinition<?> typeDefinition = typeDefinitions.iterator().next();
+        final LeafrefTypeDefinition leafrefTypeDefinition = (LeafrefTypeDefinition) typeDefinition;
+        assertTrue(leafrefTypeDefinition.requireInstance());
+
+        final LeafSchemaNode leafrefA = (LeafSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+                "leafref-a"));
+        assertNotNull(leafrefA);
+        assertRequireInstanceInLeafref(leafrefA, true);
+
+        final LeafSchemaNode leafrefB = (LeafSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+                "leafref-b"));
+        assertNotNull(leafrefB);
+        assertRequireInstanceInLeafref(leafrefB, true);
+
+        final LeafSchemaNode leafrefC = (LeafSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+                "leafref-c"));
+        assertNotNull(leafrefC);
+        assertRequireInstanceInLeafref(leafrefC, true);
+    }
+
+    private static void assertRequireInstanceInLeafref(final LeafSchemaNode leaf, final boolean requireInstance) {
+        final LeafrefTypeDefinition leafrefTypeDefnition = (LeafrefTypeDefinition) leaf.getType();
+        assertEquals(requireInstance, leafrefTypeDefnition.requireInstance());
+    }
+
+    @Test
+    public void testInvalidYang10() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/rfc7950/leafref-stmt/foo10.yang");
+            fail("Test should fail due to invalid Yang 1.0");
+        } catch (final ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith("REQUIRE_INSTANCE is not valid for TYPE"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentArgumentParsingTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentArgumentParsingTest.java
new file mode 100644 (file)
index 0000000..031ccbf
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.ReactorDeclaredModel;
+
+public class AugmentArgumentParsingTest {
+
+    private static final StatementStreamSource IMPORTED = sourceForResource(
+            "/semantic-statement-parser/augment-arg-parsing/imported.yang");
+    private static final StatementStreamSource VALID_ARGS = sourceForResource(
+            "/semantic-statement-parser/augment-arg-parsing/root-valid-aug-args.yang");
+    private static final StatementStreamSource INVALID_REL1 = sourceForResource(
+            "/semantic-statement-parser/augment-arg-parsing/root-invalid-rel1.yang");
+    private static final StatementStreamSource INVALID_REL2 = sourceForResource(
+            "/semantic-statement-parser/augment-arg-parsing/root-invalid-rel2.yang");
+    private static final StatementStreamSource INVALID_ABS = sourceForResource(
+            "/semantic-statement-parser/augment-arg-parsing/root-invalid-abs.yang");
+    private static final StatementStreamSource INVALID_ABS_PREFIXED_NO_IMP = sourceForResource(
+            "/semantic-statement-parser/augment-arg-parsing/root-invalid-abs-no-imp.yang");
+    private static final StatementStreamSource INVALID_EMPTY = sourceForResource(
+            "/semantic-statement-parser/augment-arg-parsing/root-invalid-empty.yang");
+    private static final StatementStreamSource INVALID_XPATH = sourceForResource(
+            "/semantic-statement-parser/augment-arg-parsing/root-invalid-xpath.yang");
+
+    @Test
+    public void validAugAbsTest() throws ReactorException {
+        final ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(IMPORTED, VALID_ARGS)
+                .build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void invalidAugRel1Test() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSources(INVALID_REL1);
+
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to invalid relative path");
+        } catch (ReactorException e) {
+            assertSourceExceptionCause(e, "Augment argument './aug1/aug11' is not valid");
+        }
+    }
+
+    @Test
+    public void invalidAugRel2Test() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSources(INVALID_REL2);
+
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to invalid relative path");
+        } catch (ReactorException e) {
+            assertSourceExceptionCause(e, "Augment argument '../aug1/aug11' is not valid");
+        }
+    }
+
+    @Test
+    public void invalidAugAbs() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSources(INVALID_ABS);
+
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to invalid absolute path");
+        } catch (ReactorException e) {
+            assertSourceExceptionCause(e, "Augment argument '//aug1/aug11/aug111' is not valid");
+        }
+    }
+
+    @Test
+    public void invalidAugAbsPrefixedNoImp() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSources(INVALID_ABS_PREFIXED_NO_IMP);
+
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to missing import from augment path");
+        } catch (ReactorException e) {
+            assertSourceExceptionCause(e, "Failed to parse node 'imp:aug1'");
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @Ignore
+    public void invalidAugEmptyTest() throws ReactorException {
+        RFC7950Reactors.defaultReactor().newBuild().addSources(INVALID_EMPTY).build();
+        fail("reactor.process should fail due to empty path");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @Ignore
+    public void invalidAugXPathTest() throws ReactorException {
+        RFC7950Reactors.defaultReactor().newBuild().addSources(INVALID_XPATH).build();
+        fail("reactor.process should fail due to invalid XPath");
+    }
+
+    private static void assertSourceExceptionCause(final Throwable exception, final String start) {
+        final Throwable cause = exception.getCause();
+        assertTrue(cause instanceof SourceException);
+        assertTrue(cause.getMessage().startsWith(start));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentProcessTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentProcessTest.java
new file mode 100644 (file)
index 0000000..b5660a5
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class AugmentProcessTest {
+
+    private static final StatementStreamSource AUGMENTED = sourceForResource("/stmt-test/augments/augmented.yang");
+    private static final StatementStreamSource ROOT = sourceForResource("/stmt-test/augments/aug-root.yang");
+
+    private static final QNameModule ROOT_QNAME_MODULE = QNameModule.create(URI.create("root"));
+    private static final QNameModule AUGMENTED_QNAME_MODULE = QNameModule.create(URI.create("aug"));
+
+    private final QName augParent1 = QName.create(AUGMENTED_QNAME_MODULE,
+            "aug-parent1");
+    private final QName augParent2 = QName.create(AUGMENTED_QNAME_MODULE,
+            "aug-parent2");
+    private final QName contTarget = QName.create(AUGMENTED_QNAME_MODULE,
+            "cont-target");
+
+    private final QName contAdded1 = QName.create(ROOT_QNAME_MODULE,
+            "cont-added1");
+    private final QName contAdded2 = QName.create(ROOT_QNAME_MODULE,
+            "cont-added2");
+
+    private final QName list1 = QName.create(ROOT_QNAME_MODULE, "list1");
+    private final QName axml = QName.create(ROOT_QNAME_MODULE, "axml");
+
+    private final QName contGrp = QName.create(ROOT_QNAME_MODULE,
+            "cont-grp");
+    private final QName axmlGrp = QName.create(ROOT_QNAME_MODULE,
+            "axml-grp");
+
+    private final QName augCont1 = QName.create(ROOT_QNAME_MODULE, "aug-cont1");
+    private final QName augCont2 = QName.create(ROOT_QNAME_MODULE, "aug-cont2");
+
+    private final QName grpCont2 = QName.create(ROOT_QNAME_MODULE, "grp-cont2");
+    private final QName grpCont22 = QName.create(ROOT_QNAME_MODULE,
+            "grp-cont22");
+    private final QName grpAdd = QName.create(ROOT_QNAME_MODULE, "grp-add");
+
+    private static final StatementStreamSource MULTIPLE_AUGMENT = sourceForResource(
+            "/stmt-test/augments/multiple-augment-test.yang");
+
+    private static final StatementStreamSource MULTIPLE_AUGMENT_ROOT = sourceForResource(
+            "/stmt-test/augments/multiple-augment-root.yang");
+    private static final StatementStreamSource MULTIPLE_AUGMENT_IMPORTED = sourceForResource(
+            "/stmt-test/augments/multiple-augment-imported.yang");
+    private static final StatementStreamSource MULTIPLE_AUGMENT_SUBMODULE = sourceForResource(
+            "/stmt-test/augments/multiple-augment-submodule.yang");
+
+    private static final StatementStreamSource MULTIPLE_AUGMENT_INCORRECT = sourceForResource(
+            "/stmt-test/augments/multiple-augment-incorrect.yang");
+
+    private static final StatementStreamSource MULTIPLE_AUGMENT_INCORRECT2 = sourceForResource(
+            "/stmt-test/augments/multiple-augment-incorrect2.yang");
+
+    @Test
+    public void multipleAugmentsAndMultipleModulesTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(MULTIPLE_AUGMENT_ROOT, MULTIPLE_AUGMENT_IMPORTED, MULTIPLE_AUGMENT_SUBMODULE)
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void multipleAugmentTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(MULTIPLE_AUGMENT)
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void multipleAugmentIncorrectPathTest() throws  ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(MULTIPLE_AUGMENT_INCORRECT)
+                .buildEffective();
+        assertNull(result);
+    }
+
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void multipleAugmentIncorrectPathAndGrpTest() throws  ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(MULTIPLE_AUGMENT_INCORRECT2)
+                .buildEffective();
+        assertNull(result);
+    }
+
+    @Test
+    public void readAndParseYangFileTest() throws ReactorException {
+        final SchemaContext root = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(AUGMENTED, ROOT)
+                .buildEffective();
+        assertNotNull(root);
+
+        final Module augmentedModule = root.findModules("augmented").iterator().next();
+        assertNotNull(augmentedModule);
+
+        final ContainerSchemaNode augParent1Node = (ContainerSchemaNode) root.getDataChildByName(augParent1);
+        final ContainerSchemaNode augParent2Node = (ContainerSchemaNode) augParent1Node.getDataChildByName(augParent2);
+        final ContainerSchemaNode targetContNode = (ContainerSchemaNode) augParent2Node.getDataChildByName(contTarget);
+        assertNotNull(targetContNode);
+
+        assertNotNull(targetContNode.getChildNodes());
+        assertEquals(3, targetContNode.getChildNodes().size());
+
+        final ContainerSchemaNode contAdded1Node = (ContainerSchemaNode) targetContNode.getDataChildByName(contAdded1);
+        assertNotNull(contAdded1Node);
+        final ListSchemaNode list1Node = (ListSchemaNode) contAdded1Node.getDataChildByName(list1);
+        assertNotNull(list1Node);
+
+        final ContainerSchemaNode contAdded2Node = (ContainerSchemaNode) targetContNode.getDataChildByName(contAdded2);
+        assertNotNull(contAdded2Node);
+        final AnyxmlSchemaNode axmlNode = (AnyxmlSchemaNode) contAdded2Node.getDataChildByName(axml);
+        assertNotNull(axmlNode);
+
+        final ContainerSchemaNode contGrpNode = (ContainerSchemaNode) targetContNode.getDataChildByName(contGrp);
+        assertNotNull(contGrpNode);
+        final AnyxmlSchemaNode axmlGrpNode = (AnyxmlSchemaNode) contGrpNode.getDataChildByName(axmlGrp);
+        assertNotNull(axmlGrpNode);
+
+        final ContainerSchemaNode augCont1Node = (ContainerSchemaNode) root.getDataChildByName(augCont1);
+        final ContainerSchemaNode augCont2Node = (ContainerSchemaNode) augCont1Node.getDataChildByName(augCont2);
+        assertNotNull(augCont2Node);
+
+        final ContainerSchemaNode grpCont2Node = (ContainerSchemaNode) augCont2Node.getDataChildByName(grpCont2);
+        final ContainerSchemaNode grpCont22Node = (ContainerSchemaNode) grpCont2Node.getDataChildByName(grpCont22);
+        assertNotNull(grpCont22Node);
+
+        final ContainerSchemaNode grpAddNode = (ContainerSchemaNode) grpCont22Node.getDataChildByName(grpAdd);
+        assertNotNull(grpAddNode);
+    }
+
+    @Test
+    public void caseShortHandAugmentingTest() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/choice-case-type-test-models");
+
+        assertNotNull(context);
+
+        final String rev = "2013-07-01";
+        final String ns = "urn:ietf:params:xml:ns:yang:choice-monitoring";
+        final String nsAug = "urn:ietf:params:xml:ns:yang:augment-monitoring";
+
+        final ContainerSchemaNode netconf = (ContainerSchemaNode) context.getDataChildByName(QName.create(ns, rev,
+                "netconf-state"));
+        final ContainerSchemaNode datastores = (ContainerSchemaNode) netconf.getDataChildByName(QName.create(ns, rev,
+                "datastores"));
+        final ListSchemaNode datastore = (ListSchemaNode) datastores.getDataChildByName(QName.create(ns, rev,
+                "datastore"));
+        final ContainerSchemaNode locks = (ContainerSchemaNode) datastore.getDataChildByName(QName.create(ns, rev,
+                "locks"));
+        final ChoiceSchemaNode lockType = (ChoiceSchemaNode) locks.getDataChildByName(QName
+                .create(ns, rev, "lock-type"));
+
+        final CaseSchemaNode leafAugCase = lockType.findCaseNodes("leaf-aug-case").iterator().next();
+        assertTrue(leafAugCase.isAugmenting());
+        final DataSchemaNode leafAug = leafAugCase.getDataChildByName(QName.create(nsAug, rev, "leaf-aug-case"));
+        assertFalse(leafAug.isAugmenting());
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentSimplestTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentSimplestTest.java
new file mode 100644 (file)
index 0000000..869da59
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.ReactorDeclaredModel;
+
+public class AugmentSimplestTest {
+
+    private static final StatementStreamSource AUGMENTED = sourceForResource(
+        "/semantic-statement-parser/augmented.yang");
+    private static final StatementStreamSource ROOT = sourceForResource("/semantic-statement-parser/root.yang");
+
+    @Test
+    public void readAndParseYangFileTest() throws SourceException, ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(AUGMENTED, ROOT)
+                .build();
+        assertNotNull(result);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentTest.java
new file mode 100644 (file)
index 0000000..b2f2f45
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedMap;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+public class AugmentTest {
+    private static final QNameModule FOO = QNameModule.create(
+        URI.create("urn:opendaylight.foo"), Revision.of("2013-10-13"));
+    private static final QNameModule BAR = QNameModule.create(
+        URI.create("urn:opendaylight.bar"), Revision.of("2013-10-14"));
+    private static final QNameModule BAZ = QNameModule.create(
+        URI.create("urn:opendaylight.baz"), Revision.of("2013-10-15"));
+
+    private static final QName Q0 = QName.create(BAR, "interfaces");
+    private static final QName Q1 = QName.create(BAR, "ifEntry");
+    private static final QName Q2 = QName.create(BAZ, "augment-holder");
+
+    @Test
+    public void testAugmentParsing() throws Exception {
+        final SchemaContext context = TestUtils.loadModules(getClass().getResource("/augment-test/augment-in-augment")
+            .toURI());
+        final List<QName> qnames = new ArrayList<>();
+        qnames.add(Q0);
+        qnames.add(Q1);
+        qnames.add(Q2);
+
+        // foo.yang
+        final Module module1 = TestUtils.findModule(context, "foo").get();
+        Set<AugmentationSchemaNode> augmentations = module1.getAugmentations();
+        assertEquals(1, augmentations.size());
+        final AugmentationSchemaNode augment = augmentations.iterator().next();
+        assertNotNull(augment);
+
+        SchemaPath expectedSchemaPath = SchemaPath.create(qnames, true);
+        assertEquals(expectedSchemaPath, augment.getTargetPath());
+
+        final Collection<DataSchemaNode> augmentChildren = augment.getChildNodes();
+        assertEquals(4, augmentChildren.size());
+        for (final DataSchemaNode dsn : augmentChildren) {
+            TestUtils.checkIsAugmenting(dsn, false);
+        }
+
+        final LeafSchemaNode ds0ChannelNumber = (LeafSchemaNode) augment.getDataChildByName(QName.create(
+                module1.getQNameModule(), "ds0ChannelNumber"));
+        final LeafSchemaNode interfaceId = (LeafSchemaNode) augment.getDataChildByName(QName.create(
+                module1.getQNameModule(), "interface-id"));
+        final ContainerSchemaNode schemas = (ContainerSchemaNode) augment.getDataChildByName(QName.create(
+                module1.getQNameModule(), "schemas"));
+        final ChoiceSchemaNode odl = (ChoiceSchemaNode) augment.getDataChildByName(QName.create(
+                module1.getQNameModule(), "odl"));
+
+        assertNotNull(ds0ChannelNumber);
+        assertNotNull(interfaceId);
+        assertNotNull(schemas);
+        assertNotNull(odl);
+
+        // leaf ds0ChannelNumber
+        QName qname = QName.create(FOO, "ds0ChannelNumber");
+        qnames.add(qname);
+        assertEquals(qname, ds0ChannelNumber.getQName());
+        expectedSchemaPath = SchemaPath.create(qnames, true);
+        assertEquals(expectedSchemaPath, ds0ChannelNumber.getPath());
+        assertFalse(ds0ChannelNumber.isAugmenting());
+        // type of leaf ds0ChannelNumber
+        final List<QName> typePath = Collections.singletonList(BaseTypes.STRING_QNAME);
+        expectedSchemaPath = SchemaPath.create(typePath, true);
+        assertEquals(expectedSchemaPath, ds0ChannelNumber.getType().getPath());
+
+        // leaf interface-id
+        qname = QName.create(FOO, "interface-id");
+        assertEquals(qname, interfaceId.getQName());
+        qnames.set(3, qname);
+        expectedSchemaPath = SchemaPath.create(qnames, true);
+        assertEquals(expectedSchemaPath, interfaceId.getPath());
+        assertFalse(interfaceId.isAugmenting());
+
+        // container schemas
+        qname = QName.create(FOO, "schemas");
+        assertEquals(qname, schemas.getQName());
+        qnames.set(3, qname);
+        expectedSchemaPath = SchemaPath.create(qnames, true);
+        assertEquals(expectedSchemaPath, schemas.getPath());
+        assertFalse(schemas.isAugmenting());
+
+        // choice odl
+        qname = QName.create(FOO, "odl");
+        assertEquals(qname, odl.getQName());
+        qnames.set(3, qname);
+        expectedSchemaPath = SchemaPath.create(qnames, true);
+        assertEquals(expectedSchemaPath, odl.getPath());
+        assertFalse(odl.isAugmenting());
+
+        // baz.yang
+        final Module module3 = TestUtils.findModule(context, "baz").get();
+        augmentations = module3.getAugmentations();
+        assertEquals(3, augmentations.size());
+        AugmentationSchemaNode augment1 = null;
+        AugmentationSchemaNode augment2 = null;
+        AugmentationSchemaNode augment3 = null;
+        for (final AugmentationSchemaNode as : augmentations) {
+            if (!as.getWhenCondition().isPresent()) {
+                augment3 = as;
+            } else if ("if:ifType='ds0'".equals(as.getWhenCondition().get().getOriginalString())) {
+                augment1 = as;
+            } else if ("if:ifType='ds2'".equals(as.getWhenCondition().get().getOriginalString())) {
+                augment2 = as;
+            }
+        }
+        assertNotNull(augment1);
+        assertNotNull(augment2);
+        assertNotNull(augment3);
+
+        assertEquals(1, augment1.getChildNodes().size());
+        final ContainerSchemaNode augmentHolder = (ContainerSchemaNode) augment1.getDataChildByName(QName.create(
+                module3.getQNameModule(), "augment-holder"));
+        assertNotNull(augmentHolder);
+
+        assertEquals(1, augment2.getChildNodes().size());
+        final ContainerSchemaNode augmentHolder2 = (ContainerSchemaNode) augment2.getDataChildByName(QName.create(
+                module3.getQNameModule(), "augment-holder2"));
+        assertNotNull(augmentHolder2);
+
+        assertEquals(1, augment3.getChildNodes().size());
+        final CaseSchemaNode pause = (CaseSchemaNode) augment3.getDataChildByName(QName.create(
+                module3.getQNameModule(), "pause"));
+        assertNotNull(pause);
+    }
+
+    @Test
+    public void testAugmentResolving() throws Exception {
+        final SchemaContext context = TestUtils.loadModules(getClass().getResource("/augment-test/augment-in-augment")
+            .toURI());
+        final Module module2 = TestUtils.findModule(context, "bar").get();
+        final ContainerSchemaNode interfaces = (ContainerSchemaNode) module2.getDataChildByName(QName.create(
+                module2.getQNameModule(), "interfaces"));
+        final ListSchemaNode ifEntry = (ListSchemaNode) interfaces.getDataChildByName(QName.create(
+                module2.getQNameModule(), "ifEntry"));
+
+        final List<QName> qnames = new ArrayList<>();
+        qnames.add(Q0);
+        qnames.add(Q1);
+        qnames.add(Q2);
+
+        // baz.yang
+        // augment "/br:interfaces/br:ifEntry" {
+        final ContainerSchemaNode augmentHolder = (ContainerSchemaNode) ifEntry.getDataChildByName(QName.create(BAZ,
+                "augment-holder"));
+        TestUtils.checkIsAugmenting(augmentHolder, true);
+        assertEquals(Q2, augmentHolder.getQName());
+        assertEquals(SchemaPath.create(qnames, true), augmentHolder.getPath());
+
+        // foo.yang
+        // augment "/br:interfaces/br:ifEntry/bz:augment-holder"
+        final LeafSchemaNode ds0ChannelNumber = (LeafSchemaNode) augmentHolder.getDataChildByName(QName.create(FOO,
+                "ds0ChannelNumber"));
+        final LeafSchemaNode interfaceId = (LeafSchemaNode) augmentHolder.getDataChildByName(QName.create(FOO,
+                "interface-id"));
+        final ContainerSchemaNode schemas = (ContainerSchemaNode) augmentHolder.getDataChildByName(QName.create(FOO,
+                "schemas"));
+        final ChoiceSchemaNode odl = (ChoiceSchemaNode) augmentHolder.getDataChildByName(QName.create(FOO, "odl"));
+
+        assertNotNull(ds0ChannelNumber);
+        assertNotNull(interfaceId);
+        assertNotNull(schemas);
+        assertNotNull(odl);
+
+        // leaf ds0ChannelNumber
+        QName qname = QName.create(FOO, "ds0ChannelNumber");
+        assertEquals(qname, ds0ChannelNumber.getQName());
+        qnames.add(qname);
+        assertEquals(SchemaPath.create(qnames, true), ds0ChannelNumber.getPath());
+
+        // leaf interface-id
+        qname = QName.create(FOO, "interface-id");
+        assertEquals(qname, interfaceId.getQName());
+        qnames.set(3, qname);
+        assertEquals(SchemaPath.create(qnames, true), interfaceId.getPath());
+
+        // container schemas
+        qname = QName.create(FOO, "schemas");
+        assertEquals(qname, schemas.getQName());
+        qnames.set(3, qname);
+        assertEquals(SchemaPath.create(qnames, true), schemas.getPath());
+
+        // choice odl
+        qname = QName.create(FOO, "odl");
+        assertEquals(qname, odl.getQName());
+        qnames.set(3, qname);
+        assertEquals(SchemaPath.create(qnames, true), odl.getPath());
+    }
+
+    @Test
+    public void testAugmentedChoice() throws Exception {
+        final SchemaContext context = TestUtils.loadModules(getClass().getResource("/augment-test/augment-in-augment")
+            .toURI());
+        final Module module2 = TestUtils.findModule(context, "bar").get();
+        final ContainerSchemaNode interfaces = (ContainerSchemaNode) module2.getDataChildByName(QName.create(
+                module2.getQNameModule(), "interfaces"));
+        final ListSchemaNode ifEntry = (ListSchemaNode) interfaces.getDataChildByName(QName.create(
+                module2.getQNameModule(), "ifEntry"));
+        final ContainerSchemaNode augmentedHolder = (ContainerSchemaNode) ifEntry.getDataChildByName(QName.create(
+                BAZ, "augment-holder"));
+        TestUtils.checkIsAugmenting(augmentedHolder, true);
+
+        // foo.yang
+        // augment "/br:interfaces/br:ifEntry/bz:augment-holder"
+        final ChoiceSchemaNode odl = (ChoiceSchemaNode) augmentedHolder.getDataChildByName(QName.create(FOO, "odl"));
+        assertNotNull(odl);
+        final SortedMap<QName, CaseSchemaNode> cases = odl.getCases();
+        assertEquals(4, cases.size());
+
+        CaseSchemaNode id = null;
+        CaseSchemaNode node1 = null;
+        CaseSchemaNode node2 = null;
+        CaseSchemaNode node3 = null;
+
+        for (final CaseSchemaNode ccn : cases.values()) {
+            if ("id".equals(ccn.getQName().getLocalName())) {
+                id = ccn;
+            } else if ("node1".equals(ccn.getQName().getLocalName())) {
+                node1 = ccn;
+            } else if ("node2".equals(ccn.getQName().getLocalName())) {
+                node2 = ccn;
+            } else if ("node3".equals(ccn.getQName().getLocalName())) {
+                node3 = ccn;
+            }
+        }
+
+        assertNotNull(id);
+        assertNotNull(node1);
+        assertNotNull(node2);
+        assertNotNull(node3);
+
+        final List<QName> qnames = new ArrayList<>();
+        qnames.add(Q0);
+        qnames.add(Q1);
+        qnames.add(Q2);
+        qnames.add(QName.create(FOO, "odl"));
+
+        // case id
+        QName qname = QName.create(FOO, "id");
+        assertEquals(qname, id.getQName());
+        qnames.add(qname);
+        assertEquals(SchemaPath.create(qnames, true), id.getPath());
+        final Collection<DataSchemaNode> idChildren = id.getChildNodes();
+        assertEquals(1, idChildren.size());
+
+        // case node1
+        qname = QName.create(FOO, "node1");
+        assertEquals(qname, node1.getQName());
+        qnames.set(4, qname);
+        assertEquals(SchemaPath.create(qnames, true), node1.getPath());
+        final Collection<DataSchemaNode> node1Children = node1.getChildNodes();
+        assertTrue(node1Children.isEmpty());
+
+        // case node2
+        qname = QName.create(FOO, "node2");
+        assertEquals(qname, node2.getQName());
+        qnames.set(4, qname);
+        assertEquals(SchemaPath.create(qnames, true), node2.getPath());
+        final Collection<DataSchemaNode> node2Children = node2.getChildNodes();
+        assertTrue(node2Children.isEmpty());
+
+        // case node3
+        qname = QName.create(FOO, "node3");
+        assertEquals(qname, node3.getQName());
+        qnames.set(4, qname);
+        assertEquals(SchemaPath.create(qnames, true), node3.getPath());
+        final Collection<DataSchemaNode> node3Children = node3.getChildNodes();
+        assertEquals(1, node3Children.size());
+
+        // test cases
+        qnames.clear();
+        qnames.add(Q0);
+        qnames.add(Q1);
+        qnames.add(Q2);
+        qnames.add(QName.create(FOO, "odl"));
+
+        // case id child
+        qnames.add(QName.create(FOO, "id"));
+        qnames.add(QName.create(FOO, "id"));
+        final LeafSchemaNode caseIdChild = (LeafSchemaNode) idChildren.iterator().next();
+        assertNotNull(caseIdChild);
+        assertEquals(SchemaPath.create(qnames, true), caseIdChild.getPath());
+
+        // case node3 child
+        qnames.set(4, QName.create(FOO, "node3"));
+        qnames.set(5, QName.create(FOO, "node3"));
+        final ContainerSchemaNode caseNode3Child = (ContainerSchemaNode) node3Children.iterator().next();
+        assertNotNull(caseNode3Child);
+        assertEquals(SchemaPath.create(qnames, true), caseNode3Child.getPath());
+    }
+
+    @Test
+    public void testAugmentRpc() throws Exception {
+        final SchemaContext context = TestUtils.loadModules(getClass().getResource("/augment-test/rpc").toURI());
+        final URI NS_BAR = URI.create("urn:opendaylight:bar");
+        final URI NS_FOO = URI.create("urn:opendaylight:foo");
+        final Revision revision = Revision.of("2013-10-11");
+        final Module bar = TestUtils.findModule(context, "bar").get();
+        final Set<RpcDefinition> rpcs = bar.getRpcs();
+        assertEquals(2, rpcs.size());
+
+        RpcDefinition submit = null;
+        for (final RpcDefinition rpc : rpcs) {
+            if ("submit".equals(rpc.getQName().getLocalName())) {
+                submit = rpc;
+                break;
+            }
+        }
+        assertNotNull(submit);
+
+        final QName submitQName = QName.create(NS_BAR, revision, "submit");
+        assertEquals(submitQName, submit.getQName());
+        final ContainerSchemaNode input = submit.getInput();
+        final QName inputQName = QName.create(NS_BAR, revision, "input");
+        assertEquals(inputQName, input.getQName());
+        final ChoiceSchemaNode arguments = (ChoiceSchemaNode) input.getDataChildByName(QName.create(NS_BAR, revision,
+                "arguments"));
+        final QName argumentsQName = QName.create(NS_BAR, revision, "arguments");
+        assertEquals(argumentsQName, arguments.getQName());
+        assertFalse(arguments.isAugmenting());
+        final SortedMap<QName, CaseSchemaNode> cases = arguments.getCases();
+        assertEquals(3, cases.size());
+
+        CaseSchemaNode attach = null;
+        CaseSchemaNode create = null;
+        CaseSchemaNode destroy = null;
+        for (final CaseSchemaNode child : cases.values()) {
+            if ("attach".equals(child.getQName().getLocalName())) {
+                attach = child;
+            } else if ("create".equals(child.getQName().getLocalName())) {
+                create = child;
+            } else if ("destroy".equals(child.getQName().getLocalName())) {
+                destroy = child;
+            }
+        }
+        assertNotNull(attach);
+        assertNotNull(create);
+        assertNotNull(destroy);
+
+        assertTrue(attach.isAugmenting());
+        assertTrue(create.isAugmenting());
+        assertTrue(destroy.isAugmenting());
+
+        final QName[] qnames = new QName[4];
+        qnames[0] = submitQName;
+        qnames[1] = inputQName;
+        qnames[2] = argumentsQName;
+
+        // case attach
+        qnames[3] = QName.create(NS_FOO, revision, "attach");
+        assertEquals(qnames[3], attach.getQName());
+        assertEquals(SchemaPath.create(true, qnames), attach.getPath());
+        final Collection<DataSchemaNode> attachChildren = attach.getChildNodes();
+        assertEquals(1, attachChildren.size());
+
+        // case create
+        qnames[3] = QName.create(NS_FOO, revision, "create");
+        assertEquals(qnames[3], create.getQName());
+        assertEquals(SchemaPath.create(true, qnames), create.getPath());
+        final Collection<DataSchemaNode> createChildren = create.getChildNodes();
+        assertEquals(1, createChildren.size());
+
+        // case attach
+        qnames[3] = QName.create(NS_FOO, revision, "destroy");
+        assertEquals(qnames[3], destroy.getQName());
+        assertEquals(SchemaPath.create(true, qnames), destroy.getPath());
+        final Collection<DataSchemaNode> destroyChildren = destroy.getChildNodes();
+        assertEquals(1, destroyChildren.size());
+    }
+
+    @Test
+    public void testAugmentInUsesResolving() throws Exception {
+        final SchemaContext context = TestUtils.loadModules(getClass().getResource("/augment-test/augment-in-uses")
+            .toURI());
+        assertEquals(1, context.getModules().size());
+
+        final Module test = context.getModules().iterator().next();
+        final DataNodeContainer links = (DataNodeContainer) test.getDataChildByName(QName.create(test.getQNameModule(),
+                "links"));
+        final DataNodeContainer link = (DataNodeContainer) links.getDataChildByName(QName.create(test.getQNameModule(),
+                "link"));
+        final DataNodeContainer nodes = (DataNodeContainer) link.getDataChildByName(QName.create(test.getQNameModule(),
+                "nodes"));
+        final ContainerSchemaNode node = (ContainerSchemaNode) nodes.getDataChildByName(QName.create(
+                test.getQNameModule(), "node"));
+        final Set<AugmentationSchemaNode> augments = node.getAvailableAugmentations();
+        assertEquals(1, augments.size());
+        assertEquals(1, node.getChildNodes().size());
+        final LeafSchemaNode id = (LeafSchemaNode) node.getDataChildByName(QName.create(test.getQNameModule(), "id"));
+        assertTrue(id.isAugmenting());
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentToExtensionTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/AugmentToExtensionTest.java
new file mode 100644 (file)
index 0000000..1199ad3
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+
+public class AugmentToExtensionTest {
+    private SchemaContext context;
+
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void testIncorrectPath() throws Exception {
+        context = TestUtils.loadModules(getClass().getResource("/augment-to-extension-test/incorrect-path").toURI());
+    }
+
+    /*
+     * FIXME: Figure way to determine use case of tail-f:input without hacks
+     */
+    @Test
+    public void testCorrectPathIntoUnsupportedTarget() throws Exception {
+
+        context = TestUtils.loadModules(getClass().getResource(
+                "/augment-to-extension-test/correct-path-into-unsupported-target").toURI());
+
+        final Module devicesModule = TestUtils.findModule(context, "augment-module").get();
+        final ContainerSchemaNode devicesContainer = (ContainerSchemaNode) devicesModule.getDataChildByName(
+            QName.create(devicesModule.getQNameModule(), "my-container"));
+        final Set<UsesNode> uses = devicesContainer.getUses();
+
+        for (final UsesNode usesNode : uses) {
+            assertTrue(usesNode.getAugmentations().isEmpty());
+        }
+    }
+
+
+    @Test
+    public void testCorrectAugment() throws Exception {
+        context = TestUtils.loadModules(getClass().getResource("/augment-to-extension-test/correct-augment").toURI());
+
+        final Module devicesModule = TestUtils.findModule(context, "augment-module").get();
+
+        final ContainerSchemaNode devicesContainer = (ContainerSchemaNode) devicesModule.getDataChildByName(QName
+                .create(devicesModule.getQNameModule(), "my-container"));
+        final Set<UsesNode> uses = devicesContainer.getUses();
+
+        boolean augmentationIsInContainer = false;
+        for (final UsesNode usesNode : uses) {
+            final Set<AugmentationSchemaNode> augmentations = usesNode.getAugmentations();
+            for (final AugmentationSchemaNode augmentationSchema : augmentations) {
+                augmentationIsInContainer = true;
+            }
+        }
+
+        assertTrue(augmentationIsInContainer);
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug1412Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug1412Test.java
new file mode 100644 (file)
index 0000000..62d298a
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.net.URI;
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+/**
+ * Test ANTLR4 grammar capability to parse description statement in unknown node.
+ *
+ * <p>
+ * Note: Everything under unknown node is unknown node.
+ */
+public class Bug1412Test {
+
+    @Test
+    public void test() throws Exception {
+        final Module bug1412 = TestUtils.findModule(
+            TestUtils.loadModules(getClass().getResource("/bugs/bug1412").toURI()), "bug1412").get();
+
+        final ContainerSchemaNode node = (ContainerSchemaNode) bug1412.getDataChildByName(QName.create(
+                bug1412.getQNameModule(), "node"));
+        List<UnknownSchemaNode> unknownNodes = node.getUnknownSchemaNodes();
+        assertEquals(1, unknownNodes.size());
+        final UnknownSchemaNode action = unknownNodes.get(0);
+
+        final QNameModule qm = QNameModule.create(URI.create("urn:test:bug1412"), Revision.of("2014-07-25"));
+        QName expectedNodeType = QName.create("urn:test:bug1412:ext:definitions", "2014-07-25", "action");
+        assertEquals(expectedNodeType, action.getNodeType());
+        assertEquals("hello", action.getNodeParameter());
+        final QName expectedQName = QName.create(qm, "hello");
+        assertEquals(expectedQName, action.getQName());
+
+        unknownNodes = action.getUnknownSchemaNodes();
+        assertEquals(4, unknownNodes.size());
+        UnknownSchemaNode info = null;
+        UnknownSchemaNode description = null;
+        UnknownSchemaNode actionPoint = null;
+        UnknownSchemaNode output = null;
+        for (final UnknownSchemaNode un : unknownNodes) {
+            final String name = un.getNodeType().getLocalName();
+            if ("info".equals(name)) {
+                info = un;
+            } else if ("description".equals(name)) {
+                description = un;
+            } else if ("actionpoint".equals(name)) {
+                actionPoint = un;
+            } else if ("output".equals(name)) {
+                output = un;
+            }
+        }
+
+        assertNotNull(info);
+        assertNotNull(description);
+        assertNotNull(actionPoint);
+        assertNotNull(output);
+
+        expectedNodeType = QName.create("urn:test:bug1412:ext:definitions", "2014-07-25", "info");
+        assertEquals(expectedNodeType, info.getNodeType());
+        assertEquals("greeting", info.getNodeParameter());
+
+        expectedNodeType = QName.create("urn:test:bug1412:ext:definitions", "2014-07-25", "description");
+        assertEquals(expectedNodeType, description.getNodeType());
+        assertEquals("say greeting", description.getNodeParameter());
+
+        expectedNodeType = QName.create("urn:test:bug1412:ext:definitions", "2014-07-25", "actionpoint");
+        assertEquals(expectedNodeType, actionPoint.getNodeType());
+        assertEquals("entry", actionPoint.getNodeParameter());
+
+        expectedNodeType = QName.create("urn:test:bug1412:ext:definitions", "2014-07-25", "output");
+        assertEquals(expectedNodeType, output.getNodeType());
+        assertEquals("", output.getNodeParameter());
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug1413Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug1413Test.java
new file mode 100644 (file)
index 0000000..584c4e4
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+/**
+ * Test ANTLR4 grammar capability to parse unknown node in extension argument
+ * declaration.
+ *
+ * <p>
+ * Note: Everything under unknown node is unknown node.
+ */
+public class Bug1413Test {
+
+    @Test
+    public void test() throws Exception {
+        final Module bug1413 = TestUtils.findModule(
+            TestUtils.loadModules(getClass().getResource("/bugs/bug1413").toURI()), "bug1413").get();
+        assertNotNull(bug1413);
+
+        List<ExtensionDefinition> extensions = bug1413.getExtensionSchemaNodes();
+        assertEquals(1, extensions.size());
+
+        ExtensionDefinition info = extensions.get(0);
+        assertEquals("text", info.getArgument());
+        assertTrue(info.isYinElement());
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug2291Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug2291Test.java
new file mode 100644 (file)
index 0000000..d45a3c4
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.File;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug2291Test {
+
+    @Test
+    public void testRevisionWithExt() throws Exception {
+        File extdef = new File(getClass().getResource("/bugs/bug2291/bug2291-ext.yang").toURI());
+        File bug = new File(getClass().getResource("/bugs/bug2291/bug2291.yang").toURI());
+        File inet = new File(getClass().getResource("/ietf/ietf-inet-types@2010-09-24.yang").toURI());
+
+        SchemaContext context = TestUtils.parseYangSources(extdef, bug, inet);
+        assertNotNull(context);
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug2872Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug2872Test.java
new file mode 100644 (file)
index 0000000..78d5221
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016 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.yang.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+
+public class Bug2872Test {
+
+    @Test
+    public void test() throws Exception {
+        final SchemaContext schema = StmtTestUtils.parseYangSources("/bugs/bug2872");
+        assertNotNull(schema);
+
+        final QNameModule bug2872module = QNameModule.create(URI.create("bug2872"), Revision.of("2016-06-08"));
+        final QName foo = QName.create(bug2872module, "bar");
+
+        final DataSchemaNode dataSchemaNode = schema.getDataChildByName(foo);
+        assertTrue(dataSchemaNode instanceof LeafSchemaNode);
+        final LeafSchemaNode myLeaf = (LeafSchemaNode) dataSchemaNode;
+
+        final TypeDefinition<?> type = myLeaf.getType();
+        assertTrue(type instanceof EnumTypeDefinition);
+        final EnumTypeDefinition myEnum = (EnumTypeDefinition) type;
+
+        final List<EnumTypeDefinition.EnumPair> values = myEnum.getValues();
+        assertEquals(2, values.size());
+
+        final List<String> valueNames = new ArrayList<>();
+        for (EnumTypeDefinition.EnumPair pair : values) {
+            valueNames.add(pair.getName());
+        }
+        assertTrue(valueNames.contains("value-one"));
+        assertTrue(valueNames.contains("value-two"));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug3799Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug3799Test.java
new file mode 100644 (file)
index 0000000..0c5a40e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug3799Test {
+
+    @Test
+    public void test() throws Exception {
+        SchemaContext schema = StmtTestUtils.parseYangSources("/bugs/bug3799");
+        assertNotNull(schema);
+
+        Set<Module> modules = schema.getModules();
+        assertNotNull(modules);
+        assertEquals(1, modules.size());
+
+        Module testModule = modules.iterator().next();
+        Set<Module> subModules = testModule.getSubmodules();
+        assertNotNull(subModules);
+        assertEquals(1, subModules.size());
+
+        Module testSubmodule = subModules.iterator().next();
+
+        Set<NotificationDefinition> notifications = testSubmodule
+                .getNotifications();
+        assertNotNull(notifications);
+        assertEquals(1, notifications.size());
+
+        NotificationDefinition bazNotification = notifications.iterator()
+                .next();
+        Collection<DataSchemaNode> childNodes = bazNotification.getChildNodes();
+        assertNotNull(childNodes);
+        assertEquals(1, childNodes.size());
+
+        DataSchemaNode child = childNodes.iterator().next();
+        assertTrue(child instanceof LeafSchemaNode);
+
+        LeafSchemaNode leafBar = (LeafSchemaNode) child;
+        String bar = leafBar.getQName().getLocalName();
+        assertEquals("bar", bar);
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug3859Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug3859Test.java
new file mode 100644 (file)
index 0000000..a67fdae
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+public class Bug3859Test {
+    @Test
+    public void test() throws Exception {
+        final Module bug3859 = TestUtils.findModule(
+            TestUtils.loadModules(getClass().getResource("/bugs/bug3859").toURI()), "reference-in-unknown").get();
+        assertNotNull(bug3859);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug394Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug394Test.java
new file mode 100644 (file)
index 0000000..94cf0b3
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+/**
+ * Test antlr grammar capability to parse nested unknown nodes.
+ */
+public class Bug394Test {
+
+    @Test
+    public void testParseList() throws Exception {
+        final SchemaContext context = TestUtils.loadModules(getClass().getResource("/bugs/bug394-retest").toURI());
+        final Module bug394 = TestUtils.findModule(context, "bug394").get();
+        final Module bug394_ext = TestUtils.findModule(context, "bug394-ext").get();
+
+        final ContainerSchemaNode logrecords = (ContainerSchemaNode) bug394.getDataChildByName(QName.create(
+                bug394.getQNameModule(), "logrecords"));
+        assertNotNull(logrecords);
+
+        final List<UnknownSchemaNode> nodes = logrecords.getUnknownSchemaNodes();
+        assertEquals(2, nodes.size());
+
+        final List<ExtensionDefinition> extensions = bug394_ext.getExtensionSchemaNodes();
+        assertEquals(3, extensions.size());
+
+        assertTrue(extensions.contains(nodes.get(0).getExtensionDefinition()));
+        assertTrue(extensions.contains(nodes.get(1).getExtensionDefinition()));
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4231Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4231Test.java
new file mode 100644 (file)
index 0000000..428f059
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class Bug4231Test {
+
+    @Test
+    public void test() throws Exception {
+        SchemaContext context = TestUtils.parseYangSources("/bugs/bug4231");
+
+        assertNotNull(context);
+
+        QNameModule foo = QNameModule.create(new URI("foo"), Revision.of("2015-09-02"));
+
+        SchemaPath targetPath = SchemaPath
+                .create(true, QName.create(foo, "augment-target"))
+                .createChild(QName.create(foo, "my-container-in-grouping"))
+                .createChild(QName.create(foo, "l2"));
+
+        SchemaNode targetNode = SchemaContextUtil.findNodeInSchemaContext(
+                context, targetPath.getPathFromRoot());
+        assertNotNull(targetNode);
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4410Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4410Test.java
new file mode 100644 (file)
index 0000000..5543b3f
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Throwables;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+
+public class Bug4410Test {
+
+    @Test
+    public void test() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/bugs/bug4410");
+            fail("SomeModifiersUnresolvedException should be thrown.");
+        } catch (SomeModifiersUnresolvedException e) {
+            Throwable rootCause = Throwables.getRootCause(e);
+            assertTrue(rootCause instanceof InferenceException);
+            final String message = rootCause.getMessage();
+            assertTrue(message.startsWith("Type [(foo)"));
+            assertTrue(message.contains("was not found"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4456Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4456Test.java
new file mode 100644 (file)
index 0000000..5779e28
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+public class Bug4456Test {
+    @Test
+    public void test() throws Exception {
+        SchemaContext schema = StmtTestUtils.parseYangSources("/bugs/bug4456");
+        assertNotNull(schema);
+
+        Set<Module> modules = schema.findModules(URI.create("foo"));
+        assertEquals(1, modules.size());
+        Module moduleFoo = modules.iterator().next();
+
+        List<ExtensionDefinition> extensionSchemaNodes = moduleFoo.getExtensionSchemaNodes();
+        assertEquals(5, extensionSchemaNodes.size());
+        for (ExtensionDefinition extensionDefinition : extensionSchemaNodes) {
+
+            List<UnknownSchemaNode> unknownSchemaNodes = extensionDefinition.getUnknownSchemaNodes();
+            assertEquals(1, unknownSchemaNodes.size());
+            UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.iterator().next();
+            String unknownNodeExtensionDefName = unknownSchemaNode.getExtensionDefinition().getQName().getLocalName();
+
+            List<UnknownSchemaNode> subUnknownSchemaNodes = unknownSchemaNode.getUnknownSchemaNodes();
+            assertEquals(1, subUnknownSchemaNodes.size());
+            UnknownSchemaNode subUnknownSchemaNode = subUnknownSchemaNodes.iterator().next();
+            String subUnknownNodeExtensionDefName = subUnknownSchemaNode.getExtensionDefinition().getQName()
+                    .getLocalName();
+
+            switch (extensionDefinition.getQName().getLocalName()) {
+                case "a":
+                    assertEquals("b", unknownNodeExtensionDefName);
+                    assertEquals("c", subUnknownNodeExtensionDefName);
+                    break;
+                case "b":
+                    assertEquals("c", unknownNodeExtensionDefName);
+                    assertEquals("a", subUnknownNodeExtensionDefName);
+                    break;
+                case "c":
+                    assertEquals("a", unknownNodeExtensionDefName);
+                    assertEquals("b", subUnknownNodeExtensionDefName);
+                    break;
+                case "r":
+                    assertEquals("r", unknownNodeExtensionDefName);
+                    assertEquals("r2", subUnknownNodeExtensionDefName);
+                    break;
+                case "r2":
+                    assertEquals("r2", unknownNodeExtensionDefName);
+                    assertEquals("r", subUnknownNodeExtensionDefName);
+                    break;
+                default:
+                    fail("Unexpected extension definition");
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4610Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4610Test.java
new file mode 100644 (file)
index 0000000..02b319a
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerStatement;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class Bug4610Test {
+
+    @Test
+    public void test() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug4610");
+
+        Revision revision = Revision.of("2015-12-12");
+        QNameModule foo = QNameModule.create(URI.create("foo"), revision);
+        QNameModule bar = QNameModule.create(URI.create("bar"), revision);
+
+        QName g1 = QName.create(bar, "g1");
+        QName g2 = QName.create(bar, "g2");
+        QName c1Bar = QName.create(bar, "c1");
+
+        QName c1Foo = QName.create(foo, "c1");
+        QName g3 = QName.create(foo, "g3");
+        QName root = QName.create(foo, "root");
+
+        ContainerEffectiveStatement effectiveContainerStatementG1 = findContainer(context, g1, c1Bar);
+        ContainerEffectiveStatement effectiveContainerStatementG2 = findContainer(context, g2, c1Bar);
+        ContainerEffectiveStatement effectiveContainerStatementG3 = findContainer(context, g3, c1Foo);
+        ContainerEffectiveStatement effectiveContainerStatementRoot = findContainer(context, root, c1Foo);
+
+        // check arguments
+        QName originalStatementArgument = effectiveContainerStatementG1.argument();
+        assertTrue(originalStatementArgument.equals(effectiveContainerStatementG2.argument()));
+        assertFalse(originalStatementArgument.equals(effectiveContainerStatementG3.argument()));
+        assertFalse(originalStatementArgument.equals(effectiveContainerStatementRoot.argument()));
+
+        ContainerStatement originalContainerStatement = effectiveContainerStatementG1.getDeclared();
+        ContainerStatement inGrouping2ContainerStatement = effectiveContainerStatementG2.getDeclared();
+        ContainerStatement inGrouping3ContainerStatement = effectiveContainerStatementG3.getDeclared();
+        ContainerStatement inRootContainerStatement = effectiveContainerStatementRoot.getDeclared();
+
+        // check declared instances
+        assertTrue(originalContainerStatement == inGrouping2ContainerStatement);
+        assertTrue(originalContainerStatement == inGrouping3ContainerStatement);
+        assertTrue(originalContainerStatement == inRootContainerStatement);
+
+    }
+
+    private static ContainerEffectiveStatement findContainer(final SchemaContext context, final QName... path) {
+        SchemaNode node = SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true, path));
+        assertTrue(node instanceof ContainerEffectiveStatement);
+        return (ContainerEffectiveStatement) node;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4623Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4623Test.java
new file mode 100644 (file)
index 0000000..8ec6bec
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import com.google.common.collect.Range;
+import java.io.File;
+import java.net.URI;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+
+public class Bug4623Test {
+
+    @Test
+    public void testStringTypeWithUnknownSchemaNodeAtTheEndOfTypeDefinition() throws Exception {
+        // given
+        final File extdef = new File(getClass().getResource("/bugs/bug4623/extension-def.yang").toURI());
+        final File stringWithExt = new File(getClass().getResource("/bugs/bug4623/string-with-ext.yang").toURI());
+
+        // when
+        final SchemaContext schemaContext = TestUtils.parseYangSources(extdef, stringWithExt);
+
+        final LeafSchemaNode leaf = (LeafSchemaNode) typesModule(schemaContext).getDataChildByName(
+            QName.create(URI.create("urn:custom.types.demo"), "leaf-length-pattern-unknown"));
+
+        // then
+        Assert.assertNotNull(leaf);
+
+        final TypeDefinition<?> type = leaf.getType();
+        Assert.assertNotNull(type);
+        final List<UnknownSchemaNode> unknownSchemaNodes = type.getUnknownSchemaNodes();
+        Assert.assertNotNull(unknownSchemaNodes);
+        Assert.assertFalse(unknownSchemaNodes.size() == 0);
+
+        final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.get(0);
+        Assert.assertEquals(unknownSchemaNode.getNodeParameter(), "unknown");
+        Assert.assertEquals(unknownSchemaNode.getNodeType().getModule().getNamespace().toString(),
+            "urn:simple.extension.typedefs");
+
+        final LengthConstraint lengthConstraint = ((StringTypeDefinition) type).getLengthConstraint().get();
+        final List<PatternConstraint> patternConstraints = ((StringTypeDefinition) type).getPatternConstraints();
+
+        Assert.assertNotNull(lengthConstraint);
+        Assert.assertNotNull(patternConstraints);
+        Assert.assertFalse(lengthConstraint.getAllowedRanges().isEmpty());
+        Assert.assertFalse(patternConstraints.isEmpty());
+
+        final Range<Integer> span = lengthConstraint.getAllowedRanges().span();
+        Assert.assertEquals(Integer.valueOf(2), span.lowerEndpoint());
+        Assert.assertEquals(Integer.valueOf(10), span.upperEndpoint());
+
+        final PatternConstraint patternConstraint = patternConstraints.get(0);
+        Assert.assertEquals(patternConstraint.getRegularExpressionString(), "[0-9a-fA-F]");
+    }
+
+    @Test
+    public void testStringTypeWithUnknownSchemaNodeBetweenStringRestrictionStatements() throws Exception {
+        // given
+        final File extdef = new File(getClass().getResource("/bugs/bug4623/extension-def.yang").toURI());
+        final File stringWithExt = new File(getClass().getResource("/bugs/bug4623/string-with-ext.yang").toURI());
+
+        // when
+        final SchemaContext schemaContext = TestUtils.parseYangSources(extdef, stringWithExt);
+
+        final LeafSchemaNode leaf = (LeafSchemaNode) typesModule(schemaContext).getDataChildByName(
+                QName.create(URI.create("urn:custom.types.demo"), "leaf-length-unknown-pattern"));
+
+        // then
+        Assert.assertNotNull(leaf);
+
+        final TypeDefinition<?> type = leaf.getType();
+        Assert.assertNotNull(type);
+        final List<UnknownSchemaNode> unknownSchemaNodes = type.getUnknownSchemaNodes();
+        Assert.assertNotNull(unknownSchemaNodes);
+        Assert.assertFalse(unknownSchemaNodes.size() == 0);
+
+        final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.get(0);
+        Assert.assertEquals(unknownSchemaNode.getNodeParameter(), "unknown");
+        Assert.assertEquals(unknownSchemaNode.getNodeType().getModule().getNamespace().toString(),
+            "urn:simple.extension.typedefs");
+
+        final LengthConstraint lengthConstraints = ((StringTypeDefinition) type).getLengthConstraint().get();
+        final List<PatternConstraint> patternConstraints = ((StringTypeDefinition) type).getPatternConstraints();
+
+        Assert.assertNotNull(lengthConstraints);
+        Assert.assertNotNull(patternConstraints);
+        Assert.assertEquals(1, lengthConstraints.getAllowedRanges().asRanges().size());
+        Assert.assertFalse(patternConstraints.isEmpty());
+
+        final Range<Integer> lengthConstraint = lengthConstraints.getAllowedRanges().span();
+        Assert.assertEquals(Integer.valueOf(2), lengthConstraint.lowerEndpoint());
+        Assert.assertEquals(Integer.valueOf(10), lengthConstraint.upperEndpoint());
+
+        final PatternConstraint patternConstraint = patternConstraints.get(0);
+        Assert.assertEquals(patternConstraint.getRegularExpressionString(), "[0-9a-fA-F]");
+    }
+
+    @Test
+    public void testStringTypeWithUnknownSchemaNodeOnTheStartOfTypeDefinition() throws Exception {
+        // given
+        final File extdef = new File(getClass().getResource("/bugs/bug4623/extension-def.yang").toURI());
+        final File stringWithExt = new File(getClass().getResource("/bugs/bug4623/string-with-ext.yang").toURI());
+
+        // when
+        final SchemaContext schemaContext = TestUtils.parseYangSources(extdef, stringWithExt);
+
+        final LeafSchemaNode leaf = (LeafSchemaNode) typesModule(schemaContext).getDataChildByName(
+                QName.create(URI.create("urn:custom.types.demo"), "leaf-unknown-length-pattern"));
+
+        // then
+        Assert.assertNotNull(leaf);
+
+        final TypeDefinition<?> type = leaf.getType();
+        Assert.assertNotNull(type);
+        final List<UnknownSchemaNode> unknownSchemaNodes = type.getUnknownSchemaNodes();
+        Assert.assertNotNull(unknownSchemaNodes);
+        Assert.assertFalse(unknownSchemaNodes.size() == 0);
+
+        final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.get(0);
+        Assert.assertEquals(unknownSchemaNode.getNodeParameter(), "unknown");
+        Assert.assertEquals(unknownSchemaNode.getNodeType().getModule().getNamespace().toString(),
+            "urn:simple.extension.typedefs");
+
+        final LengthConstraint lengthConstraints =
+                ((StringTypeDefinition) type).getLengthConstraint().get();
+        final List<PatternConstraint> patternConstraints = ((StringTypeDefinition) type).getPatternConstraints();
+
+        Assert.assertNotNull(lengthConstraints);
+        Assert.assertNotNull(patternConstraints);
+        Assert.assertEquals(1, lengthConstraints.getAllowedRanges().asRanges().size());
+        Assert.assertFalse(patternConstraints.size() == 0);
+
+        final Range<Integer> lengthConstraint = lengthConstraints.getAllowedRanges().span();
+        Assert.assertEquals(Integer.valueOf(2), lengthConstraint.lowerEndpoint());
+        Assert.assertEquals(Integer.valueOf(10), lengthConstraint.upperEndpoint());
+
+        final PatternConstraint patternConstraint = patternConstraints.get(0);
+        Assert.assertEquals(patternConstraint.getRegularExpressionString(), "[0-9a-fA-F]");
+    }
+
+    private static Module typesModule(final SchemaContext context) {
+        return context.findModules("types").iterator().next();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4933Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug4933Test.java
new file mode 100644 (file)
index 0000000..0c79a10
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.hamcrest.Matchers.isA;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Deviation;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public class Bug4933Test {
+
+    @Test
+    public void test() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug4933/correct");
+        assertNotNull(context);
+
+        final Module foo = context.findModules("foo").iterator().next();
+        Set<Deviation> deviations = foo.getDeviations();
+        assertEquals(4, deviations.size());
+    }
+
+    @Test
+    public void incorrectKeywordTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/bugs/bug4933/incorrect");
+            fail("ReactorException should be thrown.");
+        } catch (ReactorException e) {
+            final Throwable cause = e.getCause();
+            assertThat(cause, isA(SourceException.class));
+            assertTrue(cause.getMessage().startsWith("String 'not_supported' is not valid deviate argument"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5059Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5059Test.java
new file mode 100644 (file)
index 0000000..a931e1b
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug5059Test {
+    @Test
+    public void test() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5059");
+        assertNotNull(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5101Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5101Test.java
new file mode 100644 (file)
index 0000000..6e62bf2
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class Bug5101Test {
+    private static final String NS = "foo";
+    private static final String REV = "2016-01-29";
+
+    @Test
+    public void test() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5101");
+        assertNotNull(context);
+
+        QName grp = QName.create(NS, REV, "my-grouping");
+        QName myContainer = QName.create(NS, REV, "my-container");
+        QName root = QName.create(NS, REV, "root");
+
+        SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, grp, myContainer));
+        assertTrue(findDataSchemaNode instanceof ContainerSchemaNode);
+        ContainerSchemaNode myContainerInGrouping = (ContainerSchemaNode) findDataSchemaNode;
+        assertEquals(Status.DEPRECATED, myContainerInGrouping.getStatus());
+
+        findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true, root, myContainer));
+        assertTrue(findDataSchemaNode instanceof ContainerSchemaNode);
+        ContainerSchemaNode myContainerInRoot = (ContainerSchemaNode) findDataSchemaNode;
+        assertEquals(Status.DEPRECATED, myContainerInRoot.getStatus());
+
+        findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true, myContainer));
+        assertTrue(findDataSchemaNode instanceof ContainerSchemaNode);
+        ContainerSchemaNode myContainerInModule = (ContainerSchemaNode) findDataSchemaNode;
+        assertEquals(Status.DEPRECATED, myContainerInModule.getStatus());
+
+        findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true, root));
+        assertTrue(findDataSchemaNode instanceof ContainerSchemaNode);
+        ContainerSchemaNode rootContainer = (ContainerSchemaNode) findDataSchemaNode;
+        assertEquals(Status.CURRENT, rootContainer.getStatus());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5335Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5335Test.java
new file mode 100644 (file)
index 0000000..e325c0c
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class Bug5335Test {
+    private static final String FOO = "foo";
+    private static final String BAR = "bar";
+    private static final String REV = "2016-03-04";
+
+    private static final QName ROOT = QName.create(FOO, REV, "root");
+    private static final QName PRESENCE_CONTAINER_F = QName.create(FOO, REV, "presence-container");
+    private static final QName NON_PRESENCE_CONTAINER_F = QName.create(FOO, REV, "non-presence-container");
+    private static final QName MANDATORY_LEAF_F = QName.create(FOO, REV, "mandatory-leaf");
+    private static final QName PRESENCE_CONTAINER_B = QName.create(BAR, REV, "presence-container");
+    private static final QName NON_PRESENCE_CONTAINER_B = QName.create(BAR, REV, "non-presence-container");
+    private static final QName MANDATORY_LEAF_B = QName.create(BAR, REV, "mandatory-leaf");
+
+    private final ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+    @Before
+    public void setUp() throws UnsupportedEncodingException {
+        System.setOut(new PrintStream(output, true, "UTF-8"));
+    }
+
+    @After
+    @SuppressWarnings("checkstyle:regexpSinglelineJava")
+    public void cleanUp() {
+        System.setOut(System.out);
+    }
+
+    @Test
+    public void incorrectTest1() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5335/incorrect/case-1");
+        assertNotNull(context);
+
+        final SchemaPath schemaPath = SchemaPath.create(true, ROOT, NON_PRESENCE_CONTAINER_B, MANDATORY_LEAF_B);
+        final SchemaNode mandatoryLeaf = SchemaContextUtil.findDataSchemaNode(context, schemaPath);
+        assertNull(mandatoryLeaf);
+
+        final String testLog = output.toString();
+        assertTrue(testLog.contains(
+            "An augment cannot add node 'mandatory-leaf' because it is mandatory and in module different than target"));
+    }
+
+    @Test
+    public void incorrectTest2() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5335/incorrect/case-2");
+        assertNotNull(context);
+
+        final SchemaPath schemaPath = SchemaPath.create(true, ROOT, PRESENCE_CONTAINER_F, MANDATORY_LEAF_B);
+        final SchemaNode mandatoryLeaf = SchemaContextUtil.findDataSchemaNode(context, schemaPath);
+        assertNull(mandatoryLeaf);
+
+        final String testLog = output.toString();
+        assertTrue(testLog.contains(
+            "An augment cannot add node 'mandatory-leaf' because it is mandatory and in module different than target"));
+    }
+
+    @Test
+    public void incorrectTest3() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5335/incorrect/case-2");
+        assertNotNull(context);
+
+        final SchemaPath schemaPath = SchemaPath.create(true, ROOT, PRESENCE_CONTAINER_F, NON_PRESENCE_CONTAINER_B,
+                MANDATORY_LEAF_B);
+        final SchemaNode mandatoryLeaf = SchemaContextUtil.findDataSchemaNode(context, schemaPath);
+        assertNull(mandatoryLeaf);
+
+        final String testLog = output.toString();
+        assertTrue(testLog.contains(
+            "An augment cannot add node 'mandatory-leaf' because it is mandatory and in module different than target"));
+    }
+
+    @Test
+    public void correctTest1() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5335/correct/case-1");
+        assertNotNull(context);
+
+        final SchemaPath schemaPath = SchemaPath.create(true, ROOT, PRESENCE_CONTAINER_B, MANDATORY_LEAF_B);
+        final SchemaNode mandatoryLeaf = SchemaContextUtil.findDataSchemaNode(context, schemaPath);
+        assertTrue(mandatoryLeaf instanceof LeafSchemaNode);
+    }
+
+    @Test
+    public void correctTest2() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5335/correct/case-2");
+        assertNotNull(context);
+
+        final SchemaPath schemaPath = SchemaPath.create(true, ROOT, PRESENCE_CONTAINER_B, NON_PRESENCE_CONTAINER_B,
+                MANDATORY_LEAF_B);
+        final SchemaNode mandatoryLeaf = SchemaContextUtil.findDataSchemaNode(context, schemaPath);
+        assertTrue(mandatoryLeaf instanceof LeafSchemaNode);
+    }
+
+    @Test
+    public void correctTest3() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5335/correct/case-3");
+        assertNotNull(context);
+
+        final SchemaPath schemaPath = SchemaPath.create(true, ROOT, PRESENCE_CONTAINER_B, NON_PRESENCE_CONTAINER_B,
+                MANDATORY_LEAF_B);
+        final SchemaNode mandatoryLeaf = SchemaContextUtil.findDataSchemaNode(context, schemaPath);
+        assertTrue(mandatoryLeaf instanceof LeafSchemaNode);
+    }
+
+    @Test
+    public void correctTest4() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5335/correct/case-4");
+        assertNotNull(context);
+
+        final SchemaPath schemaPath = SchemaPath.create(true, ROOT, NON_PRESENCE_CONTAINER_F, MANDATORY_LEAF_F);
+        final SchemaNode mandatoryLeaf = SchemaContextUtil.findDataSchemaNode(context, schemaPath);
+        assertTrue(mandatoryLeaf instanceof LeafSchemaNode);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5396Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5396Test.java
new file mode 100644 (file)
index 0000000..caa5a82
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class Bug5396Test {
+    @Test
+    public void test() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5396");
+        assertNotNull(context);
+
+        QName root = QName.create("foo", "root");
+        QName myLeaf2 = QName.create("foo", "my-leaf2");
+
+        SchemaPath schemaPath = SchemaPath.create(true, root, myLeaf2);
+        SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context, schemaPath);
+        assertTrue(findDataSchemaNode instanceof LeafSchemaNode);
+
+        LeafSchemaNode leaf2 = (LeafSchemaNode) findDataSchemaNode;
+        TypeDefinition<?> type = leaf2.getType();
+        assertTrue(type instanceof UnionTypeDefinition);
+
+        UnionTypeDefinition union = (UnionTypeDefinition) type;
+        List<TypeDefinition<?>> types = union.getTypes();
+
+        assertEquals(4, types.size());
+
+        TypeDefinition<?> type0 = types.get(0);
+        TypeDefinition<?> type1 = types.get(1);
+        TypeDefinition<?> type2 = types.get(2);
+        TypeDefinition<?> type3 = types.get(3);
+
+        assertFalse(type0.equals(type1));
+        assertFalse(type0.equals(type2));
+        assertFalse(type0.equals(type3));
+
+        assertTrue(type0 instanceof StringTypeDefinition);
+        assertTrue(type1 instanceof StringTypeDefinition);
+        assertTrue(type2 instanceof StringTypeDefinition);
+        assertTrue(type3 instanceof StringTypeDefinition);
+
+        StringTypeDefinition stringType0 = (StringTypeDefinition) type0;
+        StringTypeDefinition stringType1 = (StringTypeDefinition) type1;
+        StringTypeDefinition stringType2 = (StringTypeDefinition) type2;
+        StringTypeDefinition stringType3 = (StringTypeDefinition) type3;
+
+        final List<PatternConstraint> patternConstraints0 = stringType0.getPatternConstraints();
+        final List<PatternConstraint> patternConstraints1 = stringType1.getPatternConstraints();
+        final List<PatternConstraint> patternConstraints2 = stringType2.getPatternConstraints();
+        final List<PatternConstraint> patternConstraints3 = stringType3.getPatternConstraints();
+
+        assertEquals(1, patternConstraints0.size());
+        assertEquals(1, patternConstraints1.size());
+        assertEquals(1, patternConstraints2.size());
+        assertEquals(1, patternConstraints3.size());
+
+        final PatternConstraint patternConstraint0 = patternConstraints0.get(0);
+        final PatternConstraint patternConstraint1 = patternConstraints1.get(0);
+        final PatternConstraint patternConstraint2 = patternConstraints2.get(0);
+        final PatternConstraint patternConstraint3 = patternConstraints3.get(0);
+
+        assertEquals("^(?:dp[0-9]+o[0-9]+(d[0-9]+)?)$", patternConstraint0.getJavaPatternString());
+        assertEquals("^(?:dp[0-9]+s[0-9]+(f[0-9]+)?(d[0-9]+)?)$", patternConstraint1.getJavaPatternString());
+        assertEquals("^(?:dp[0-9]+(P[0-9]+)?p[0-9]{1,3}s[0-9]{1,3}(f[0-9]+)?(d[0-9]+)?)$",
+                patternConstraint2.getJavaPatternString());
+        assertEquals("^(?:dp[0-9]+p[0-9]+p[0-9]+)$", patternConstraint3.getJavaPatternString());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5437Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5437Test.java
new file mode 100644 (file)
index 0000000..81739a5
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.hamcrest.Matchers.isA;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class Bug5437Test {
+    private static final String NS = "foo";
+    private static final String REV = "2016-03-01";
+
+    @Test
+    public void test() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5437");
+        assertNotNull(context);
+
+        QName root = QName.create(NS, REV, "root");
+        QName leafRef2 = QName.create(NS, REV, "leaf-ref-2");
+        QName conGrp = QName.create(NS, REV, "con-grp");
+        QName leafRef = QName.create(NS, REV, "leaf-ref");
+
+        SchemaPath leafRefPath = SchemaPath.create(true, root, conGrp, leafRef);
+        SchemaPath leafRef2Path = SchemaPath.create(true, root, leafRef2);
+        SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context, leafRefPath);
+        SchemaNode findDataSchemaNode2 = SchemaContextUtil.findDataSchemaNode(context, leafRef2Path);
+        assertThat(findDataSchemaNode, isA(LeafSchemaNode.class));
+        assertThat(findDataSchemaNode2, isA(LeafSchemaNode.class));
+
+        LeafSchemaNode leafRefNode = (LeafSchemaNode) findDataSchemaNode;
+        LeafSchemaNode leafRefNode2 = (LeafSchemaNode) findDataSchemaNode2;
+
+        assertThat(leafRefNode.getType(), isA(LeafrefTypeDefinition.class));
+        assertThat(leafRefNode2.getType(), isA(LeafrefTypeDefinition.class));
+
+        TypeDefinition<?> baseTypeForLeafRef = SchemaContextUtil.getBaseTypeForLeafRef(
+                (LeafrefTypeDefinition) leafRefNode.getType(), context, leafRefNode);
+        TypeDefinition<?> baseTypeForLeafRef2 = SchemaContextUtil.getBaseTypeForLeafRef(
+                (LeafrefTypeDefinition) leafRefNode2.getType(), context, leafRefNode2);
+
+        assertThat(baseTypeForLeafRef, isA(BinaryTypeDefinition.class));
+        assertThat(baseTypeForLeafRef2, isA(Int16TypeDefinition.class));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5481Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5481Test.java
new file mode 100644 (file)
index 0000000..44b2cf1
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath.WithExpression;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.Status;
+
+public class Bug5481Test {
+    @Test
+    public void test() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5481");
+        assertNotNull(context);
+
+        ContainerSchemaNode topContainer = verifyTopContainer(context);
+        verifyExtendedLeaf(topContainer);
+    }
+
+    private static ContainerSchemaNode verifyTopContainer(final SchemaContext context) {
+        QName top = QName.create("http://example.com/module1", "2016-03-09", "top");
+        DataSchemaNode dataChildByName = context.getDataChildByName(top);
+        assertTrue(dataChildByName instanceof ContainerSchemaNode);
+
+        ContainerSchemaNode topContainer = (ContainerSchemaNode) dataChildByName;
+
+        assertFalse(topContainer.getWhenCondition().isPresent());
+        assertEquals(Status.CURRENT, topContainer.getStatus());
+        assertFalse(topContainer.getDescription().isPresent());
+        assertFalse(topContainer.getReference().isPresent());
+        return topContainer;
+    }
+
+    private static void verifyExtendedLeaf(final ContainerSchemaNode topContainer) {
+        DataSchemaNode dataChildByName2 = topContainer.getDataChildByName(QName.create("http://example.com/module2",
+                "2016-03-09", "extended-leaf"));
+        assertTrue(dataChildByName2 instanceof LeafSchemaNode);
+
+        LeafSchemaNode extendedLeaf = (LeafSchemaNode) dataChildByName2;
+        assertEquals(Status.DEPRECATED, extendedLeaf.getStatus());
+        assertEquals(Optional.of("text"), extendedLeaf.getDescription());
+        assertEquals(Optional.of("ref"), extendedLeaf.getReference());
+
+        RevisionAwareXPath whenConditionExtendedLeaf = extendedLeaf.getWhenCondition().get();
+        assertFalse(whenConditionExtendedLeaf.isAbsolute());
+        assertThat(whenConditionExtendedLeaf, instanceOf(WithExpression.class));
+        assertEquals("module1:top = 'extended'", whenConditionExtendedLeaf.getOriginalString());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5518Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5518Test.java
new file mode 100644 (file)
index 0000000..2464c15
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug5518Test {
+    @Test
+    public void test() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5518");
+        assertNotNull(context);
+
+        final DataSchemaNode dataChildByName = context.getDataChildByName(QName.create("foo", "root"));
+        assertTrue(dataChildByName instanceof ContainerSchemaNode);
+        final ContainerSchemaNode root = (ContainerSchemaNode) dataChildByName;
+        final Collection<MustDefinition> mustConstraints = root.getMustConstraints();
+        assertEquals(1, mustConstraints.size());
+        final MustDefinition must = mustConstraints.iterator().next();
+        assertEquals("not(deref(.)/../same-pass)", must.getXpath().getOriginalString());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5550Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5550Test.java
new file mode 100644 (file)
index 0000000..c673952
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class Bug5550Test {
+    private static final String NS = "foo";
+    private static final String REV = "2016-03-18";
+
+    @Test
+    public void test() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5550");
+        assertNotNull(context);
+
+        QName root = QName.create(NS, REV, "root");
+        QName containerInGrouping = QName.create(NS, REV, "container-in-grouping");
+        QName leaf1 = QName.create(NS, REV, "leaf-1");
+
+        SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, root, containerInGrouping, leaf1));
+        assertTrue(findDataSchemaNode instanceof LeafSchemaNode);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5693Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5693Test.java
new file mode 100644 (file)
index 0000000..292a6c7
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.repo.api.YinTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.xml.sax.SAXException;
+
+public class Bug5693Test {
+
+    private Module foo;
+
+    /**
+     * Use input stream to load Yin module.
+     */
+    @Before
+    public void initTest() throws ReactorException, SAXException, IOException {
+        foo = TestUtils.loadYinModule(YinTextSchemaSource.forResource(getClass(), "/bugs/bug5693/foo.yin"));
+    }
+
+    /**
+     * Test presence of testing feature (parsed in the last phase), if it is present then parsing was successful.
+     * Meaning that stream was not closed after the first parsing phase.
+     */
+    @Test
+    public void bug5693Test() {
+        assertNotNull(foo.getFeatures());
+        assertEquals("Module should has exactly one feature", 1, foo.getFeatures().size());
+        assertEquals("Present feature should has expected local name", "test-input-stream-not-closed",
+                foo.getFeatures().iterator().next().getQName().getLocalName());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5712Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5712Test.java
new file mode 100644 (file)
index 0000000..84578c7
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+public class Bug5712Test {
+
+    @Test
+    public void testTypedefWithNewStatementParser() throws Exception {
+        SchemaContext schemaContext = StmtTestUtils.parseYangSources("/bugs/bug5712");
+        assertNotNull(schemaContext);
+
+        Module badModule = schemaContext.findModules("bad").iterator().next();
+        assertNotNull(badModule);
+        checkThing2TypeDef(badModule);
+    }
+
+    private static void checkThing2TypeDef(final Module badModule) {
+        TypeDefinition<?> thing2 = null;
+        for (TypeDefinition<?> typeDef : badModule.getTypeDefinitions()) {
+            if (typeDef.getQName().getLocalName().equals("thing2")) {
+                thing2 = typeDef;
+                break;
+            }
+        }
+
+        assertNotNull(thing2);
+        TypeDefinition<?> baseType = thing2.getBaseType();
+        assertEquals(QName.create("urn:opendaylight:bad", "2016-04-11", "thing"), baseType.getQName());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5884Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5884Test.java
new file mode 100644 (file)
index 0000000..f533b61
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.hamcrest.Matchers.isA;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.util.Iterator;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug5884Test {
+    private static final String NS = "urn:yang.foo";
+    private static final String REV = "2016-01-01";
+
+    @Test
+    public void testBug5884() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5884");
+        assertNotNull(context);
+
+        final QName root = QName.create(NS, REV, "main-container");
+        final QName choice = QName.create(NS, REV, "test-choice");
+        final QName testContainerQname = QName.create(NS, REV, "test");
+        final Module foo = context.findModule("foo", Revision.of("2016-01-01")).get();
+        final ContainerSchemaNode rootContainer = (ContainerSchemaNode) context.getDataChildByName(root);
+        final ContainerSchemaNode testContainer = (ContainerSchemaNode) rootContainer.getDataChildByName(
+            testContainerQname);
+        final ChoiceSchemaNode dataChildByName = (ChoiceSchemaNode) testContainer.getDataChildByName(choice);
+        final Set<AugmentationSchemaNode> augmentations = foo.getAugmentations();
+        final Set<AugmentationSchemaNode> availableAugmentations = dataChildByName.getAvailableAugmentations();
+        final Iterator<AugmentationSchemaNode> iterator = augmentations.iterator();
+        final Iterator<AugmentationSchemaNode> availableIterator = availableAugmentations.iterator();
+
+        testIterator(iterator);
+        testIterator(availableIterator);
+    }
+
+    private static void testIterator(final Iterator<AugmentationSchemaNode> iterator) {
+        while (iterator.hasNext()) {
+            AugmentationSchemaNode allAugments = iterator.next();
+            final DataSchemaNode currentChoice = allAugments.getChildNodes().iterator().next();
+            assertThat(currentChoice, isA(CaseSchemaNode.class));
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5899Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5899Test.java
new file mode 100644 (file)
index 0000000..a9bf847
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug5899Test {
+
+    @Test
+    public void testBug5899() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources("/bugs/bug5899");
+        assertNotNull(schemaContext);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5942Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5942Test.java
new file mode 100644 (file)
index 0000000..6e219a8
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath.WithExpression;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+public class Bug5942Test {
+    @Test
+    public void test() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources("/bugs/bug5942");
+        assertNotNull(schemaContext);
+
+        final DataSchemaNode root = schemaContext.getDataChildByName(QName.create("foo", "2016-06-02", "root"));
+        assertTrue(root instanceof ContainerSchemaNode);
+
+        final Set<UsesNode> uses = ((ContainerSchemaNode) root).getUses();
+        assertEquals(1, uses.size());
+        final UsesNode usesNode = uses.iterator().next();
+
+        assertEquals(Optional.of("uses description"), usesNode.getDescription());
+        assertEquals(Optional.of("uses reference"), usesNode.getReference());
+        assertEquals(Status.DEPRECATED, usesNode.getStatus());
+
+        final RevisionAwareXPath when = usesNode.getWhenCondition().get();
+        assertFalse(when.isAbsolute());
+        assertThat(when, instanceOf(WithExpression.class));
+        assertEquals("0!=1", when.getOriginalString());
+
+        final List<UnknownSchemaNode> unknownSchemaNodes = usesNode.getUnknownSchemaNodes();
+        assertEquals(1, unknownSchemaNodes.size());
+        final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.iterator().next();
+        assertEquals("argument", unknownSchemaNode.getNodeParameter());
+        assertEquals(QName.create("foo", "2016-06-02", "e"), unknownSchemaNode.getExtensionDefinition().getQName());
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5946Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5946Test.java
new file mode 100644 (file)
index 0000000..a062e3b
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.util.Collection;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.UniqueConstraint;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Relative;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class Bug5946Test {
+    private static final String NS = "foo";
+    private static final String REV = "2016-05-26";
+    private static final QName L1 = QName.create(NS, REV, "l1");
+    private static final QName L2 = QName.create(NS, REV, "l2");
+    private static final QName L3 = QName.create(NS, REV, "l3");
+    private static final QName C = QName.create(NS, REV, "c");
+    private static final QName WITHOUT_UNIQUE = QName.create(NS, REV, "without-unique");
+    private static final QName SIMPLE_UNIQUE = QName.create(NS, REV, "simple-unique");
+    private static final QName MULTIPLE_UNIQUE = QName.create(NS, REV, "multiple-unique");
+    private static final SchemaNodeIdentifier L1_ID = SchemaNodeIdentifier.create(false, L1);
+    private static final SchemaNodeIdentifier L2_ID = SchemaNodeIdentifier.create(false, L2);
+    private static final SchemaNodeIdentifier C_L3_ID = SchemaNodeIdentifier.create(false, C, L3);
+
+    @Test
+    public void test() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources(new File(getClass()
+                .getResource("/bugs/bug5946/foo.yang").toURI()));
+        assertNotNull(context);
+
+        Collection<UniqueConstraint> uniqueConstraints = getListConstraints(context, WITHOUT_UNIQUE);
+        assertNotNull(uniqueConstraints);
+        assertTrue(uniqueConstraints.isEmpty());
+
+        Collection<UniqueConstraint> simpleUniqueConstraints = getListConstraints(context, SIMPLE_UNIQUE);
+        assertNotNull(simpleUniqueConstraints);
+        assertEquals(1, simpleUniqueConstraints.size());
+        Collection<Relative> simpleUniqueConstraintTag = simpleUniqueConstraints.iterator().next().getTag();
+        assertTrue(simpleUniqueConstraintTag.contains(L1_ID));
+        assertTrue(simpleUniqueConstraintTag.contains(C_L3_ID));
+
+        Collection<UniqueConstraint> multipleUniqueConstraints = getListConstraints(context, MULTIPLE_UNIQUE);
+        assertNotNull(multipleUniqueConstraints);
+        assertEquals(3, multipleUniqueConstraints.size());
+        boolean l1l2 = false;
+        boolean l1cl3 = false;
+        boolean cl3l2 = false;
+        for (UniqueConstraint uniqueConstraint : multipleUniqueConstraints) {
+            Collection<Relative> uniqueConstraintTag = uniqueConstraint.getTag();
+            if (uniqueConstraintTag.contains(L1_ID) && uniqueConstraintTag.contains(L2_ID)) {
+                l1l2 = true;
+            } else if (uniqueConstraintTag.contains(L1_ID) && uniqueConstraintTag.contains(C_L3_ID)) {
+                l1cl3 = true;
+            } else if (uniqueConstraintTag.contains(C_L3_ID) && uniqueConstraintTag.contains(L2_ID)) {
+                cl3l2 = true;
+            }
+        }
+        assertTrue(l1l2 && l1cl3 && cl3l2);
+    }
+
+    @Test
+    public void testInvalid() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources(new File(getClass().getResource("/bugs/bug5946/foo-invalid.yang").toURI()));
+            fail("Should fail due to invalid argument of unique constraint");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                    "Unique statement argument '/simple-unique/l1' contains schema node identifier '/simple-unique/l1'"
+                            + " which is not in the descendant node identifier form."));
+        }
+    }
+
+    private static Collection<UniqueConstraint> getListConstraints(final SchemaContext context, final QName listQName) {
+        DataSchemaNode dataChildByName = context.getDataChildByName(listQName);
+        assertTrue(dataChildByName instanceof ListSchemaNode);
+        return ((ListSchemaNode) dataChildByName).getUniqueConstraints();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6131Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6131Test.java
new file mode 100644 (file)
index 0000000..3502ec6
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public class Bug6131Test {
+    @Test(expected = NullPointerException.class)
+    public void test() throws ReactorException, URISyntaxException, SourceException, IOException,
+            YangSyntaxErrorException {
+        StmtTestUtils.parseYangSources("/bugs/bug6131");
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6150Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6150Test.java
new file mode 100644 (file)
index 0000000..8d63dbc
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class Bug6150Test {
+
+    private static final StatementStreamSource TARGET = sourceForResource("/bugs/bug6150/target.yang");
+    private static final StatementStreamSource AUGMENT_FIRST = sourceForResource("/bugs/bug6150/aug-first.yang");
+    private static final StatementStreamSource AUGMENT_SECOND = sourceForResource("/bugs/bug6150/aug-second.yang");
+
+    @Test
+    public void effectiveAugmentFirstTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(TARGET, AUGMENT_FIRST)
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void effectiveAugmentSecondTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(TARGET, AUGMENT_SECOND)
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void effectiveAugmentBothTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(TARGET, AUGMENT_FIRST, AUGMENT_SECOND)
+                .buildEffective();
+        assertNotNull(result);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6180Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6180Test.java
new file mode 100644 (file)
index 0000000..c418579
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.regex.Pattern;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+
+public class Bug6180Test {
+
+    @Test
+    public void stringTest() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(new File(getClass().getResource(
+                "/bugs/bug6180/string-test.yang").toURI()));
+        assertNotNull(schemaContext);
+        assertEquals(1, schemaContext.getModules().size());
+        final Module module = schemaContext.getModules().iterator().next();
+        assertEquals(Optional.of("    1. this text contains \"string enclosed in double quotes\" and"
+                + " special characters: \\,\n,\t          2. this text contains \"string enclosed in double quotes\""
+                + " and special characters: \\,\n,\n,                     3. this text contains \"string enclosed in"
+                + " double quotes\" and special characters: \\,\n,\t      "), module.getDescription());
+    }
+
+    @Test
+    public void doubleQuotesTest() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(new File(getClass().getResource(
+                "/bugs/bug6180/double-quotes.yang").toURI()));
+        assertNotNull(schemaContext);
+        verifyDoubleQuotesExpression(schemaContext);
+    }
+
+    @Test
+    public void doubleQuotesSinbleInsideTest() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(new File(getClass().getResource(
+                "/bugs/bug6180/double-quotes-single-inside.yang").toURI()));
+        assertNotNull(schemaContext);
+        verifySingleQuotesExpression(schemaContext);
+    }
+
+    @Test
+    public void singleQuotesTest() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(new File(getClass().getResource(
+                "/bugs/bug6180/single-quotes.yang").toURI()));
+        assertNotNull(schemaContext);
+        verifyDoubleQuotesExpression(schemaContext);
+    }
+
+    private static void verifyDoubleQuotesExpression(final SchemaContext schemaContext) {
+        final DataSchemaNode dataNodeBar = schemaContext.getDataChildByName(QName.create("foo", "2016-07-11", "bar"));
+        assertTrue(dataNodeBar instanceof ContainerSchemaNode);
+        final ContainerSchemaNode bar = (ContainerSchemaNode) dataNodeBar;
+        final RevisionAwareXPath whenCondition = bar.getWhenCondition().get();
+        assertEquals("/foo != \"bar\"", whenCondition.getOriginalString());
+
+        final Set<TypeDefinition<?>> typeDefinitions = schemaContext.getTypeDefinitions();
+        assertEquals(1, typeDefinitions.size());
+        final TypeDefinition<?> type = typeDefinitions.iterator().next();
+        assertTrue(type instanceof StringTypeDefinition);
+        final List<PatternConstraint> patternConstraints = ((StringTypeDefinition) type).getPatternConstraints();
+        assertEquals(1, patternConstraints.size());
+        final PatternConstraint pattern = patternConstraints.iterator().next();
+        assertEquals("^(?:\".*\")$", pattern.getJavaPatternString());
+        assertTrue(Pattern.compile(pattern.getJavaPatternString()).matcher("\"enclosed string in quotes\"").matches());
+    }
+
+    private static void verifySingleQuotesExpression(final SchemaContext schemaContext) {
+        final DataSchemaNode dataNodeBar = schemaContext.getDataChildByName(QName.create("foo", "2016-07-11", "bar"));
+        assertTrue(dataNodeBar instanceof ContainerSchemaNode);
+        final ContainerSchemaNode bar = (ContainerSchemaNode) dataNodeBar;
+        final RevisionAwareXPath whenCondition = bar.getWhenCondition().get();
+        assertEquals("/foo != 'bar'", whenCondition.getOriginalString());
+
+        final Set<TypeDefinition<?>> typeDefinitions = schemaContext.getTypeDefinitions();
+        assertEquals(1, typeDefinitions.size());
+        final TypeDefinition<?> type = typeDefinitions.iterator().next();
+        assertTrue(type instanceof StringTypeDefinition);
+        final List<PatternConstraint> patternConstraints = ((StringTypeDefinition) type).getPatternConstraints();
+        assertEquals(1, patternConstraints.size());
+        final PatternConstraint pattern = patternConstraints.iterator().next();
+        assertEquals("^(?:'.*')$", pattern.getJavaPatternString());
+        assertTrue(Pattern.compile(pattern.getJavaPatternString()).matcher("'enclosed string in quotes'").matches());
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6183Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6183Test.java
new file mode 100644 (file)
index 0000000..dd1ded7
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug6183Test {
+    private static final String FOO_NS = "foo";
+
+    @Test
+    public void testYang10() throws Exception {
+        assertSchemaContext(StmtTestUtils.parseYangSources("/bugs/bug6183/yang10"));
+    }
+
+    @Test
+    public void testYang11() throws Exception {
+        assertSchemaContext(StmtTestUtils.parseYangSources("/bugs/bug6183/yang11"));
+    }
+
+    public void assertSchemaContext(final SchemaContext context) throws Exception {
+        assertNotNull(context);
+        assertEquals(3, context.getChildNodes().size());
+        assertEquals(1, context.getModules().size());
+        assertEquals(4, context.getModules().iterator().next().getAugmentations().size());
+
+        assertTrue(context.getDataChildByName(foo("before")) instanceof ContainerSchemaNode);
+        assertTrue(context.getDataChildByName(foo("after")) instanceof ContainerSchemaNode);
+
+        final DataSchemaNode dataChildByName = context.getDataChildByName(foo("my-choice"));
+        assertTrue(dataChildByName instanceof ChoiceSchemaNode);
+        final ChoiceSchemaNode myChoice = (ChoiceSchemaNode) dataChildByName;
+
+        assertEquals(4, myChoice.getCases().size());
+
+        final CaseSchemaNode implCase = myChoice.getCaseNodeByName(foo("implicit-case-container"));
+        assertNotNull(implCase);
+        final CaseSchemaNode declCaseOne = myChoice.getCaseNodeByName(foo("declared-case-one"));
+        assertNotNull(declCaseOne);
+        final CaseSchemaNode secondImplCase = myChoice.getCaseNodeByName(foo("second-implicit-case-container"));
+        assertNotNull(secondImplCase);
+        final CaseSchemaNode declCaseTwo = myChoice.getCaseNodeByName(foo("declared-case-two"));
+        assertNotNull(declCaseTwo);
+
+        assertEquals(1, declCaseOne.getChildNodes().size());
+        assertFalse(getLeafSchemaNode(declCaseOne, "leaf-in-declare-case-one").isAugmenting());
+        assertEquals(1, declCaseTwo.getChildNodes().size());
+        assertFalse(getLeafSchemaNode(declCaseTwo, "leaf-in-declare-case-two").isAugmenting());
+
+        assertEquals(2, implCase.getChildNodes().size());
+        assertTrue(getLeafSchemaNode(implCase, "leaf-after-container").isAugmenting());
+        final ContainerSchemaNode implCaseContainer = getContainerSchemaNode(implCase, "implicit-case-container");
+
+        assertEquals(3, implCaseContainer.getChildNodes().size());
+        assertTrue(getLeafSchemaNode(implCaseContainer, "leaf-inside-container").isAugmenting());
+        assertFalse(getLeafSchemaNode(implCaseContainer, "declared-leaf-in-case-container").isAugmenting());
+        final ContainerSchemaNode declContInCaseCont = getContainerSchemaNode(implCaseContainer,
+                "declared-container-in-case-container");
+
+        assertEquals(1, declContInCaseCont.getChildNodes().size());
+        assertFalse(getLeafSchemaNode(declContInCaseCont, "declared-leaf").isAugmenting());
+
+        assertEquals(2, secondImplCase.getChildNodes().size());
+        assertTrue(getLeafSchemaNode(secondImplCase, "leaf-after-second-container").isAugmenting());
+        final ContainerSchemaNode secondImplCaseContainer = getContainerSchemaNode(secondImplCase,
+                "second-implicit-case-container");
+
+        assertEquals(2, secondImplCaseContainer.getChildNodes().size());
+        assertTrue(getLeafSchemaNode(secondImplCaseContainer, "leaf-inside-second-container").isAugmenting());
+        assertFalse(getLeafSchemaNode(secondImplCaseContainer, "declared-leaf-in-second-case-container")
+            .isAugmenting());
+    }
+
+    private static ContainerSchemaNode getContainerSchemaNode(final DataNodeContainer parent,
+            final String containerName) {
+        final DataSchemaNode dataChildByName = parent.getDataChildByName(foo(containerName));
+        assertTrue(dataChildByName instanceof ContainerSchemaNode);
+        return (ContainerSchemaNode) dataChildByName;
+    }
+
+    private static LeafSchemaNode getLeafSchemaNode(final DataNodeContainer parent, final String leafName) {
+        final DataSchemaNode dataChildByName = parent.getDataChildByName(foo(leafName));
+        assertTrue(dataChildByName instanceof LeafSchemaNode);
+        return (LeafSchemaNode) dataChildByName;
+    }
+
+    private static QName foo(final String localName) {
+        return QName.create(FOO_NS, localName);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6240Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6240Test.java
new file mode 100644 (file)
index 0000000..df81429
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+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.parser.spi.meta.SomeModifiersUnresolvedException;
+
+public class Bug6240Test {
+    private static final String NS = "bar";
+    private static final String REV = "2016-07-19";
+
+    @Test
+    public void testModels() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6240/correct");
+        assertNotNull(context);
+
+        final Set<Module> modules = context.getModules();
+        assertEquals(2, modules.size());
+
+        Module bar = null;
+        for (final Module module : modules) {
+            if ("bar".equals(module.getName())) {
+                bar = module;
+                break;
+            }
+        }
+
+        assertNotNull(bar);
+        assertTrue(bar.getDataChildByName(QName.create(NS, REV, "foo-grp-con")) instanceof ContainerSchemaNode);
+        assertTrue(bar.getDataChildByName(QName.create(NS, REV, "sub-foo-grp-con")) instanceof ContainerSchemaNode);
+
+        assertEquals(1, bar.getSubmodules().size());
+
+        final DataSchemaNode dataChildByName = bar.getDataChildByName(QName.create(NS, REV, "sub-bar-con"));
+        assertTrue(dataChildByName instanceof ContainerSchemaNode);
+        final ContainerSchemaNode subBarCon = (ContainerSchemaNode) dataChildByName;
+
+        assertTrue(subBarCon.getDataChildByName(QName.create(NS, REV, "foo-grp-con")) instanceof ContainerSchemaNode);
+        assertTrue(subBarCon.getDataChildByName(QName.create(NS, REV, "sub-foo-grp-con"))
+            instanceof ContainerSchemaNode);
+    }
+
+    @Test
+    public void testInvalidModels() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/bugs/bug6240/incorrect");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getCause().getMessage().startsWith(
+                "Grouping '(bar?revision=2016-07-19)foo-imp-grp' was not resolved."));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6316Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6316Test.java
new file mode 100644 (file)
index 0000000..addab2d
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+
+public class Bug6316Test {
+    @Test
+    public void test() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6316");
+        assertNotNull(context);
+        verifyEnumTypedefinition(context);
+        verifyBitsTypedefinition(context);
+    }
+
+    private static void verifyEnumTypedefinition(final SchemaContext context) {
+        final DataSchemaNode dataChildByName = context.getDataChildByName(QName.create("foo", "enum-leaf"));
+        assertTrue(dataChildByName instanceof LeafSchemaNode);
+        final LeafSchemaNode enumLeaf = (LeafSchemaNode) dataChildByName;
+        final TypeDefinition<? extends TypeDefinition<?>> type = enumLeaf.getType();
+        assertTrue(type instanceof EnumTypeDefinition);
+        final EnumTypeDefinition myEnumeration = (EnumTypeDefinition) type;
+        final List<EnumPair> values = myEnumeration.getValues();
+        for (final EnumPair enumPair : values) {
+            final String name = enumPair.getName();
+            switch (name) {
+                case "zero":
+                    assertEquals(0, enumPair.getValue());
+                    break;
+                case "twenty":
+                    assertEquals(20, enumPair.getValue());
+                    break;
+                case "twenty-one":
+                    assertEquals(21, enumPair.getValue());
+                    break;
+                case "two":
+                    assertEquals(2, enumPair.getValue());
+                    break;
+                case "twenty-two":
+                    assertEquals(22, enumPair.getValue());
+                    break;
+                default:
+                    fail("Unexpected enum name.");
+            }
+        }
+    }
+
+    private static void verifyBitsTypedefinition(final SchemaContext context) {
+        final DataSchemaNode dataChildByName = context.getDataChildByName(QName.create("foo", "bits-leaf"));
+        assertTrue(dataChildByName instanceof LeafSchemaNode);
+        final LeafSchemaNode bitsLeaf = (LeafSchemaNode) dataChildByName;
+        final TypeDefinition<? extends TypeDefinition<?>> type = bitsLeaf.getType();
+        assertTrue(type instanceof BitsTypeDefinition);
+        final BitsTypeDefinition myBits = (BitsTypeDefinition) type;
+        final List<Bit> positions = myBits.getBits();
+        for (final Bit bit : positions) {
+            final String name = bit.getName();
+            switch (name) {
+                case "zero":
+                    assertEquals(0, bit.getPosition());
+                    break;
+                case "twenty":
+                    assertEquals(20, bit.getPosition());
+                    break;
+                case "twenty-one":
+                    assertEquals(21, bit.getPosition());
+                    break;
+                case "two":
+                    assertEquals(2, bit.getPosition());
+                    break;
+                case "twenty-two":
+                    assertEquals(22, bit.getPosition());
+                    break;
+                default:
+                    fail("Unexpected bit name.");
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6410Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6410Test.java
new file mode 100644 (file)
index 0000000..e9bce70
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public class Bug6410Test {
+
+    @Test
+    public void testTypedefsInRpc() throws ReactorException {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(sourceForResource("/bugs/bug6410/foo.yang"));
+
+        final Set<Module> modules = schemaContext.getModules();
+        assertEquals(1, modules.size());
+        final Module module = modules.iterator().next();
+
+        final Set<RpcDefinition> rpcs = module.getRpcs();
+        assertEquals(1, rpcs.size());
+        final RpcDefinition rpc = rpcs.iterator().next();
+
+        final Set<TypeDefinition<?>> typeDefs = rpc.getTypeDefinitions();
+        assertEquals(2, typeDefs.size());
+    }
+
+    @Test
+    public void shouldFailOnDuplicateTypedefs() {
+        try {
+            StmtTestUtils.parseYangSources(sourceForResource("/bugs/bug6410/bar.yang"));
+            fail("A ReactorException should have been thrown.");
+        } catch (ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().contains("Duplicate name for typedef"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6491Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6491Test.java
new file mode 100644 (file)
index 0000000..eec7984
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug6491Test {
+    private static final Revision DATE = Revision.of("2016-01-01");
+
+    @Test
+    public void tetststs() throws Exception {
+        testRevision("withoutRevision", null, Optional.empty());
+        testRevision("withRevision", DATE, Optional.of(DATE));
+        testRevision("importedModuleRevisionOnly", null, Optional.of(DATE));
+        testRevision("moduleRevisionOnly", DATE, Optional.empty());
+    }
+
+    private static void testRevision(final String path, final Revision moduleRevision,
+            final Optional<Revision> importedRevision) throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6491/".concat(path));
+        assertNotNull(context);
+        final Module module = context.findModule("bar", moduleRevision).get();
+        final Set<ModuleImport> imports = module.getImports();
+        assertNotNull(imports);
+        assertEquals(1, imports.size());
+        assertEquals(importedRevision, imports.iterator().next().getRevision());
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6669Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6669Test.java
new file mode 100644 (file)
index 0000000..2e520c2
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class Bug6669Test {
+    private static final String REV = "2016-09-08";
+    private static final String FOO_NS = "foo";
+    private static final String BAR_NS = "bar";
+    private static final QName ROOT = QName.create(FOO_NS, REV, "root");
+    private static final QName BAR = QName.create(BAR_NS, REV, "bar");
+    private static final QName BAR_1 = QName.create(BAR_NS, REV, "bar1");
+    private static final QName BAR_2 = QName.create(BAR_NS, REV, "bar2");
+    private static final QName M = QName.create(BAR_NS, REV, "m");
+    private static final QName L = QName.create(BAR_NS, REV, "l");
+
+    @Test
+    public void testInvalidAugment() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6669/invalid/test1");
+        assertNotNull(context);
+
+        final SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, ROOT, BAR, BAR_1, M));
+        assertNull(findDataSchemaNode);
+    }
+
+    @Test
+    public void testInvalidAugment2() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6669/invalid/test2");
+        assertNotNull(context);
+
+        final SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, ROOT, BAR, BAR_1, BAR_2, M));
+        assertNull(findDataSchemaNode);
+    }
+
+    @Test
+    public void testInvalidAugment3() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6669/invalid/test3");
+        assertNotNull(context);
+
+        final SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, ROOT, BAR, BAR_1, BAR_2, L));
+        assertNull(findDataSchemaNode);
+    }
+
+    @Test
+    public void testValidAugment() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6669/valid/test1");
+        assertNotNull(context);
+
+        final SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, ROOT, BAR, BAR_1, M));
+        assertTrue(findDataSchemaNode instanceof LeafSchemaNode);
+    }
+
+    @Test
+    public void testValidAugment2() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6669/valid/test2");
+        assertNotNull(context);
+
+        final SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, ROOT, BAR, BAR_1, BAR_2, M));
+        assertTrue(findDataSchemaNode instanceof LeafSchemaNode);
+    }
+
+    @Test
+    public void testValidAugment3() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6669/valid/test3");
+        assertNotNull(context);
+
+        final SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, ROOT, BAR, BAR_1, BAR_2, L));
+        assertTrue(findDataSchemaNode instanceof ListSchemaNode);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6771Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6771Test.java
new file mode 100644 (file)
index 0000000..432f5f5
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class Bug6771Test {
+    private static final String NS = "http://www.example.com/typedef-bug";
+    private static final QName ROOT = QName.create(NS, "root");
+    private static final QName CONT_B = QName.create(NS, "container-b");
+    private static final QName LEAF_CONT_B = QName.create(NS, "leaf-container-b");
+    private static final QName INNER_CONTAINER = QName.create(NS, "inner-container");
+
+    @Test
+    public void augmentTest() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6771/augment");
+        assertNotNull(context);
+
+        verifyLeafType(SchemaContextUtil
+                .findDataSchemaNode(context, SchemaPath.create(true, ROOT, CONT_B, LEAF_CONT_B)));
+        verifyLeafType(SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, ROOT, CONT_B, INNER_CONTAINER, LEAF_CONT_B)));
+    }
+
+    @Test
+    public void choiceCaseTest() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6771/choice-case");
+        assertNotNull(context);
+
+        final QName myChoice = QName.create(NS, "my-choice");
+        final QName caseOne = QName.create(NS, "one");
+        final QName caseTwo = QName.create(NS, "two");
+        final QName caseThree = QName.create(NS, "three");
+        final QName containerOne = QName.create(NS, "container-one");
+        final QName containerTwo = QName.create(NS, "container-two");
+        final QName containerThree = QName.create(NS, "container-three");
+
+        verifyLeafType(SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, ROOT, myChoice, caseOne, containerOne, LEAF_CONT_B)));
+        verifyLeafType(SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, ROOT, myChoice, caseTwo, containerTwo, LEAF_CONT_B)));
+        verifyLeafType(SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, ROOT, myChoice, caseThree, containerThree, INNER_CONTAINER, LEAF_CONT_B)));
+    }
+
+    @Test
+    public void groupingTest() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6771/grouping");
+        assertNotNull(context);
+        verifyLeafType(SchemaContextUtil
+                .findDataSchemaNode(context, SchemaPath.create(true, ROOT, CONT_B, LEAF_CONT_B)));
+    }
+
+    private static void verifyLeafType(final SchemaNode schemaNode) {
+        assertTrue(schemaNode instanceof LeafSchemaNode);
+        assertTrue(((LeafSchemaNode) schemaNode).getType() instanceof Uint32TypeDefinition);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6961Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6961Test.java
new file mode 100644 (file)
index 0000000..779711d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.model.util.SimpleSchemaContext;
+
+public class Bug6961Test {
+
+    @Test
+    public void testBug6961SchemaContext() throws Exception {
+        final Optional<Revision> revision = Revision.ofNullable("2016-01-01");
+        final SourceIdentifier foo = RevisionSourceIdentifier.create("foo", revision);
+        final SourceIdentifier sub1Foo = RevisionSourceIdentifier.create("sub1-foo", revision);
+        final SourceIdentifier sub2Foo = RevisionSourceIdentifier.create("sub2-foo", revision);
+        final SourceIdentifier bar = RevisionSourceIdentifier.create("bar", revision);
+        final SourceIdentifier sub1Bar = RevisionSourceIdentifier.create("sub1-bar", revision);
+        final SourceIdentifier baz = RevisionSourceIdentifier.create("baz", revision);
+        final Set<SourceIdentifier> testSet = ImmutableSet.of(foo, sub1Foo, sub2Foo, bar, sub1Bar, baz);
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug6961/");
+        assertNotNull(context);
+        final Set<SourceIdentifier> allModuleIdentifiers = SchemaContextUtil.getConstituentModuleIdentifiers(context);
+        assertNotNull(allModuleIdentifiers);
+        assertEquals(6, allModuleIdentifiers.size());
+        final SchemaContext schemaContext = SimpleSchemaContext.forModules(context.getModules());
+        assertNotNull(schemaContext);
+        final Set<SourceIdentifier> allModuleIdentifiersResolved = SchemaContextUtil.getConstituentModuleIdentifiers(
+            schemaContext);
+        assertNotNull(allModuleIdentifiersResolved);
+        assertEquals(6, allModuleIdentifiersResolved.size());
+        assertEquals(allModuleIdentifiersResolved, allModuleIdentifiers);
+        assertEquals(allModuleIdentifiers, testSet);
+        assertTrue(allModuleIdentifiers.contains(foo));
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6972Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug6972Test.java
new file mode 100644 (file)
index 0000000..6a445e9
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnitsEffectiveStatement;
+
+public class Bug6972Test {
+
+    @Ignore
+    @Test
+    public void allUnitsShouldBeTheSameInstance() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources("/bugs/bug6972");
+        assertNotNull(schemaContext);
+        assertEquals(3, schemaContext.getModules().size());
+
+        final Revision revision = Revision.of("2016-10-20");
+        final Module foo = schemaContext.findModule("foo", revision).get();
+        final Module bar = schemaContext.findModule("bar", revision).get();
+        final Module baz = schemaContext.findModule("baz", revision).get();
+
+        final QName barExportCont = QName.create("bar-ns", "bar-export", revision);
+        final QName barFooCont = QName.create("bar-ns", "bar-foo", revision);
+        final QName barFooLeaf = QName.create("bar-ns", "foo", revision);
+
+        final UnitsEffectiveStatement unitsBar1 = getEffectiveUnits(bar, barExportCont, barFooLeaf);
+        final UnitsEffectiveStatement unitsBar2 = getEffectiveUnits(bar, barFooCont, barFooLeaf);
+
+        final QName bazExportCont = QName.create("baz-ns", "baz-export", revision);
+        final QName bazFooCont = QName.create("baz-ns", "baz-foo", revision);
+        final QName bazFooLeaf = QName.create("baz-ns", "foo", revision);
+
+        final UnitsEffectiveStatement unitsBaz1 = getEffectiveUnits(baz, bazExportCont, bazFooLeaf);
+        final UnitsEffectiveStatement unitsBaz2 = getEffectiveUnits(baz, bazFooCont, bazFooLeaf);
+
+        assertTrue(unitsBar1 == unitsBar2 && unitsBar1 == unitsBaz1 && unitsBar1 == unitsBaz2);
+    }
+
+    private static UnitsEffectiveStatement getEffectiveUnits(final Module module, final QName containerQName,
+            final QName leafQName) {
+        UnitsEffectiveStatement units = null;
+
+        final ContainerSchemaNode cont = (ContainerSchemaNode) module.getDataChildByName(containerQName);
+        assertNotNull(cont);
+        final LeafSchemaNode leaf = (LeafSchemaNode) cont.getDataChildByName(leafQName);
+        assertNotNull(leaf);
+
+        for (EffectiveStatement<?, ?> effStmt : ((LeafEffectiveStatement) leaf).effectiveSubstatements()) {
+            if (effStmt instanceof UnitsEffectiveStatement) {
+                units = (UnitsEffectiveStatement) effStmt;
+                break;
+            }
+        }
+
+        return units;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7037Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7037Test.java
new file mode 100644 (file)
index 0000000..e041a04
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+public class Bug7037Test {
+    private static final String FOO_NS = "foo";
+    private static final String BAR_NS = "bar";
+
+    @Test
+    public void test() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug7037");
+        assertNotNull(context);
+
+        final List<UnknownSchemaNode> unknownSchemaNodes = context.getUnknownSchemaNodes();
+        assertEquals(1, unknownSchemaNodes.size());
+
+        final UnknownSchemaNode first = unknownSchemaNodes.iterator().next();
+        final List<UnknownSchemaNode> firstUnknownNodes = first.getUnknownSchemaNodes();
+        assertEquals(1, firstUnknownNodes.size());
+
+        final UnknownSchemaNode barExtCont = firstUnknownNodes.iterator().next();
+        assertEquals(bar("container"), barExtCont.getNodeType());
+        assertEquals(foo("bar-ext-con"), barExtCont.getQName());
+
+        final DataSchemaNode root = context.getDataChildByName(foo("root"));
+        assertTrue(root instanceof ContainerSchemaNode);
+
+        final List<UnknownSchemaNode> rootUnknownNodes = root.getUnknownSchemaNodes();
+        assertEquals(2, rootUnknownNodes.size());
+
+        final Map<QName, UnknownSchemaNode> rootUnknownNodeMap = rootUnknownNodes.stream()
+                .collect(Collectors.toMap(u -> u.getNodeType(), u -> u));
+
+        final UnknownSchemaNode barExt = rootUnknownNodeMap.get(bar("bar-ext"));
+        final List<UnknownSchemaNode> barExtUnknownNodes = barExt.getUnknownSchemaNodes();
+        assertEquals(3, barExtUnknownNodes.size());
+
+        final Iterator<UnknownSchemaNode> iterator = barExtUnknownNodes.iterator();
+        UnknownSchemaNode barExtCont2 = null;
+        while (iterator.hasNext()) {
+            final UnknownSchemaNode next = iterator.next();
+            if (bar("container").equals(next.getNodeType())) {
+                barExtCont2 = next;
+                break;
+            }
+        }
+        assertNotNull(barExtCont2);
+        assertEquals(foo("bar-ext-con-2"), barExtCont2.getQName());
+
+        final UnknownSchemaNode fooExt = rootUnknownNodeMap.get(foo("foo-ext"));
+        final List<UnknownSchemaNode> fooUnknownNodes = fooExt.getUnknownSchemaNodes();
+        assertEquals(1, fooUnknownNodes.size());
+
+        final UnknownSchemaNode fooExtCont = fooUnknownNodes.iterator().next();
+        assertEquals(foo("container"), fooExtCont.getNodeType());
+        assertEquals(foo("foo-ext-con"), fooExtCont.getQName());
+    }
+
+    private static QName foo(final String localName) {
+        return QName.create(FOO_NS, localName);
+    }
+
+    private static QName bar(final String localName) {
+        return QName.create(BAR_NS, localName);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7038Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7038Test.java
new file mode 100644 (file)
index 0000000..1143db4
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+
+public class Bug7038Test {
+    @Test
+    public void unknownNodeTest() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug7038");
+        assertNotNull(context);
+        assertEquals(1, context.getUnknownSchemaNodes().size());
+    }
+
+    @Test
+    public void testYang11() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug7038/yang11");
+        assertNotNull(context);
+    }
+
+    @Test
+    public void testYang10() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/bugs/bug7038/yang10");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith("REQUIRE_INSTANCE is not valid for TYPE"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7146Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7146Test.java
new file mode 100644 (file)
index 0000000..90935fd
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class Bug7146Test {
+
+    @Test
+    public void shouldFailOnSyntaxError() throws ReactorException {
+        try {
+            StmtTestUtils.parseYangSources(sourceForResource("/bugs/bug7146/foo.yang"));
+            fail("RuntimeException should have been thrown because of an unknown character in yang module.");
+        } catch (IllegalArgumentException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof YangSyntaxErrorException);
+            assertTrue(cause.getMessage().contains("extraneous input '#'"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7424Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7424Test.java
new file mode 100644 (file)
index 0000000..7dbc82c
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+
+public class Bug7424Test {
+    @Test
+    public void testRpc() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/bugs/bug7424/foo-rpc.yang");
+            fail("Test should fail due to invalid yang model.");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                "Error in module 'foo': cannot add '(foo)name'. Node name collision: '(foo)name' already declared"));
+        }
+    }
+
+    @Test
+    public void testNotification() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/bugs/bug7424/foo-notification.yang");
+            fail("Test should fail due to invalid yang model.");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                "Error in module 'foo': cannot add '(foo)name'. Node name collision: '(foo)name' already declared"));
+        }
+    }
+
+    @Test
+    public void testData() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/bugs/bug7424/foo-data.yang");
+            fail("Test should fail due to invalid yang model.");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                "Error in module 'foo': cannot add '(foo)name'. Node name collision: '(foo)name' already declared"));
+        }
+    }
+
+    @Test
+    public void testRpcUses() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/bugs/bug7424/foo-rpc-uses.yang");
+            fail("Test should fail due to invalid yang model.");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(e.getCause().getMessage().startsWith(
+                "Error in module 'foo': cannot add '(foo)name'. Node name collision: '(foo)name' already declared"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7440Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7440Test.java
new file mode 100644 (file)
index 0000000..e736b3f
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.DeviateDefinition;
+import org.opendaylight.yangtools.yang.model.api.Deviation;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+public class Bug7440Test {
+
+    @Test
+    public void testRestrictedTypeParentSchemaPathInDeviate() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources("/bugs/bug7440");
+        assertNotNull(schemaContext);
+
+        final Revision revision = Revision.of("2016-12-23");
+        final Module foo = schemaContext.findModule("foo", revision).get();
+        final Module bar = schemaContext.findModule("bar", revision).get();
+
+        final Set<Deviation> deviations = foo.getDeviations();
+        assertEquals(1, deviations.size());
+        final Deviation deviation = deviations.iterator().next();
+
+        final List<DeviateDefinition> deviates = deviation.getDeviates();
+        assertEquals(1, deviates.size());
+        final DeviateDefinition deviateReplace = deviates.iterator().next();
+
+        final SchemaPath deviatedTypePath = SchemaPath.create(true, QName.create(bar.getQNameModule(), "test-leaf"),
+                QName.create(bar.getQNameModule(), "uint32"));
+
+        final TypeDefinition<?> deviatedType = deviateReplace.getDeviatedType();
+        assertEquals(deviatedTypePath, deviatedType.getPath());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7480Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7480Test.java
new file mode 100644 (file)
index 0000000..dbb6ad5
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.net.URI;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+
+public class Bug7480Test {
+    @Test
+    public void libSourcesTest() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug7480/files", "/bugs/bug7480/lib");
+        assertNotNull(context);
+
+        final Set<Module> modules = context.getModules();
+        assertEquals(8, modules.size());
+
+        assertNotNull(context.findModule(new URI("foo-imp"), Revision.of("2017-01-23")));
+        assertEquals(1, context.findModules(new URI("foo-imp-2")).size());
+        assertEquals(1, context.findModules(new URI("foo-imp-imp")).size());
+        assertEquals(1, context.findModules(new URI("bar")).size());
+        assertEquals(1, context.findModules(new URI("baz")).size());
+        assertTrue(context.findModule(new URI("baz-imp"), Revision.of("2002-01-01")).isPresent());
+        final Set<Module> foo = context.findModules(new URI("foo"));
+        assertEquals(1, foo.size());
+        final Set<Module> subFoos = foo.iterator().next().getSubmodules();
+        assertEquals(1, subFoos.size());
+
+        final Module parentMod = context.findModule(new URI("parent-mod-ns"), Revision.of("2017-09-07")).get();
+        assertEquals(1, parentMod.getSubmodules().size());
+    }
+
+    @Test
+    public void missingRelevantImportTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/bugs/bug7480/files-2", "/bugs/bug7480/lib-2");
+            fail("Test should fail due to missing import of required yang source from library");
+        } catch (final SomeModifiersUnresolvedException e) {
+            final String message = e.getSuppressed().length > 0 ? e.getSuppressed()[0].getCause().getMessage() : e
+                    .getCause().getCause().getMessage();
+            assertTrue(message.startsWith("Imported module [missing-lib] was not found."));
+        }
+    }
+
+    @Test
+    public void testHandlingOfMainSourceConflictingWithLibSource() throws Exception {
+        // parent module as main source and as lib source at the same time
+        // parser should remove it from the required lib sources and thus avoid module namespace collision
+        final SchemaContext schemaContext =  RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(StmtTestUtils.sourceForResource(
+                        "/bugs/bug7480/main-source-lib-source-conflict-test/parent-module.yang"))
+                .addLibSources(
+                    StmtTestUtils.sourceForResource(
+                        "/bugs/bug7480/main-source-lib-source-conflict-test/child-module.yang"),
+                    StmtTestUtils.sourceForResource(
+                        "/bugs/bug7480/main-source-lib-source-conflict-test/parent-module.yang"))
+                .buildEffective();
+        assertNotNull(schemaContext);
+    }
+
+    @Test
+    public void testHandlingOfMainSourceConflictingWithLibSource2() throws Exception {
+        // submodule as main source and as lib source at the same time
+        // parser should remove it from the required lib sources and thus avoid submodule name collision
+        final SchemaContext schemaContext = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(StmtTestUtils.sourceForResource(
+                        "/bugs/bug7480/main-source-lib-source-conflict-test/child-module.yang"))
+                .addLibSources(
+                    StmtTestUtils.sourceForResource(
+                            "/bugs/bug7480/main-source-lib-source-conflict-test/parent-module.yang"),
+                    StmtTestUtils.sourceForResource(
+                            "/bugs/bug7480/main-source-lib-source-conflict-test/child-module.yang"))
+                .buildEffective();
+        assertNotNull(schemaContext);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7865Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7865Test.java
new file mode 100644 (file)
index 0000000..8298bb7
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+public class Bug7865Test {
+    private static final String NS = "foo";
+
+    @Test
+    public void test() throws Exception {
+        final SchemaContext context = TestUtils.parseYangSources("/bugs/bug7865");
+        assertNotNull(context);
+
+        final DataSchemaNode root = context.getDataChildByName(foo("root"));
+        assertTrue(root instanceof ContainerSchemaNode);
+        final List<UnknownSchemaNode> unknownSchemaNodes = root.getUnknownSchemaNodes();
+        assertEquals(1, unknownSchemaNodes.size());
+
+        final UnknownSchemaNode unknownNode = unknownSchemaNodes.iterator().next();
+        final List<UnknownSchemaNode> subUnknownSchemaNodes = unknownNode.getUnknownSchemaNodes();
+        assertEquals(1, subUnknownSchemaNodes.size());
+
+        final UnknownSchemaNode subUnknownNode = subUnknownSchemaNodes.iterator().next();
+        final List<UnknownSchemaNode> subSubUnknownSchemaNodes = subUnknownNode.getUnknownSchemaNodes();
+        assertEquals(1, subSubUnknownSchemaNodes.size());
+
+        final UnknownSchemaNode subSubUnknownNode = subSubUnknownSchemaNodes.iterator().next();
+        final SchemaPath expectedPath = SchemaPath.create(true, foo("root"), foo("p"), foo("p"), foo("p"));
+        assertEquals(expectedPath, subSubUnknownNode.getPath());
+    }
+
+    private static QName foo(final String localName) {
+        return QName.create(NS, localName);
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7879Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7879Test.java
new file mode 100644 (file)
index 0000000..687d085
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class Bug7879Test {
+    private static final String NS = "my-model-ns";
+
+    @Test
+    public void test() throws Exception {
+        final SchemaContext context = TestUtils.parseYangSources("/bugs/bug7879");
+        assertNotNull(context);
+
+        assertTrue(findNode(context, qN("my-alarm"), qN("my-content"), qN("my-event-container"))
+            instanceof ContainerSchemaNode);
+        final SchemaNode myEventValueLeaf = findNode(context, qN("my-alarm"), qN("my-content"), qN("my-event-value"));
+        assertTrue(myEventValueLeaf instanceof LeafSchemaNode);
+        assertEquals(Optional.of("new description"), myEventValueLeaf.getDescription());
+    }
+
+    private static SchemaNode findNode(final SchemaContext context, final QName... qnames) {
+        return SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true, qnames));
+    }
+
+    private static QName qN(final String localName) {
+        return QName.create(NS, localName);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7954Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug7954Test.java
new file mode 100644 (file)
index 0000000..5492924
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public class Bug7954Test {
+
+    @Test
+    public void testParsingTheSameModuleTwice() throws Exception {
+        final File yang = new File(getClass().getResource("/bugs/bug7954/foo.yang").toURI());
+
+        try {
+            StmtTestUtils.parseYangSources(yang, yang);
+            fail("An exception should have been thrown because of adding the same YANG module twice.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith("Module namespace collision: foo-ns."));
+        }
+    }
+
+    @Test
+    public void testParsingTheSameSubmoduleTwice() throws Exception {
+        final File yang = new File(getClass().getResource("/bugs/bug7954/bar.yang").toURI());
+        final File childYang = new File(getClass().getResource("/bugs/bug7954/subbar.yang").toURI());
+
+        try {
+            StmtTestUtils.parseYangSources(yang, childYang, childYang);
+            fail("An exception should have been thrown because of adding the same YANG submodule twice.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith("Submodule name collision: subbar."));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8126Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8126Test.java
new file mode 100644 (file)
index 0000000..f245320
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class Bug8126Test {
+    private static final String FOO_NS = "foo";
+    private static final String BAR_NS = "bar";
+
+    @Test
+    public void test() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug8126");
+        assertNotNull(context);
+        assertTrue(findNode(context, foo("root"), bar("my-container"), bar("my-choice"), bar("one"), bar("one"),
+            bar("mandatory-leaf")) instanceof LeafSchemaNode);
+        assertTrue(findNode(context, foo("root"), bar("my-list"), bar("two"), bar("mandatory-leaf-2"))
+            instanceof LeafSchemaNode);
+
+        assertNull(findNode(context, foo("root"), bar("mandatory-list")));
+        assertNull(findNode(context, foo("root"), bar("mandatory-container"), bar("mandatory-choice")));
+        assertNull(findNode(context, foo("root"), bar("mandatory-container-2"), bar("one"), bar("mandatory-leaf-3")));
+    }
+
+    private static SchemaNode findNode(final SchemaContext context, final QName... qnames) {
+        return SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true, qnames));
+    }
+
+    private static QName foo(final String localName) {
+        return QName.create(FOO_NS, localName);
+    }
+
+    private static QName bar(final String localName) {
+        return QName.create(BAR_NS, localName);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8307Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8307Test.java
new file mode 100644 (file)
index 0000000..29c8c21
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.SetMultimap;
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+
+public class Bug8307Test {
+
+    private static final StatementStreamSource FOO_MODULE = sourceForResource("/bugs/bug8307/foo.yang");
+    private static final StatementStreamSource BAR_MODULE = sourceForResource("/bugs/bug8307/bar.yang");
+    private static final StatementStreamSource BAZ_MODULE = sourceForResource("/bugs/bug8307/baz.yang");
+    private static final StatementStreamSource FOOBAR_MODULE = sourceForResource("/bugs/bug8307/foobar.yang");
+    private static final StatementStreamSource FOO_INVALID_MODULE = sourceForResource("/bugs/bug8307/foo-invalid.yang");
+    private static final StatementStreamSource BAR_INVALID_MODULE = sourceForResource("/bugs/bug8307/bar-invalid.yang");
+    private static final StatementStreamSource BAZ_INVALID_MODULE = sourceForResource("/bugs/bug8307/baz-invalid.yang");
+
+    private static final URI FOO_NS = URI.create("foo-ns");
+    private static final URI BAR_NS = URI.create("bar-ns");
+    private static final URI BAZ_NS = URI.create("baz-ns");
+
+    private static final Revision REVISION = Revision.of("2017-05-16");
+    private static final QNameModule FOO = QNameModule.create(FOO_NS, REVISION);
+    private static final QName MY_FOO_CONT_A = QName.create(FOO, "my-foo-cont-a");
+    private static final QName MY_FOO_CONT_B = QName.create(FOO, "my-foo-cont-b");
+    private static final QName MY_FOO_CONT_C = QName.create(FOO, "my-foo-cont-c");
+    private static final QNameModule BAR = QNameModule.create(BAR_NS, REVISION);
+    private static final QName MY_BAR_CONT_A = QName.create(BAR, "my-bar-cont-a");
+    private static final QName MY_BAR_CONT_B = QName.create(BAR, "my-bar-cont-b");
+    private static final QNameModule BAZ = QNameModule.create(BAZ_NS, REVISION);
+    private static final QName MY_BAZ_CONT = QName.create(BAZ, "my-baz-cont");
+
+    @Test
+    public void testDeviationsSupportedInSomeModules() throws Exception {
+        final SetMultimap<QNameModule, QNameModule> modulesWithSupportedDeviations =
+                ImmutableSetMultimap.<QNameModule, QNameModule>builder()
+                .put(FOO, BAR)
+                .put(FOO, BAZ)
+                .put(BAR, BAZ)
+                .build();
+
+        final SchemaContext schemaContext = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(FOO_MODULE, BAR_MODULE, BAZ_MODULE, FOOBAR_MODULE)
+                .setModulesWithSupportedDeviations(modulesWithSupportedDeviations)
+                .buildEffective();
+        assertNotNull(schemaContext);
+
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_A)));
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_B)));
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_C)));
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_BAR_CONT_A)));
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_BAR_CONT_B)));
+    }
+
+    @Test
+    public void testDeviationsSupportedInAllModules() throws Exception {
+        final SchemaContext schemaContext = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(FOO_MODULE, BAR_MODULE, BAZ_MODULE, FOOBAR_MODULE)
+                .buildEffective();
+        assertNotNull(schemaContext);
+
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_A)));
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_B)));
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_C)));
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_BAR_CONT_A)));
+        assertNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_BAR_CONT_B)));
+    }
+
+    @Test
+    public void testDeviationsSupportedInNoModule() throws Exception {
+        final SchemaContext schemaContext = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(FOO_MODULE, BAR_MODULE, BAZ_MODULE, FOOBAR_MODULE)
+                .setModulesWithSupportedDeviations(ImmutableSetMultimap.of())
+                .buildEffective();
+        assertNotNull(schemaContext);
+
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_A)));
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_B)));
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_FOO_CONT_C)));
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_BAR_CONT_A)));
+        assertNotNull(SchemaContextUtil.findDataSchemaNode(schemaContext, SchemaPath.create(true, MY_BAR_CONT_B)));
+    }
+
+    @Test
+    public void shouldFailOnAttemptToDeviateTheSameModule() {
+        final BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSources(FOO_INVALID_MODULE);
+
+        try {
+            reactor.buildEffective();
+            fail("Deviation that targets the same module as the one it is defined is forbidden.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InferenceException);
+            assertTrue(cause.getMessage().startsWith(
+                    "Deviation must not target the same module as the one it is defined in"));
+        }
+    }
+
+    @Test
+    public void shouldFailOnAttemptToDeviateTheSameModule2() {
+        final BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(BAR_INVALID_MODULE, BAZ_INVALID_MODULE);
+
+        try {
+            reactor.buildEffective();
+            fail("Deviation that targets the same module as the one it is defined is forbidden.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InferenceException);
+            assertTrue(cause.getMessage().startsWith(
+                    "Deviation must not target the same module as the one it is defined in"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8597Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8597Test.java
new file mode 100644 (file)
index 0000000..c3dd3c2
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug8597Test {
+    @Test
+    public void test() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug8597");
+        assertNotNull(context);
+
+        final Module foo = context.findModule("foo").get();
+        final Set<ModuleImport> imports = foo.getImports();
+
+        for (final ModuleImport moduleImport : imports) {
+            switch (moduleImport.getModuleName()) {
+                case "bar":
+                    assertEquals(Revision.ofNullable("1970-01-01"), moduleImport.getRevision());
+                    assertEquals(Optional.of("bar-ref"), moduleImport.getReference());
+                    assertEquals(Optional.of("bar-desc"), moduleImport.getDescription());
+                    break;
+                case "baz":
+                    assertEquals(Revision.ofNullable("2010-10-10"), moduleImport.getRevision());
+                    assertEquals(Optional.of("baz-ref"), moduleImport.getReference());
+                    assertEquals(Optional.of("baz-desc"), moduleImport.getDescription());
+                    break;
+                default:
+                    fail("Module 'foo' should only contains import of module 'bar' and 'baz'");
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8831Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8831Test.java
new file mode 100644 (file)
index 0000000..e770d96
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+
+public class Bug8831Test {
+    @Test
+    public void test() throws Exception {
+        final SchemaContext context = TestUtils.parseYangSources("/bugs/bug8831/valid");
+        assertNotNull(context);
+    }
+
+    @Test
+    public void invalidModelsTest() throws Exception {
+        try {
+            TestUtils.parseYangSource("/bugs/bug8831/invalid/inv-model.yang");
+            fail("Test should fails due to invalid yang 1.1 model");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(
+                    e.getCause().getMessage().contains("has default value 'any' marked with an if-feature statement"));
+        }
+    }
+
+    @Test
+    public void invalidModelsTest2() throws Exception {
+        try {
+            TestUtils.parseYangSource("/bugs/bug8831/invalid/inv-model2.yang");
+            fail("Test should fails due to invalid yang 1.1 model");
+        } catch (final SomeModifiersUnresolvedException e) {
+            assertTrue(
+                    e.getCause().getMessage().contains("has default value 'any' marked with an if-feature statement"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8922Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug8922Test.java
new file mode 100644 (file)
index 0000000..f6777e2
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class Bug8922Test {
+    private static final String NS = "foo";
+
+    @Test
+    public void testAllFeaturesSupported() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSource("/bugs/bug8922/foo.yang");
+        assertNotNull(context);
+        final SchemaNode findNode = findNode(context, qN("target"), qN("my-con"));
+        assertTrue(findNode instanceof ContainerSchemaNode);
+        assertEquals(Optional.of("New description"), findNode.getDescription());
+    }
+
+    @Test
+    public void testNoFeatureSupported() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSource("/bugs/bug8922/foo.yang", ImmutableSet.of());
+        assertNotNull(context);
+        final SchemaNode findNode = findNode(context, qN("target"), qN("my-con"));
+        assertNull(findNode);
+        assertTrue(context.getAvailableAugmentations().isEmpty());
+    }
+
+    private static SchemaNode findNode(final SchemaContext context, final QName... qnames) {
+        return SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true, qnames));
+    }
+
+    private static QName qN(final String localName) {
+        return QName.create(NS, localName);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug9005Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug9005Test.java
new file mode 100644 (file)
index 0000000..4031585
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug9005Test {
+    @Test
+    public void test() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug9005");
+        assertNotNull(context);
+
+        final Module foo = context.findModule("foo", Revision.of("2017-07-07")).get();
+
+        final Set<ModuleImport> imports = foo.getImports();
+        assertEquals(1, imports.size());
+        final ModuleImport imp1 = imports.iterator().next();
+        assertEquals("bar-2", imp1.getModuleName());
+        assertEquals("bar", imp1.getPrefix());
+        assertEquals(Revision.ofNullable("2000-01-02"), imp1.getRevision());
+
+        final Set<Module> submodules = foo.getSubmodules();
+        assertEquals(1, submodules.size());
+        final Module submodule = submodules.iterator().next();
+        final Set<ModuleImport> subImports = submodule.getImports();
+
+        assertEquals(1, subImports.size());
+        final ModuleImport subImp1 = subImports.iterator().next();
+        assertEquals("bar-1", subImp1.getModuleName());
+        assertEquals("bar", subImp1.getPrefix());
+        assertEquals(Revision.ofNullable("2000-01-01"), subImp1.getRevision());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug9242Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug9242Test.java
new file mode 100644 (file)
index 0000000..2c4a435
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.Deviation;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+public class Bug9242Test {
+
+    @Test
+    public void testDeviateReplaceWithUserDefinedTypes() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources("/bugs/bug9242/");
+        assertNotNull(schemaContext);
+
+        final Revision revision = Revision.of("2017-10-13");
+        final Module rootModule = schemaContext.findModule("root-module", revision).get();
+        final Module impModule = schemaContext.findModule("imp-module", revision).get();
+
+        TypeDefinition<?> deviatedMyLeafType = null;
+        TypeDefinition<?> deviatedMyLeaf2Type = null;
+
+        for (final Deviation deviation : rootModule.getDeviations()) {
+            if (deviation.getTargetPath().getLastComponent().equals(QName.create(
+                    impModule.getQNameModule(), "my-leaf"))) {
+                deviatedMyLeafType = deviation.getDeviates().iterator().next().getDeviatedType();
+            }
+
+            if (deviation.getTargetPath().getLastComponent().equals(QName.create(
+                    impModule.getQNameModule(), "my-leaf-2"))) {
+                deviatedMyLeaf2Type = deviation.getDeviates().iterator().next().getDeviatedType();
+            }
+        }
+
+        assertNotNull(deviatedMyLeafType);
+        assertNotNull(deviatedMyLeaf2Type);
+
+        final LeafSchemaNode myLeaf = (LeafSchemaNode) impModule.getDataChildByName(QName.create(
+                impModule.getQNameModule(), "my-leaf"));
+        assertSame(deviatedMyLeafType, myLeaf.getType());
+
+        final LeafSchemaNode myLeaf2 = (LeafSchemaNode) impModule.getDataChildByName(QName.create(
+                impModule.getQNameModule(), "my-leaf-2"));
+        assertSame(deviatedMyLeaf2Type, myLeaf2.getType());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug9244Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug9244Test.java
new file mode 100644 (file)
index 0000000..768183c
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class Bug9244Test {
+
+    @Test
+    public void testDeviateReplaceOfImplicitSubstatements() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources("/bugs/bug9244/");
+        assertNotNull(schemaContext);
+
+        final Module barModule = schemaContext.findModule("bar", Revision.of("2017-10-13")).get();
+        final ContainerSchemaNode barCont = (ContainerSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "bar-cont"));
+        assertNotNull(barCont);
+        assertFalse(barCont.isConfiguration());
+
+        final LeafListSchemaNode barLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "bar-leaf-list"));
+        assertNotNull(barLeafList);
+        final ElementCountConstraint constraint = barLeafList.getElementCountConstraint().get();
+        assertEquals(5, constraint.getMinElements().intValue());
+        assertEquals(10, constraint.getMaxElements().intValue());
+
+        final LeafSchemaNode barLeaf = (LeafSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "bar-leaf"));
+        assertNotNull(barLeaf);
+        assertTrue(barLeaf.isMandatory());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/CaseStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/CaseStmtTest.java
new file mode 100644 (file)
index 0000000..bea2781
--- /dev/null
@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+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.parser.spi.meta.ReactorException;
+
+public class CaseStmtTest {
+    private SchemaContext schema;
+    private Module rootFoo;
+    private Module rootBar;
+    private QNameModule qnameFoo;
+    private QNameModule qnameBar;
+    private DataSchemaNode tempChild;
+    private DataSchemaNode tempSecondChild;
+    private DataSchemaNode tempThirdChild;
+    private CaseSchemaNode tempChoice;
+
+    @Rule
+    public ExpectedException expectedEx = ExpectedException.none();
+
+    @Before
+    public void setup() throws Exception {
+        schema = StmtTestUtils.parseYangSources("/case-test");
+        Revision rev = Revision.of("2015-09-09");
+        rootFoo = schema.findModule("foo", rev).get();
+        rootBar = schema.findModule("bar", rev).get();
+        assertNotNull(rootFoo);
+        assertNotNull(rootBar);
+        qnameFoo = QNameModule.create(URI.create("foo"), rev);
+        qnameBar = QNameModule.create(URI.create("bar"), rev);
+        assertNotNull(qnameFoo);
+        assertNotNull(qnameBar);
+    }
+
+    @Test
+    public void caseTest() {
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-fff"));
+        assertNotNull(tempChild);
+        assertFalse(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-ffn"));
+        assertNotNull(tempChild);
+        assertFalse(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-fnf"));
+        assertNotNull(tempChild);
+        assertFalse(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-nff"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-nnf"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-nfn"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-fnn"));
+        assertNotNull(tempChild);
+        assertFalse(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-ttt"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-ttn"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-tnt"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-ntt"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-nnt"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-ntn"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-tnn"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-tff"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-tnf"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-tfn"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootFoo.getDataChildByName(QName.create(qnameFoo, "root-ntf"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+    }
+
+    @Test
+    public void shortCaseTest() {
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-fff"));
+        assertNotNull(tempChild);
+        assertFalse(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-ffn"));
+        assertNotNull(tempChild);
+        assertFalse(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-fnf"));
+        assertNotNull(tempChild);
+        assertFalse(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-nff"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-nnf"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-nfn"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-fnn"));
+        assertNotNull(tempChild);
+        assertFalse(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-ttt"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-ttn"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-tnt"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-ntt"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-nnt"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-ntn"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-tnn"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertTrue(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertTrue(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-tff"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-tnf"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-tfn"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertFalse(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+
+        tempChild = rootBar.getDataChildByName(QName.create(qnameBar, "sh-root-ntf"));
+        assertNotNull(tempChild);
+        assertTrue(tempChild.isConfiguration());
+        tempSecondChild = ((ContainerSchemaNode) tempChild).getChildNodes().iterator().next();
+        assertNotNull(tempSecondChild);
+        assertTrue(tempSecondChild.isConfiguration());
+        tempChoice = ((ChoiceSchemaNode) tempSecondChild).getCases().values().iterator().next();
+        assertNotNull(tempChoice);
+        assertFalse(tempChoice.isConfiguration());
+        tempThirdChild = tempChoice.getChildNodes().iterator().next();
+        assertNotNull(tempThirdChild);
+        assertFalse(tempThirdChild.isConfiguration());
+    }
+
+    @Test
+    public void testInferenceExceptionChoice() throws Exception {
+        expectedEx.expect(ReactorException.class);
+        schema = StmtTestUtils.parseYangSources("/case-test/case-test-exceptions/choice");
+    }
+
+    @Test
+    public void testInferenceExceptionCase() throws Exception {
+        expectedEx.expect(ReactorException.class);
+        schema = StmtTestUtils.parseYangSources("/case-test/case-test-exceptions/case");
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ChoiceStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ChoiceStmtTest.java
new file mode 100644 (file)
index 0000000..23d504c
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class ChoiceStmtTest {
+
+    private static final StatementStreamSource CHOICE_MODULE = sourceForResource("/model/foo.yang");
+    private static final StatementStreamSource IMPORTED_MODULE1 = sourceForResource("/model/bar.yang");
+    private static final StatementStreamSource IMPORTED_MODULE2 = sourceForResource("/model/baz.yang");
+    private static final StatementStreamSource INCLUDED_MODULE = sourceForResource("/model/subfoo.yang");
+
+    @Test
+    public void choiceAndCaseTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(CHOICE_MODULE, IMPORTED_MODULE1, IMPORTED_MODULE2, INCLUDED_MODULE)
+                .buildEffective();
+        assertNotNull(result);
+
+        final Module testModule = result.findModules("foo").iterator().next();
+        assertNotNull(testModule);
+
+        final ContainerSchemaNode container = (ContainerSchemaNode) testModule.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "transfer"));
+        assertNotNull(container);
+
+        final ChoiceSchemaNode choice = (ChoiceSchemaNode) container.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "how"));
+        assertNotNull(choice);
+        assertEquals(5, choice.getCases().size());
+
+        CaseSchemaNode caseNode = choice.findCaseNodes("input").iterator().next();
+        assertNotNull(caseNode);
+        caseNode = choice.findCaseNodes("output").iterator().next();
+        assertNotNull(caseNode);
+        caseNode = choice.findCaseNodes("interval").iterator().next();
+        assertNotNull(caseNode);
+        caseNode = choice.findCaseNodes("daily").iterator().next();
+        assertNotNull(caseNode);
+        caseNode = choice.findCaseNodes("manual").iterator().next();
+        assertNotNull(caseNode);
+        assertEquals("interval", choice.getDefaultCase().get().getQName().getLocalName());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ControllerStmtParserTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ControllerStmtParserTest.java
new file mode 100644 (file)
index 0000000..1aff5ba
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+public class ControllerStmtParserTest {
+
+    @Test
+    public void test() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/sal-broker-impl");
+        assertNotNull(context);
+
+        salDomBrokerImplModuleTest(context);
+        configModuleTest(context);
+    }
+
+    private static void salDomBrokerImplModuleTest(final SchemaContext context) {
+        final Module module = context.findModule("opendaylight-sal-dom-broker-impl", Revision.of("2013-10-28")).get();
+
+        final Set<AugmentationSchemaNode> augmentations = module.getAugmentations();
+        boolean checked = false;
+        for (final AugmentationSchemaNode augmentationSchema : augmentations) {
+            final DataSchemaNode dataNode = augmentationSchema
+                    .getDataChildByName(QName.create(module.getQNameModule(), "dom-broker-impl"));
+            if (dataNode instanceof CaseSchemaNode) {
+                final CaseSchemaNode caseNode = (CaseSchemaNode) dataNode;
+                final DataSchemaNode dataNode2 = caseNode
+                        .getDataChildByName(QName.create(module.getQNameModule(), "async-data-broker"));
+                if (dataNode2 instanceof ContainerSchemaNode) {
+                    final ContainerSchemaNode containerNode = (ContainerSchemaNode) dataNode2;
+                    final DataSchemaNode leaf = containerNode
+                            .getDataChildByName(QName.create(module.getQNameModule(), "type"));
+                    final List<UnknownSchemaNode> unknownSchemaNodes = leaf.getUnknownSchemaNodes();
+                    assertEquals(1, unknownSchemaNodes.size());
+
+                    final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.get(0);
+                    assertEquals("dom-async-data-broker", unknownSchemaNode.getQName().getLocalName());
+                    assertEquals(unknownSchemaNode.getQName(), unknownSchemaNode.getPath().getLastComponent());
+
+                    checked = true;
+                }
+            }
+        }
+        assertTrue(checked);
+    }
+
+    private static void configModuleTest(final SchemaContext context) {
+        final Module configModule = context.findModule("config", Revision.of("2013-04-05")).get();
+        final Module module = context.findModule("opendaylight-sal-dom-broker-impl", Revision.of("2013-10-28")).get();
+
+        final DataSchemaNode dataNode = configModule.getDataChildByName(QName.create(configModule.getQNameModule(),
+            "modules"));
+        assertTrue(dataNode instanceof ContainerSchemaNode);
+
+        final ContainerSchemaNode moduleContainer = (ContainerSchemaNode) dataNode;
+        final DataSchemaNode dataChildList = moduleContainer
+                .getDataChildByName(QName.create(configModule.getQNameModule(), "module"));
+
+        assertTrue(dataChildList instanceof ListSchemaNode);
+
+        final ListSchemaNode listModule = (ListSchemaNode) dataChildList;
+        final DataSchemaNode dataChildChoice = listModule
+                .getDataChildByName(QName.create(configModule.getQNameModule(), "configuration"));
+
+        assertTrue(dataChildChoice instanceof ChoiceSchemaNode);
+
+        final ChoiceSchemaNode confChoice = (ChoiceSchemaNode) dataChildChoice;
+        final CaseSchemaNode caseNodeByName = confChoice.findCaseNodes("dom-broker-impl").iterator().next();
+
+        assertNotNull(caseNodeByName);
+        final DataSchemaNode dataNode2 = caseNodeByName
+                .getDataChildByName(QName.create(module.getQNameModule(), "async-data-broker"));
+        assertTrue(dataNode2 instanceof ContainerSchemaNode);
+
+        final ContainerSchemaNode containerNode = (ContainerSchemaNode) dataNode2;
+        final DataSchemaNode leaf = containerNode.getDataChildByName(QName.create(module.getQNameModule(), "type"));
+        final List<UnknownSchemaNode> unknownSchemaNodes = leaf.getUnknownSchemaNodes();
+
+        assertEquals(1, unknownSchemaNodes.size());
+
+        final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.get(0);
+
+        assertEquals(unknownSchemaNode.getQName(), unknownSchemaNode.getPath().getLastComponent());
+        assertEquals("dom-async-data-broker", unknownSchemaNode.getQName().getLocalName());
+
+        final CaseSchemaNode domInmemoryDataBroker = confChoice.findCaseNodes("dom-inmemory-data-broker").iterator()
+                .next();
+
+        assertNotNull(domInmemoryDataBroker);
+        final DataSchemaNode schemaService = domInmemoryDataBroker
+                .getDataChildByName(QName.create(module.getQNameModule(), "schema-service"));
+        assertTrue(schemaService instanceof ContainerSchemaNode);
+
+        final ContainerSchemaNode schemaServiceContainer = (ContainerSchemaNode) schemaService;
+
+        assertEquals(1, schemaServiceContainer.getUses().size());
+        final UsesNode uses = schemaServiceContainer.getUses().iterator().next();
+        final QName groupingQName = QName.create("urn:opendaylight:params:xml:ns:yang:controller:config", "2013-04-05",
+            "service-ref");
+        final QName usesGroupingPathLastComponent = uses.getGroupingPath().getLastComponent();
+        assertEquals(groupingQName, usesGroupingPathLastComponent);
+        assertEquals(0, getChildNodeSizeWithoutUses(schemaServiceContainer));
+
+        final DataSchemaNode type = schemaServiceContainer.getDataChildByName(QName.create(module.getQNameModule(),
+            "type"));
+        final List<UnknownSchemaNode> typeUnknownSchemaNodes = type.getUnknownSchemaNodes();
+        assertEquals(1, typeUnknownSchemaNodes.size());
+
+        final UnknownSchemaNode typeUnknownSchemaNode = typeUnknownSchemaNodes.get(0);
+        final QNameModule qNameModule = QNameModule.create(
+            URI.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom"), Revision.of("2013-10-28"));
+        final QName qName = QName.create(qNameModule, "schema-service");
+
+        assertEquals(qName, typeUnknownSchemaNode.getQName());
+        assertEquals(typeUnknownSchemaNode.getQName(), typeUnknownSchemaNode
+                .getPath().getLastComponent());
+    }
+
+    private static int getChildNodeSizeWithoutUses(final DataNodeContainer csn) {
+        int result = 0;
+        for (final DataSchemaNode dsn : csn.getChildNodes()) {
+            if (!dsn.isAddedByUses()) {
+                result++;
+            }
+        }
+        return result;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/DeclaredStatementsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/DeclaredStatementsTest.java
new file mode 100644 (file)
index 0000000..03545c4
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.Set;
+import java.util.function.Predicate;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ArgumentStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AugmentEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.CaseStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DataDefinitionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DefaultStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.GroupingStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ImportStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IncludeStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MustStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PresenceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ReferenceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.StatusStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.WhenStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.YangVersionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class DeclaredStatementsTest {
+
+    @Test
+    public void testDeclaredAnyXml() throws ReactorException {
+        final StatementStreamSource anyxmlStmtModule =
+                sourceForResource("/declared-statements-test/anyxml-declared-test.yang");
+
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(anyxmlStmtModule);
+        assertNotNull(schemaContext);
+
+        final Module testModule = schemaContext.findModules("anyxml-declared-test").iterator().next();
+        assertNotNull(testModule);
+
+        final AnyxmlSchemaNode anyxmlSchemaNode = (AnyxmlSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "foobar"));
+        assertNotNull(anyxmlSchemaNode);
+        final AnyxmlStatement anyxmlStatement = ((AnyxmlEffectiveStatement) anyxmlSchemaNode).getDeclared();
+
+        final QName name = anyxmlStatement.getName();
+        assertNotNull(name);
+
+        final WhenStatement whenStatement = anyxmlStatement.getWhenStatement().get();
+        final RevisionAwareXPath whenRevisionAwareXPath = whenStatement.getCondition();
+        assertNotNull(whenRevisionAwareXPath);
+        final DescriptionStatement whenStatementDescription = whenStatement.getDescription().get();
+        assertTrue(whenStatement.getReference().isPresent());
+
+        final Collection<? extends IfFeatureStatement> ifFeatureStatements = anyxmlStatement.getIfFeatures();
+        assertNotNull(ifFeatureStatements);
+        assertEquals(1, ifFeatureStatements.size());
+        final Predicate<Set<QName>> ifFeaturePredicate = ifFeatureStatements.iterator().next().getIfFeaturePredicate();
+        assertNotNull(ifFeaturePredicate);
+
+        final Collection<? extends MustStatement> mustStatements = anyxmlStatement.getMustStatements();
+        assertNotNull(mustStatements);
+        assertEquals(1, mustStatements.size());
+        final MustStatement mustStatement = mustStatements.iterator().next();
+        final RevisionAwareXPath mustRevisionAwareXPath = mustStatement.getCondition();
+        assertNotNull(mustRevisionAwareXPath);
+        assertTrue(mustStatement.getErrorAppTagStatement().isPresent());
+        assertTrue(mustStatement.getErrorMessageStatement().isPresent());
+        assertTrue(mustStatement.getDescription().isPresent());
+        assertTrue(mustStatement.getReference().isPresent());
+
+        final ConfigStatement configStatement = anyxmlStatement.getConfig().get();
+        assertFalse(configStatement.getValue());
+
+        final StatusStatement statusStatement = anyxmlStatement.getStatus().get();
+        final Status status = statusStatement.getValue();
+        assertNotNull(status);
+
+        final DescriptionStatement descriptionStatement = anyxmlStatement.getDescription().get();
+        assertEquals("anyxml description", descriptionStatement.getText());
+
+        final ReferenceStatement referenceStatement = anyxmlStatement.getReference().get();
+        assertEquals("anyxml reference", referenceStatement.getText());
+
+        assertTrue(anyxmlStatement.getMandatory().isPresent());
+    }
+
+    @Test
+    public void testDeclaredChoice() throws ReactorException {
+        final StatementStreamSource choiceStmtModule =
+                sourceForResource("/declared-statements-test/choice-declared-test.yang");
+
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(choiceStmtModule);
+        assertNotNull(schemaContext);
+
+        final Module testModule = schemaContext.findModules("choice-declared-test").iterator().next();
+        assertNotNull(testModule);
+
+        final ChoiceSchemaNode choiceSchemaNode = (ChoiceSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-choice"));
+        assertNotNull(choiceSchemaNode);
+        final ChoiceStatement choiceStatement = ((ChoiceEffectiveStatement) choiceSchemaNode).getDeclared();
+
+        final QName name = choiceStatement.getName();
+        assertNotNull(name);
+
+        final DefaultStatement defaultStatement = choiceStatement.getDefault().get();
+        assertEquals("case-two", defaultStatement.getValue());
+
+        assertTrue(choiceStatement.getConfig().isPresent());
+        assertTrue(choiceStatement.getMandatory().isPresent());
+
+        final Collection<? extends CaseStatement> caseStatements = choiceStatement.getCases();
+        assertNotNull(caseStatements);
+        assertEquals(3, caseStatements.size());
+        final CaseStatement caseStatement = caseStatements.iterator().next();
+        final QName caseStatementName = caseStatement.getName();
+        assertNotNull(caseStatementName);
+        final WhenStatement caseStatementWhen = caseStatement.getWhenStatement().get();
+        final Collection<? extends IfFeatureStatement> caseStatementIfFeatures = caseStatement.getIfFeatures();
+        assertNotNull(caseStatementIfFeatures);
+        assertEquals(1, caseStatementIfFeatures.size());
+        final Collection<? extends DataDefinitionStatement> caseStatementDataDefinitions =
+                caseStatement.getDataDefinitions();
+        assertNotNull(caseStatementDataDefinitions);
+        assertEquals(1, caseStatementDataDefinitions.size());
+        assertTrue(caseStatement.getStatus().isPresent());
+        assertTrue(caseStatement.getDescription().isPresent());
+        assertTrue(caseStatement.getReference().isPresent());
+
+        final WhenStatement whenStatement = choiceStatement.getWhenStatement().get();
+
+        final Collection<? extends IfFeatureStatement> ifFeatureStatements = choiceStatement.getIfFeatures();
+        assertNotNull(ifFeatureStatements);
+        assertEquals(1, ifFeatureStatements.size());
+
+        assertTrue(choiceStatement.getStatus().isPresent());
+        assertTrue(choiceStatement.getDescription().isPresent());
+        assertTrue(choiceStatement.getReference().isPresent());
+    }
+
+    @Test
+    public void testDeclaredAugment() throws ReactorException {
+        final StatementStreamSource augmentStmtModule =
+                sourceForResource("/declared-statements-test/augment-declared-test.yang");
+
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(augmentStmtModule);
+        assertNotNull(schemaContext);
+
+        final Module testModule = schemaContext.findModules("augment-declared-test").iterator().next();
+        assertNotNull(testModule);
+
+        final Set<AugmentationSchemaNode> augmentationSchemas = testModule.getAugmentations();
+        assertNotNull(augmentationSchemas);
+        assertEquals(1, augmentationSchemas.size());
+
+        final AugmentationSchemaNode augmentationSchema = augmentationSchemas.iterator().next();
+        final AugmentStatement augmentStatement = ((AugmentEffectiveStatement) augmentationSchema).getDeclared();
+
+        final SchemaNodeIdentifier targetNode = augmentStatement.getTargetNode();
+        assertNotNull(targetNode);
+
+        final Collection<? extends DataDefinitionStatement> augmentStatementDataDefinitions =
+                augmentStatement.getDataDefinitions();
+        assertNotNull(augmentStatementDataDefinitions);
+        assertEquals(1, augmentStatementDataDefinitions.size());
+    }
+
+    @Test
+    public void testDeclaredModuleAndSubmodule() throws ReactorException {
+        final StatementStreamSource parentModule =
+                sourceForResource("/declared-statements-test/parent-module-declared-test.yang");
+
+        final StatementStreamSource childModule =
+                sourceForResource("/declared-statements-test/child-module-declared-test.yang");
+
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(parentModule, childModule);
+        assertNotNull(schemaContext);
+
+        final Module testModule = schemaContext.findModules("parent-module-declared-test").iterator().next();
+        assertNotNull(testModule);
+
+        final ModuleStatement moduleStatement = ((ModuleEffectiveStatement) testModule).getDeclared();
+
+        final String moduleStatementName = moduleStatement.getName();
+        assertNotNull(moduleStatementName);
+
+        final YangVersionStatement moduleStatementYangVersion = moduleStatement.getYangVersion();
+        assertNotNull(moduleStatementYangVersion);
+        assertNotNull(moduleStatementYangVersion.getValue());
+
+        final NamespaceStatement moduleStatementNamspace = moduleStatement.getNamespace();
+        assertNotNull(moduleStatementNamspace);
+        assertNotNull(moduleStatementNamspace.getUri());
+
+        final PrefixStatement moduleStatementPrefix = moduleStatement.getPrefix();
+        assertNotNull(moduleStatementPrefix);
+        assertNotNull(moduleStatementPrefix.getValue());
+
+        assertEquals(1, moduleStatement.getIncludes().size());
+        final IncludeStatement includeStatement = moduleStatement.getIncludes().iterator().next();
+        assertEquals("child-module-declared-test", includeStatement.getModule());
+
+        final Set<Module> submodules = testModule.getSubmodules();
+        assertNotNull(submodules);
+        assertEquals(1, submodules.size());
+
+        final Module submodule = submodules.iterator().next();
+        final SubmoduleStatement submoduleStatement = ((SubmoduleEffectiveStatement) submodule).getDeclared();
+
+        final String submoduleStatementName = submoduleStatement.getName();
+        assertNotNull(submoduleStatementName);
+
+        final YangVersionStatement submoduleStatementYangVersion = submoduleStatement.getYangVersion();
+        assertNotNull(submoduleStatementYangVersion);
+
+        final BelongsToStatement belongsToStatement = submoduleStatement.getBelongsTo();
+        assertNotNull(belongsToStatement);
+        assertNotNull(belongsToStatement.getModule());
+        assertNotNull(belongsToStatement.getPrefix());
+    }
+
+    @Test
+    public void testDeclaredModule() throws ReactorException, ParseException {
+        final StatementStreamSource rootModule =
+                sourceForResource("/declared-statements-test/root-module-declared-test.yang");
+
+        final StatementStreamSource importedModule =
+                sourceForResource("/declared-statements-test/imported-module-declared-test.yang");
+
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(rootModule, importedModule);
+        assertNotNull(schemaContext);
+
+        final Revision revision = Revision.of("2016-09-28");
+        final Module testModule = schemaContext.findModule("root-module-declared-test", revision).get();
+        assertNotNull(testModule);
+
+        final ModuleStatement moduleStatement = ((ModuleEffectiveStatement) testModule).getDeclared();
+
+        assertEquals(1, moduleStatement.getImports().size());
+        final ImportStatement importStatement = moduleStatement.getImports().iterator().next();
+        assertEquals("imported-module-declared-test", importStatement.getModule());
+        assertEquals("imdt", importStatement.getPrefix().getValue());
+        assertEquals(revision, importStatement.getRevisionDate().getDate());
+
+        assertEquals("test description", moduleStatement.getDescription().get().getText());
+        assertEquals("test reference", moduleStatement.getReference().get().getText());
+        assertEquals("test organization", moduleStatement.getOrganization().get().getText());
+        assertEquals("test contact", moduleStatement.getContact().get().getText());
+
+        assertEquals(1, moduleStatement.getRevisions().size());
+        final RevisionStatement revisionStatement = moduleStatement.getRevisions().iterator().next();
+        assertEquals(revision, revisionStatement.getDate());
+        assertEquals("test description", revisionStatement.getDescription().get().getText());
+        assertEquals("test reference", revisionStatement.getReference().get().getText());
+
+        assertEquals(1, moduleStatement.getExtensions().size());
+        final ExtensionStatement extensionStatement = moduleStatement.getExtensions().iterator().next();
+        assertEquals(Status.CURRENT, extensionStatement.getStatus().get().getValue());
+        assertEquals("test description", extensionStatement.getDescription().get().getText());
+        assertEquals("test reference", extensionStatement.getReference().get().getText());
+        final ArgumentStatement argumentStatement = extensionStatement.getArgument();
+        assertEquals("ext-argument", argumentStatement.getName().getLocalName());
+        assertTrue(argumentStatement.getYinElement().getValue());
+
+        assertEquals(2, moduleStatement.getFeatures().size());
+        final FeatureStatement featureStatement = moduleStatement.getFeatures().iterator().next();
+        assertEquals(Status.CURRENT, featureStatement.getStatus().get().getValue());
+        assertEquals("test description", featureStatement.getDescription().get().getText());
+        assertEquals("test reference", featureStatement.getReference().get().getText());
+        assertEquals("test-feature", featureStatement.getName().getLocalName());
+        assertEquals(1, featureStatement.getIfFeatures().size());
+
+        assertEquals(2, moduleStatement.getIdentities().size());
+        IdentityStatement identityStatement = null;
+        for (final IdentityStatement identity : moduleStatement.getIdentities()) {
+            if (identity.getName().getLocalName().equals("test-id")) {
+                identityStatement = identity;
+            }
+        }
+
+        assertEquals("test-base-id", identityStatement.getBases().iterator().next().getName().getLocalName());
+        assertEquals(Status.CURRENT, identityStatement.getStatus().get().getValue());
+        assertEquals("test description", identityStatement.getDescription().get().getText());
+        assertEquals("test reference", identityStatement.getReference().get().getText());
+        assertEquals("test-id", identityStatement.getName().getLocalName());
+
+        assertEquals(1, moduleStatement.getTypedefs().size());
+        final TypedefStatement typedefStatement = moduleStatement.getTypedefs().iterator().next();
+        assertEquals(Status.CURRENT, typedefStatement.getStatus().get().getValue());
+        assertEquals("test description", typedefStatement.getDescription().get().getText());
+        assertEquals("test reference", typedefStatement.getReference().get().getText());
+        assertEquals("test-typedef", typedefStatement.getName().getLocalName());
+        assertEquals("int32", typedefStatement.getType().getName());
+        assertEquals("meter", typedefStatement.getUnits().get().getName());
+    }
+
+    @Test
+    public void testDeclaredContainer() throws ReactorException {
+        final StatementStreamSource containerStmtModule =
+                sourceForResource("/declared-statements-test/container-declared-test.yang");
+
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(containerStmtModule);
+        assertNotNull(schemaContext);
+
+        final Module testModule = schemaContext.findModules("container-declared-test").iterator().next();
+        assertNotNull(testModule);
+
+        final ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container"));
+        assertNotNull(containerSchemaNode);
+        final ContainerStatement containerStatement =
+                ((ContainerEffectiveStatement) containerSchemaNode).getDeclared();
+
+        final QName name = containerStatement.getName();
+        assertNotNull(name);
+
+        final WhenStatement containerStatementWhen = containerStatement.getWhenStatement().get();
+
+        final Collection<? extends IfFeatureStatement> containerStatementIfFeatures =
+                containerStatement.getIfFeatures();
+        assertNotNull(containerStatementIfFeatures);
+        assertEquals(1, containerStatementIfFeatures.size());
+
+        final Collection<? extends MustStatement> containerStatementMusts = containerStatement.getMustStatements();
+        assertNotNull(containerStatementMusts);
+        assertEquals(1, containerStatementMusts.size());
+
+        final PresenceStatement containerStatementPresence = containerStatement.getPresence();
+        assertNotNull(containerStatementPresence);
+        assertNotNull(containerStatementPresence.getValue());
+
+        assertTrue(containerStatement.getConfig().isPresent());
+        assertTrue(containerStatement.getStatus().isPresent());
+        assertTrue(containerStatement.getDescription().isPresent());
+        assertTrue(containerStatement.getReference().isPresent());
+
+        final Collection<? extends TypedefStatement> containerStatementTypedefs = containerStatement.getTypedefs();
+        assertNotNull(containerStatementTypedefs);
+        assertEquals(1, containerStatementTypedefs.size());
+
+        final Collection<? extends GroupingStatement> containerStatementGroupings = containerStatement.getGroupings();
+        assertNotNull(containerStatementGroupings);
+        assertEquals(1, containerStatementGroupings.size());
+
+        final Collection<? extends DataDefinitionStatement> containerStatementDataDefinitions =
+                containerStatement.getDataDefinitions();
+
+        assertNotNull(containerStatementDataDefinitions);
+        assertEquals(1, containerStatementDataDefinitions.size());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/DeviationResolutionTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/DeviationResolutionTest.java
new file mode 100644 (file)
index 0000000..c97e81d
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+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.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InvalidSubstatementException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class DeviationResolutionTest {
+
+    @Test
+    public void testDeviateNotSupported() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(
+                "/deviation-resolution-test/deviation-not-supported");
+        assertNotNull(schemaContext);
+
+        final Module importedModule = schemaContext.findModule("imported", Revision.of("2017-01-20")).get();
+        final ContainerSchemaNode myContA = (ContainerSchemaNode) importedModule.getDataChildByName(
+                QName.create(importedModule.getQNameModule(), "my-cont-a"));
+        assertNotNull(myContA);
+
+        assertEquals(1, myContA.getChildNodes().size());
+        assertNotNull(myContA.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-a3")));
+
+        final ContainerSchemaNode myContB = (ContainerSchemaNode) importedModule.getDataChildByName(
+                QName.create(importedModule.getQNameModule(), "my-cont-b"));
+        assertNull(myContB);
+
+        final ContainerSchemaNode myContC = (ContainerSchemaNode) importedModule.getDataChildByName(
+                QName.create(importedModule.getQNameModule(), "my-cont-c"));
+        assertNotNull(myContC);
+
+        assertEquals(2, myContC.getChildNodes().size());
+        assertNotNull(myContC.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-c1")));
+        assertNotNull(myContC.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-c2")));
+    }
+
+    @Test
+    public void testDeviateAdd() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(
+                sourceForResource("/deviation-resolution-test/deviation-add/foo.yang"),
+                sourceForResource("/deviation-resolution-test/deviation-add/bar.yang"));
+        assertNotNull(schemaContext);
+
+        final Module barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).get();
+        final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-leaf-list"));
+        assertNotNull(myLeafList);
+
+        assertFalse(myLeafList.isConfiguration());
+        assertEquals(3, myLeafList.getDefaults().size());
+
+        final ElementCountConstraint constraint = myLeafList.getElementCountConstraint().get();
+        assertEquals(10, constraint.getMaxElements().intValue());
+        assertEquals(5, constraint.getMinElements().intValue());
+        assertNotNull(myLeafList.getType().getUnits());
+
+        final ListSchemaNode myList = (ListSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-list"));
+        assertNotNull(myList);
+        assertEquals(2, myList.getUniqueConstraints().size());
+
+        final ChoiceSchemaNode myChoice = (ChoiceSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-choice"));
+        assertNotNull(myChoice);
+        assertEquals("c2", myChoice.getDefaultCase().get().getQName().getLocalName());
+
+        final RpcDefinition myRpc = barModule.getRpcs().iterator().next();
+        final ContainerSchemaNode input = myRpc.getInput();
+        assertEquals(2, input.getMustConstraints().size());
+        final ContainerSchemaNode output = myRpc.getOutput();
+        assertEquals(2, output.getMustConstraints().size());
+
+        final NotificationDefinition myNotification = barModule.getNotifications().iterator().next();
+        assertEquals(2, myNotification.getMustConstraints().size());
+
+        final AnyxmlSchemaNode myAnyxml = (AnyxmlSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-anyxml"));
+        assertNotNull(myAnyxml);
+        assertTrue(myAnyxml.isMandatory());
+        assertEquals(2, myAnyxml.getUnknownSchemaNodes().size());
+
+        final AnydataSchemaNode myAnyData = (AnydataSchemaNode) barModule.findDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-anydata")).orElse(null);
+        assertNotNull(myAnyData);
+        assertTrue(myAnyData.isMandatory());
+        assertEquals(2, myAnyData.getUnknownSchemaNodes().size());
+    }
+
+    @Test
+    public void testDeviateReplace() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(
+                sourceForResource("/deviation-resolution-test/deviation-replace/foo.yang"),
+                sourceForResource("/deviation-resolution-test/deviation-replace/bar.yang"));
+        assertNotNull(schemaContext);
+
+        final Module barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).get();
+        assertNotNull(barModule);
+
+        final LeafSchemaNode myLeaf = (LeafSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-leaf"));
+        assertNotNull(myLeaf);
+
+        assertTrue(myLeaf.getType() instanceof Uint32TypeDefinition);
+        assertEquals(Optional.of("bytes"), myLeaf.getType().getUnits());
+        assertEquals(Optional.of("10"), myLeaf.getType().getDefaultValue());
+
+        final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-leaf-list-test"));
+        assertNotNull(myLeafList);
+
+        final ElementCountConstraint constraint = myLeafList.getElementCountConstraint().get();
+        assertEquals(6, constraint.getMaxElements().intValue());
+        assertEquals(3, constraint.getMinElements().intValue());
+        assertTrue(myLeafList.isConfiguration());
+
+        final ChoiceSchemaNode myChoice = (ChoiceSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-choice"));
+        assertNotNull(myChoice);
+
+        assertFalse(myChoice.isMandatory());
+        assertEquals(1, myChoice.getUnknownSchemaNodes().size());
+        assertEquals("new arg", myChoice.getUnknownSchemaNodes().iterator().next().getNodeParameter());
+
+        final ContainerSchemaNode myCont = (ContainerSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-cont"));
+        assertNotNull(myCont);
+
+        final LeafSchemaNode myAugLeaf = (LeafSchemaNode) myCont.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-aug-leaf"));
+        assertNotNull(myAugLeaf);
+        assertTrue(myAugLeaf.getType() instanceof Uint32TypeDefinition);
+        assertEquals(Optional.of("seconds"), myAugLeaf.getType().getUnits());
+        assertEquals(Optional.of("new-def-val"), myAugLeaf.getType().getDefaultValue());
+        assertEquals(1, myAugLeaf.getUnknownSchemaNodes().size());
+        assertEquals("new arg", myAugLeaf.getUnknownSchemaNodes().iterator().next().getNodeParameter());
+
+        final LeafSchemaNode myUsedLeaf = (LeafSchemaNode) myCont.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-used-leaf"));
+        assertNotNull(myUsedLeaf);
+        assertTrue(myUsedLeaf.getType() instanceof Uint32TypeDefinition);
+        assertEquals(Optional.of("weeks"), myUsedLeaf.getType().getUnits());
+        assertEquals(Optional.of("new-def-val"), myUsedLeaf.getType().getDefaultValue());
+        assertEquals(1, myUsedLeaf.getUnknownSchemaNodes().size());
+        assertEquals("new arg", myUsedLeaf.getUnknownSchemaNodes().iterator().next().getNodeParameter());
+    }
+
+    @Test
+    public void testDeviateDelete() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSources(
+                sourceForResource("/deviation-resolution-test/deviation-delete/foo.yang"),
+                sourceForResource("/deviation-resolution-test/deviation-delete/bar.yang"));
+        assertNotNull(schemaContext);
+
+        final Module barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).get();
+        final LeafSchemaNode myLeaf = (LeafSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-leaf"));
+        assertNotNull(myLeaf);
+
+        assertEquals(Optional.empty(), myLeaf.getType().getDefaultValue());
+        assertEquals(Optional.empty(), myLeaf.getType().getUnits());
+        assertEquals(0, myLeaf.getUnknownSchemaNodes().size());
+
+        final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-leaf-list"));
+        assertNotNull(myLeafList);
+
+        assertEquals(0, myLeafList.getDefaults().size());
+        assertEquals(0, myLeafList.getMustConstraints().size());
+
+        final ListSchemaNode myList = (ListSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-list"));
+        assertNotNull(myList);
+
+        assertEquals(0, myList.getUniqueConstraints().size());
+        assertEquals(0, myList.getUnknownSchemaNodes().size());
+
+        final ContainerSchemaNode myCont = (ContainerSchemaNode) barModule.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-cont"));
+        assertNotNull(myCont);
+
+        final LeafSchemaNode myAugLeaf = (LeafSchemaNode) myCont.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-aug-leaf"));
+        assertNotNull(myAugLeaf);
+        assertEquals(Optional.empty(), myAugLeaf.getType().getDefaultValue());
+        assertEquals(Optional.empty(), myAugLeaf.getType().getUnits());
+        assertEquals(0, myAugLeaf.getMustConstraints().size());
+        assertEquals(0, myAugLeaf.getUnknownSchemaNodes().size());
+
+        final LeafSchemaNode myUsedLeaf = (LeafSchemaNode) myCont.getDataChildByName(
+                QName.create(barModule.getQNameModule(), "my-used-leaf"));
+        assertNotNull(myUsedLeaf);
+        assertEquals(Optional.empty(), myUsedLeaf.getType().getDefaultValue());
+        assertEquals(Optional.empty(), myUsedLeaf.getType().getUnits());
+        assertEquals(0, myUsedLeaf.getMustConstraints().size());
+        assertEquals(0, myUsedLeaf.getUnknownSchemaNodes().size());
+    }
+
+    @Test
+    public void shouldFailOnInvalidYang10Model() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources(
+                    sourceForResource("/deviation-resolution-test/deviation-add/foo10-invalid.yang"),
+                    sourceForResource("/deviation-resolution-test/deviation-add/bar10-invalid.yang"));
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidSubstatementException);
+            assertTrue(cause.getMessage().startsWith("Maximal count of DEFAULT for DEVIATE is 1, detected 2."));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidYang10Model2() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources(
+                    sourceForResource("/deviation-resolution-test/deviation-delete/foo10-invalid.yang"),
+                    sourceForResource("/deviation-resolution-test/deviation-delete/bar10-invalid.yang"));
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidSubstatementException);
+            assertTrue(cause.getMessage().startsWith("Maximal count of DEFAULT for DEVIATE is 1, detected 2."));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidDeviationTarget() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources(sourceForResource(
+                    "/deviation-resolution-test/foo-invalid-deviation-target.yang"),
+                    sourceForResource("/deviation-resolution-test/bar.yang"));
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InferenceException);
+            assertTrue(cause.getMessage().startsWith("(bar?revision=2017-01-20)my-cont is not a valid deviation "
+                    + "target for substatement (urn:ietf:params:xml:ns:yang:yin:1)max-elements."));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidDeviationPath() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources(
+                    sourceForResource("/deviation-resolution-test/foo-invalid-deviation-path.yang"),
+                    sourceForResource("/deviation-resolution-test/bar.yang"));
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause().getCause();
+            assertTrue(cause instanceof InferenceException);
+            assertTrue(cause.getMessage().startsWith("Deviation target 'Absolute{path=[(bar?revision=2017-01-20)"
+                    + "invalid, (bar?revision=2017-01-20)path]}' not found"));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidDeviateAdd() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources(
+                    sourceForResource("/deviation-resolution-test/deviation-add/foo-invalid.yang"),
+                    sourceForResource("/deviation-resolution-test/deviation-add/bar-invalid.yang"));
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InferenceException);
+            assertTrue(cause.getMessage().startsWith("Deviation cannot add substatement (urn:ietf:params:xml:ns:yang"
+                    + ":yin:1)config to target node (bar?revision=2017-01-20)my-leaf because it is already defined in"
+                    + " target and can appear only once."));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidDeviateAdd2() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources(
+                    sourceForResource("/deviation-resolution-test/deviation-add/foo-invalid-2.yang"),
+                    sourceForResource("/deviation-resolution-test/deviation-add/bar-invalid-2.yang"));
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InferenceException);
+            assertTrue(cause.getMessage().startsWith("Deviation cannot add substatement (urn:ietf:params:xml:ns:yang"
+                    + ":yin:1)default to target node (bar?revision=2017-01-20)my-leaf because it is already defined in"
+                    + " target and can appear only once."));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidDeviateAdd3() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources(
+                    sourceForResource("/deviation-resolution-test/deviation-add/foo-invalid-4.yang"),
+                    sourceForResource("/deviation-resolution-test/deviation-add/bar-invalid-4.yang"));
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InferenceException);
+            assertTrue(cause.getMessage().startsWith("Deviation cannot add substatement (urn:ietf:params:xml:ns:yang"
+                    + ":yin:1)default to target node (bar?revision=2017-02-01)my-used-leaf because it is already "
+                    + "defined in target and can appear only once."));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidDeviateReplace() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources(
+                    sourceForResource("/deviation-resolution-test/deviation-replace/foo-invalid.yang"),
+                    sourceForResource("/deviation-resolution-test/deviation-replace/bar-invalid.yang"));
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InferenceException);
+            assertTrue(cause.getMessage().startsWith("Deviation cannot replace substatement "
+                    + "(urn:ietf:params:xml:ns:yang:yin:1)units in target node (bar?revision=2017-01-20)my-leaf "
+                    + "because it does not exist in target node."));
+        }
+    }
+
+    @Test
+    @SuppressWarnings("checkstyle:regexpSinglelineJava")
+    public void shouldLogInvalidDeviateReplaceAttempt() throws Exception {
+        final PrintStream stdout = System.out;
+        final ByteArrayOutputStream output = new ByteArrayOutputStream();
+        final String testLog;
+
+        System.setOut(new PrintStream(output, true, "UTF-8"));
+
+        StmtTestUtils.parseYangSources(
+                sourceForResource("/deviation-resolution-test/deviation-replace/foo-invalid-2.yang"),
+                sourceForResource("/deviation-resolution-test/deviation-replace/bar-invalid-2.yang"));
+
+        testLog = output.toString();
+        System.setOut(stdout);
+        assertTrue(testLog.contains("Deviation cannot replace substatement (urn:ietf:params:xml:ns:yang:yin:1)default"
+                + " in target leaf-list (bar?revision=2017-01-20)my-leaf-list because a leaf-list can have multiple "
+                + "default statements."));
+    }
+
+    @Test
+    @SuppressWarnings("checkstyle:regexpSinglelineJava")
+    public void shouldLogInvalidDeviateDeleteAttempt() throws Exception {
+        final PrintStream stdout = System.out;
+        final ByteArrayOutputStream output = new ByteArrayOutputStream();
+        final String testLog;
+
+        System.setOut(new PrintStream(output, true, "UTF-8"));
+
+        StmtTestUtils.parseYangSources(
+                sourceForResource("/deviation-resolution-test/deviation-delete/foo-invalid.yang"),
+                sourceForResource("/deviation-resolution-test/deviation-delete/bar-invalid.yang"));
+
+        testLog = output.toString();
+        System.setOut(stdout);
+        assertTrue(testLog.contains("Deviation cannot delete substatement (urn:ietf:params:xml:ns:yang:yin:1)units "
+                + "with argument 'seconds' in target node (bar?revision=2017-01-20)my-leaf because it does not exist "
+                + "in the target node."));
+    }
+
+    @Test
+    public void shouldFailOnInvalidDeviateAddSubstatement() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources(
+                    sourceForResource("/deviation-resolution-test/deviation-add/foo-invalid-3.yang"),
+                    sourceForResource("/deviation-resolution-test/deviation-add/bar-invalid-3.yang"));
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidSubstatementException);
+            assertTrue(cause.getMessage().startsWith("TYPE is not valid for DEVIATE."));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidDeviateReplaceSubstatement() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources(
+                    sourceForResource("/deviation-resolution-test/deviation-replace/foo-invalid-3.yang"),
+                    sourceForResource("/deviation-resolution-test/deviation-replace/bar-invalid-3.yang"));
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidSubstatementException);
+            assertTrue(cause.getMessage().startsWith("MUST is not valid for DEVIATE."));
+        }
+    }
+
+    @Test
+    public void shouldFailOnInvalidDeviateDeleteSubstatement() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources(
+                    sourceForResource("/deviation-resolution-test/deviation-delete/foo-invalid-2.yang"),
+                    sourceForResource("/deviation-resolution-test/deviation-delete/bar-invalid-2.yang"));
+            fail("An exception should have been thrown.");
+        } catch (final ReactorException ex) {
+            final Throwable cause = ex.getCause();
+            assertTrue(cause instanceof InvalidSubstatementException);
+            assertTrue(cause.getMessage().startsWith("CONFIG is not valid for DEVIATE."));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/DeviationStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/DeviationStmtTest.java
new file mode 100644 (file)
index 0000000..283b015
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.DeviateDefinition;
+import org.opendaylight.yangtools.yang.model.api.DeviateKind;
+import org.opendaylight.yangtools.yang.model.api.Deviation;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class DeviationStmtTest {
+
+    private static final StatementStreamSource FOO_MODULE = sourceForResource("/deviation-stmt-test/foo.yang");
+    private static final StatementStreamSource FOO_IMP_MODULE = sourceForResource("/deviation-stmt-test/foo-imp.yang");
+    private static final StatementStreamSource BAR_MODULE = sourceForResource("/deviation-stmt-test/bar.yang");
+    private static final StatementStreamSource BAR_IMP_MODULE = sourceForResource("/deviation-stmt-test/bar-imp.yang");
+
+    @Test
+    public void testDeviationAndDeviate() throws ReactorException {
+        final SchemaContext schemaContext = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(FOO_MODULE, FOO_IMP_MODULE, BAR_MODULE, BAR_IMP_MODULE)
+                .buildEffective();
+        assertNotNull(schemaContext);
+
+        Module testModule = schemaContext.findModule("foo", Revision.of("2016-06-23")).get();
+        Set<Deviation> deviations = testModule.getDeviations();
+        assertEquals(4, deviations.size());
+
+        for (Deviation deviation : deviations) {
+            final List<DeviateDefinition> deviates = deviation.getDeviates();
+            final String targetLocalName = deviation.getTargetPath().getLastComponent().getLocalName();
+            if ("test-leaf".equals(targetLocalName)) {
+                assertEquals(Optional.of("test-leaf is not supported"), deviation.getDescription());
+                assertEquals(1, deviates.size());
+                assertEquals(DeviateKind.NOT_SUPPORTED, deviates.iterator().next().getDeviateType());
+            } else if ("test-leaf-2".equals(targetLocalName)) {
+                assertEquals(1, deviates.size());
+                assertEquals(DeviateKind.ADD, deviates.iterator().next().getDeviateType());
+                assertEquals("added-def-val", deviates.iterator().next().getDeviatedDefault());
+                assertFalse(deviates.iterator().next().getDeviatedConfig());
+                assertTrue(deviates.iterator().next().getDeviatedMandatory());
+            } else if ("test-leaf-list".equals(targetLocalName)) {
+                assertEquals(3, deviates.size());
+                for (DeviateDefinition deviate : deviates) {
+                    if (DeviateKind.ADD.equals(deviate.getDeviateType())) {
+                        assertEquals(12, deviate.getDeviatedMaxElements().intValue());
+                    } else if (DeviateKind.REPLACE.equals(deviate.getDeviateType())) {
+                        assertEquals(5, deviate.getDeviatedMinElements().intValue());
+                        assertTrue(deviate.getDeviatedType() instanceof Uint32TypeDefinition);
+                    } else {
+                        assertEquals(2, deviate.getDeviatedMusts().size());
+                        assertEquals("minutes", deviate.getDeviatedUnits());
+                    }
+                }
+            } else {
+                assertEquals(1, deviation.getDeviates().size());
+                assertEquals(DeviateKind.DELETE, deviates.iterator().next().getDeviateType());
+                assertEquals(2, deviates.iterator().next().getDeviatedUniques().size());
+            }
+        }
+
+        testModule = schemaContext.findModule("bar", Revision.of("2016-09-22")).get();
+        assertNotNull(testModule);
+
+        deviations = testModule.getDeviations();
+        assertEquals(7, deviations.size());
+
+        Deviation deviation1 = null;
+        Deviation deviation2 = null;
+        Deviation deviation3 = null;
+        Deviation deviation4 = null;
+        Deviation deviation5 = null;
+        Deviation deviation6 = null;
+        Deviation deviation7 = null;
+
+        for (Deviation deviation : deviations) {
+            final List<DeviateDefinition> deviates = deviation.getDeviates();
+            final String targetLocalName = deviation.getTargetPath().getLastComponent().getLocalName();
+
+            if ("bar-container-1".equals(targetLocalName)) {
+                deviation1 = deviation;
+            }
+
+            if ("bar-container-2".equals(targetLocalName)) {
+                DeviateKind deviateKind = deviates.iterator().next().getDeviateType();
+                if (deviateKind.equals(DeviateKind.DELETE)) {
+                    deviation2 = deviation;
+                } else if (deviateKind.equals(DeviateKind.ADD)) {
+                    deviation3 = deviation;
+                }
+            }
+
+            if ("bar-leaf-1".equals(targetLocalName)) {
+                if (Optional.of("desc").equals(deviation.getDescription())) {
+                    deviation4 = deviation;
+                } else {
+                    deviation5 = deviation;
+                }
+            }
+
+            if ("bar-leaf-2".equals(targetLocalName)) {
+                if (Optional.of("ref").equals(deviation.getReference())) {
+                    deviation6 = deviation;
+                } else {
+                    deviation7 = deviation;
+                }
+            }
+        }
+
+        assertEquals(1, deviation1.getUnknownSchemaNodes().size());
+        assertTrue(deviation1.equals(deviation1));
+        assertFalse(deviation1.equals(null));
+        assertFalse(deviation1.equals("str"));
+
+        DeviateDefinition deviate = deviation1.getDeviates().iterator().next();
+        assertTrue(deviate.equals(deviate));
+        assertFalse(deviate.equals(null));
+        assertFalse(deviate.equals("str"));
+
+        assertFalse(deviation1.equals(deviation2));
+        assertFalse(deviation2.equals(deviation3));
+        assertFalse(deviation4.equals(deviation5));
+        assertFalse(deviation6.equals(deviation7));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveBuildTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveBuildTest.java
new file mode 100644 (file)
index 0000000..a0b208b
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.io.FileNotFoundException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class EffectiveBuildTest {
+
+    private static final StatementStreamSource SIMPLE_MODULE = sourceForResource(
+            "/stmt-test/effective-build/simple-module.yang");
+    private static final QNameModule SIMPLE_MODULE_QNAME = QNameModule.create(URI.create("simple.yang"));
+    private static final StatementStreamSource YANG_EXT = sourceForResource(
+            "/stmt-test/extensions/yang-ext.yang");
+
+    @Test
+    public void effectiveBuildTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild().addSources(SIMPLE_MODULE)
+                .buildEffective();
+
+        assertNotNull(result);
+
+        Module simpleModule = result.findModules("simple-module").iterator().next();
+        assertNotNull(simpleModule);
+
+        final QName q1 = QName.create(SIMPLE_MODULE_QNAME, "root-container");
+        final QName q2 = QName.create(SIMPLE_MODULE_QNAME, "sub-container");
+        final QName q3 = QName.create(SIMPLE_MODULE_QNAME, "sub-sub-container");
+        final QName q4 = QName.create(SIMPLE_MODULE_QNAME, "root-container2");
+        final QName q5 = QName.create(SIMPLE_MODULE_QNAME, "sub-container2");
+        final QName q6 = QName.create(SIMPLE_MODULE_QNAME, "sub-sub-container2");
+        final QName q7 = QName.create(SIMPLE_MODULE_QNAME, "grp");
+
+        ContainerSchemaNode rootCon = (ContainerSchemaNode) simpleModule.getDataChildByName(q1);
+        assertNotNull(rootCon);
+
+        ContainerSchemaNode subCon = (ContainerSchemaNode) rootCon.getDataChildByName(q2);
+        assertNotNull(subCon);
+
+        ContainerSchemaNode subSubCon = (ContainerSchemaNode) subCon.getDataChildByName(q3);
+        assertNotNull(subSubCon);
+
+        ContainerSchemaNode rootCon2 = (ContainerSchemaNode) simpleModule.getDataChildByName(q4);
+        assertNotNull(rootCon2);
+
+        ContainerSchemaNode subCon2 = (ContainerSchemaNode) rootCon2.getDataChildByName(q5);
+        assertNotNull(subCon2);
+
+        ContainerSchemaNode subSubCon2 = (ContainerSchemaNode) subCon2.getDataChildByName(q6);
+        assertNotNull(subSubCon2);
+
+        GroupingDefinition grp = simpleModule.getGroupings().iterator().next();
+        assertNotNull(grp);
+        assertEquals(q7, grp.getQName());
+
+        ContainerSchemaNode grpSubCon2 = (ContainerSchemaNode) grp.getDataChildByName(q5);
+        assertNotNull(grpSubCon2);
+
+        ContainerSchemaNode grpSubSubCon2 = (ContainerSchemaNode) grpSubCon2.getDataChildByName(q6);
+        assertNotNull(grpSubSubCon2);
+
+        assertEquals(SchemaPath.create(true, q1, q2, q3), subSubCon.getPath());
+        assertEquals(SchemaPath.create(true, q4, q5, q6), subSubCon2.getPath());
+        assertEquals(SchemaPath.create(true, q7, q5, q6), grpSubSubCon2.getPath());
+    }
+
+    @Test
+    public void extensionsTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild().addSource(YANG_EXT).buildEffective();
+        assertNotNull(result);
+
+        Set<GroupingDefinition> groupings = result.getGroupings();
+        assertEquals(1, groupings.size());
+
+        GroupingDefinition grp = groupings.iterator().next();
+
+        Collection<DataSchemaNode> childNodes = grp.getChildNodes();
+        assertEquals(1, childNodes.size());
+        DataSchemaNode child = childNodes.iterator().next();
+
+        assertTrue(child instanceof LeafSchemaNode);
+        LeafSchemaNode leaf = (LeafSchemaNode) child;
+
+        assertNotNull(leaf.getType());
+    }
+
+    @Test
+    public void mockTest() throws ReactorException, FileNotFoundException, URISyntaxException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild().addSource(YANG_EXT).buildEffective();
+        assertNotNull(result);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveIdentityTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveIdentityTest.java
new file mode 100644 (file)
index 0000000..bf43855
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import com.google.common.collect.Iterables;
+import java.net.URISyntaxException;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+
+public class EffectiveIdentityTest {
+
+    private static final StatementStreamSource IDENTITY_TEST = sourceForResource(
+            "/stmt-test/identity/identity-test.yang");
+
+    private static final StatementStreamSource CYCLIC_IDENTITY_TEST = sourceForResource(
+            "/stmt-test/identity/cyclic-identity-test.yang");
+
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void cyclicefineTest() throws SourceException, ReactorException, URISyntaxException {
+
+        CrossSourceStatementReactor.BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(CYCLIC_IDENTITY_TEST);
+        try {
+            reactor.buildEffective();
+        } catch (SomeModifiersUnresolvedException e) {
+            StmtTestUtils.log(e, "      ");
+            throw e;
+        }
+    }
+
+    @Test
+    public void identityTest() throws SourceException, ReactorException,
+            URISyntaxException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(IDENTITY_TEST)
+                .buildEffective();
+
+        assertNotNull(result);
+
+        Module module = result.findModule("identity-test").get();
+        Set<IdentitySchemaNode> identities = module.getIdentities();
+
+        assertNotNull(identities);
+        assertEquals(4, identities.size());
+
+        IdentitySchemaNode root = null;
+        IdentitySchemaNode child1 = null;
+        IdentitySchemaNode child2 = null;
+        IdentitySchemaNode child12 = null;
+        for (IdentitySchemaNode identitySchemaNode : identities) {
+            switch (identitySchemaNode.getQName().getLocalName()) {
+                case "root-identity":
+                    root = identitySchemaNode;
+                    break;
+                case "child-identity-1":
+                    child1 = identitySchemaNode;
+                    break;
+                case "child-identity-2":
+                    child2 = identitySchemaNode;
+                    break;
+                case "child-identity-1-2":
+                    child12 = identitySchemaNode;
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        assertNotNull(root);
+        assertNotNull(child1);
+        assertNotNull(child2);
+        assertNotNull(child12);
+
+        assertTrue(root.getBaseIdentities().isEmpty());
+
+        Set<IdentitySchemaNode> rootDerivedIdentities = root
+                .getDerivedIdentities();
+        assertEquals(2, rootDerivedIdentities.size());
+
+        assertTrue(rootDerivedIdentities.contains(child1));
+        assertTrue(rootDerivedIdentities.contains(child2));
+        assertFalse(rootDerivedIdentities.contains(child12));
+        assertFalse(child1.equals(child2));
+
+        assertTrue(root == Iterables.getOnlyElement(child1.getBaseIdentities()));
+        assertTrue(root == Iterables.getOnlyElement(child2.getBaseIdentities()));
+
+        assertTrue(child2.getDerivedIdentities().isEmpty());
+
+        Set<IdentitySchemaNode> child1DerivedIdentities = child1
+                .getDerivedIdentities();
+        assertEquals(1, child1DerivedIdentities.size());
+        assertTrue(child1DerivedIdentities.contains(child12));
+        assertFalse(child1DerivedIdentities.contains(child1));
+
+        assertTrue(child1 == Iterables.getOnlyElement(child12.getBaseIdentities()));
+        assertTrue(child12 == child1DerivedIdentities.iterator().next());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveModuleTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveModuleTest.java
new file mode 100644 (file)
index 0000000..1bb594e
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DeviateKind;
+import org.opendaylight.yangtools.yang.model.api.Deviation;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class EffectiveModuleTest {
+
+    private static final StatementStreamSource ROOT_MODULE = sourceForResource(
+            "/semantic-statement-parser/effective-module/root.yang");
+    private static final StatementStreamSource IMPORTED_MODULE = sourceForResource(
+            "/semantic-statement-parser/effective-module/imported.yang");
+    private static final StatementStreamSource SUBMODULE = sourceForResource(
+            "/semantic-statement-parser/effective-module/submod.yang");
+
+    private static final QNameModule ROOT_MODULE_QNAME = QNameModule.create(URI.create("root-ns"));
+
+    private static final QName CONT = QName.create(ROOT_MODULE_QNAME, "cont");
+    private static final QName FEATURE1 = QName.create(ROOT_MODULE_QNAME, "feature1");
+
+    private static final SchemaPath CONT_SCHEMA_PATH = SchemaPath.create(true, CONT);
+    private static final SchemaPath FEATURE1_SCHEMA_PATH = SchemaPath.create(true, FEATURE1);
+
+    private static final Revision REVISION = Revision.of("2000-01-01");
+
+    @Test
+    public void effectiveBuildTest() throws SourceException, ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(ROOT_MODULE, IMPORTED_MODULE, SUBMODULE)
+                .buildEffective();
+
+        assertNotNull(result);
+
+        Module rootModule = result.findModules("root").iterator().next();
+        assertNotNull(rootModule);
+
+        assertEquals("root-pref", rootModule.getPrefix());
+        assertEquals(YangVersion.VERSION_1, rootModule.getYangVersion());
+        assertEquals(Optional.of("cisco"), rootModule.getOrganization());
+        assertEquals(Optional.of("cisco email"), rootModule.getContact());
+
+        final ContainerSchemaNode contSchemaNode = (ContainerSchemaNode) rootModule.getDataChildByName(CONT);
+        assertNotNull(contSchemaNode);
+
+        final Set<AugmentationSchemaNode> augmentations = rootModule.getAugmentations();
+        assertEquals(1, augmentations.size());
+        assertEquals(CONT_SCHEMA_PATH, augmentations.iterator().next().getTargetPath());
+
+        final Set<ModuleImport> imports = rootModule.getImports();
+        assertEquals(1, imports.size());
+        final ModuleImport importStmt = imports.iterator().next();
+        assertNotNull(importStmt);
+        assertEquals("imported", importStmt.getModuleName());
+        assertEquals(Optional.of(REVISION), importStmt.getRevision());
+        assertEquals("imp-pref", importStmt.getPrefix());
+
+        final Set<Module> submodules = rootModule.getSubmodules();
+        assertEquals(1, submodules.size());
+        assertEquals("submod", submodules.iterator().next().getName());
+
+        final Set<NotificationDefinition> notifications = rootModule.getNotifications();
+        assertEquals(1, notifications.size());
+        assertEquals("notif1", notifications.iterator().next().getQName().getLocalName());
+
+        final Set<RpcDefinition> rpcs = rootModule.getRpcs();
+        assertEquals(1, rpcs.size());
+        assertEquals("rpc1", rpcs.iterator().next().getQName().getLocalName());
+
+        final Set<Deviation> deviations = rootModule.getDeviations();
+        assertEquals(1, deviations.size());
+        final Deviation deviationStmt = deviations.iterator().next();
+        assertNotNull(deviationStmt);
+        final QNameModule importedModuleQName = QNameModule.create(URI.create("imported"), REVISION);
+        final QName importedContQName = QName.create(importedModuleQName, "cont");
+        final SchemaPath importedContSchemaPath = SchemaPath.create(true, importedContQName);
+        assertEquals(importedContSchemaPath, deviationStmt.getTargetPath());
+        assertEquals(DeviateKind.ADD, deviationStmt.getDeviates().iterator().next().getDeviateType());
+        assertEquals(Optional.of("deviate reference"), deviationStmt.getReference());
+
+        final Set<IdentitySchemaNode> identities = rootModule.getIdentities();
+        assertEquals(1, identities.size());
+        assertEquals("identity1", identities.iterator().next().getQName().getLocalName());
+
+        final Set<FeatureDefinition> features = rootModule.getFeatures();
+        assertEquals(1, features.size());
+        final FeatureDefinition featureStmt = features.iterator().next();
+        assertNotNull(featureStmt);
+        assertEquals(FEATURE1, featureStmt.getQName());
+        assertEquals(FEATURE1_SCHEMA_PATH, featureStmt.getPath());
+        assertEquals(Optional.of("feature1 description"), featureStmt.getDescription());
+        assertEquals(Optional.of("feature1 reference"), featureStmt.getReference());
+        assertEquals(Status.CURRENT, featureStmt.getStatus());
+
+        final List<ExtensionDefinition> extensionSchemaNodes = rootModule.getExtensionSchemaNodes();
+        assertEquals(1, extensionSchemaNodes.size());
+        assertEquals("ext1", extensionSchemaNodes.iterator().next().getQName().getLocalName());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveModulesAndSubmodulesTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveModulesAndSubmodulesTest.java
new file mode 100644 (file)
index 0000000..47d876a
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+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.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class EffectiveModulesAndSubmodulesTest {
+
+    private static final StatementStreamSource ROOT_MODULE = sourceForResource(
+            "/stmt-test/submodules/root-module.yang");
+    private static final StatementStreamSource IMPORTED_MODULE = sourceForResource(
+            "/stmt-test/submodules/imported-module.yang");
+    private static final StatementStreamSource SUBMODULE_1 = sourceForResource(
+            "/stmt-test/submodules/submodule-1.yang");
+    private static final StatementStreamSource SUBMODULE_2 = sourceForResource(
+            "/stmt-test/submodules/submodule-2.yang");
+    private static final StatementStreamSource SUBMODULE_TO_SUBMODULE_1 = sourceForResource(
+            "/stmt-test/submodules/submodule-to-submodule-1.yang");
+
+    private static final QNameModule ROOT = QNameModule.create(URI.create("root-module"));
+
+    @Test
+    public void modulesAndSubmodulesSimpleReferencesTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(ROOT_MODULE, IMPORTED_MODULE, SUBMODULE_1, SUBMODULE_2, SUBMODULE_TO_SUBMODULE_1)
+                .buildEffective();
+
+        assertNotNull(result);
+
+        final Set<Module> modules = result.getModules();
+        assertNotNull(modules);
+        assertEquals(2, modules.size());
+
+        Module root = null;
+        Module imported = null;
+        for (final Module module : modules) {
+            switch (module.getName()) {
+                case "root-module":
+                    root = module;
+                    break;
+                case "imported-module":
+                    imported = module;
+                    break;
+                default:
+            }
+            StmtTestUtils.printReferences(module, false, "");
+            StmtTestUtils.printChilds(module.getChildNodes(), "      ");
+        }
+
+        assertNotNull(root);
+        assertNotNull(imported);
+
+        final Collection<DataSchemaNode> rootChildNodes = root.getChildNodes();
+        final Collection<DataSchemaNode> importedChildNodes = imported
+                .getChildNodes();
+
+        assertNotNull(rootChildNodes);
+        assertNotNull(importedChildNodes);
+
+        assertEquals(3, rootChildNodes.size());
+        assertEquals(1, importedChildNodes.size());
+
+        final Set<Module> rootSubmodules = root.getSubmodules();
+        final Set<Module> importedSubmodules = imported.getSubmodules();
+
+        assertNotNull(rootSubmodules);
+        assertNotNull(importedSubmodules);
+
+        assertEquals(2, rootSubmodules.size());
+        assertEquals(0, importedSubmodules.size());
+
+        Module sub1 = null;
+        Module sub2 = null;
+        for (final Module rootSubmodule : rootSubmodules) {
+            switch (rootSubmodule.getName()) {
+                case "submodule-1":
+                    sub1 = rootSubmodule;
+                    break;
+                case "submodule-2":
+                    sub2 = rootSubmodule;
+                    break;
+                default:
+            }
+        }
+
+        assertNotNull(sub1);
+        assertNotNull(sub2);
+
+        assertEquals(ROOT, sub1.getQNameModule());
+        assertEquals(ROOT, sub2.getQNameModule());
+
+        final Collection<DataSchemaNode> sub1ChildNodes = sub1.getChildNodes();
+        final Collection<DataSchemaNode> sub2ChildNodes = sub2.getChildNodes();
+
+        assertNotNull(sub1ChildNodes);
+        assertNotNull(sub2ChildNodes);
+
+        assertEquals(1, sub1ChildNodes.size());
+        assertEquals(1, sub2ChildNodes.size());
+
+        final Set<Module> sub1Submodules = sub1.getSubmodules();
+        final Set<Module> sub2Submodules = sub2.getSubmodules();
+
+        assertNotNull(sub1Submodules);
+        assertNotNull(sub2Submodules);
+
+        assertEquals(1, sub1Submodules.size());
+        assertEquals(0, sub2Submodules.size());
+
+        Module sub1Submodule = null;
+        for (final Module submodule : sub1Submodules) {
+            switch (submodule.getName()) {
+                case "submodule-to-submodule-1":
+                    sub1Submodule = submodule;
+                    break;
+                default:
+            }
+        }
+
+        assertNotNull(sub1Submodule);
+
+        assertEquals(ROOT, sub1Submodule.getQNameModule());
+
+        final Collection<DataSchemaNode> sub1SubmoduleChildNodes = sub1Submodule.getChildNodes();
+        assertNotNull(sub1SubmoduleChildNodes);
+        assertEquals(1, sub1SubmoduleChildNodes.size());
+
+        final Set<Module> sub1SubmoduleSubmodules = sub1Submodule.getSubmodules();
+        assertNotNull(sub1SubmoduleSubmodules);
+        assertEquals(0, sub1SubmoduleSubmodules.size());
+
+        findModulesSubTest(result, root, imported);
+
+        getDataChildByNameSubTest(result, root);
+    }
+
+    private static void getDataChildByNameSubTest(final SchemaContext result, final Module root) {
+        final DataSchemaNode containerInRoot = result.getDataChildByName(QName
+                .create(root.getQNameModule(), "container-in-root-module"));
+        assertNotNull(containerInRoot);
+        assertEquals(Optional.of("desc"), containerInRoot.getDescription());
+    }
+
+    private static void findModulesSubTest(final SchemaContext result, final Module root, final Module imported) {
+        final Module foundRoot = result.findModule("root-module").get();
+        final Set<Module> foundRoots = result.findModules(URI.create("root-module"));
+        final Module foundRoot3 = result.findModule(URI.create("root-module")).get();
+
+        assertNotNull(foundRoot);
+        assertNotNull(foundRoots);
+        assertEquals(1, foundRoots.size());
+        final Module foundRoot2 = foundRoots.iterator().next();
+
+        assertNotNull(foundRoot2);
+        assertNotNull(foundRoot3);
+
+        assertEquals(root, foundRoot);
+        assertEquals(root, foundRoot2);
+        assertEquals(root, foundRoot3);
+
+        final Module foundImported = result.findModule("imported-module").get();
+        final Set<Module> foundImporteds = result.findModules(URI.create("imported-module"));
+        final Module foundImported3 = result.findModule(URI.create("imported-module")).get();
+
+        assertNotNull(foundImported);
+        assertNotNull(foundImporteds);
+        assertEquals(1, foundImporteds.size());
+        final Module foundImported2 = foundImporteds.iterator().next();
+
+        assertNotNull(foundImported2);
+        assertNotNull(foundImported3);
+
+        assertEquals(imported, foundImported);
+        assertEquals(imported, foundImported2);
+        assertEquals(imported, foundImported3);
+
+        assertFalse(root.equals(imported));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveSchemaContextTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveSchemaContextTest.java
new file mode 100644 (file)
index 0000000..70fb76b
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.util.SimpleSchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.EffectiveSchemaContext;
+
+public class EffectiveSchemaContextTest {
+
+    @Test
+    public void testEffectiveSchemaContext() throws ReactorException, ParseException, URISyntaxException, IOException,
+            YangSyntaxErrorException {
+        final EffectiveSchemaContext schemaContext = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(YangStatementStreamSource.create(YangTextSchemaSource.forResource(
+                        "/effective-schema-context-test/foo.yang")))
+                .addSource(YangStatementStreamSource.create(YangTextSchemaSource.forResource(
+                        "/effective-schema-context-test/bar.yang")))
+                .addSource(YangStatementStreamSource.create(YangTextSchemaSource.forResource(
+                        "/effective-schema-context-test/baz.yang")))
+                .buildEffective();
+        assertNotNull(schemaContext);
+
+        final Set<DataSchemaNode> dataDefinitions = schemaContext.getDataDefinitions();
+        assertEquals(3, dataDefinitions.size());
+
+        final Collection<DataSchemaNode> childNodes = schemaContext.getChildNodes();
+        assertEquals(3, childNodes.size());
+
+        final Set<NotificationDefinition> notifications = schemaContext.getNotifications();
+        assertEquals(3, notifications.size());
+
+        final Set<RpcDefinition> rpcs = schemaContext.getOperations();
+        assertEquals(3, rpcs.size());
+
+        final Set<ExtensionDefinition> extensions = schemaContext.getExtensions();
+        assertEquals(3, extensions.size());
+
+        final List<UnknownSchemaNode> unknownSchemaNodes = schemaContext.getUnknownSchemaNodes();
+        assertEquals(3, unknownSchemaNodes.size());
+
+        assertNull(schemaContext.getDataChildByName(QName.create("foo-namespace", "2016-09-21", "foo-cont")));
+
+        assertFalse(schemaContext.findModule("foo", Revision.of("2016-08-21")).isPresent());
+        assertFalse(schemaContext.findModule(URI.create("foo-namespace"), Revision.of("2016-08-21")).isPresent());
+
+        assertFalse(schemaContext.isAugmenting());
+        assertFalse(schemaContext.isAddedByUses());
+        assertFalse(schemaContext.isConfiguration());
+        assertFalse(schemaContext.isPresenceContainer());
+        assertFalse(schemaContext.getWhenCondition().isPresent());
+        assertEquals(0, schemaContext.getMustConstraints().size());
+        assertFalse(schemaContext.getDescription().isPresent());
+        assertFalse(schemaContext.getReference().isPresent());
+        assertEquals(SchemaContext.NAME, schemaContext.getQName());
+        assertEquals(SchemaPath.ROOT, schemaContext.getPath());
+        assertEquals(Status.CURRENT, schemaContext.getStatus());
+        assertNotNull(schemaContext.getUses());
+        assertTrue(schemaContext.getUses().isEmpty());
+        assertNotNull(schemaContext.getAvailableAugmentations());
+        assertTrue(schemaContext.getAvailableAugmentations().isEmpty());
+
+        Module fooModule = schemaContext.findModule("foo", Revision.of("2016-09-21")).get();
+        assertEquals(3, schemaContext.getModules().size());
+        assertEquals(3, schemaContext.getRootDeclaredStatements().size());
+        assertEquals(3, schemaContext.getModuleStatements().size());
+
+        final Set<Module> modules = schemaContext.getModules();
+        final SchemaContext copiedSchemaContext =  SimpleSchemaContext.forModules(modules);
+        assertNotNull(copiedSchemaContext);
+        assertEquals(modules, copiedSchemaContext.getModules());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveStatementTypeTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveStatementTypeTest.java
new file mode 100644 (file)
index 0000000..75c31dc
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import com.google.common.collect.Range;
+import java.util.List;
+import java.util.Optional;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.type.BitsSpecificationEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.type.Decimal64SpecificationEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.type.EnumSpecificationEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.type.IdentityRefSpecificationEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.type.LeafrefSpecificationEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.type.UnionSpecificationEffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class EffectiveStatementTypeTest {
+    private static SchemaContext effectiveSchemaContext;
+    private static Module types;
+
+    private LeafSchemaNode currentLeaf;
+
+    @BeforeClass
+    public static void setup() throws ReactorException {
+        effectiveSchemaContext = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/type-tests/types.yang"))
+                .buildEffective();
+        types = effectiveSchemaContext.findModules("types").iterator().next();
+        assertNotNull(types);
+    }
+
+    @AfterClass
+    public static void teardown() {
+        effectiveSchemaContext = null;
+        types = null;
+    }
+
+    @Test
+    public void testBinary() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-binary"));
+        assertNotNull(currentLeaf.getType());
+
+        final BinaryTypeDefinition binaryEff = (BinaryTypeDefinition)
+                ((TypeEffectiveStatement<?>) ((LeafEffectiveStatement) currentLeaf)
+                .effectiveSubstatements().iterator().next()).getTypeDefinition();
+
+        assertNull(binaryEff.getBaseType());
+        assertEquals(Optional.empty(), binaryEff.getUnits());
+        assertEquals(Optional.empty(), binaryEff.getDefaultValue());
+        assertEquals("binary", binaryEff.getQName().getLocalName());
+        assertFalse(binaryEff.getLengthConstraint().isPresent());
+        assertEquals(Status.CURRENT, binaryEff.getStatus());
+        assertEquals("binary", binaryEff.getPath().getPathFromRoot().iterator().next().getLocalName());
+        assertNotNull(binaryEff.getUnknownSchemaNodes());
+        assertFalse(binaryEff.getDescription().isPresent());
+        assertFalse(binaryEff.getReference().isPresent());
+    }
+
+    @Test
+    public void testBits() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-bits"));
+        assertNotNull(currentLeaf.getType());
+
+        final List<BitsTypeDefinition.Bit> bitsEffIter = ((BitsTypeDefinition) currentLeaf.getType()).getBits();
+        final Bit bitEff = bitsEffIter.get(0);
+        final Bit bitEffSecond = bitsEffIter.get(1);
+
+        final BitsTypeDefinition bitsEff = ((BitsSpecificationEffectiveStatement)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next())
+                .getTypeDefinition();
+
+        assertNull(bitsEff.getBaseType());
+        assertNotNull(bitsEff.getQName());
+        assertEquals("bits", bitsEff.getQName().getLocalName());
+        assertEquals("bits", bitsEff.getPath().getLastComponent().getLocalName());
+        assertNotNull(bitsEff.getUnknownSchemaNodes());
+        assertFalse(bitsEff.getDescription().isPresent());
+        assertFalse(bitsEff.getReference().isPresent());
+        assertEquals(Status.CURRENT, bitsEff.getStatus());
+        assertEquals(Optional.empty(), bitsEff.getUnits());
+        assertNotNull(bitsEff.toString());
+        assertNotNull(bitsEff.hashCode());
+        assertFalse(bitsEff.equals(null));
+        assertFalse(bitsEff.equals("test"));
+        assertTrue(bitsEff.equals(bitsEff));
+        assertEquals(3, bitsEff.getBits().size());
+        assertEquals(Optional.empty(), bitsEff.getDefaultValue());
+
+        assertNotNull(bitEff.getPath());
+        assertNotNull(bitEff.getUnknownSchemaNodes());
+        assertEquals(Optional.of("test bit"), bitEff.getDescription());
+        assertEquals(Optional.of("test bit ref"), bitEff.getReference());
+        assertEquals(Status.CURRENT, bitEff.getStatus());
+        assertNotNull(bitEff.hashCode());
+        assertFalse(bitEff.equals(null));
+        assertFalse(bitEff.equals("test"));
+        assertFalse(bitEff.equals(bitEffSecond));
+        assertNotNull(bitEff.toString());
+        assertEquals("one", bitEff.getName());
+        assertNotNull(bitEff.getQName());
+        assertEquals(0, bitEff.getPosition());
+    }
+
+    @Test
+    public void testBoolean() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-boolean"));
+        assertNotNull(currentLeaf.getType());
+        final BooleanTypeDefinition booleanEff = (BooleanTypeDefinition) ((TypeEffectiveStatement<?>)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next())
+                .getTypeDefinition();
+
+        assertNull(booleanEff.getBaseType());
+        assertEquals(Optional.empty(), booleanEff.getUnits());
+        assertEquals(Optional.empty(), booleanEff.getDefaultValue());
+        assertEquals("boolean", booleanEff.getQName().getLocalName());
+        assertNull(booleanEff.getPath().getParent().getParent());
+        assertNotNull(booleanEff.getUnknownSchemaNodes());
+        assertFalse(booleanEff.getDescription().isPresent());
+        assertFalse(booleanEff.getReference().isPresent());
+        assertEquals(Status.CURRENT, booleanEff.getStatus());
+        assertNotNull(booleanEff.toString());
+    }
+
+    @Test
+    public void testDecimal64() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-decimal64"));
+        assertNotNull(currentLeaf.getType());
+        final DecimalTypeDefinition decimal64Eff = ((Decimal64SpecificationEffectiveStatement)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next())
+                .getTypeDefinition();
+
+        assertNull(decimal64Eff.getBaseType());
+        assertEquals(Optional.empty(), decimal64Eff.getUnits());
+        assertEquals(Optional.empty(), decimal64Eff.getDefaultValue());
+        assertEquals("decimal64", decimal64Eff.getQName().getLocalName());
+        assertNotNull(decimal64Eff.getUnknownSchemaNodes());
+
+        // FIXME: The yang model api is wrong: description/reference/status are not allowed under 'type', how come we
+        // parse it?
+        // allowed under 'type', how come we parse it?
+        assertFalse(decimal64Eff.getDescription().isPresent());
+        assertFalse(decimal64Eff.getReference().isPresent());
+        assertEquals(Status.CURRENT, decimal64Eff.getStatus());
+
+        assertEquals(3, decimal64Eff.getRangeConstraint().get().getAllowedRanges().asRanges().size());
+        assertNotNull(decimal64Eff.toString());
+        assertNotNull(decimal64Eff.hashCode());
+        assertEquals(2, decimal64Eff.getFractionDigits());
+        assertFalse(decimal64Eff.equals(null));
+        assertFalse(decimal64Eff.equals("test"));
+        assertTrue(decimal64Eff.equals(decimal64Eff));
+        assertEquals("decimal64", decimal64Eff.getPath().getLastComponent().getLocalName());
+    }
+
+    @Test
+    public void testEmpty() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-empty"));
+        assertNotNull(currentLeaf.getType());
+        final EmptyTypeDefinition emptyEff = (EmptyTypeDefinition) ((TypeEffectiveStatement<?>)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next())
+                .getTypeDefinition();
+
+        assertEquals(Optional.empty(), emptyEff.getUnits());
+        assertEquals(Optional.empty(), emptyEff.getDefaultValue());
+        assertNull(emptyEff.getBaseType());
+        assertEquals("empty", emptyEff.getQName().getLocalName());
+        assertNull(emptyEff.getPath().getParent().getParent());
+        assertNotNull(emptyEff.getUnknownSchemaNodes());
+        assertFalse(emptyEff.getDescription().isPresent());
+        assertFalse(emptyEff.getReference().isPresent());
+        assertEquals("CURRENT", emptyEff.getStatus().toString());
+        assertNotNull(emptyEff.toString());
+    }
+
+    @Test
+    public void testEnum() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-enum"));
+        assertNotNull(currentLeaf.getType());
+        final List<EnumTypeDefinition.EnumPair> enumEffIter = ((EnumTypeDefinition) currentLeaf.getType()).getValues();
+        final EnumPair enumEff = enumEffIter.iterator().next();
+
+        final EnumTypeDefinition enumSpecEff = ((EnumSpecificationEffectiveStatement)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next())
+                .getTypeDefinition();
+
+        assertEquals("enumeration", enumSpecEff.getQName().getLocalName());
+        assertEquals("enumeration", enumSpecEff.getPath().getLastComponent().getLocalName());
+        assertEquals(Optional.empty(), enumSpecEff.getDefaultValue());
+        assertEquals(3, enumSpecEff.getValues().size());
+        assertNull(enumSpecEff.getBaseType());
+        assertNotNull(enumSpecEff.getUnknownSchemaNodes());
+        assertEquals(Status.CURRENT, enumSpecEff.getStatus());
+        assertFalse(enumSpecEff.getDescription().isPresent());
+        assertFalse(enumSpecEff.getReference().isPresent());
+        assertEquals(Optional.empty(), enumSpecEff.getUnits());
+        assertNotNull(enumSpecEff.toString());
+        assertNotNull(enumSpecEff.hashCode());
+        assertFalse(enumSpecEff.equals(null));
+        assertFalse(enumSpecEff.equals("test"));
+        assertTrue(enumSpecEff.equals(enumSpecEff));
+
+        assertEquals("zero", enumEff.getName());
+        assertNotNull(enumEff.getUnknownSchemaNodes());
+        assertEquals(Optional.of("test enum"), enumEff.getDescription());
+        assertEquals(Optional.of("test enum ref"), enumEff.getReference());
+        assertEquals(Status.CURRENT, enumEff.getStatus());
+        assertEquals(0, enumEff.getValue());
+    }
+
+    @Test
+    public void testIdentityRef() {
+        currentLeaf = (LeafSchemaNode) types
+                .getDataChildByName(QName.create(types.getQNameModule(), "leaf-identityref"));
+        assertNotNull(currentLeaf.getType());
+        final IdentityrefTypeDefinition identityRefEff = ((IdentityRefSpecificationEffectiveStatement)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next())
+                .getTypeDefinition();
+
+        assertEquals(Optional.empty(), identityRefEff.getDefaultValue());
+        assertEquals("identityref", identityRefEff.getQName().getLocalName());
+        assertEquals("identityref", identityRefEff.getPath().getLastComponent().getLocalName());
+        assertNull(identityRefEff.getBaseType());
+        assertNotNull(identityRefEff.getUnknownSchemaNodes());
+        assertEquals(Status.CURRENT, identityRefEff.getStatus());
+        assertEquals("test-identity", identityRefEff.getIdentities().iterator().next().getQName().getLocalName());
+        assertFalse(identityRefEff.getDescription().isPresent());
+        assertFalse(identityRefEff.getReference().isPresent());
+        assertNotNull(identityRefEff.toString());
+
+        // FIXME: the model is wrong, but we accept units in 'type' statement
+        assertEquals(Optional.empty(), identityRefEff.getUnits());
+    }
+
+    @Test
+    public void testInstanceIdentifier() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(),
+                "leaf-instance-identifier"));
+        assertNotNull(currentLeaf.getType());
+        final InstanceIdentifierTypeDefinition instanceIdentEff = (InstanceIdentifierTypeDefinition)
+                ((TypeEffectiveStatement<?>) ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements()
+                        .iterator().next()).getTypeDefinition();
+        assertNotNull(instanceIdentEff.toString());
+
+        assertFalse(instanceIdentEff.requireInstance());
+        assertEquals("instance-identifier", instanceIdentEff.getQName().getLocalName());
+        assertEquals("instance-identifier", instanceIdentEff.getPath().getLastComponent().getLocalName());
+        assertNull(instanceIdentEff.getBaseType());
+        assertEquals(Optional.empty(), instanceIdentEff.getDefaultValue());
+        assertNotNull(instanceIdentEff.getUnknownSchemaNodes());
+        assertFalse(instanceIdentEff.getDescription().isPresent());
+        assertFalse(instanceIdentEff.getReference().isPresent());
+        assertEquals(Optional.empty(), instanceIdentEff.getUnits());
+        assertEquals(Status.CURRENT, instanceIdentEff.getStatus());
+        assertNotNull(instanceIdentEff.hashCode());
+        assertFalse(instanceIdentEff.equals(null));
+        assertFalse(instanceIdentEff.equals("test"));
+        assertTrue(instanceIdentEff.equals(instanceIdentEff));
+    }
+
+    @Test
+    public void testLeafref() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-leafref"));
+        assertNotNull(currentLeaf.getType());
+
+        final LeafrefTypeDefinition leafrefEff = ((LeafrefSpecificationEffectiveStatement)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next())
+                .getTypeDefinition();
+
+        assertEquals("/container-test/leaf-test", leafrefEff.getPathStatement().getOriginalString());
+        assertNull(leafrefEff.getBaseType());
+        assertEquals(Optional.empty(), leafrefEff.getUnits());
+        assertEquals(Optional.empty(), leafrefEff.getDefaultValue());
+        assertNotNull(leafrefEff.toString());
+        assertEquals("leafref", leafrefEff.getQName().getLocalName());
+        assertEquals(Status.CURRENT, leafrefEff.getStatus());
+        assertNotNull(leafrefEff.getUnknownSchemaNodes());
+        assertEquals("leafref", leafrefEff.getPath().getLastComponent().getLocalName());
+        assertFalse(leafrefEff.getDescription().isPresent());
+        assertFalse(leafrefEff.getReference().isPresent());
+        assertNotNull(leafrefEff.hashCode());
+        assertFalse(leafrefEff.equals(null));
+        assertFalse(leafrefEff.equals("test"));
+        assertTrue(leafrefEff.equals(leafrefEff));
+    }
+
+    @Test
+    public void testIntAll() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-int8"));
+        assertNotNull(currentLeaf.getType());
+        final TypeEffectiveStatement<?> int8Eff = (TypeEffectiveStatement<?>)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next();
+        assertNotNull(int8Eff.toString());
+
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-int16"));
+        assertNotNull(currentLeaf.getType());
+        final TypeEffectiveStatement<?> int16Eff = (TypeEffectiveStatement<?>)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next();
+        assertNotNull(int16Eff.toString());
+
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-int32"));
+        assertNotNull(currentLeaf.getType());
+        final TypeEffectiveStatement<?> int32Eff = (TypeEffectiveStatement<?>)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next();
+        assertNotNull(int32Eff.toString());
+
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-int64"));
+        assertNotNull(currentLeaf.getType());
+        final TypeEffectiveStatement<?> int64Eff = (TypeEffectiveStatement<?>)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next();
+        assertNotNull(int64Eff.toString());
+
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-uint8"));
+        assertNotNull(currentLeaf.getType());
+        final TypeEffectiveStatement<?> uint8Eff = (TypeEffectiveStatement<?>)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next();
+        assertNotNull(uint8Eff.toString());
+
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-uint16"));
+        assertNotNull(currentLeaf.getType());
+        final TypeEffectiveStatement<?> uint16Eff = (TypeEffectiveStatement<?>)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next();
+        assertNotNull(uint16Eff.toString());
+
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-uint32"));
+        assertNotNull(currentLeaf.getType());
+        final TypeEffectiveStatement<?> uint32Eff = (TypeEffectiveStatement<?>)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next();
+        assertNotNull(uint32Eff.toString());
+
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-uint64"));
+        assertNotNull(currentLeaf.getType());
+        final TypeEffectiveStatement<?> uint64Eff = (TypeEffectiveStatement<?>)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next();
+        assertNotNull(uint64Eff.toString());
+    }
+
+    @Test
+    public void testUnion() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-union"));
+        assertNotNull(currentLeaf.getType());
+        final UnionTypeDefinition unionEff = ((UnionSpecificationEffectiveStatement)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next())
+                .getTypeDefinition();
+
+        assertEquals(2, unionEff.getTypes().size());
+        assertEquals("union", unionEff.getQName().getLocalName());
+        assertEquals("CURRENT", unionEff.getStatus().toString());
+        assertNotNull(unionEff.getUnknownSchemaNodes());
+        assertNull(unionEff.getBaseType());
+        assertEquals(Optional.empty(), unionEff.getUnits());
+        assertEquals(Optional.empty(), unionEff.getDefaultValue());
+        assertFalse(unionEff.getDescription().isPresent());
+        assertFalse(unionEff.getReference().isPresent());
+        assertNotNull(unionEff.toString());
+        assertNotNull(unionEff.hashCode());
+        assertFalse(unionEff.equals(null));
+        assertFalse(unionEff.equals("test"));
+        assertTrue(unionEff.equals(unionEff));
+        assertEquals("union", unionEff.getPath().getLastComponent().getLocalName());
+    }
+
+    @Test
+    public void testLengthConstraint() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(),
+                "leaf-length-pattern"));
+
+        final StringTypeDefinition leafType = (StringTypeDefinition) currentLeaf.getType();
+        assertNotNull(leafType);
+        final LengthConstraint lengthConstraint = leafType.getLengthConstraint().get();
+
+        final Range<Integer> span = lengthConstraint.getAllowedRanges().span();
+        assertEquals(1, span.lowerEndpoint().intValue());
+        assertEquals(255, span.upperEndpoint().intValue());
+        assertFalse(lengthConstraint.getReference().isPresent());
+        assertFalse(lengthConstraint.getDescription().isPresent());
+        assertFalse(lengthConstraint.getErrorMessage().isPresent());
+        assertFalse(lengthConstraint.getErrorAppTag().isPresent());
+        assertNotNull(lengthConstraint.toString());
+        assertNotNull(lengthConstraint.hashCode());
+        assertFalse(lengthConstraint.equals(null));
+        assertFalse(lengthConstraint.equals("test"));
+
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(),
+                "leaf-length-pattern-second"));
+        assertNotNull(currentLeaf.getType());
+        final LengthConstraint lengthConstraintSecond = ((StringTypeDefinition) currentLeaf.getType())
+                .getLengthConstraint().get();
+        assertFalse(lengthConstraint.equals(lengthConstraintSecond));
+    }
+
+    @Test
+    public void testPatternConstraint() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(),
+                "leaf-length-pattern"));
+        assertNotNull(currentLeaf.getType());
+        final PatternConstraint patternConstraint = ((StringTypeDefinition) currentLeaf.getType())
+                .getPatternConstraints().get(0);
+        final PatternConstraint patternConstraintThird = ((StringTypeDefinition) currentLeaf.getType())
+                .getPatternConstraints().get(0);
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(),
+                "leaf-length-pattern-second"));
+        assertNotNull(currentLeaf.getType());
+        final PatternConstraint patternConstraintSecond = ((StringTypeDefinition) currentLeaf.getType())
+                .getPatternConstraints().get(0);
+
+        assertEquals("^(?:[0-9a-fA-F]*)$", patternConstraint.getJavaPatternString());
+        assertFalse(patternConstraint.getReference().isPresent());
+        assertFalse(patternConstraint.getDescription().isPresent());
+        assertEquals(Optional.empty(), patternConstraint.getErrorMessage());
+        assertEquals(Optional.of("invalid-regular-expression"), patternConstraint.getErrorAppTag());
+        assertNotNull(patternConstraint.toString());
+        assertNotNull(patternConstraint.hashCode());
+        assertFalse(patternConstraint.equals(null));
+        assertFalse(patternConstraint.equals("test"));
+        assertFalse(patternConstraint.equals(patternConstraintSecond));
+        assertTrue(patternConstraint.equals(patternConstraintThird));
+    }
+
+    @Test
+    public void testString() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-string"));
+        assertNotNull(currentLeaf.getType());
+        final StringTypeDefinition stringEff = (StringTypeDefinition) ((TypeEffectiveStatement<?>)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next())
+                .getTypeDefinition();
+
+        assertEquals("string", stringEff.getQName().getLocalName());
+        assertEquals(Status.CURRENT, stringEff.getStatus());
+        assertEquals(Optional.empty(), stringEff.getUnits());
+        assertEquals(Optional.empty(), stringEff.getDefaultValue());
+        assertNotNull(stringEff.getUnknownSchemaNodes());
+        assertNull(stringEff.getBaseType());
+        assertFalse(stringEff.getDescription().isPresent());
+        assertFalse(stringEff.getReference().isPresent());
+        assertNotNull(stringEff.toString());
+        assertNotNull(stringEff.hashCode());
+        assertFalse(stringEff.equals(null));
+        assertFalse(stringEff.equals("test"));
+        assertTrue(stringEff.equals(stringEff));
+        assertEquals("string", stringEff.getPath().getLastComponent().getLocalName());
+        assertFalse(stringEff.getLengthConstraint().isPresent());
+        assertNotNull(stringEff.getPatternConstraints());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveUsesRefineAndConstraintsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveUsesRefineAndConstraintsTest.java
new file mode 100644 (file)
index 0000000..d611e0d
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+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.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class EffectiveUsesRefineAndConstraintsTest {
+
+    @Test
+    public void refineTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/stmt-test/uses/refine-test.yang"))
+                .buildEffective();
+        assertNotNull(result);
+
+        Set<Module> modules = result.getModules();
+        assertNotNull(modules);
+        assertEquals(1, modules.size());
+
+        Module module = modules.iterator().next();
+
+        final QNameModule qnameModule = module.getQNameModule();
+        final QName rootContainer = QName.create(qnameModule, "root-container");
+        final QName grp1 = QName.create(qnameModule, "grp-1");
+
+        final QName containerFromGrouping = QName.create(qnameModule, "container-from-grouping");
+        final QName listInContainer = QName.create(qnameModule, "list-in-container");
+        final QName choiceFromGrp = QName.create(qnameModule, "choice-from-grp");
+
+        final QName containerFromGrouping2 = QName.create(qnameModule, "container-from-grouping2");
+        final QName presenceContainer = QName.create(qnameModule, "presence-container");
+
+        SchemaPath listInContainerPath = SchemaPath.create(true, rootContainer, containerFromGrouping, listInContainer);
+        SchemaPath choiceFromGrpPath = SchemaPath.create(true, rootContainer, containerFromGrouping, choiceFromGrp);
+        SchemaPath presenceContainerPath = SchemaPath.create(true, rootContainer, containerFromGrouping2,
+            presenceContainer);
+
+        checkRefinedList(result, listInContainerPath);
+        checkRefinedChoice(result, choiceFromGrpPath);
+        checkRefinedContainer(result, presenceContainerPath);
+
+        SchemaPath originalListInContainerPath = SchemaPath.create(true, grp1, containerFromGrouping, listInContainer);
+        SchemaPath originalChoiceFromGrpPath = SchemaPath.create(true, grp1, containerFromGrouping, choiceFromGrp);
+        SchemaPath originalPresenceContainerPath = SchemaPath.create(true, grp1, containerFromGrouping2,
+            presenceContainer);
+
+        checkOriginalList(result, originalListInContainerPath);
+        checkOriginalChoice(result, originalChoiceFromGrpPath);
+        checkOriginalContainer(result, originalPresenceContainerPath);
+    }
+
+    private static void checkOriginalContainer(final SchemaContext result, final SchemaPath path) {
+        SchemaNode containerInContainerNode = SchemaContextUtil.findDataSchemaNode(result, path);
+        assertNotNull(containerInContainerNode);
+
+        ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) containerInContainerNode;
+        assertFalse(containerSchemaNode.getReference().isPresent());
+        assertFalse(containerSchemaNode.getDescription().isPresent());
+        assertTrue(containerSchemaNode.isConfiguration());
+        assertFalse(containerSchemaNode.isPresenceContainer());
+
+        assertEquals(0, containerSchemaNode.getMustConstraints().size());
+    }
+
+    private static void checkOriginalChoice(final SchemaContext result, final SchemaPath path) {
+        SchemaNode choiceInContainerNode = SchemaContextUtil.findDataSchemaNode(result, path);
+        assertNotNull(choiceInContainerNode);
+
+        ChoiceSchemaNode choiceSchemaNode = (ChoiceSchemaNode) choiceInContainerNode;
+        assertFalse(choiceSchemaNode.isMandatory());
+    }
+
+    private static void checkOriginalList(final SchemaContext result, final SchemaPath path) {
+        SchemaNode listInContainerNode = SchemaContextUtil.findDataSchemaNode(result, path);
+        assertNotNull(listInContainerNode);
+
+        ListSchemaNode listSchemaNode = (ListSchemaNode) listInContainerNode;
+        assertEquals(Optional.of("original reference"), listSchemaNode.getReference());
+        assertEquals(Optional.of("original description"), listSchemaNode.getDescription());
+        assertFalse(listSchemaNode.isConfiguration());
+
+        ElementCountConstraint listConstraints = listSchemaNode.getElementCountConstraint().get();
+        assertEquals(10, listConstraints.getMinElements().intValue());
+        assertEquals(20, listConstraints.getMaxElements().intValue());
+        assertEquals(1, listSchemaNode.getMustConstraints().size());
+    }
+
+    private static void checkRefinedContainer(final SchemaContext result, final SchemaPath path) {
+        SchemaNode containerInContainerNode = SchemaContextUtil.findDataSchemaNode(result, path);
+        assertNotNull(containerInContainerNode);
+
+        ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) containerInContainerNode;
+        assertEquals(Optional.of("new reference"), containerSchemaNode.getReference());
+        assertEquals(Optional.of("new description"), containerSchemaNode.getDescription());
+        assertTrue(containerSchemaNode.isConfiguration());
+        assertTrue(containerSchemaNode.isPresenceContainer());
+        assertEquals(1, containerSchemaNode.getMustConstraints().size());
+    }
+
+    private static void checkRefinedChoice(final SchemaContext result, final SchemaPath path) {
+        SchemaNode choiceInContainerNode = SchemaContextUtil.findDataSchemaNode(result, path);
+        assertNotNull(choiceInContainerNode);
+
+        ChoiceSchemaNode choiceSchemaNode = (ChoiceSchemaNode) choiceInContainerNode;
+        assertTrue(choiceSchemaNode.isMandatory());
+    }
+
+    private static void checkRefinedList(final SchemaContext result, final SchemaPath path) {
+        SchemaNode listInContainerNode = SchemaContextUtil.findDataSchemaNode(result, path);
+        assertNotNull(listInContainerNode);
+
+        ListSchemaNode listSchemaNode = (ListSchemaNode) listInContainerNode;
+        assertEquals(Optional.of("new reference"), listSchemaNode.getReference());
+        assertEquals(Optional.of("new description"), listSchemaNode.getDescription());
+        assertTrue(listSchemaNode.isConfiguration());
+
+        ElementCountConstraint listConstraints = listSchemaNode.getElementCountConstraint().get();
+        assertEquals(5, listConstraints.getMinElements().intValue());
+        assertEquals(7, listConstraints.getMaxElements().intValue());
+        assertEquals(2, listSchemaNode.getMustConstraints().size());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ExtensionStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ExtensionStmtTest.java
new file mode 100644 (file)
index 0000000..10d4527
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class ExtensionStmtTest {
+    @Test
+    public void testExtensionDefinition() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/model/bar.yang"))
+                .buildEffective();
+        assertNotNull(result);
+
+        final Module testModule = result.findModules("bar").iterator().next();
+        assertNotNull(testModule);
+
+        assertEquals(1, testModule.getExtensionSchemaNodes().size());
+
+        final List<ExtensionDefinition> extensions = testModule.getExtensionSchemaNodes();
+        final ExtensionDefinition extension = extensions.get(0);
+        assertEquals("opendaylight", extension.getQName().getLocalName());
+        assertEquals("name", extension.getArgument());
+        assertTrue(extension.isYinElement());
+    }
+
+    @Test
+    public void testExtensionUsage() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/semantic-statement-parser/ext-typedef.yang"))
+                .addSource(sourceForResource("/semantic-statement-parser/ext-use.yang"))
+                .buildEffective();
+        assertNotNull(result);
+
+        final Module testModule1 = result.findModules("ext-typedef").iterator().next();
+        assertNotNull(testModule1);
+
+        assertEquals(1, testModule1.getExtensionSchemaNodes().size());
+
+        final List<ExtensionDefinition> extensions = testModule1.getExtensionSchemaNodes();
+        final ExtensionDefinition extensionDefinition = extensions.get(0);
+
+        final Module testModule2 = result.findModules("ext-use").iterator().next();
+        assertNotNull(testModule2);
+
+        final LeafSchemaNode leaf = (LeafSchemaNode) testModule2.getDataChildByName(
+            QName.create(testModule2.getQNameModule(), "value"));
+        assertNotNull(leaf);
+
+        assertEquals(1, leaf.getUnknownSchemaNodes().size());
+        final List<UnknownSchemaNode> unknownNodes = leaf.getUnknownSchemaNodes();
+        final UnknownSchemaNode extensionUse = unknownNodes.get(0);
+        assertEquals(extensionDefinition.getQName().getLocalName(), extensionUse.getExtensionDefinition().getQName()
+                .getLocalName());
+        assertEquals(extensionDefinition.getArgument(), extensionUse.getExtensionDefinition().getArgument());
+
+        assertEquals("key:value", extensionUse.getNodeParameter());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/GroupingAndUsesStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/GroupingAndUsesStmtTest.java
new file mode 100644 (file)
index 0000000..b7399bd
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+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.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class GroupingAndUsesStmtTest {
+
+    private static final StatementStreamSource MODULE = sourceForResource("/model/bar.yang");
+    private static final StatementStreamSource SUBMODULE = sourceForResource("/model/subfoo.yang");
+    private static final StatementStreamSource GROUPING_MODULE = sourceForResource("/model/baz.yang");
+    private static final StatementStreamSource USES_MODULE = sourceForResource("/model/foo.yang");
+
+    @Test
+    public void groupingTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(MODULE, GROUPING_MODULE)
+                .buildEffective();
+        assertNotNull(result);
+
+        final Module testModule = result.findModules("baz").iterator().next();
+        assertNotNull(testModule);
+
+        final Set<GroupingDefinition> groupings = testModule.getGroupings();
+        assertEquals(1, groupings.size());
+
+        final Iterator<GroupingDefinition> groupingsIterator = groupings.iterator();
+        final GroupingDefinition grouping = groupingsIterator.next();
+        assertEquals("target", grouping.getQName().getLocalName());
+        assertEquals(5, grouping.getChildNodes().size());
+
+        final AnyxmlSchemaNode anyXmlNode = (AnyxmlSchemaNode) grouping.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "data"));
+        assertNotNull(anyXmlNode);
+        final ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) grouping.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "how"));
+        assertNotNull(choiceNode);
+        final LeafSchemaNode leafNode = (LeafSchemaNode) grouping.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "address"));
+        assertNotNull(leafNode);
+        final ContainerSchemaNode containerNode = (ContainerSchemaNode) grouping.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "port"));
+        assertNotNull(containerNode);
+        final ListSchemaNode listNode = (ListSchemaNode) grouping.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "addresses"));
+        assertNotNull(listNode);
+
+        assertEquals(1, grouping.getGroupings().size());
+        assertEquals("target-inner", grouping.getGroupings().iterator().next().getQName().getLocalName());
+
+        assertEquals(1, grouping.getTypeDefinitions().size());
+        assertEquals("group-type", grouping.getTypeDefinitions().iterator().next().getQName().getLocalName());
+
+        final List<UnknownSchemaNode> unknownSchemaNodes = grouping.getUnknownSchemaNodes();
+        assertEquals(1, unknownSchemaNodes.size());
+        final UnknownSchemaNode extensionUse = unknownSchemaNodes.get(0);
+        assertEquals("opendaylight", extensionUse.getExtensionDefinition().getQName().getLocalName());
+    }
+
+    @Test
+    public void usesAndRefinesTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(MODULE, SUBMODULE, GROUPING_MODULE, USES_MODULE)
+                .buildEffective();
+        assertNotNull(result);
+
+        final Module testModule = result.findModules("foo").iterator().next();
+        assertNotNull(testModule);
+
+        final Set<UsesNode> usesNodes = testModule.getUses();
+        assertEquals(1, usesNodes.size());
+
+        UsesNode usesNode = usesNodes.iterator().next();
+        assertEquals("target", usesNode.getGroupingPath().getLastComponent().getLocalName());
+        assertEquals(1, usesNode.getAugmentations().size());
+
+        ContainerSchemaNode container = (ContainerSchemaNode) testModule.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "peer"));
+        assertNotNull(container);
+        container = (ContainerSchemaNode) container.getDataChildByName(QName.create(testModule.getQNameModule(),
+            "destination"));
+        assertEquals(1, container.getUses().size());
+
+        usesNode = container.getUses().iterator().next();
+        assertEquals("target", usesNode.getGroupingPath().getLastComponent().getLocalName());
+
+        final Map<SchemaPath, SchemaNode> refines = usesNode.getRefines();
+        assertEquals(4, refines.size());
+
+        final Iterator<SchemaPath> refinesKeysIterator = refines.keySet().iterator();
+        SchemaPath path = refinesKeysIterator.next();
+        assertThat(path.getLastComponent().getLocalName(), anyOf(is("port"), is("address"), is("addresses"), is("id")));
+        path = refinesKeysIterator.next();
+        assertThat(path.getLastComponent().getLocalName(), anyOf(is("port"), is("address"), is("addresses"), is("id")));
+        path = refinesKeysIterator.next();
+        assertThat(path.getLastComponent().getLocalName(), anyOf(is("port"), is("address"), is("addresses"), is("id")));
+        path = refinesKeysIterator.next();
+        assertThat(path.getLastComponent().getLocalName(), anyOf(is("port"), is("address"), is("addresses"), is("id")));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/GroupingTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/GroupingTest.java
new file mode 100644 (file)
index 0000000..dea745f
--- /dev/null
@@ -0,0 +1,628 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.SortedMap;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class GroupingTest {
+    private SchemaContext ctx;
+    private Module foo;
+    private Module baz;
+
+    @Before
+    public void init() throws Exception {
+        ctx = TestUtils.loadModules(getClass().getResource("/model").toURI());
+        foo = TestUtils.findModule(ctx, "foo").get();
+        baz = TestUtils.findModule(ctx, "baz").get();
+        assertEquals(3, ctx.getModules().size());
+    }
+
+    @Test
+    public void testRefine() {
+        final Module testModule = TestUtils.findModule(ctx, "foo").get();
+        final ContainerSchemaNode peer = (ContainerSchemaNode) testModule.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "peer"));
+        final ContainerSchemaNode destination = (ContainerSchemaNode) peer.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "destination"));
+
+        final Set<UsesNode> usesNodes = destination.getUses();
+        assertEquals(1, usesNodes.size());
+        final UsesNode usesNode = usesNodes.iterator().next();
+        final Map<SchemaPath, SchemaNode> refines = usesNode.getRefines();
+        assertEquals(4, refines.size());
+
+        LeafSchemaNode refineLeaf = null;
+        ContainerSchemaNode refineContainer = null;
+        ListSchemaNode refineList = null;
+        LeafSchemaNode refineInnerLeaf = null;
+        for (final Map.Entry<SchemaPath, SchemaNode> entry : refines.entrySet()) {
+            final SchemaNode value = entry.getValue();
+            if ("address".equals(value.getQName().getLocalName())) {
+                refineLeaf = (LeafSchemaNode) destination.getDataChildByName(value.getQName());
+            } else if ("port".equals(value.getQName().getLocalName())) {
+                refineContainer = (ContainerSchemaNode) destination.getDataChildByName(value.getQName());
+            } else if ("addresses".equals(value.getQName().getLocalName())) {
+                refineList = (ListSchemaNode) destination.getDataChildByName(value.getQName());
+            }
+        }
+
+        assertNotNull(refineList);
+        for (final Map.Entry<SchemaPath, SchemaNode> entry : refines.entrySet()) {
+            final SchemaNode value = entry.getValue();
+            if ("id".equals(value.getQName().getLocalName())) {
+                refineInnerLeaf = (LeafSchemaNode) refineList.getDataChildByName(value.getQName());
+            }
+        }
+
+        // leaf address
+        assertNotNull(refineLeaf);
+        assertEquals(Optional.of("IP address of target node"), refineLeaf.getDescription());
+        assertEquals(Optional.of("address reference added by refine"), refineLeaf.getReference());
+        assertFalse(refineLeaf.isConfiguration());
+        assertFalse(refineLeaf.isMandatory());
+        final Collection<MustDefinition> leafMustConstraints = refineLeaf.getMustConstraints();
+        assertEquals(1, leafMustConstraints.size());
+        final MustDefinition leafMust = leafMustConstraints.iterator().next();
+        assertEquals("ifType != 'ethernet' or (ifType = 'ethernet' and ifMTU = 1500)", leafMust.toString());
+        assertEquals(1, refineLeaf.getUnknownSchemaNodes().size());
+
+        // container port
+        assertNotNull(refineContainer);
+        final Collection<MustDefinition> mustConstraints = refineContainer.getMustConstraints();
+        assertTrue(mustConstraints.isEmpty());
+        assertEquals(Optional.of("description of port defined by refine"), refineContainer.getDescription());
+        assertEquals(Optional.of("port reference added by refine"), refineContainer.getReference());
+        assertFalse(refineContainer.isConfiguration());
+        assertTrue(refineContainer.isPresenceContainer());
+
+        // list addresses
+        assertEquals(Optional.of("description of addresses defined by refine"), refineList.getDescription());
+        assertEquals(Optional.of("addresses reference added by refine"), refineList.getReference());
+        assertFalse(refineList.isConfiguration());
+
+        final ElementCountConstraint constraint = refineList.getElementCountConstraint().get();
+        assertEquals(2, constraint.getMinElements().intValue());
+        assertNull(constraint.getMaxElements());
+
+        // leaf id
+        assertNotNull(refineInnerLeaf);
+        assertEquals(Optional.of("id of address"), refineInnerLeaf.getDescription());
+    }
+
+    @Test
+    public void testGrouping() {
+        final Module testModule = TestUtils.findModule(ctx, "baz").get();
+        final Set<GroupingDefinition> groupings = testModule.getGroupings();
+        assertEquals(1, groupings.size());
+        final GroupingDefinition grouping = groupings.iterator().next();
+        final Collection<DataSchemaNode> children = grouping.getChildNodes();
+        assertEquals(5, children.size());
+    }
+
+    @Test
+    public void testUses() {
+        // suffix _u = added by uses
+        // suffix _g = defined in grouping
+
+
+        // get grouping
+        final Set<GroupingDefinition> groupings = baz.getGroupings();
+        assertEquals(1, groupings.size());
+        final GroupingDefinition grouping = groupings.iterator().next();
+
+        // get node containing uses
+        final ContainerSchemaNode peer = (ContainerSchemaNode) foo.getDataChildByName(QName.create(
+                foo.getQNameModule(), "peer"));
+        final ContainerSchemaNode destination = (ContainerSchemaNode) peer.getDataChildByName(QName.create(
+                foo.getQNameModule(), "destination"));
+
+        // check uses
+        final Set<UsesNode> uses = destination.getUses();
+        assertEquals(1, uses.size());
+
+        // check uses process
+        final AnyxmlSchemaNode data_u = (AnyxmlSchemaNode) destination.getDataChildByName(QName.create(
+                foo.getQNameModule(), "data"));
+        assertNotNull(data_u);
+        assertTrue(data_u.isAddedByUses());
+
+        final AnyxmlSchemaNode data_g = (AnyxmlSchemaNode) grouping.getDataChildByName(QName.create(
+                baz.getQNameModule(), "data"));
+        assertNotNull(data_g);
+        assertFalse(data_g.isAddedByUses());
+        assertFalse(data_u.equals(data_g));
+        assertEquals(data_g, SchemaNodeUtils.getRootOriginalIfPossible(data_u));
+
+        final ChoiceSchemaNode how_u = (ChoiceSchemaNode) destination.getDataChildByName(QName.create(
+                foo.getQNameModule(), "how"));
+        assertNotNull(how_u);
+        TestUtils.checkIsAddedByUses(how_u, true);
+        assertEquals(2, how_u.getCases().size());
+
+        final ChoiceSchemaNode how_g = (ChoiceSchemaNode) grouping.getDataChildByName(QName.create(
+                baz.getQNameModule(), "how"));
+        assertNotNull(how_g);
+        TestUtils.checkIsAddedByUses(how_g, false);
+        assertEquals(2, how_g.getCases().size());
+        assertFalse(how_u.equals(how_g));
+        assertEquals(how_g, SchemaNodeUtils.getRootOriginalIfPossible(how_u));
+
+        final LeafSchemaNode address_u = (LeafSchemaNode) destination.getDataChildByName(QName.create(
+                foo.getQNameModule(), "address"));
+        assertNotNull(address_u);
+        assertEquals(Optional.of("1.2.3.4"), address_u.getType().getDefaultValue());
+        assertEquals(Optional.of("IP address of target node"), address_u.getDescription());
+        assertEquals(Optional.of("address reference added by refine"), address_u.getReference());
+        assertFalse(address_u.isConfiguration());
+        assertTrue(address_u.isAddedByUses());
+        assertFalse(address_u.isMandatory());
+
+        final LeafSchemaNode address_g = (LeafSchemaNode) grouping.getDataChildByName(QName.create(
+                baz.getQNameModule(), "address"));
+        assertNotNull(address_g);
+        assertFalse(address_g.isAddedByUses());
+        assertEquals(Optional.empty(), address_g.getType().getDefaultValue());
+        assertEquals(Optional.of("Target IP address"), address_g.getDescription());
+        assertFalse(address_g.getReference().isPresent());
+        assertTrue(address_g.isConfiguration());
+        assertFalse(address_u.equals(address_g));
+        assertTrue(address_g.isMandatory());
+        assertEquals(address_g, SchemaNodeUtils.getRootOriginalIfPossible(address_u));
+
+        final ContainerSchemaNode port_u = (ContainerSchemaNode) destination.getDataChildByName(QName.create(
+                foo.getQNameModule(), "port"));
+        assertNotNull(port_u);
+        TestUtils.checkIsAddedByUses(port_u, true);
+
+        final ContainerSchemaNode port_g = (ContainerSchemaNode) grouping.getDataChildByName(QName.create(
+                baz.getQNameModule(), "port"));
+        assertNotNull(port_g);
+        TestUtils.checkIsAddedByUses(port_g, false);
+        assertFalse(port_u.equals(port_g));
+        assertEquals(port_g, SchemaNodeUtils.getRootOriginalIfPossible(port_u));
+
+        final ListSchemaNode addresses_u = (ListSchemaNode) destination.getDataChildByName(QName.create(
+                foo.getQNameModule(), "addresses"));
+        assertNotNull(addresses_u);
+        TestUtils.checkIsAddedByUses(addresses_u, true);
+
+        final ListSchemaNode addresses_g = (ListSchemaNode) grouping.getDataChildByName(QName.create(
+                baz.getQNameModule(), "addresses"));
+        assertNotNull(addresses_g);
+        TestUtils.checkIsAddedByUses(addresses_g, false);
+        assertFalse(addresses_u.equals(addresses_g));
+        assertEquals(addresses_g, SchemaNodeUtils.getRootOriginalIfPossible(addresses_u));
+
+        // grouping defined by 'uses'
+        final Set<GroupingDefinition> groupings_u = destination.getGroupings();
+        assertEquals(1, groupings_u.size());
+        final GroupingDefinition grouping_u = groupings_u.iterator().next();
+        TestUtils.checkIsAddedByUses(grouping_u, true);
+
+        // grouping defined in 'grouping' node
+        final Set<GroupingDefinition> groupings_g = grouping.getGroupings();
+        assertEquals(1, groupings_g.size());
+        final GroupingDefinition grouping_g = groupings_g.iterator().next();
+        TestUtils.checkIsAddedByUses(grouping_g, false);
+        assertFalse(grouping_u.equals(grouping_g));
+
+        final List<UnknownSchemaNode> nodes_u = destination.getUnknownSchemaNodes();
+        assertEquals(1, nodes_u.size());
+        final UnknownSchemaNode node_u = nodes_u.get(0);
+        assertTrue(node_u.isAddedByUses());
+
+        final List<UnknownSchemaNode> nodes_g = grouping.getUnknownSchemaNodes();
+        assertEquals(1, nodes_g.size());
+        final UnknownSchemaNode node_g = nodes_g.get(0);
+        assertFalse(node_g.isAddedByUses());
+        assertFalse(node_u.equals(node_g));
+    }
+
+    @Test
+    public void testUsesUnderModule() {
+        // suffix _u = added by uses
+        // suffix _g = defined in grouping
+
+        // get grouping
+        final Set<GroupingDefinition> groupings = baz.getGroupings();
+        assertEquals(1, groupings.size());
+        final GroupingDefinition grouping = groupings.iterator().next();
+
+        // check uses
+        final Set<UsesNode> uses = foo.getUses();
+        assertEquals(1, uses.size());
+
+        // check uses process
+        final AnyxmlSchemaNode data_u = (AnyxmlSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+                "data"));
+        assertNotNull(data_u);
+        assertTrue(data_u.isAddedByUses());
+
+        final AnyxmlSchemaNode data_g = (AnyxmlSchemaNode) grouping.getDataChildByName(QName.create(
+            baz.getQNameModule(), "data"));
+        assertNotNull(data_g);
+        assertFalse(data_g.isAddedByUses());
+        assertFalse(data_u.equals(data_g));
+        assertEquals(data_g, SchemaNodeUtils.getRootOriginalIfPossible(data_u));
+
+        final ChoiceSchemaNode how_u = (ChoiceSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+                "how"));
+        assertNotNull(how_u);
+        TestUtils.checkIsAddedByUses(how_u, true);
+        assertFalse(how_u.isAugmenting());
+        final SortedMap<QName, CaseSchemaNode> cases_u = how_u.getCases();
+        assertEquals(2, cases_u.size());
+        final CaseSchemaNode interval = how_u.findCaseNodes("interval").iterator().next();
+        assertFalse(interval.isAugmenting());
+        final LeafSchemaNode name = (LeafSchemaNode) interval.getDataChildByName(QName.create(foo.getQNameModule(),
+                "name"));
+        assertTrue(name.isAugmenting());
+        final LeafSchemaNode intervalLeaf = (LeafSchemaNode) interval.getDataChildByName(QName.create(
+                foo.getQNameModule(), "interval"));
+        assertFalse(intervalLeaf.isAugmenting());
+
+        final ChoiceSchemaNode how_g = (ChoiceSchemaNode) grouping.getDataChildByName(QName.create(
+                baz.getQNameModule(), "how"));
+        assertNotNull(how_g);
+        TestUtils.checkIsAddedByUses(how_g, false);
+        assertFalse(how_u.equals(how_g));
+        assertEquals(how_g, SchemaNodeUtils.getRootOriginalIfPossible(how_u));
+
+        final LeafSchemaNode address_u = (LeafSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+                "address"));
+        assertNotNull(address_u);
+        assertEquals(Optional.empty(), address_u.getType().getDefaultValue());
+        assertEquals(Optional.of("Target IP address"), address_u.getDescription());
+        assertFalse(address_u.getReference().isPresent());
+        assertTrue(address_u.isConfiguration());
+        assertTrue(address_u.isAddedByUses());
+
+        final LeafSchemaNode address_g = (LeafSchemaNode) grouping.getDataChildByName(QName.create(
+                baz.getQNameModule(), "address"));
+        assertNotNull(address_g);
+        assertFalse(address_g.isAddedByUses());
+        assertEquals(Optional.empty(), address_g.getType().getDefaultValue());
+        assertEquals(Optional.of("Target IP address"), address_g.getDescription());
+        assertFalse(address_g.getReference().isPresent());
+        assertTrue(address_g.isConfiguration());
+        assertFalse(address_u.equals(address_g));
+        assertEquals(address_g, SchemaNodeUtils.getRootOriginalIfPossible(address_u));
+
+        final ContainerSchemaNode port_u = (ContainerSchemaNode) foo.getDataChildByName(QName.create(
+                foo.getQNameModule(), "port"));
+        assertNotNull(port_u);
+        TestUtils.checkIsAddedByUses(port_u, true);
+
+        final ContainerSchemaNode port_g = (ContainerSchemaNode) grouping.getDataChildByName(QName.create(
+                baz.getQNameModule(), "port"));
+        assertNotNull(port_g);
+        TestUtils.checkIsAddedByUses(port_g, false);
+        assertFalse(port_u.equals(port_g));
+        assertEquals(port_g, SchemaNodeUtils.getRootOriginalIfPossible(port_u));
+
+        final ListSchemaNode addresses_u = (ListSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+                "addresses"));
+        assertNotNull(addresses_u);
+        TestUtils.checkIsAddedByUses(addresses_u, true);
+
+        final ListSchemaNode addresses_g = (ListSchemaNode) grouping.getDataChildByName(QName.create(
+                baz.getQNameModule(), "addresses"));
+        assertNotNull(addresses_g);
+        TestUtils.checkIsAddedByUses(addresses_g, false);
+        assertFalse(addresses_u.equals(addresses_g));
+        assertEquals(addresses_g, SchemaNodeUtils.getRootOriginalIfPossible(addresses_u));
+
+        // grouping defined by 'uses'
+        final Set<GroupingDefinition> groupings_u = foo.getGroupings();
+        assertEquals(1, groupings_u.size());
+        final GroupingDefinition grouping_u = groupings_u.iterator().next();
+        TestUtils.checkIsAddedByUses(grouping_u, true);
+
+        // grouping defined in 'grouping' node
+        final Set<GroupingDefinition> groupings_g = grouping.getGroupings();
+        assertEquals(1, groupings_g.size());
+        final GroupingDefinition grouping_g = groupings_g.iterator().next();
+        TestUtils.checkIsAddedByUses(grouping_g, false);
+        assertFalse(grouping_u.equals(grouping_g));
+
+        final List<UnknownSchemaNode> nodes_u = foo.getUnknownSchemaNodes();
+        assertEquals(1, nodes_u.size());
+        final UnknownSchemaNode node_u = nodes_u.get(0);
+        assertTrue(node_u.isAddedByUses());
+
+        final List<UnknownSchemaNode> nodes_g = grouping.getUnknownSchemaNodes();
+        assertEquals(1, nodes_g.size());
+        final UnknownSchemaNode node_g = nodes_g.get(0);
+        assertFalse(node_g.isAddedByUses());
+        assertFalse(node_u.equals(node_g));
+
+        final UsesNode un = uses.iterator().next();
+        final Set<AugmentationSchemaNode> usesAugments = un.getAugmentations();
+        assertEquals(1, usesAugments.size());
+        final AugmentationSchemaNode augment = usesAugments.iterator().next();
+        assertEquals(Optional.of("inner augment"), augment.getDescription());
+        final Collection<DataSchemaNode> children = augment.getChildNodes();
+        assertEquals(1, children.size());
+        final DataSchemaNode leaf = children.iterator().next();
+        assertTrue(leaf instanceof LeafSchemaNode);
+        assertEquals("name", leaf.getQName().getLocalName());
+    }
+
+    @Test
+    public void testCascadeUses() throws ReactorException, ParseException, IOException, YangSyntaxErrorException {
+        ctx = TestUtils.loadModuleResources(getClass(), "/grouping-test/cascade-uses.yang");
+        assertEquals(1, ctx.getModules().size());
+
+        final Module testModule = TestUtils.findModule(ctx, "cascade-uses").get();
+        final Set<GroupingDefinition> groupings = testModule.getGroupings();
+
+        GroupingDefinition gu = null;
+        GroupingDefinition gv = null;
+        GroupingDefinition gx = null;
+        GroupingDefinition gy = null;
+        GroupingDefinition gz = null;
+        GroupingDefinition gzz = null;
+        for (final GroupingDefinition gd : groupings) {
+            final String name = gd.getQName().getLocalName();
+            switch (name) {
+                case "grouping-U":
+                    gu = gd;
+                    break;
+                case "grouping-V":
+                    gv = gd;
+                    break;
+                case "grouping-X":
+                    gx = gd;
+                    break;
+                case "grouping-Y":
+                    gy = gd;
+                    break;
+                case "grouping-Z":
+                    gz = gd;
+                    break;
+                case "grouping-ZZ":
+                    gzz = gd;
+                    break;
+                default:
+                    break;
+            }
+        }
+        assertNotNull(gu);
+        assertNotNull(gv);
+        assertNotNull(gx);
+        assertNotNull(gy);
+        assertNotNull(gz);
+        assertNotNull(gzz);
+
+        final QNameModule expectedModule = QNameModule.create(URI.create("urn:grouping:cascade-uses"),
+            Revision.of("2013-07-18"));
+
+        // grouping-U
+        Collection<DataSchemaNode> childNodes = gu.getChildNodes();
+        assertEquals(7, childNodes.size());
+
+        final LeafSchemaNode leafGroupingU = (LeafSchemaNode) gu.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "leaf-grouping-U"));
+        assertNotNull(leafGroupingU);
+        assertFalse(leafGroupingU.isAddedByUses());
+        assertFalse(SchemaNodeUtils.getOriginalIfPossible(leafGroupingU).isPresent());
+
+        for (final DataSchemaNode childNode : childNodes) {
+            if (!childNode.getQName().equals(leafGroupingU.getQName())) {
+                TestUtils.checkIsAddedByUses(childNode, true);
+            }
+        }
+
+        // grouping-V
+        childNodes = gv.getChildNodes();
+        assertEquals(4, childNodes.size());
+        LeafSchemaNode leafGroupingV = null;
+        ContainerSchemaNode containerGroupingV = null;
+        for (final DataSchemaNode childNode : childNodes) {
+            if ("leaf-grouping-V".equals(childNode.getQName().getLocalName())) {
+                leafGroupingV = (LeafSchemaNode) childNode;
+            } else if ("container-grouping-V".equals(childNode.getQName().getLocalName())) {
+                containerGroupingV = (ContainerSchemaNode) childNode;
+            } else {
+                TestUtils.checkIsAddedByUses(childNode, true);
+            }
+        }
+        assertNotNull(leafGroupingV);
+        assertFalse(leafGroupingV.isAddedByUses());
+
+        // grouping-V/container-grouping-V
+        assertNotNull(containerGroupingV);
+        assertFalse(containerGroupingV.isAddedByUses());
+        SchemaPath expectedPath = TestUtils.createPath(true, expectedModule, "grouping-V", "container-grouping-V");
+        assertEquals(expectedPath, containerGroupingV.getPath());
+        childNodes = containerGroupingV.getChildNodes();
+        assertEquals(2, childNodes.size());
+        for (final DataSchemaNode childNode : childNodes) {
+            TestUtils.checkIsAddedByUses(childNode, true);
+        }
+
+        // grouping-V/container-grouping-V/leaf-grouping-X
+        final LeafSchemaNode leafXinContainerV = (LeafSchemaNode) containerGroupingV.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "leaf-grouping-X"));
+        assertNotNull(leafXinContainerV);
+        expectedPath = TestUtils.createPath(true, expectedModule, "grouping-V", "container-grouping-V",
+            "leaf-grouping-X");
+        assertEquals(expectedPath, leafXinContainerV.getPath());
+        // grouping-V/container-grouping-V/leaf-grouping-Y
+        final LeafSchemaNode leafYinContainerV = (LeafSchemaNode) containerGroupingV.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "leaf-grouping-Y"));
+        assertNotNull(leafYinContainerV);
+        expectedPath = TestUtils.createPath(true, expectedModule, "grouping-V", "container-grouping-V",
+            "leaf-grouping-Y");
+        assertEquals(expectedPath, leafYinContainerV.getPath());
+
+        // grouping-X
+        childNodes = gx.getChildNodes();
+        assertEquals(2, childNodes.size());
+
+        // grouping-X/leaf-grouping-X
+        final LeafSchemaNode leafXinGX = (LeafSchemaNode) gx.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "leaf-grouping-X"));
+        assertNotNull(leafXinGX);
+        assertFalse(leafXinGX.isAddedByUses());
+        expectedPath = TestUtils.createPath(true, expectedModule, "grouping-X", "leaf-grouping-X");
+        assertEquals(expectedPath, leafXinGX.getPath());
+
+        // grouping-X/leaf-grouping-Y
+        final LeafSchemaNode leafYinGX = (LeafSchemaNode) gx.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "leaf-grouping-Y"));
+        assertNotNull(leafYinGX);
+        assertTrue(leafYinGX.isAddedByUses());
+        expectedPath = TestUtils.createPath(true, expectedModule, "grouping-X", "leaf-grouping-Y");
+        assertEquals(expectedPath, leafYinGX.getPath());
+
+        // grouping-Y
+        childNodes = gy.getChildNodes();
+        assertEquals(1, childNodes.size());
+
+        // grouping-Y/leaf-grouping-Y
+        final LeafSchemaNode leafYinGY = (LeafSchemaNode) gy.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "leaf-grouping-Y"));
+        assertNotNull(leafYinGY);
+        assertFalse(leafYinGY.isAddedByUses());
+        expectedPath = TestUtils.createPath(true, expectedModule, "grouping-Y", "leaf-grouping-Y");
+        assertEquals(expectedPath, leafYinGY.getPath());
+
+        // grouping-Z
+        childNodes = gz.getChildNodes();
+        assertEquals(1, childNodes.size());
+
+        // grouping-Z/leaf-grouping-Z
+        final LeafSchemaNode leafZinGZ = (LeafSchemaNode) gz.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "leaf-grouping-Z"));
+        assertNotNull(leafZinGZ);
+        assertFalse(leafZinGZ.isAddedByUses());
+        expectedPath = TestUtils.createPath(true, expectedModule, "grouping-Z", "leaf-grouping-Z");
+        assertEquals(expectedPath, leafZinGZ.getPath());
+
+        // grouping-ZZ
+        childNodes = gzz.getChildNodes();
+        assertEquals(1, childNodes.size());
+
+        // grouping-ZZ/leaf-grouping-ZZ
+        final LeafSchemaNode leafZZinGZZ = (LeafSchemaNode) gzz.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "leaf-grouping-ZZ"));
+        assertNotNull(leafZZinGZZ);
+        assertFalse(leafZZinGZZ.isAddedByUses());
+        expectedPath = TestUtils.createPath(true, expectedModule, "grouping-ZZ", "leaf-grouping-ZZ");
+        assertEquals(expectedPath, leafZZinGZZ.getPath());
+
+        // TEST getOriginal from grouping-U
+        assertEquals(
+                gv.getDataChildByName(QName.create(testModule.getQNameModule(), "leaf-grouping-V")),
+                SchemaNodeUtils.getRootOriginalIfPossible(gu.getDataChildByName(QName.create(
+                        testModule.getQNameModule(), "leaf-grouping-V"))));
+        containerGroupingV = (ContainerSchemaNode) gu.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "container-grouping-V"));
+        assertEquals(gv.getDataChildByName(QName.create(testModule.getQNameModule(), "container-grouping-V")),
+                SchemaNodeUtils.getRootOriginalIfPossible(containerGroupingV));
+        assertEquals(
+                gx.getDataChildByName(QName.create(testModule.getQNameModule(), "leaf-grouping-X")),
+                SchemaNodeUtils.getRootOriginalIfPossible(containerGroupingV.getDataChildByName(QName.create(
+                        testModule.getQNameModule(), "leaf-grouping-X"))));
+        assertEquals(
+                gy.getDataChildByName(QName.create(testModule.getQNameModule(), "leaf-grouping-Y")),
+                SchemaNodeUtils.getRootOriginalIfPossible(containerGroupingV.getDataChildByName(QName.create(
+                        testModule.getQNameModule(), "leaf-grouping-Y"))));
+
+        assertEquals(
+                gz.getDataChildByName(QName.create(testModule.getQNameModule(), "leaf-grouping-Z")),
+                SchemaNodeUtils.getRootOriginalIfPossible(gu.getDataChildByName(QName.create(
+                        testModule.getQNameModule(), "leaf-grouping-Z"))));
+        assertEquals(
+                gzz.getDataChildByName(QName.create(testModule.getQNameModule(), "leaf-grouping-ZZ")),
+                SchemaNodeUtils.getRootOriginalIfPossible(gu.getDataChildByName(QName.create(
+                        testModule.getQNameModule(), "leaf-grouping-ZZ"))));
+
+        // TEST getOriginal from grouping-V
+        assertEquals(
+                gz.getDataChildByName(QName.create(testModule.getQNameModule(), "leaf-grouping-Z")),
+                SchemaNodeUtils.getRootOriginalIfPossible(gv.getDataChildByName(QName.create(
+                        testModule.getQNameModule(), "leaf-grouping-Z"))));
+        assertEquals(
+                gzz.getDataChildByName(QName.create(testModule.getQNameModule(), "leaf-grouping-ZZ")),
+                SchemaNodeUtils.getRootOriginalIfPossible(gv.getDataChildByName(QName.create(
+                        testModule.getQNameModule(), "leaf-grouping-ZZ"))));
+
+        // TEST getOriginal from grouping-X
+        assertEquals(
+                gy.getDataChildByName(QName.create(testModule.getQNameModule(), "leaf-grouping-Y")),
+                SchemaNodeUtils.getRootOriginalIfPossible(gx.getDataChildByName(QName.create(
+                        testModule.getQNameModule(), "leaf-grouping-Y"))));
+    }
+
+    @Test
+    public void testAddedByUsesLeafTypeQName() throws Exception {
+        final SchemaContext loadModules = TestUtils.loadModules(getClass().getResource("/added-by-uses-leaf-test")
+                .toURI());
+        assertEquals(2, loadModules.getModules().size());
+        foo = TestUtils.findModule(loadModules, "foo").get();
+        final Module imp = TestUtils.findModule(loadModules, "import-module").get();
+
+        final LeafSchemaNode leaf = (LeafSchemaNode) ((ContainerSchemaNode) foo.getDataChildByName(QName.create(
+                foo.getQNameModule(), "my-container")))
+                .getDataChildByName(QName.create(foo.getQNameModule(), "my-leaf"));
+
+        TypeDefinition<?> impType = null;
+        final Set<TypeDefinition<?>> typeDefinitions = imp.getTypeDefinitions();
+        for (final TypeDefinition<?> typeDefinition : typeDefinitions) {
+            if (typeDefinition.getQName().getLocalName().equals("imp-type")) {
+                impType = typeDefinition;
+                break;
+            }
+        }
+
+        assertNotNull(impType);
+        assertEquals(leaf.getType().getQName(), impType.getQName());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IdentityIssueIetfRoutingTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IdentityIssueIetfRoutingTest.java
new file mode 100644 (file)
index 0000000..5b4a4e2
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2017 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class IdentityIssueIetfRoutingTest {
+    @Test
+    public void test() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/identity-ietf-routing-test/");
+        assertNotNull(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IdentityStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IdentityStmtTest.java
new file mode 100644 (file)
index 0000000..a58ece2
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.Iterator;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class IdentityStmtTest {
+
+    private static final StatementStreamSource ILLEGAL_IDENTITY_MODULE = sourceForResource(
+        "/identity/identitytest.yang");
+    private static final StatementStreamSource ILLEGAL_IDENTITY_MODULE2 = sourceForResource(
+        "/identity/prefixidentitytest.yang");
+    private static final StatementStreamSource LEGAL_IDENTITY_MODULE = sourceForResource(
+        "/identity/import/dummy.yang");
+    private static final StatementStreamSource LEGAL_IDENTITY_MODULE2 = sourceForResource(
+        "/identity/import/prefiximportidentitytest.yang");
+    private static final StatementStreamSource ILLEGAL_IDENTITY_MODULE3 = sourceForResource(
+        "/identity/illegal-chained-identity-test.yang");
+    private static final StatementStreamSource LEGAL_IDENTITY_MODULE3 = sourceForResource(
+        "/identity/legal-chained-identity-test.yang");
+    private static final StatementStreamSource DUPLICATE_IDENTITY_MODULE = sourceForResource(
+        "/identity/duplicate-identity-test.yang");
+
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void selfReferencingIdentityTest() throws ReactorException {
+        RFC7950Reactors.defaultReactor().newBuild().addSource(ILLEGAL_IDENTITY_MODULE).buildEffective();
+    }
+
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void selfReferencingIdentityWithPrefixTest() throws ReactorException {
+        RFC7950Reactors.defaultReactor().newBuild().addSource(ILLEGAL_IDENTITY_MODULE2).buildEffective();
+    }
+
+    @Test
+    public void importedIdentityTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(LEGAL_IDENTITY_MODULE, LEGAL_IDENTITY_MODULE2)
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void selfReferencingIdentityThroughChaining() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(ILLEGAL_IDENTITY_MODULE3)
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void chainedIdentityTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(LEGAL_IDENTITY_MODULE3)
+                .buildEffective();
+        assertNotNull(result);
+
+        Module testModule = result.findModules("legal-chained-identity-test").iterator().next();
+        assertNotNull(testModule);
+
+        Set<IdentitySchemaNode> identities = testModule.getIdentities();
+        assertEquals(4, identities.size());
+
+        Iterator<IdentitySchemaNode> identitiesIterator = identities.iterator();
+        IdentitySchemaNode identity = identitiesIterator.next();
+        assertThat(identity.getQName().getLocalName(), anyOf(is("first-identity"), is("second-identity"),
+            is("third-identity"), is("fourth-identity")));
+
+        identity = identitiesIterator.next();
+        assertThat(identity.getQName().getLocalName(), anyOf(is("first-identity"), is("second-identity"),
+            is("third-identity"), is("fourth-identity")));
+
+        identity = identitiesIterator.next();
+        assertThat(identity.getQName().getLocalName(), anyOf(is("first-identity"), is("second-identity"),
+            is("third-identity"), is("fourth-identity")));
+
+        identity = identitiesIterator.next();
+        assertThat(identity.getQName().getLocalName(), anyOf(is("first-identity"), is("second-identity"),
+            is("third-identity"), is("fourth-identity")));
+    }
+
+    @Test
+    public void duplicateIdentityTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(DUPLICATE_IDENTITY_MODULE)
+                .buildEffective();
+        assertNotNull(result);
+
+        Module testModule = result.findModules("duplicate-identity-test").iterator().next();
+        Set<IdentitySchemaNode> identities = testModule.getIdentities();
+        assertEquals(1, identities.size());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IfFeatureResolutionTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IfFeatureResolutionTest.java
new file mode 100644 (file)
index 0000000..82b3dbc
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class IfFeatureResolutionTest {
+
+    private static final StatementStreamSource FOO_MODULE = sourceForResource("/if-feature-resolution-test/foo.yang");
+    private static final StatementStreamSource BAR_MODULE = sourceForResource("/if-feature-resolution-test/bar.yang");
+
+    @Test
+    public void testSomeFeaturesSupported() throws ReactorException {
+        final Set<QName> supportedFeatures = ImmutableSet.of(
+                QName.create("foo-namespace", "test-feature-1"),
+                QName.create("foo-namespace", "test-feature-2"),
+                QName.create("foo-namespace", "test-feature-3"),
+                QName.create("bar-namespace", "imp-feature"));
+
+        final SchemaContext schemaContext = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(FOO_MODULE, BAR_MODULE)
+                .setSupportedFeatures(supportedFeatures)
+                .buildEffective();
+        assertNotNull(schemaContext);
+
+        final Module testModule = schemaContext.findModule("foo").get();
+        assertEquals(9, testModule.getChildNodes().size());
+
+        final ContainerSchemaNode testContainerA = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-a"));
+        assertNull(testContainerA);
+
+        final ContainerSchemaNode testContainerB = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-b"));
+        assertNotNull(testContainerB);
+        final LeafSchemaNode testLeafB = (LeafSchemaNode) testContainerB.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-b"));
+        assertNotNull(testLeafB);
+
+        final ContainerSchemaNode testContainerC = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-c"));
+        assertNotNull(testContainerC);
+        final LeafSchemaNode testLeafC = (LeafSchemaNode) testContainerC.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-c"));
+        assertNotNull(testLeafC);
+
+        final ContainerSchemaNode testContainerD = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-d"));
+        assertNull(testContainerD);
+
+        final ContainerSchemaNode testContainerE = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-e"));
+        assertNotNull(testContainerE);
+        final ContainerSchemaNode testSubContainerE = (ContainerSchemaNode) testContainerE.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-subcontainer-e"));
+        assertNotNull(testSubContainerE);
+        final LeafSchemaNode testLeafE = (LeafSchemaNode) testSubContainerE.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-e"));
+        assertNull(testLeafE);
+
+        final ContainerSchemaNode testContainerF = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-f"));
+        assertNotNull(testContainerF);
+        final ContainerSchemaNode testSubContainerF = (ContainerSchemaNode) testContainerF.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-subcontainer-f"));
+        assertNull(testSubContainerF);
+
+        final ContainerSchemaNode testContainerG = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-g"));
+        assertNotNull(testContainerG);
+        assertEquals(1, testContainerG.getAvailableAugmentations().size());
+        final LeafSchemaNode testLeafG = (LeafSchemaNode) testContainerG.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-g"));
+        assertNotNull(testLeafG);
+        final LeafSchemaNode augmentingTestLeafG = (LeafSchemaNode) testContainerG.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "augmenting-test-leaf-g"));
+        assertNull(augmentingTestLeafG);
+        final AnyxmlSchemaNode augmentingTestAnyxmlG = (AnyxmlSchemaNode) testContainerG.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "augmenting-test-anyxml-g"));
+        assertNotNull(augmentingTestAnyxmlG);
+
+        final ContainerSchemaNode testContainerH = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-h"));
+        assertNotNull(testContainerH);
+        assertEquals(0, testContainerH.getChildNodes().size());
+        assertEquals(0, testContainerH.getUses().size());
+
+        final ContainerSchemaNode testContainerI = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-i"));
+        assertNotNull(testContainerI);
+        assertEquals(1, testContainerI.getUses().size());
+        ContainerSchemaNode testGroupingSubContainer = (ContainerSchemaNode) testContainerI.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-subcontainer"));
+        assertNotNull(testGroupingSubContainer);
+        final LeafSchemaNode testGroupingLeaf = (LeafSchemaNode) testGroupingSubContainer.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-leaf"));
+        assertNull(testGroupingLeaf);
+
+        final ContainerSchemaNode testContainerJ = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-j"));
+        assertNotNull(testContainerJ);
+        final LeafSchemaNode testLeafJ = (LeafSchemaNode) testContainerJ.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-j"));
+        assertNotNull(testLeafJ);
+
+        final ContainerSchemaNode testContainerK = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-k"));
+        assertNotNull(testContainerK);
+        assertEquals(1, testContainerK.getUses().size());
+        testGroupingSubContainer = (ContainerSchemaNode) testContainerK.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-subcontainer"));
+        assertNotNull(testGroupingSubContainer);
+        assertEquals(1, testGroupingSubContainer.getAvailableAugmentations().size());
+        final LeafSchemaNode augmentingTestGroupingLeaf = (LeafSchemaNode) testGroupingSubContainer.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "augmenting-test-grouping-leaf"));
+        assertNotNull(augmentingTestGroupingLeaf);
+        final LeafSchemaNode augmentingTestGroupingLeaf2 = (LeafSchemaNode) testGroupingSubContainer.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "augmenting-test-grouping-leaf-2"));
+        assertNull(augmentingTestGroupingLeaf2);
+    }
+
+    @Test
+    public void testAllFeaturesSupported() throws ReactorException {
+        final SchemaContext schemaContext = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(FOO_MODULE, BAR_MODULE)
+                .buildEffective();
+        assertNotNull(schemaContext);
+
+        final Module testModule = schemaContext.findModules("foo").iterator().next();
+        assertNotNull(testModule);
+
+        assertEquals(11, testModule.getChildNodes().size());
+
+        final ContainerSchemaNode testContainerA = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-a"));
+        assertNotNull(testContainerA);
+        final LeafSchemaNode testLeafA = (LeafSchemaNode) testContainerA.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-a"));
+        assertNotNull(testLeafA);
+
+
+        final ContainerSchemaNode testContainerB = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-b"));
+        assertNotNull(testContainerB);
+        final LeafSchemaNode testLeafB = (LeafSchemaNode) testContainerB.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-b"));
+        assertNotNull(testLeafB);
+
+        final ContainerSchemaNode testContainerC = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-c"));
+        assertNotNull(testContainerC);
+        final LeafSchemaNode testLeafC = (LeafSchemaNode) testContainerC.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-c"));
+        assertNotNull(testLeafC);
+
+        final ContainerSchemaNode testContainerD = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-d"));
+        assertNotNull(testContainerD);
+        final LeafSchemaNode testLeafD = (LeafSchemaNode) testContainerD.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-d"));
+        assertNotNull(testLeafD);
+
+        final ContainerSchemaNode testContainerE = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-e"));
+        assertNotNull(testContainerE);
+        final ContainerSchemaNode testSubContainerE = (ContainerSchemaNode) testContainerE.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-subcontainer-e"));
+        assertNotNull(testSubContainerE);
+        final LeafSchemaNode testLeafE = (LeafSchemaNode) testSubContainerE.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-e"));
+        assertNotNull(testLeafE);
+
+        final ContainerSchemaNode testContainerF = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-f"));
+        assertNotNull(testContainerF);
+        final ContainerSchemaNode testSubContainerF = (ContainerSchemaNode) testContainerF.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-subcontainer-f"));
+        assertNotNull(testSubContainerF);
+        final ContainerSchemaNode testSubSubContainerF = (ContainerSchemaNode) testSubContainerF.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-subsubcontainer-f"));
+        assertNotNull(testSubSubContainerF);
+        final LeafSchemaNode testLeafF = (LeafSchemaNode) testSubSubContainerF.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-f"));
+        assertNotNull(testLeafF);
+
+        final ContainerSchemaNode testContainerG = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-g"));
+        assertNotNull(testContainerG);
+        assertEquals(2, testContainerG.getAvailableAugmentations().size());
+        final LeafSchemaNode testLeafG = (LeafSchemaNode) testContainerG.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-g"));
+        assertNotNull(testLeafG);
+        final LeafSchemaNode augmentingTestLeafG = (LeafSchemaNode) testContainerG.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "augmenting-test-leaf-g"));
+        assertNotNull(augmentingTestLeafG);
+        final AnyxmlSchemaNode augmentingTestAnyxmlG = (AnyxmlSchemaNode) testContainerG.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "augmenting-test-anyxml-g"));
+        assertNotNull(augmentingTestAnyxmlG);
+
+        final ContainerSchemaNode testContainerH = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-h"));
+        assertNotNull(testContainerH);
+        assertEquals(1, testContainerH.getUses().size());
+        ContainerSchemaNode testGroupingSubContainer = (ContainerSchemaNode) testContainerH.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-subcontainer"));
+        assertNotNull(testGroupingSubContainer);
+        LeafSchemaNode testGroupingLeaf = (LeafSchemaNode) testGroupingSubContainer.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-leaf"));
+        assertNotNull(testGroupingLeaf);
+
+        final ContainerSchemaNode testContainerI = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-i"));
+        assertNotNull(testContainerI);
+        assertEquals(1, testContainerI.getUses().size());
+        testGroupingSubContainer = (ContainerSchemaNode) testContainerI.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-subcontainer"));
+        assertNotNull(testGroupingSubContainer);
+        testGroupingLeaf = (LeafSchemaNode) testGroupingSubContainer.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-leaf"));
+        assertNotNull(testGroupingLeaf);
+
+        final ContainerSchemaNode testContainerJ = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-j"));
+        assertNotNull(testContainerJ);
+        final LeafSchemaNode testLeafJ = (LeafSchemaNode) testContainerJ.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-j"));
+        assertNotNull(testLeafJ);
+
+        final ContainerSchemaNode testContainerK = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-k"));
+        assertNotNull(testContainerK);
+        assertEquals(1, testContainerK.getUses().size());
+        testGroupingSubContainer = (ContainerSchemaNode) testContainerK.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-subcontainer"));
+        assertNotNull(testGroupingSubContainer);
+        assertEquals(1, testGroupingSubContainer.getAvailableAugmentations().size());
+        final LeafSchemaNode augmentingTestGroupingLeaf = (LeafSchemaNode) testGroupingSubContainer.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "augmenting-test-grouping-leaf"));
+        assertNotNull(augmentingTestGroupingLeaf);
+        final LeafSchemaNode augmentingTestGroupingLeaf2 = (LeafSchemaNode) testGroupingSubContainer.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "augmenting-test-grouping-leaf-2"));
+        assertNotNull(augmentingTestGroupingLeaf2);
+        testGroupingLeaf = (LeafSchemaNode) testGroupingSubContainer.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-leaf"));
+        assertNotNull(testGroupingLeaf);
+    }
+
+    @Test
+    public void testNoFeaturesSupported() throws ReactorException {
+        final SchemaContext schemaContext = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(FOO_MODULE, BAR_MODULE)
+                .setSupportedFeatures(ImmutableSet.of())
+                .buildEffective();
+        assertNotNull(schemaContext);
+
+        final Module testModule = schemaContext.findModules("foo").iterator().next();
+        assertNotNull(testModule);
+
+        assertEquals(6, testModule.getChildNodes().size());
+
+        final ContainerSchemaNode testContainerE = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-e"));
+        assertNotNull(testContainerE);
+        final ContainerSchemaNode testSubContainerE = (ContainerSchemaNode) testContainerE.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-subcontainer-e"));
+        assertNotNull(testSubContainerE);
+        final LeafSchemaNode testLeafE = (LeafSchemaNode) testSubContainerE.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-e"));
+        assertNull(testLeafE);
+
+        final ContainerSchemaNode testContainerF = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-f"));
+        assertNotNull(testContainerF);
+        final ContainerSchemaNode testSubContainerF = (ContainerSchemaNode) testContainerF.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-subcontainer-f"));
+        assertNull(testSubContainerF);
+
+        final ContainerSchemaNode testContainerG = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-g"));
+        assertNotNull(testContainerG);
+        assertEquals(1, testContainerG.getAvailableAugmentations().size());
+        final LeafSchemaNode testLeafG = (LeafSchemaNode) testContainerG.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-leaf-g"));
+        assertNotNull(testLeafG);
+        final LeafSchemaNode augmentingTestLeafG = (LeafSchemaNode) testContainerG.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "augmenting-test-leaf-g"));
+        assertNull(augmentingTestLeafG);
+        final AnyxmlSchemaNode augmentingTestAnyxmlG = (AnyxmlSchemaNode) testContainerG.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "augmenting-test-anyxml-g"));
+        assertNotNull(augmentingTestAnyxmlG);
+
+        final ContainerSchemaNode testContainerH = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-h"));
+        assertNotNull(testContainerH);
+        assertEquals(0, testContainerH.getChildNodes().size());
+        assertEquals(0, testContainerH.getUses().size());
+
+        final ContainerSchemaNode testContainerI = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-i"));
+        assertNotNull(testContainerI);
+        assertEquals(1, testContainerI.getUses().size());
+        ContainerSchemaNode testGroupingSubContainer = (ContainerSchemaNode) testContainerI.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-subcontainer"));
+        assertNotNull(testGroupingSubContainer);
+        LeafSchemaNode testGroupingLeaf = (LeafSchemaNode) testGroupingSubContainer.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-leaf"));
+        assertNull(testGroupingLeaf);
+
+        final ContainerSchemaNode testContainerK = (ContainerSchemaNode) testModule.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-container-k"));
+        assertNotNull(testContainerK);
+        assertEquals(1, testContainerK.getUses().size());
+        testGroupingSubContainer = (ContainerSchemaNode) testContainerK.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-subcontainer"));
+        assertNotNull(testGroupingSubContainer);
+        assertEquals(1, testGroupingSubContainer.getAvailableAugmentations().size());
+        final LeafSchemaNode augmentingTestGroupingLeaf = (LeafSchemaNode) testGroupingSubContainer.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "augmenting-test-grouping-leaf"));
+        assertNull(augmentingTestGroupingLeaf);
+        final LeafSchemaNode augmentingTestGroupingLeaf2 = (LeafSchemaNode) testGroupingSubContainer.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "augmenting-test-grouping-leaf-2"));
+        assertNull(augmentingTestGroupingLeaf2);
+        testGroupingLeaf = (LeafSchemaNode) testGroupingSubContainer.getDataChildByName(
+                QName.create(testModule.getQNameModule(), "test-grouping-leaf"));
+        assertNull(testGroupingLeaf);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ImportResolutionBasicTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ImportResolutionBasicTest.java
new file mode 100644 (file)
index 0000000..15f093a
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.ReactorDeclaredModel;
+
+public class ImportResolutionBasicTest {
+
+    private static final StatementStreamSource ROOT_WITHOUT_IMPORT = sourceForResource(
+            "/semantic-statement-parser/import-arg-parsing/nature.yang");
+    private static final StatementStreamSource IMPORT_ROOT = sourceForResource(
+            "/semantic-statement-parser/import-arg-parsing/mammal.yang");
+    private static final StatementStreamSource IMPORT_DERIVED = sourceForResource(
+            "/semantic-statement-parser/import-arg-parsing/human.yang");
+    private static final StatementStreamSource IMPORT_SELF = sourceForResource(
+            "/semantic-statement-parser/import-arg-parsing/egocentric.yang");
+    private static final StatementStreamSource CYCLE_YIN = sourceForResource(
+            "/semantic-statement-parser/import-arg-parsing/cycle-yin.yang");
+    private static final StatementStreamSource CYCLE_YANG = sourceForResource(
+            "/semantic-statement-parser/import-arg-parsing/cycle-yang.yang");
+    private static final StatementStreamSource FOO = sourceForResource(
+            "/semantic-statement-parser/bug2649/foo.yang");
+    private static final StatementStreamSource IMPORT = sourceForResource(
+            "/semantic-statement-parser/bug2649/import-module.yang");
+
+
+    @Test
+    public void inImportOrderTest() throws ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(ROOT_WITHOUT_IMPORT, IMPORT_ROOT, IMPORT_DERIVED)
+                .build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void inInverseOfImportOrderTest() throws ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(IMPORT_DERIVED, IMPORT_ROOT, ROOT_WITHOUT_IMPORT)
+                .build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void missingImportedSourceTest() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(IMPORT_DERIVED, ROOT_WITHOUT_IMPORT);
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to missing imported source");
+        } catch (ReactorException e) {
+            assertTrue(e instanceof SomeModifiersUnresolvedException);
+            assertEquals(ModelProcessingPhase.SOURCE_PRE_LINKAGE, e.getPhase());
+        }
+
+    }
+
+    @Test
+    public void circularImportsTest() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSources(CYCLE_YIN, CYCLE_YANG);
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to circular import");
+        } catch (ReactorException e) {
+            assertTrue(e instanceof SomeModifiersUnresolvedException);
+            assertEquals(ModelProcessingPhase.SOURCE_PRE_LINKAGE, e.getPhase());
+        }
+    }
+
+    @Test
+    public void selfImportTest() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(IMPORT_SELF, IMPORT_ROOT, ROOT_WITHOUT_IMPORT);
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to self import");
+        } catch (ReactorException e) {
+            assertTrue(e instanceof SomeModifiersUnresolvedException);
+            assertEquals(ModelProcessingPhase.SOURCE_PRE_LINKAGE, e.getPhase());
+        }
+    }
+
+    @Test
+    public void bug2649Test() throws ReactorException {
+        SchemaContext buildEffective = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(FOO, IMPORT)
+                .buildEffective();
+        assertNotNull(buildEffective);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ImportRevisionsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ImportRevisionsTest.java
new file mode 100644 (file)
index 0000000..b05608b
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.ReactorDeclaredModel;
+
+public class ImportRevisionsTest {
+
+    private static final StatementStreamSource ROOT_WITH_EQUAL_DATE = sourceForResource(
+        "/import-revision-date-test/root-with-equal-revision-date.yang");
+    private static final StatementStreamSource IMPORTED_WITH_EQUAL_DATE = sourceForResource(
+            "/import-revision-date-test/imported-module-with-equal-revision-date.yang");
+    private static final StatementStreamSource ROOT_WITH_UNEQUAL_DATE = sourceForResource(
+            "/import-revision-date-test/root-with-unequal-revision-date.yang");
+    private static final StatementStreamSource IMPORTED_WITH_UNEQUAL_DATE = sourceForResource(
+            "/import-revision-date-test/imported-module-with-unequal-revision-date.yang");
+    private static final StatementStreamSource ROOT_WITH_DATE = sourceForResource(
+            "/import-revision-date-test/root-with-revision-date.yang");
+    private static final StatementStreamSource IMPORTED_WITHOUT_DATE = sourceForResource(
+            "/import-revision-date-test/imported-module-without-revision-date.yang");
+    private static final StatementStreamSource ROOT_WITHOUT_DATE = sourceForResource(
+            "/import-revision-date-test/root-without-revision-date.yang");
+    private static final StatementStreamSource IMPORTED_WITH_DATE = sourceForResource(
+            "/import-revision-date-test/imported-module-with-revision-date.yang");
+    private static final StatementStreamSource ROOT_WITH_NO_DATE = sourceForResource(
+            "/import-revision-date-test/root-with-no-revision-date.yang");
+    private static final StatementStreamSource IMPORTED_WITH_NO_DATE = sourceForResource(
+            "/import-revision-date-test/imported-module-with-no-revision-date.yang");
+
+    @Test
+    public void equalRevisionDatesTest() throws ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(ROOT_WITH_EQUAL_DATE, IMPORTED_WITH_EQUAL_DATE)
+                .build();
+        assertNotNull(result);
+    }
+
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void unequalRevisionDatesTest() throws ReactorException {
+        RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(ROOT_WITH_UNEQUAL_DATE, IMPORTED_WITH_UNEQUAL_DATE)
+                .build();
+    }
+
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void revisionDatesInRootOnlyTest() throws ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(ROOT_WITH_DATE, IMPORTED_WITHOUT_DATE)
+                .build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void revisionDatesInImportedOnlyTest() throws ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(ROOT_WITHOUT_DATE, IMPORTED_WITH_DATE)
+                .build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void noRevisionInRootAndImportedTest() throws ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(ROOT_WITH_NO_DATE, IMPORTED_WITH_NO_DATE)
+                .build();
+        assertNotNull(result);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IncludeResolutionTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IncludeResolutionTest.java
new file mode 100644 (file)
index 0000000..5daf0a8
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.ReactorDeclaredModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class IncludeResolutionTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(IncludeResolutionTest.class);
+
+    private static final StatementStreamSource ROOT = sourceForResource(
+            "/semantic-statement-parser/include-arg-parsing/root-module.yang");
+    private static final StatementStreamSource SUBMODULE1 = sourceForResource(
+            "/semantic-statement-parser/include-arg-parsing/submodule-1.yang");
+    private static final StatementStreamSource SUBMODULE2 = sourceForResource(
+            "/semantic-statement-parser/include-arg-parsing/submodule-2.yang");
+    private static final StatementStreamSource ERROR_MODULE = sourceForResource(
+            "/semantic-statement-parser/include-arg-parsing/error-module.yang");
+    private static final StatementStreamSource ERROR_SUBMODULE = sourceForResource(
+            "/semantic-statement-parser/include-arg-parsing/error-submodule.yang");
+
+    private static final StatementStreamSource MISSING_PARENT_MODULE = sourceForResource(
+            "/semantic-statement-parser/include-arg-parsing/missing-parent.yang");
+
+    @Test
+    public void includeTest() throws SourceException, ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(ROOT, SUBMODULE1, SUBMODULE2)
+                .build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void missingIncludedSourceTest() throws SourceException {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSource(ERROR_MODULE);
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to missing included source");
+        } catch (ReactorException e) {
+            assertTrue(e instanceof SomeModifiersUnresolvedException);
+            assertEquals(ModelProcessingPhase.SOURCE_LINKAGE, e.getPhase());
+            LOG.info(e.getMessage());
+        }
+
+    }
+
+    @Test
+    public void missingIncludedSourceTest2() throws SourceException {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSource(ERROR_SUBMODULE);
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to missing included source");
+        } catch (ReactorException e) {
+            assertTrue(e instanceof SomeModifiersUnresolvedException);
+            assertEquals(ModelProcessingPhase.SOURCE_LINKAGE, e.getPhase());
+            LOG.info(e.getMessage());
+        }
+
+    }
+
+    @Test
+    public void missingIncludedSourceTest3() throws SourceException, ReactorException {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSource(MISSING_PARENT_MODULE);
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to missing belongsTo source");
+        } catch (ReactorException e) {
+            LOG.info(e.getMessage());
+        }
+
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IncludeRevisionsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IncludeRevisionsTest.java
new file mode 100644 (file)
index 0000000..694fc7e
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.ReactorDeclaredModel;
+
+public class IncludeRevisionsTest {
+
+    private static final StatementStreamSource EQUAL_ROOT = sourceForResource("/revisions/equal-root.yang");
+    private static final StatementStreamSource EQUAL_REV = sourceForResource("/revisions/equal-rev.yang");
+    private static final StatementStreamSource UNEQUAL_ROOT = sourceForResource("/revisions/unequal-root.yang");
+    private static final StatementStreamSource UNEQUAL_REV = sourceForResource("/revisions/unequal-rev.yang");
+    private static final StatementStreamSource SUBMOD_ONLY_ROOT = sourceForResource("/revisions/submod-only-root.yang");
+    private static final StatementStreamSource SUBMOD_ONLY_REV = sourceForResource("/revisions/submod-only-rev.yang");
+    private static final StatementStreamSource MOD_ONLY_ROOT = sourceForResource("/revisions/mod-only-root.yang");
+    private static final StatementStreamSource MOD_ONLY_REV = sourceForResource("/revisions/mod-only-rev.yang");
+    private static final StatementStreamSource NOWHERE_ROOT = sourceForResource("/revisions/nowhere-root.yang");
+    private static final StatementStreamSource NOWHERE_REV = sourceForResource("/revisions/nowhere-rev.yang");
+
+    @Test
+    public void revsEqualTest() throws ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(EQUAL_REV, EQUAL_ROOT)
+                .build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void revsUnequalTest() throws ReactorException {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSources(UNEQUAL_REV, UNEQUAL_ROOT);
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to unequal revisions in include and submodule");
+        } catch (ReactorException e) {
+            assertTrue(e instanceof SomeModifiersUnresolvedException);
+            assertEquals(ModelProcessingPhase.SOURCE_LINKAGE, e.getPhase());
+        }
+    }
+
+    @Test
+    public void revIncludeOnly() throws ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(SUBMOD_ONLY_REV, SUBMOD_ONLY_ROOT)
+                .build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void revInModuleOnly() throws ReactorException {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSources(MOD_ONLY_REV, MOD_ONLY_ROOT);
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to missing revision in included submodule");
+        } catch (ReactorException e) {
+            assertTrue(e instanceof SomeModifiersUnresolvedException);
+            assertEquals(ModelProcessingPhase.SOURCE_LINKAGE, e.getPhase());
+        }
+    }
+
+    @Test
+    public void revNowhereTest() throws ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(NOWHERE_REV, NOWHERE_ROOT)
+                .build();
+        assertNotNull(result);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IncludedStmtsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/IncludedStmtsTest.java
new file mode 100644 (file)
index 0000000..e078597
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.Iterator;
+import java.util.Set;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class IncludedStmtsTest {
+    private static SchemaContext result;
+
+    @BeforeClass
+    public static void setup() throws ReactorException {
+        result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/included-statements-test/root-module.yang"))
+                .addSource(sourceForResource("/included-statements-test/child-module.yang"))
+                .buildEffective();
+    }
+
+    @AfterClass
+    public static void teardown() {
+        result = null;
+    }
+
+    @Test
+    public void includedTypedefsTest() {
+        final Module testModule = result.findModules("root-module").iterator().next();
+        assertNotNull(testModule);
+
+        final Set<TypeDefinition<?>> typedefs = testModule.getTypeDefinitions();
+        assertEquals(2, typedefs.size());
+
+        final Iterator<TypeDefinition<?>> typedefsIterator = typedefs.iterator();
+        TypeDefinition<?> typedef = typedefsIterator.next();
+        assertThat(typedef.getQName().getLocalName(), anyOf(is("new-string-type"), is("new-int32-type")));
+        assertThat(typedef.getBaseType().getQName().getLocalName(), anyOf(is("string"), is("int32")));
+        typedef = typedefsIterator.next();
+        assertThat(typedef.getQName().getLocalName(), anyOf(is("new-string-type"), is("new-int32-type")));
+        assertThat(typedef.getBaseType().getQName().getLocalName(), anyOf(is("string"), is("int32")));
+    }
+
+    @Test
+    public void includedFeaturesTest() {
+        final Module testModule = result.findModules("root-module").iterator().next();
+        assertNotNull(testModule);
+
+        final Set<FeatureDefinition> features = testModule.getFeatures();
+        assertEquals(2, features.size());
+
+        final Iterator<FeatureDefinition> featuresIterator = features.iterator();
+        FeatureDefinition feature = featuresIterator.next();
+        assertThat(feature.getQName().getLocalName(), anyOf(is("new-feature1"), is("new-feature2")));
+        feature = featuresIterator.next();
+        assertThat(feature.getQName().getLocalName(), anyOf(is("new-feature1"), is("new-feature2")));
+    }
+
+    @Test
+    public void includedContainersAndListsTest() {
+        final Module testModule = result.findModules("root-module").iterator().next();
+        assertNotNull(testModule);
+
+        ContainerSchemaNode cont = (ContainerSchemaNode) testModule.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "parent-container"));
+        assertNotNull(cont);
+        cont = (ContainerSchemaNode) cont.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "child-container"));
+        assertNotNull(cont);
+        assertEquals(2, cont.getChildNodes().size());
+
+        LeafSchemaNode leaf = (LeafSchemaNode) cont.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "autumn-leaf"));
+        assertNotNull(leaf);
+        leaf = (LeafSchemaNode) cont.getDataChildByName(QName.create(testModule.getQNameModule(), "winter-snow"));
+        assertNotNull(leaf);
+    }
+
+    @Test
+    public void submoduleNamespaceTest() {
+        final Module testModule = result.findModules("root-module").iterator().next();
+        assertNotNull(testModule);
+
+        final Module subModule = testModule.getSubmodules().iterator().next();
+        assertEquals("urn:opendaylight.org/root-module", subModule.getNamespace().toString());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/KeyTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/KeyTest.java
new file mode 100644 (file)
index 0000000..1d0bbc3
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.ReactorDeclaredModel;
+
+public class KeyTest {
+
+    private static final StatementStreamSource KEY_SIMPLE_AND_COMP = sourceForResource(
+            "/semantic-statement-parser/key-arg-parsing/key-simple-and-comp.yang");
+    private static final StatementStreamSource KEY_COMP_DUPLICATE = sourceForResource(
+            "/semantic-statement-parser/key-arg-parsing/key-comp-duplicate.yang");
+
+    @Test
+    public void keySimpleTest() throws ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(KEY_SIMPLE_AND_COMP)
+                .build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void keyCompositeInvalid() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSource(KEY_COMP_DUPLICATE);
+        try {
+            reactor.build();
+            fail("reactor.process should fail due to duplicate name in key");
+        } catch (ReactorException e) {
+            final Throwable cause = e.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith("Key argument 'key1 key2 key2' contains duplicates"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ListKeysTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ListKeysTest.java
new file mode 100644 (file)
index 0000000..7899ffc
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+
+public class ListKeysTest {
+
+    @Test
+    public void correctListKeysTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/list-keys-test/correct-list-keys-test.yang"))
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void incorrectListKeysTest1() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/list-keys-test/incorrect-list-keys-test.yang"));
+        try {
+            reactor.buildEffective();
+            fail("effective build should fail due to list instead of leaf referenced in list key");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith("Key 'test1_key1 test1_key2' misses node 'test1_key2'"));
+        }
+    }
+
+    @Test
+    public void incorrectListKeysTest2() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/list-keys-test/incorrect-list-keys-test2.yang"));
+        try {
+            reactor.buildEffective();
+            fail("effective build should fail due to missing leaf referenced in list key");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith("Key 'test1_key1 test1_key2' misses node 'test1_key2'"));
+        }
+    }
+
+    @Test
+    public void incorrectListKeysTest3() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/list-keys-test/incorrect-list-keys-test3.yang"));
+        try {
+            reactor.buildEffective();
+            fail("effective build should fail due to list instead of leaf in grouping referenced in list key");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith("Key 'grp_list' misses node 'grp_list'"));
+        }
+    }
+
+    @Test
+    public void incorrectListKeysTest4()  {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/list-keys-test/incorrect-list-keys-test4.yang"));
+        try {
+            reactor.buildEffective();
+            fail("effective build should fail due to list instead of leaf in grouping augmented to list referenced "
+                    + "in list key");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getMessage().startsWith("Key 'grp_leaf' misses node 'grp_leaf'"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ListTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ListTest.java
new file mode 100644 (file)
index 0000000..c8479e0
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.List;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+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.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class ListTest {
+
+    @Test
+    public void listAndLeavesTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/list-test/list-test.yang"))
+                .buildEffective();
+        assertNotNull(result);
+
+        final Module testModule = result.findModules("list-test").iterator().next();
+        assertNotNull(testModule);
+
+        final ListSchemaNode list = (ListSchemaNode) testModule.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "simple-list"));
+        assertNotNull(list);
+
+        assertTrue(list.isUserOrdered());
+        assertTrue(list.isConfiguration());
+        final List<QName> keys = list.getKeyDefinition();
+        assertEquals(2, keys.size());
+
+        assertEquals("key1", keys.get(0).getLocalName());
+        assertEquals("key2", keys.get(1).getLocalName());
+
+        ElementCountConstraint constraint = list.getElementCountConstraint().get();
+        assertEquals(1, constraint.getMinElements().intValue());
+        assertEquals(10, constraint.getMaxElements().intValue());
+
+        assertEquals(5, list.getChildNodes().size());
+
+        LeafSchemaNode leaf = (LeafSchemaNode) list.getDataChildByName(QName.create(testModule.getQNameModule(),
+            "key1"));
+        assertNotNull(leaf);
+        assertTrue(leaf.isMandatory());
+        assertEquals("int32", leaf.getType().getQName().getLocalName());
+
+        leaf = (LeafSchemaNode) list.getDataChildByName(QName.create(testModule.getQNameModule(), "key2"));
+        assertNotNull(leaf);
+        assertTrue(leaf.isMandatory());
+        assertEquals("int16", leaf.getType().getQName().getLocalName());
+
+        leaf = (LeafSchemaNode) list.getDataChildByName(QName.create(testModule.getQNameModule(), "old-leaf"));
+        assertNotNull(leaf);
+        assertFalse(leaf.isMandatory());
+        assertEquals("string", leaf.getType().getQName().getLocalName());
+
+        leaf = (LeafSchemaNode) list.getDataChildByName(QName.create(testModule.getQNameModule(), "young-leaf"));
+        assertNotNull(leaf);
+        assertFalse(leaf.isMandatory());
+        assertEquals("young-leaf", leaf.getType().getQName().getLocalName());
+        assertEquals(Optional.of("default-value"), leaf.getType().getDefaultValue());
+
+        final LeafListSchemaNode leafList = (LeafListSchemaNode) list.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "list-of-leaves"));
+        assertNotNull(leafList);
+        assertTrue(leafList.isUserOrdered());
+
+        constraint = leafList.getElementCountConstraint().get();
+        assertEquals(2, constraint.getMinElements().intValue());
+        assertEquals(20, constraint.getMaxElements().intValue());
+        assertEquals("string", leafList.getType().getQName().getLocalName());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/MoreRevisionsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/MoreRevisionsTest.java
new file mode 100644 (file)
index 0000000..55cb141
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.net.URI;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class MoreRevisionsTest {
+
+    private static final StatementStreamSource REVFILE = sourceForResource(
+            "/semantic-statement-parser/revisions/more-revisions-test.yang");
+
+    private static final StatementStreamSource TED_20130712 = sourceForResource(
+            "/semantic-statement-parser/two-revisions/ted@2013-07-12.yang");
+
+    private static final StatementStreamSource TED_20131021 = sourceForResource(
+            "/semantic-statement-parser/two-revisions/ted@2013-10-21.yang");
+
+    private static final StatementStreamSource IETF_TYPES = sourceForResource(
+            "/ietf/ietf-inet-types@2010-09-24.yang");
+
+    private static final StatementStreamSource NETWORK_TOPOLOGY_20130712 = sourceForResource(
+            "/ietf/network-topology@2013-07-12.yang");
+
+    private static final StatementStreamSource NETWORK_TOPOLOGY_20131021 = sourceForResource(
+            "/ietf/network-topology@2013-10-21.yang");
+
+    private static final StatementStreamSource ISIS_20130712 = sourceForResource(
+            "/semantic-statement-parser/two-revisions/isis-topology@2013-07-12.yang");
+
+    private static final StatementStreamSource ISIS_20131021 = sourceForResource(
+            "/semantic-statement-parser/two-revisions/isis-topology@2013-10-21.yang");
+
+    private static final StatementStreamSource L3_20130712 = sourceForResource(
+            "/semantic-statement-parser/two-revisions/l3-unicast-igp-topology@2013-07-12.yang");
+
+    private static final StatementStreamSource L3_20131021 = sourceForResource(
+            "/semantic-statement-parser/two-revisions/l3-unicast-igp-topology@2013-10-21.yang");
+
+    @Test
+    public void readAndParseYangFileTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild().addSource(REVFILE).buildEffective();
+        assertNotNull(result);
+        final Module moduleByName = result.getModules().iterator().next();
+        assertEquals("2015-06-07", moduleByName.getQNameModule().getRevision().get().toString());
+    }
+
+    @Test
+    public void twoRevisionsTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(TED_20130712, TED_20131021, IETF_TYPES)
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void twoRevisionsTest2() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(NETWORK_TOPOLOGY_20130712, NETWORK_TOPOLOGY_20131021, IETF_TYPES)
+                .buildEffective();
+        assertNotNull(result);
+        Set<Module> modules = result.getModules();
+
+        assertEquals(3, modules.size());
+        assertEquals(2, StmtTestUtils.findModules(modules, "network-topology").size());
+    }
+
+    @Test
+    public void moreRevisionsListKeyTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(TED_20130712, TED_20131021, ISIS_20130712, ISIS_20131021, L3_20130712, L3_20131021)
+                .addSources(IETF_TYPES,NETWORK_TOPOLOGY_20130712, NETWORK_TOPOLOGY_20131021)
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void multipleRevisionsTest() throws Exception {
+        for (int i = 0; i < 100; i++) {
+            SchemaContext context = StmtTestUtils
+                    .parseYangSources("/semantic-statement-parser/multiple-revisions");
+            assertNotNull(context);
+        }
+    }
+
+    @Test
+    public void multipleRevisionsFullTest() throws Exception {
+        for (int i = 0; i < 100; i++) {
+            SchemaContext context = StmtTestUtils
+                    .parseYangSources("/semantic-statement-parser/multiple-revisions/full");
+            assertNotNull(context);
+            assertEquals(6, context.getModules().size());
+            checkContentFullTest(context);
+        }
+    }
+
+    private static void checkContentFullTest(final SchemaContext context) {
+        URI yangTypesNS = URI.create("urn:ietf:params:xml:ns:yang:ietf-yang-types");
+
+        final Revision rev20100924 = Revision.of("2010-09-24");
+        final Revision rev20130516 = Revision.of("2013-05-16");
+        final Revision rev20130715 = Revision.of("2013-07-15");
+
+        final QNameModule yangTypes20100924 = QNameModule.create(yangTypesNS, rev20100924);
+        final QNameModule yangTypes20130516 = QNameModule.create(yangTypesNS, rev20130516);
+        final QNameModule yangTypes20130715 = QNameModule.create(yangTypesNS, rev20130715);
+
+        final QName dateTimeTypeDef20100924 = QName.create(yangTypes20100924, "date-and-time");
+        final QName dateTimeTypeDef20130516 = QName.create(yangTypes20130516, "date-and-time");
+        final QName dateTimeTypeDef20130715 = QName.create(yangTypes20130715, "date-and-time");
+
+        Module yangTypesModule20100924 = context.findModule("ietf-yang-types", rev20100924).get();
+        Module yangTypesModule20130516 = context.findModule("ietf-yang-types", rev20130516).get();
+        Module yangTypesModule20130715 = context.findModule("ietf-yang-types", rev20130715).get();
+        assertTrue(findTypeDef(yangTypesModule20100924, dateTimeTypeDef20100924));
+        assertTrue(findTypeDef(yangTypesModule20130516, dateTimeTypeDef20130516));
+        assertTrue(findTypeDef(yangTypesModule20130715, dateTimeTypeDef20130715));
+
+        checkNetconfMonitoringModuleFullTest(context, rev20130715, dateTimeTypeDef20130715);
+        checkInterfacesModuleFullTest(context, rev20100924, dateTimeTypeDef20100924);
+    }
+
+    private static void checkInterfacesModuleFullTest(final SchemaContext context, final Revision rev20100924,
+            final QName dateTimeTypeDef20100924) {
+        Revision rev20121115 = Revision.of("2012-11-15");
+
+        Module interfacesModule20121115 = context.findModule("ietf-interfaces", rev20121115).get();
+        Set<ModuleImport> imports = interfacesModule20121115.getImports();
+        assertEquals(1, imports.size());
+        ModuleImport interfacesImport = imports.iterator().next();
+        assertEquals("ietf-yang-types", interfacesImport.getModuleName());
+        assertEquals(Optional.of(rev20100924), interfacesImport.getRevision());
+    }
+
+    private static void checkNetconfMonitoringModuleFullTest(final SchemaContext context,
+            final Revision rev20130715, final QName dateTimeTypeDef20130715) {
+        Revision rev20101004 = Revision.of("2010-10-04");
+
+        Module monitoringModule20101004 = context.findModule("ietf-netconf-monitoring", rev20101004).get();
+        Set<ModuleImport> imports = monitoringModule20101004.getImports();
+        assertEquals(2, imports.size());
+        for (ModuleImport monitoringImport : imports) {
+            if (monitoringImport.getModuleName().equals("ietf-yang-types")) {
+                assertEquals(Optional.of(rev20130715), monitoringImport.getRevision());
+            }
+        }
+    }
+
+    @Test
+    public void multipleRevisionsSimpleTest() throws Exception {
+        for (int i = 0; i < 1000; i++) {
+            SchemaContext context = StmtTestUtils.parseYangSources(
+                "/semantic-statement-parser/multiple-revisions/simple");
+            assertNotNull(context);
+            assertEquals(5, context.getModules().size());
+            checkContentSimpleTest(context);
+        }
+    }
+
+    private static void checkContentSimpleTest(final SchemaContext context) {
+        URI yangTypesNS = URI.create("urn:ietf:params:xml:ns:yang:ietf-yang-types");
+
+        final Revision rev20100924 = Revision.of("2010-09-24");
+        final Revision rev20130516 = Revision.of("2013-05-16");
+        final Revision rev20130715 = Revision.of("2013-07-15");
+
+        final QNameModule yangTypes20100924 = QNameModule.create(yangTypesNS, rev20100924);
+        final QNameModule yangTypes20130516 = QNameModule.create(yangTypesNS, rev20130516);
+        final QNameModule yangTypes20130715 = QNameModule.create(yangTypesNS, rev20130715);
+
+        final QName dateTimeTypeDef20100924 = QName.create(yangTypes20100924, "date-and-time");
+        final QName dateTimeTypeDef20130516 = QName.create(yangTypes20130516, "date-and-time");
+        final QName dateTimeTypeDef20130715 = QName.create(yangTypes20130715, "date-and-time");
+
+        Module yangTypesModule20100924 = context.findModule("ietf-yang-types", rev20100924).get();
+        Module yangTypesModule20130516 = context.findModule("ietf-yang-types", rev20130516).get();
+        Module yangTypesModule20130715 = context.findModule("ietf-yang-types", rev20130715).get();
+        assertTrue(findTypeDef(yangTypesModule20100924, dateTimeTypeDef20100924));
+        assertTrue(findTypeDef(yangTypesModule20130516, dateTimeTypeDef20130516));
+        assertTrue(findTypeDef(yangTypesModule20130715, dateTimeTypeDef20130715));
+
+        checkNetconfMonitoringModuleSimpleTest(context, rev20130715, dateTimeTypeDef20130715);
+        checkInterfacesModuleSimpleTest(context, rev20100924, dateTimeTypeDef20100924);
+    }
+
+    private static void checkInterfacesModuleSimpleTest(final SchemaContext context,
+            final Revision rev20100924, final QName dateTimeTypeDef20100924) {
+        URI interfacesNS = URI.create("urn:ietf:params:xml:ns:yang:ietf-interfaces");
+        Revision rev20121115 = Revision.of("2012-11-15");
+        final QNameModule interfacesNS20121115 = QNameModule.create(interfacesNS, rev20121115);
+        QName lastChange = QName.create(interfacesNS20121115, "last-change");
+
+        Module interfacesModule20121115 = context.findModule("ietf-interfaces", rev20121115).get();
+        DataSchemaNode leafLastChange = interfacesModule20121115.getDataChildByName(lastChange);
+        assertNotNull(leafLastChange);
+
+        assertTrue(leafLastChange instanceof LeafSchemaNode);
+        QName lastChangeTypeQName = ((LeafSchemaNode) leafLastChange).getType().getQName();
+        assertEquals(dateTimeTypeDef20100924, lastChangeTypeQName);
+
+        Set<ModuleImport> imports = interfacesModule20121115.getImports();
+        assertEquals(1, imports.size());
+        ModuleImport interfacesImport = imports.iterator().next();
+        assertEquals("ietf-yang-types", interfacesImport.getModuleName());
+        assertEquals(Optional.of(rev20100924), interfacesImport.getRevision());
+    }
+
+    private static void checkNetconfMonitoringModuleSimpleTest(final SchemaContext context,
+            final Revision rev20130715, final QName dateTimeTypeDef20130715) {
+        URI monitoringNS = URI.create("urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring");
+        final QNameModule monitoring19700101 = QNameModule.create(monitoringNS);
+        QName lockedTime = QName.create(monitoring19700101, "locked-time");
+
+        Module monitoringModule19700101 = context.findModule("ietf-netconf-monitoring").get();
+        DataSchemaNode leafLockedTime = monitoringModule19700101.getDataChildByName(lockedTime);
+        assertNotNull(leafLockedTime);
+
+        assertTrue(leafLockedTime instanceof LeafSchemaNode);
+        QName lockedTimeTypeQName = ((LeafSchemaNode) leafLockedTime).getType().getQName();
+        assertEquals(dateTimeTypeDef20130715, lockedTimeTypeQName);
+
+        Set<ModuleImport> imports = monitoringModule19700101.getImports();
+        assertEquals(1, imports.size());
+        ModuleImport monitoringImport = imports.iterator().next();
+        assertEquals("ietf-yang-types", monitoringImport.getModuleName());
+        assertEquals(Optional.of(rev20130715), monitoringImport.getRevision());
+    }
+
+    private static boolean findTypeDef(final Module module, final QName typedef) {
+        for (TypeDefinition<?> typeDefinition : module.getTypeDefinitions()) {
+            if (typeDefinition.getQName().equals(typedef)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Test
+    public void nodeTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources(
+            "/semantic-statement-parser/multiple-revisions/node-test");
+        assertNotNull(context);
+
+        QName root = QName.create("foo", "2016-04-06", "foo-root");
+        QName container20160404 = QName.create("foo", "2016-04-06", "con20160404");
+        SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true, root,
+            container20160404));
+        assertTrue(findDataSchemaNode instanceof ContainerSchemaNode);
+
+        QName container20160405 = QName.create("foo", "2016-04-06", "con20160405");
+        findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true, root,
+            container20160405));
+        assertTrue(findDataSchemaNode instanceof ContainerSchemaNode);
+
+        QName container20160406 = QName.create("foo", "2016-04-06", "con20160406");
+        findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true, root,
+            container20160406));
+        assertNull(findDataSchemaNode);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/MustAndWhenStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/MustAndWhenStmtTest.java
new file mode 100644 (file)
index 0000000..98e6656
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class MustAndWhenStmtTest {
+    @Test
+    public void mustStmtTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/must-when-stmt-test/must-test.yang"))
+                .buildEffective();
+        assertNotNull(result);
+
+        final Module testModule = result.findModules("must-test").iterator().next();
+        assertNotNull(testModule);
+
+        final ContainerSchemaNode container = (ContainerSchemaNode) testModule.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "interface"));
+        assertNotNull(container);
+        assertTrue(container.isPresenceContainer());
+
+        final Collection<MustDefinition> musts = container.getMustConstraints();
+        assertEquals(2, musts.size());
+
+        final Iterator<MustDefinition> mustsIterator = musts.iterator();
+        MustDefinition mustStmt = mustsIterator.next();
+        assertThat(mustStmt.getXpath().getOriginalString(), anyOf(is("ifType != 'ethernet' or (ifType = 'ethernet' and "
+                + "ifMTU = 1500)"), is("ifType != 'atm' or (ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)")));
+        assertThat(mustStmt.getErrorMessage(), anyOf(is(Optional.of("An ethernet MTU must be 1500")),
+            is(Optional.of("An atm MTU must be 64 .. 17966"))));
+        assertThat(mustStmt.getErrorAppTag(), anyOf(is(Optional.of("An ethernet error")),
+            is(Optional.of("An atm error"))));
+        mustStmt = mustsIterator.next();
+        assertThat(mustStmt.getXpath().getOriginalString(), anyOf(
+            is("ifType != 'ethernet' or (ifType = 'ethernet' and ifMTU = 1500)"),
+            is("ifType != 'atm' or (ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)")));
+        assertThat(mustStmt.getErrorMessage(), anyOf(is(Optional.of("An ethernet MTU must be 1500")),
+            is(Optional.of("An atm MTU must be 64 .. 17966"))));
+        assertThat(mustStmt.getErrorAppTag(), anyOf(is(Optional.of("An ethernet error")),
+            is(Optional.of("An atm error"))));
+    }
+
+    @Test
+    public void whenStmtTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/must-when-stmt-test/when-test.yang"))
+                .buildEffective();
+        assertNotNull(result);
+
+        final Module testModule = result.findModules("when-test").iterator().next();
+        assertNotNull(testModule);
+
+        final ContainerSchemaNode container = (ContainerSchemaNode) testModule.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "test-container"));
+        assertNotNull(container);
+        assertEquals("conditional-leaf = 'autumn-leaf'", container.getWhenCondition().get().getOriginalString());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/NameCollisionWithinCaseTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/NameCollisionWithinCaseTest.java
new file mode 100644 (file)
index 0000000..574654d
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.stmt;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public class NameCollisionWithinCaseTest {
+    @Test
+    public void testChildNameCollisionOfAugmentCase() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/bugs/name-collision-within-case/foo.yang");
+            fail("Expected failure due to node name collision");
+        } catch (ReactorException e) {
+            final Throwable cause = e.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith(
+                "Cannot add data tree child with name (foo?revision=2018-02-11)bar, a conflicting child already exists "
+                        + "[at "));
+        }
+    }
+
+    @Test
+    public void testChildNameCollisionOfAugmentChoice() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/bugs/name-collision-within-case/bar.yang");
+            fail("Expected failure due to node name collision");
+        } catch (ReactorException e) {
+            final Throwable cause = e.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith(
+                "Cannot add data tree child with name (bar?revision=2018-02-11)bar, a conflicting child already exists "
+                        + "[at "));
+        }
+    }
+
+    @Test
+    public void testChildNameCollisionNormal() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/bugs/name-collision-within-case/baz.yang");
+            fail("Expected failure due to node name collision");
+        } catch (ReactorException e) {
+            final Throwable cause = e.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith(
+                "Error in module 'baz': cannot add '(baz?revision=2018-02-28)bar'. Node name collision: "));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/NotificationStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/NotificationStmtTest.java
new file mode 100644 (file)
index 0000000..87f6356
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class NotificationStmtTest {
+
+    @Test
+    public void notificationTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(sourceForResource("/model/baz.yang"), sourceForResource("/model/bar.yang"))
+                .buildEffective();
+        assertNotNull(result);
+
+        final Module testModule = result.findModules("baz").iterator().next();
+        assertNotNull(testModule);
+
+        final Set<NotificationDefinition> notifications = testModule.getNotifications();
+        assertEquals(1, notifications.size());
+
+        final NotificationDefinition notification = notifications.iterator().next();
+        assertEquals("event", notification.getQName().getLocalName());
+        assertEquals(3, notification.getChildNodes().size());
+
+        LeafSchemaNode leaf = (LeafSchemaNode) notification.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "event-class"));
+        assertNotNull(leaf);
+        leaf = (LeafSchemaNode) notification.getDataChildByName(QName.create(testModule.getQNameModule(), "severity"));
+        assertNotNull(leaf);
+        final AnyxmlSchemaNode anyXml = (AnyxmlSchemaNode) notification.getDataChildByName(QName.create(
+            testModule.getQNameModule(), "reporting-entity"));
+        assertNotNull(anyXml);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/OrderingTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/OrderingTest.java
new file mode 100644 (file)
index 0000000..9b44d3b
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class OrderingTest {
+
+    private SchemaContext context;
+    private Module foo;
+    private Module bar;
+    private Module baz;
+
+    @Before
+    public void setup() throws ReactorException, IOException, YangSyntaxErrorException, URISyntaxException {
+        context = TestUtils.loadModules(getClass().getResource("/model").toURI());
+        foo = TestUtils.findModule(context, "foo").get();
+        bar = TestUtils.findModule(context, "bar").get();
+        baz = TestUtils.findModule(context, "baz").get();
+    }
+
+    @Test
+    public void testOrderingTypedef() throws Exception {
+        final Set<TypeDefinition<?>> typedefs = bar.getTypeDefinitions();
+        final String[] expectedOrder = { "int32-ext1", "int32-ext2", "string-ext1", "string-ext2", "string-ext3",
+            "string-ext4", "invalid-string-pattern", "multiple-pattern-string", "my-decimal-type", "my-union",
+            "my-union-ext", "nested-union2"
+        };
+        final String[] actualOrder = new String[typedefs.size()];
+
+        int offset = 0;
+        for (final TypeDefinition<?> type : typedefs) {
+            actualOrder[offset] = type.getQName().getLocalName();
+            offset++;
+        }
+        assertArrayEquals(expectedOrder, actualOrder);
+    }
+
+    @Test
+    public void testOrderingChildNodes() throws Exception {
+        AugmentationSchemaNode augment1 = null;
+        for (final AugmentationSchemaNode as : foo.getAugmentations()) {
+            if (as.getChildNodes().size() == 5) {
+                augment1 = as;
+                break;
+            }
+        }
+        assertNotNull(augment1);
+
+        final String[] expectedOrder = { "ds0ChannelNumber", "interface-id", "my-type", "schemas", "odl" };
+        final String[] actualOrder = new String[expectedOrder.length];
+
+        int offset = 0;
+        for (final DataSchemaNode augmentChild : augment1.getChildNodes()) {
+            actualOrder[offset] = augmentChild.getQName().getLocalName();
+            offset++;
+        }
+
+        assertArrayEquals(expectedOrder, actualOrder);
+    }
+
+    @Test
+    public void testOrderingNestedChildNodes1() throws Exception {
+        final Collection<DataSchemaNode> childNodes = foo.getChildNodes();
+        final String[] expectedOrder = { "int32-leaf", "string-leaf", "invalid-pattern-string-leaf",
+            "invalid-direct-string-pattern-def-leaf", "multiple-pattern-string-leaf",
+            "multiple-pattern-direct-string-def-leaf", "length-leaf", "decimal-leaf", "decimal-leaf2", "ext",
+            "union-leaf", "custom-union-leaf", "transfer", "datas", "mycont", "data", "how", "address", "port",
+            "addresses", "peer", "id", "foo-id", "sub-ext", "sub-transfer", "sub-datas"
+        };
+        final String[] actualOrder = new String[childNodes.size()];
+
+        int offset = 0;
+        for (final DataSchemaNode child : childNodes) {
+            actualOrder[offset] = child.getQName().getLocalName();
+            offset++;
+        }
+        assertArrayEquals(expectedOrder, actualOrder);
+    }
+
+    @Test
+    public void testOrderingNestedChildNodes2() throws Exception {
+        final Set<GroupingDefinition> groupings = baz.getGroupings();
+        assertEquals(1, groupings.size());
+        final GroupingDefinition target = groupings.iterator().next();
+
+        final Collection<DataSchemaNode> childNodes = target.getChildNodes();
+        final String[] expectedOrder = { "data", "how", "address", "port", "addresses" };
+        final String[] actualOrder = new String[childNodes.size()];
+
+        int offset = 0;
+        for (final DataSchemaNode child : childNodes) {
+            actualOrder[offset] = child.getQName().getLocalName();
+            offset++;
+        }
+        assertArrayEquals(expectedOrder, actualOrder);
+    }
+
+    @Test
+    public void testOrderingNestedChildNodes3() throws Exception {
+        final Module justFoo = TestUtils.loadModuleResources(getClass(), "/ordering/foo.yang")
+                .getModules().iterator().next();
+        final ContainerSchemaNode x = (ContainerSchemaNode) justFoo
+                .getDataChildByName(QName.create(justFoo.getQNameModule(), "x"));
+        final Collection<DataSchemaNode> childNodes = x.getChildNodes();
+
+        final String[] expectedOrder = { "x15", "x10", "x5", "x1", "a5", "a1", "x2", "b5", "b1", "x3", "ax15", "ax5" };
+        final String[] actualOrder = new String[childNodes.size()];
+
+        int offset = 0;
+        for (final DataSchemaNode child : childNodes) {
+            actualOrder[offset] = child.getQName().getLocalName();
+            offset++;
+        }
+        assertArrayEquals(expectedOrder, actualOrder);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ParsingExtensionValueTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/ParsingExtensionValueTest.java
new file mode 100644 (file)
index 0000000..197ef7d
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+/**
+ * Test for testing of extensions and their arguments.
+ *
+ * @author Lukas Sedlak &lt;lsedlak@cisco.com&gt;
+ */
+public class ParsingExtensionValueTest {
+
+    @Test
+    public void extensionTest() throws IOException, YangSyntaxErrorException, URISyntaxException {
+        try {
+            TestUtils.loadModules(getClass().getResource("/extensions").toURI());
+        } catch (ReactorException e) {
+            assertEquals(SomeModifiersUnresolvedException.class, e.getClass());
+            assertTrue(e.getCause() instanceof SourceException);
+            assertTrue(e.getCause().getMessage().startsWith("ext:id is not a YANG statement or use of extension"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/RpcStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/RpcStmtTest.java
new file mode 100644 (file)
index 0000000..0458bf3
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class RpcStmtTest {
+
+    @Test
+    public void rpcTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/model/baz.yang"))
+                .addSource(sourceForResource("/model/bar.yang"))
+                .addSource(sourceForResource("/rpc-stmt-test/foo.yang"))
+                .buildEffective();
+        assertNotNull(result);
+
+        final Module testModule = result.findModules("baz").iterator().next();
+        assertEquals(1, testModule.getRpcs().size());
+
+        final RpcDefinition rpc = testModule.getRpcs().iterator().next();
+        assertEquals("get-config", rpc.getQName().getLocalName());
+
+        final ContainerSchemaNode input = rpc.getInput();
+        assertNotNull(input);
+        assertEquals(2, input.getChildNodes().size());
+
+        final ContainerSchemaNode container = (ContainerSchemaNode) input.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "source"));
+        assertNotNull(container);
+        AnyxmlSchemaNode anyXml = (AnyxmlSchemaNode) input.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "filter"));
+        assertNotNull(anyXml);
+
+        final ContainerSchemaNode output = rpc.getOutput();
+        assertNotNull(output);
+        assertEquals(1, output.getChildNodes().size());
+
+        anyXml = (AnyxmlSchemaNode) output.getDataChildByName(QName.create(testModule.getQNameModule(), "data"));
+        assertNotNull(anyXml);
+
+        final Module fooModule = result.findModule("foo", Revision.of("2016-09-23")).get();
+        final Set<RpcDefinition> rpcs = fooModule.getRpcs();
+        assertEquals(2, rpcs.size());
+
+        RpcDefinition fooRpc1 = null;
+        RpcDefinition fooRpc2 = null;
+
+        for (RpcDefinition rpcDefinition : rpcs) {
+            if ("foo-rpc-1".equals(rpcDefinition.getQName().getLocalName())) {
+                fooRpc1 = rpcDefinition;
+            } else if ("foo-rpc-2".equals(rpcDefinition.getQName().getLocalName())) {
+                fooRpc2 = rpcDefinition;
+            }
+        }
+
+        assertFalse(fooRpc1.equals(null));
+        assertFalse(fooRpc1.equals("str"));
+        assertFalse(fooRpc1.equals(fooRpc2));
+
+        assertNotEquals(fooRpc1.getInput().hashCode(), fooRpc2.getInput().hashCode());
+        assertNotEquals(fooRpc1.getOutput().hashCode(), fooRpc2.getOutput().hashCode());
+
+        assertTrue(fooRpc1.getInput().equals(fooRpc1.getInput()));
+        assertFalse(fooRpc1.getInput().equals(null));
+        assertFalse(fooRpc1.getInput().equals("str"));
+        assertFalse(fooRpc1.getInput().equals(fooRpc2.getInput()));
+
+        assertTrue(fooRpc1.getOutput().equals(fooRpc1.getOutput()));
+        assertFalse(fooRpc1.getOutput().equals(null));
+        assertFalse(fooRpc1.getOutput().equals("str"));
+        assertFalse(fooRpc1.getOutput().equals(fooRpc2.getOutput()));
+    }
+
+    @Test
+    public void testImplicitInputAndOutput() throws Exception {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rpc-stmt-test/bar.yang");
+        assertNotNull(schemaContext);
+
+        final Module barModule = schemaContext.findModule("bar", Revision.of("2016-11-25")).get();
+        final Set<RpcDefinition> rpcs = barModule.getRpcs();
+        assertEquals(1, rpcs.size());
+
+        final RpcDefinition barRpc = rpcs.iterator().next();
+
+        final ContainerSchemaNode input = barRpc.getInput();
+        assertNotNull(input);
+        assertEquals(2, input.getChildNodes().size());
+        assertEquals(StatementSource.CONTEXT, ((EffectiveStatement<?, ?>) input).getDeclared().getStatementSource());
+
+        final ContainerSchemaNode output = barRpc.getOutput();
+        assertNotNull(output);
+        assertEquals(2, output.getChildNodes().size());
+        assertEquals(StatementSource.CONTEXT, ((EffectiveStatement<?, ?>) output).getDeclared().getStatementSource());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/StmtTestUtils.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/StmtTestUtils.java
new file mode 100644 (file)
index 0000000..b2042cf
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import com.google.common.io.Files;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+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.ModuleImport;
+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.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YinStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YinTextToDomTransformer;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+public final class StmtTestUtils {
+
+    public static final FileFilter YANG_FILE_FILTER =
+        file -> file.getName().endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION) && file.isFile();
+
+    public static final FileFilter YIN_FILE_FILTER =
+        file -> file.getName().endsWith(YangConstants.RFC6020_YIN_FILE_EXTENSION) && file.isFile();
+
+    private static final Logger LOG = LoggerFactory.getLogger(StmtTestUtils.class);
+
+    private StmtTestUtils() {
+
+    }
+
+    public static void log(final Throwable exception, final String indent) {
+        LOG.debug("{}{}", indent, exception.getMessage());
+
+        final Throwable[] suppressed = exception.getSuppressed();
+        for (final Throwable throwable : suppressed) {
+            log(throwable, indent + "        ");
+        }
+    }
+
+    public static List<Module> findModules(final Set<Module> modules, final String moduleName) {
+        final List<Module> result = new ArrayList<>();
+        for (final Module module : modules) {
+            if (module.getName().equals(moduleName)) {
+                result.add(module);
+            }
+        }
+        return result;
+    }
+
+    public static StatementStreamSource sourceForResource(final String resourceName) {
+        try {
+            return YangStatementStreamSource.create(YangTextSchemaSource.forResource(resourceName));
+        } catch (IOException | YangSyntaxErrorException e) {
+            throw new IllegalArgumentException("Failed to create source", e);
+        }
+    }
+
+    public static void printReferences(final Module module, final boolean isSubmodule, final String indent) {
+        LOG.debug("{}{} {}", indent, (isSubmodule ? "Submodule" : "Module"), module.getName());
+        final Set<Module> submodules = module.getSubmodules();
+        for (final Module submodule : submodules) {
+            printReferences(submodule, true, indent + "      ");
+            printChilds(submodule.getChildNodes(), indent + "            ");
+        }
+    }
+
+    public static void printChilds(final Collection<DataSchemaNode> childNodes, final String indent) {
+
+        for (final DataSchemaNode child : childNodes) {
+            LOG.debug("{}{} {}", indent, "Child", child.getQName().getLocalName());
+            if (child instanceof DataNodeContainer) {
+                printChilds(((DataNodeContainer) child).getChildNodes(), indent + "      ");
+            }
+        }
+    }
+
+    public static SchemaContext parseYangSource(final String yangSourcePath) throws ReactorException,
+            URISyntaxException, IOException, YangSyntaxErrorException {
+        return parseYangSource(yangSourcePath, StatementParserMode.DEFAULT_MODE, null);
+    }
+
+    public static SchemaContext parseYangSource(final String yangSourcePath, final Set<QName> supportedFeatures)
+            throws ReactorException, URISyntaxException, IOException, YangSyntaxErrorException {
+        return parseYangSource(yangSourcePath, StatementParserMode.DEFAULT_MODE, supportedFeatures);
+    }
+
+    public static SchemaContext parseYangSource(final String yangSourcePath,
+            final StatementParserMode statementParserMode, final Set<QName> supportedFeatures)
+                    throws ReactorException, URISyntaxException, IOException, YangSyntaxErrorException {
+        final URL source = StmtTestUtils.class.getResource(yangSourcePath);
+        final File sourceFile = new File(source.toURI());
+        return parseYangSources(statementParserMode, supportedFeatures, sourceFile);
+    }
+
+    public static SchemaContext parseYangSources(final StatementStreamSource... sources) throws ReactorException {
+        return parseYangSources(StatementParserMode.DEFAULT_MODE, null, sources);
+    }
+
+    public static SchemaContext parseYangSources(final StatementParserMode statementParserMode,
+            final Set<QName> supportedFeatures, final StatementStreamSource... sources) throws ReactorException {
+        return parseYangSources(statementParserMode, supportedFeatures, Arrays.asList(sources));
+    }
+
+    public static SchemaContext parseYangSources(final StatementParserMode statementParserMode,
+            final Set<QName> supportedFeatures, final Collection<? extends StatementStreamSource> sources)
+            throws ReactorException {
+        final BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild(statementParserMode)
+                .addSources(sources);
+        if (supportedFeatures != null) {
+            reactor.setSupportedFeatures(supportedFeatures);
+        }
+
+        return reactor.buildEffective();
+    }
+
+    public static SchemaContext parseYangSources(final File... files) throws ReactorException, IOException,
+            YangSyntaxErrorException {
+        return parseYangSources(StatementParserMode.DEFAULT_MODE, null, files);
+    }
+
+    public static SchemaContext parseYangSources(final StatementParserMode statementParserMode,
+            final Set<QName> supportedFeatures, final File... files) throws  ReactorException, IOException,
+            YangSyntaxErrorException {
+
+        final Collection<YangStatementStreamSource> sources = new ArrayList<>(files.length);
+        for (File file : files) {
+            sources.add(YangStatementStreamSource.create(YangTextSchemaSource.forFile(file)));
+        }
+
+        return parseYangSources(statementParserMode, supportedFeatures, sources);
+    }
+
+    public static SchemaContext parseYangSources(final Collection<File> files) throws ReactorException, IOException,
+            YangSyntaxErrorException {
+        return parseYangSources(files, StatementParserMode.DEFAULT_MODE);
+    }
+
+    public static SchemaContext parseYangSources(final Collection<File> files,
+            final StatementParserMode statementParserMode) throws ReactorException, IOException,
+            YangSyntaxErrorException {
+        return parseYangSources(statementParserMode, null, files.toArray(new File[files.size()]));
+    }
+
+    public static SchemaContext parseYangSources(final String yangSourcesDirectoryPath) throws
+            ReactorException, URISyntaxException, IOException, YangSyntaxErrorException {
+        return parseYangSources(yangSourcesDirectoryPath, StatementParserMode.DEFAULT_MODE);
+    }
+
+    public static SchemaContext parseYangSources(final String yangSourcesDirectoryPath,
+            final StatementParserMode statementParserMode) throws ReactorException, URISyntaxException, IOException,
+            YangSyntaxErrorException {
+        return parseYangSources(yangSourcesDirectoryPath, null, statementParserMode);
+    }
+
+    public static SchemaContext parseYangSources(final String yangSourcesDirectoryPath,
+            final Set<QName> supportedFeatures, final StatementParserMode statementParserMode) throws ReactorException,
+            URISyntaxException, IOException, YangSyntaxErrorException {
+
+        final URL resourceDir = StmtTestUtils.class.getResource(yangSourcesDirectoryPath);
+        final File testSourcesDir = new File(resourceDir.toURI());
+
+        return parseYangSources(statementParserMode, supportedFeatures, testSourcesDir.listFiles(YANG_FILE_FILTER));
+    }
+
+    public static SchemaContext parseYangSources(final String yangFilesDirectoryPath,
+            final String yangLibsDirectoryPath)
+            throws URISyntaxException, ReactorException, IOException, YangSyntaxErrorException {
+        return parseYangSources(yangFilesDirectoryPath, yangLibsDirectoryPath, null);
+    }
+
+    public static SchemaContext parseYangSources(final String yangFilesDirectoryPath,
+            final String yangLibsDirectoryPath, final Set<QName> supportedFeatures) throws URISyntaxException,
+            ReactorException, IOException, YangSyntaxErrorException {
+        final File yangsDir = new File(StmtTestUtils.class.getResource(yangFilesDirectoryPath).toURI());
+        final File libsDir = new File(StmtTestUtils.class.getResource(yangLibsDirectoryPath).toURI());
+
+        return parseYangSources(yangsDir.listFiles(YANG_FILE_FILTER), libsDir.listFiles(YANG_FILE_FILTER),
+                supportedFeatures);
+    }
+
+    private static SchemaContext parseYangSources(final File[] yangFiles, final File[] libFiles,
+            final Set<QName> supportedFeatures) throws ReactorException, IOException, YangSyntaxErrorException {
+        final StatementStreamSource[] yangSources = new StatementStreamSource[yangFiles.length];
+        for (int i = 0; i < yangFiles.length; i++) {
+            yangSources[i] = YangStatementStreamSource.create(YangTextSchemaSource.forFile(yangFiles[i]));
+        }
+
+        final StatementStreamSource[] libSources = new StatementStreamSource[libFiles.length];
+        for (int i = 0; i < libFiles.length; i++) {
+            libSources[i] = YangStatementStreamSource.create(YangTextSchemaSource.forFile(libFiles[i]));
+        }
+
+        return parseYangSources(yangSources, libSources, supportedFeatures);
+    }
+
+    private static SchemaContext parseYangSources(final StatementStreamSource[] yangSources,
+            final StatementStreamSource[] libSources, final Set<QName> supportedFeatures) throws ReactorException {
+
+        final BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(yangSources).addLibSources(libSources);
+        if (supportedFeatures != null) {
+            reactor.setSupportedFeatures(supportedFeatures);
+        }
+
+        return reactor.buildEffective();
+    }
+
+    public static SchemaContext parseYinSources(final String yinSourcesDirectoryPath,
+            final StatementParserMode statementParserMode) throws URISyntaxException, SAXException, IOException,
+            ReactorException {
+        final URL resourceDir = StmtTestUtils.class.getResource(yinSourcesDirectoryPath);
+        final File[] files = new File(resourceDir.toURI()).listFiles(YIN_FILE_FILTER);
+        final StatementStreamSource[] sources = new StatementStreamSource[files.length];
+        for (int i = 0; i < files.length; i++) {
+            final SourceIdentifier identifier = YinTextSchemaSource.identifierFromFilename(files[i].getName());
+
+            sources[i] = YinStatementStreamSource.create(YinTextToDomTransformer.transformSource(
+                YinTextSchemaSource.delegateForByteSource(identifier, Files.asByteSource(files[i]))));
+        }
+
+        return parseYinSources(statementParserMode, sources);
+    }
+
+    public static SchemaContext parseYinSources(final StatementParserMode statementParserMode,
+            final StatementStreamSource... sources) throws ReactorException {
+        return RFC7950Reactors.defaultReactor().newBuild(statementParserMode).addSources(sources).buildEffective();
+    }
+
+    public static Module findImportedModule(final SchemaContext context, final Module rootModule,
+            final String importedModuleName) {
+        ModuleImport requestedModuleImport = null;
+        final Set<ModuleImport> rootImports = rootModule.getImports();
+        for (final ModuleImport moduleImport : rootImports) {
+            if (moduleImport.getModuleName().equals(importedModuleName)) {
+                requestedModuleImport = moduleImport;
+                break;
+            }
+        }
+
+        return context.findModule(requestedModuleImport.getModuleName(), requestedModuleImport.getRevision())
+                .orElse(null);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/SubstatementValidatorTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/SubstatementValidatorTest.java
new file mode 100644 (file)
index 0000000..21cb097
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Set;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+
+public class SubstatementValidatorTest {
+    @SuppressWarnings("checkstyle:regexpSinglelineJava")
+    private final PrintStream stdout = System.out;
+    private final ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+    @Rule
+    public ExpectedException expectedEx = ExpectedException.none();
+
+    @Before
+    public void setUp() throws UnsupportedEncodingException {
+        System.setOut(new PrintStream(output, true, "UTF-8"));
+    }
+
+    @After
+    public void cleanUp() {
+        System.setOut(stdout);
+    }
+
+    @Test
+    public void noException() throws Exception {
+        assertNotNull(TestUtils.loadModules(getClass().getResource("/augment-test/augment-in-augment").toURI()));
+    }
+
+    @Test
+    public void undesirableElementException() throws Exception {
+        try {
+            TestUtils.loadModules(getClass().getResource("/substatement-validator/undesirable-element").toURI());
+            fail("Unexpected success");
+        } catch (ReactorException ex) {
+            assertNotNull(ex.getCause());
+            assertTrue(ex.getCause().getMessage().contains("TYPE is not valid for REVISION"));
+        }
+    }
+
+    @Test
+    public void maximalElementCountException() throws Exception {
+        try {
+            TestUtils.loadModules(getClass().getResource("/substatement-validator/maximal-element").toURI());
+            fail("Unexpected success");
+        } catch (ReactorException ex) {
+            assertNotNull(ex.getCause());
+            assertTrue(ex.getCause().getMessage().contains("Maximal count of DESCRIPTION for AUGMENT is 1"));
+        }
+    }
+
+    @Test
+    public void missingElementException() throws Exception {
+        expectedEx.expect(SomeModifiersUnresolvedException.class);
+
+        TestUtils.loadModules(getClass().getResource("/substatement-validator/missing-element").toURI());
+    }
+
+    @Test
+    public void bug6173Test() throws Exception {
+        final Set<Module> loadModules = TestUtils.loadModules(getClass().getResource(
+                "/substatement-validator/empty-element").toURI()).getModules();
+        assertEquals(1, loadModules.size());
+    }
+
+    @Test
+    public void bug4310test() throws Exception {
+        expectedEx.expect(SomeModifiersUnresolvedException.class);
+        TestUtils.loadModules(getClass().getResource("/substatement-validator/bug-4310").toURI());
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TestUtils.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TestUtils.java
new file mode 100644 (file)
index 0000000..1f2ba43
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.api.YinTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YinStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YinTextToDomTransformer;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+public final class TestUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(TestUtils.class);
+
+    private TestUtils() {
+    }
+
+    public static SchemaContext loadModules(final URI resourceDirectory)
+            throws ReactorException, IOException, YangSyntaxErrorException {
+        final BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild();
+        File[] files = new File(resourceDirectory).listFiles();
+
+        for (File file : files) {
+            if (file.getName().endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION)) {
+                reactor.addSource(YangStatementStreamSource.create(YangTextSchemaSource.forFile(file)));
+            } else {
+                LOG.info("Ignoring non-yang file {}", file);
+            }
+        }
+
+        return reactor.buildEffective();
+    }
+
+    public static SchemaContext loadModuleResources(final Class<?> refClass, final String... resourceNames)
+            throws IOException, ReactorException, YangSyntaxErrorException {
+        final BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild();
+
+        for (String resourceName : resourceNames) {
+            reactor.addSource(YangStatementStreamSource.create(YangTextSchemaSource.forResource(refClass,
+                resourceName)));
+        }
+
+        return reactor.buildEffective();
+    }
+
+    public static SchemaContext loadYinModules(final URI resourceDirectory) throws ReactorException, SAXException,
+            IOException {
+        final BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild();
+
+        for (File file : new File(resourceDirectory).listFiles()) {
+            reactor.addSource(YinStatementStreamSource.create(YinTextToDomTransformer.transformSource(
+                YinTextSchemaSource.forFile(file))));
+        }
+
+        return reactor.buildEffective();
+    }
+
+    public static Module loadYinModule(final YinTextSchemaSource source) throws ReactorException, SAXException,
+            IOException {
+        final SchemaContext ctx = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(YinStatementStreamSource.create(YinTextToDomTransformer.transformSource(source)))
+                .buildEffective();
+        return ctx.getModules().iterator().next();
+    }
+
+    public static Optional<Module> findModule(final SchemaContext context, final String moduleName) {
+        return context.getModules().stream().filter(module -> moduleName.equals(module.getName())).findAny();
+    }
+
+    public static ModuleImport findImport(final Set<ModuleImport> imports, final String prefix) {
+        ModuleImport result = null;
+        for (ModuleImport moduleImport : imports) {
+            if (moduleImport.getPrefix().equals(prefix)) {
+                result = moduleImport;
+                break;
+            }
+        }
+        return result;
+    }
+
+    public static TypeDefinition<?> findTypedef(final Set<TypeDefinition<?>> typedefs, final String name) {
+        TypeDefinition<?> result = null;
+        for (TypeDefinition<?> td : typedefs) {
+            if (td.getQName().getLocalName().equals(name)) {
+                result = td;
+                break;
+            }
+        }
+        return result;
+    }
+
+    public static SchemaPath createPath(final boolean absolute, final QNameModule module, final String... names) {
+        List<QName> path = new ArrayList<>(names.length);
+        for (String name : names) {
+            path.add(QName.create(module, name));
+        }
+        return SchemaPath.create(path, absolute);
+    }
+
+    /**
+     * Test if node has augmenting flag set to expected value. In case this is
+     * DataNodeContainer/ChoiceNode, check its child nodes/case nodes too.
+     *
+     * @param node
+     *            node to check
+     * @param expected
+     *            expected value
+     */
+    public static void checkIsAugmenting(final DataSchemaNode node, final boolean expected) {
+        assertEquals(expected, node.isAugmenting());
+        if (node instanceof DataNodeContainer) {
+            for (DataSchemaNode child : ((DataNodeContainer) node)
+                    .getChildNodes()) {
+                checkIsAugmenting(child, expected);
+            }
+        } else if (node instanceof ChoiceSchemaNode) {
+            for (CaseSchemaNode caseNode : ((ChoiceSchemaNode) node).getCases().values()) {
+                checkIsAugmenting(caseNode, expected);
+            }
+        }
+    }
+
+    /**
+     * Check if node has addedByUses flag set to expected value. In case this is
+     * DataNodeContainer/ChoiceNode, check its child nodes/case nodes too.
+     *
+     * @param node
+     *            node to check
+     * @param expected
+     *            expected value
+     */
+    public static void checkIsAddedByUses(final DataSchemaNode node, final boolean expected) {
+        assertEquals(expected, node.isAddedByUses());
+        if (node instanceof DataNodeContainer) {
+            for (DataSchemaNode child : ((DataNodeContainer) node)
+                    .getChildNodes()) {
+                checkIsAddedByUses(child, expected);
+            }
+        } else if (node instanceof ChoiceSchemaNode) {
+            for (CaseSchemaNode caseNode : ((ChoiceSchemaNode) node).getCases().values()) {
+                checkIsAddedByUses(caseNode, expected);
+            }
+        }
+    }
+
+    public static void checkIsAddedByUses(final GroupingDefinition node, final boolean expected) {
+        assertEquals(expected, node.isAddedByUses());
+        for (DataSchemaNode child : node.getChildNodes()) {
+            checkIsAddedByUses(child, expected);
+        }
+    }
+
+    public static List<Module> findModules(final Set<Module> modules, final String moduleName) {
+        List<Module> result = new ArrayList<>();
+        for (Module module : modules) {
+            if (module.getName().equals(moduleName)) {
+                result.add(module);
+            }
+        }
+        return result;
+    }
+
+    public static SchemaContext parseYangSources(final StatementStreamSource... sources) throws ReactorException {
+        return RFC7950Reactors.defaultReactor().newBuild().addSources(sources).buildEffective();
+    }
+
+    public static SchemaContext parseYangSources(final File... files)
+            throws ReactorException, IOException, YangSyntaxErrorException {
+
+        StatementStreamSource[] sources = new StatementStreamSource[files.length];
+
+        for (int i = 0; i < files.length; i++) {
+            sources[i] = YangStatementStreamSource.create(YangTextSchemaSource.forFile(files[i]));
+        }
+
+        return parseYangSources(sources);
+    }
+
+    public static SchemaContext parseYangSources(final Collection<File> files)
+            throws ReactorException, IOException, YangSyntaxErrorException {
+        return parseYangSources(files.toArray(new File[files.size()]));
+    }
+
+    public static SchemaContext parseYangSources(final String yangSourcesDirectoryPath)
+            throws ReactorException, URISyntaxException, IOException, YangSyntaxErrorException {
+
+        URL resourceDir = StmtTestUtils.class.getResource(yangSourcesDirectoryPath);
+        File testSourcesDir = new File(resourceDir.toURI());
+
+        return parseYangSources(testSourcesDir.listFiles());
+    }
+
+    public static SchemaContext parseYangSource(final String yangSourceFilePath)
+            throws ReactorException, URISyntaxException, IOException, YangSyntaxErrorException {
+
+        URL resourceFile = StmtTestUtils.class.getResource(yangSourceFilePath);
+        File testSourcesFile = new File(resourceFile.toURI());
+
+        return parseYangSources(testSourcesFile);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TwoRevisionsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TwoRevisionsTest.java
new file mode 100644 (file)
index 0000000..cd1e068
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+public class TwoRevisionsTest {
+
+    @Test
+    public void testTwoRevisions() throws Exception {
+        Set<Module> modules = TestUtils.loadModules(getClass().getResource("/ietf").toURI()).getModules();
+        //FIXME: following assert needs module revisions .equals() solution first
+        assertEquals(2, TestUtils.findModules(modules, "network-topology").size());
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TypedefConstraintsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TypedefConstraintsTest.java
new file mode 100644 (file)
index 0000000..70e0c9e
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.Range;
+import java.math.BigDecimal;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.BaseTypes;
+
+public class TypedefConstraintsTest {
+
+    @Test
+    public void decimalRangeConstraintsTest() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/stmt-test/constraints");
+
+        assertNotNull(context);
+
+        final Set<TypeDefinition<?>> typeDefinitions = context.getTypeDefinitions();
+        assertNotNull(typeDefinitions);
+        assertEquals(1, typeDefinitions.size());
+
+        final TypeDefinition<?> myDecimal = typeDefinitions.iterator().next();
+
+        assertNotNull(myDecimal);
+        assertTrue(myDecimal instanceof DecimalTypeDefinition);
+
+        final Set<? extends Range<?>> rangeConstraints = ((DecimalTypeDefinition) myDecimal).getRangeConstraint()
+                .get().getAllowedRanges().asRanges();
+
+        assertNotNull(rangeConstraints);
+        assertEquals(1, rangeConstraints.size());
+
+        final DataSchemaNode dataNode = context.getDataChildByName(QName.create("urn:opendaylight.foo", "2013-10-08",
+            "id-decimal64"));
+        assertNotNull(dataNode);
+        assertTrue(dataNode instanceof LeafSchemaNode);
+
+        final LeafSchemaNode leafDecimal = (LeafSchemaNode) dataNode;
+        final TypeDefinition<?> type = leafDecimal.getType();
+
+        assertTrue(type instanceof DecimalTypeDefinition);
+        final DecimalTypeDefinition decType = (DecimalTypeDefinition) type;
+
+        final Set<? extends Range<?>> decRangeConstraints = decType.getRangeConstraint().get().getAllowedRanges()
+                .asRanges();
+
+        assertEquals(1, decRangeConstraints.size());
+
+        final Range<?> range = decRangeConstraints.iterator().next();
+        assertEquals(new BigDecimal(1.5), range.lowerEndpoint());
+        assertEquals(new BigDecimal(5.5), range.upperEndpoint());
+
+        assertTrue(decType.getQName().getModule().equals(leafDecimal.getQName().getModule()));
+        assertTrue(decType.getQName().getLocalName().equals(BaseTypes.DECIMAL64_QNAME.getLocalName()));
+        assertNull(decType.getBaseType());
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TypedefSubStmtsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TypedefSubStmtsTest.java
new file mode 100644 (file)
index 0000000..b967de0
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class TypedefSubStmtsTest {
+
+    @Test
+    public void typedefSubStmtsTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/typedef-substmts-test/typedef-substmts-test.yang"))
+                .buildEffective();
+        assertNotNull(result);
+
+        Set<TypeDefinition<?>> typedefs = result.getTypeDefinitions();
+        assertEquals(1, typedefs.size());
+
+        TypeDefinition<?> typedef = typedefs.iterator().next();
+        assertEquals("time-of-the-day", typedef.getQName().getLocalName());
+        assertEquals("string", typedef.getBaseType().getQName().getLocalName());
+        assertEquals(Optional.of("24-hour-clock"), typedef.getUnits());
+        assertEquals("1am", typedef.getDefaultValue().map(Object::toString).orElse(null));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TypesResolutionTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/TypesResolutionTest.java
new file mode 100644 (file)
index 0000000..254b4ca
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Range;
+import java.net.URI;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+
+public class TypesResolutionTest {
+    private SchemaContext context;
+
+    @Before
+    public void init() throws Exception {
+        final StatementStreamSource yangFile = sourceForResource("/types/custom-types-test@2012-04-04.yang");
+        final StatementStreamSource yangFileDependency1 = sourceForResource("/ietf/iana-timezones@2012-07-09.yang");
+        final StatementStreamSource yangFileDependency2 = sourceForResource("/ietf/ietf-inet-types@2010-09-24.yang");
+        final StatementStreamSource yangFileDependency3 = sourceForResource("/ietf/ietf-yang-types@2010-09-24.yang");
+        context = TestUtils.parseYangSources(yangFile, yangFileDependency1, yangFileDependency2, yangFileDependency3);
+        assertEquals(4, context.getModules().size());
+    }
+
+    @Test
+    public void testIPVersion() {
+        Module tested = TestUtils.findModule(context, "ietf-inet-types").get();
+        Set<TypeDefinition<?>> typedefs = tested.getTypeDefinitions();
+        assertEquals(14, typedefs.size());
+
+        TypeDefinition<?> type = TestUtils.findTypedef(typedefs, "ip-version");
+        assertTrue(type.getDescription().get().contains("This value represents the version of the IP protocol."));
+        assertTrue(type.getReference().get().contains("RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"));
+
+        EnumTypeDefinition enumType = (EnumTypeDefinition) type.getBaseType();
+        List<EnumPair> values = enumType.getValues();
+        assertEquals(3, values.size());
+
+        EnumPair value0 = values.get(0);
+        assertEquals("unknown", value0.getName());
+        assertEquals(0, value0.getValue());
+        assertEquals(Optional.of("An unknown or unspecified version of the Internet protocol."),
+            value0.getDescription());
+
+        EnumPair value1 = values.get(1);
+        assertEquals("ipv4", value1.getName());
+        assertEquals(1, value1.getValue());
+        assertEquals(Optional.of("The IPv4 protocol as defined in RFC 791."), value1.getDescription());
+
+        EnumPair value2 = values.get(2);
+        assertEquals("ipv6", value2.getName());
+        assertEquals(2, value2.getValue());
+        assertEquals(Optional.of("The IPv6 protocol as defined in RFC 2460."), value2.getDescription());
+    }
+
+    @Test
+    public void testEnumeration() {
+        Module tested = TestUtils.findModule(context, "custom-types-test").get();
+        Set<TypeDefinition<?>> typedefs = tested.getTypeDefinitions();
+
+        TypeDefinition<?> type = TestUtils.findTypedef(typedefs, "ip-version");
+        EnumTypeDefinition enumType = (EnumTypeDefinition) type.getBaseType();
+        List<EnumPair> values = enumType.getValues();
+        assertEquals(4, values.size());
+
+        EnumPair value0 = values.get(0);
+        assertEquals("unknown", value0.getName());
+        assertEquals(0, value0.getValue());
+        assertEquals(Optional.of("An unknown or unspecified version of the Internet protocol."),
+            value0.getDescription());
+
+        EnumPair value1 = values.get(1);
+        assertEquals("ipv4", value1.getName());
+        assertEquals(19, value1.getValue());
+        assertEquals(Optional.of("The IPv4 protocol as defined in RFC 791."), value1.getDescription());
+
+        EnumPair value2 = values.get(2);
+        assertEquals("ipv6", value2.getName());
+        assertEquals(7, value2.getValue());
+        assertEquals(Optional.of("The IPv6 protocol as defined in RFC 2460."), value2.getDescription());
+
+        EnumPair value3 = values.get(3);
+        assertEquals("default", value3.getName());
+        assertEquals(20, value3.getValue());
+        assertEquals(Optional.of("default ip"), value3.getDescription());
+    }
+
+    @Test
+    public void testIpAddress() {
+        Module tested = TestUtils.findModule(context, "ietf-inet-types").get();
+        Set<TypeDefinition<?>> typedefs = tested.getTypeDefinitions();
+        TypeDefinition<?> type = TestUtils.findTypedef(typedefs, "ip-address");
+        UnionTypeDefinition baseType = (UnionTypeDefinition) type.getBaseType();
+        List<TypeDefinition<?>> unionTypes = baseType.getTypes();
+
+        StringTypeDefinition ipv4 = (StringTypeDefinition) unionTypes.get(0);
+        assertNotNull(ipv4.getBaseType());
+        String expectedPattern = "^(?:(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}"
+                + "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])" + "(%[\\p{N}\\p{L}]+)?)$";
+        assertEquals(expectedPattern, ipv4.getPatternConstraints().get(0).getJavaPatternString());
+
+        StringTypeDefinition ipv6 = (StringTypeDefinition) unionTypes.get(1);
+        assertNotNull(ipv6.getBaseType());
+        List<PatternConstraint> ipv6Patterns = ipv6.getPatternConstraints();
+        expectedPattern = "^(?:((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}"
+                + "((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|" + "(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\\.){3}"
+                + "(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))" + "(%[\\p{N}\\p{L}]+)?)$";
+        assertEquals(expectedPattern, ipv6Patterns.get(0).getJavaPatternString());
+
+        expectedPattern = "^(?:(([^:]+:){6}(([^:]+:[^:]+)|(.*\\..*)))|" + "((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)"
+                + "(%.+)?)$";
+        assertEquals(expectedPattern, ipv6Patterns.get(1).getJavaPatternString());
+    }
+
+    @Test
+    public void testDomainName() {
+        Module tested = TestUtils.findModule(context, "ietf-inet-types").get();
+        Set<TypeDefinition<?>> typedefs = tested.getTypeDefinitions();
+        StringTypeDefinition type = (StringTypeDefinition) TestUtils.findTypedef(typedefs, "domain-name");
+        assertNotNull(type.getBaseType());
+        List<PatternConstraint> patterns = type.getPatternConstraints();
+        assertEquals(1, patterns.size());
+        String expectedPattern = "^(?:((([a-zA-Z0-9_]([a-zA-Z0-9\\-_]){0,61})?[a-zA-Z0-9]\\.)*"
+                + "([a-zA-Z0-9_]([a-zA-Z0-9\\-_]){0,61})?[a-zA-Z0-9]\\.?)" + "|\\.)$";
+        assertEquals(expectedPattern, patterns.get(0).getJavaPatternString());
+
+        LengthConstraint lengths = type.getLengthConstraint().get();
+        assertEquals(1, lengths.getAllowedRanges().asRanges().size());
+        Range<Integer> length = lengths.getAllowedRanges().span();
+        assertEquals(Integer.valueOf(1), length.lowerEndpoint());
+        assertEquals(Integer.valueOf(253), length.upperEndpoint());
+    }
+
+    @Test
+    public void testInstanceIdentifier1() {
+        Module tested = TestUtils.findModule(context, "custom-types-test").get();
+        LeafSchemaNode leaf = (LeafSchemaNode) tested.getDataChildByName(
+                QName.create(tested.getQNameModule(), "inst-id-leaf1"));
+        InstanceIdentifierTypeDefinition leafType = (InstanceIdentifierTypeDefinition) leaf.getType();
+        assertFalse(leafType.requireInstance());
+        assertEquals(1, leaf.getUnknownSchemaNodes().size());
+    }
+
+    @Test
+    public void testInstanceIdentifier2() {
+        Module tested = TestUtils.findModule(context, "custom-types-test").get();
+        LeafSchemaNode leaf = (LeafSchemaNode) tested.getDataChildByName(
+                QName.create(tested.getQNameModule(), "inst-id-leaf2"));
+        InstanceIdentifierTypeDefinition leafType = (InstanceIdentifierTypeDefinition) leaf.getType();
+        assertFalse(leafType.requireInstance());
+    }
+
+    @Test
+    public void testIdentity() {
+        Module tested = TestUtils.findModule(context, "custom-types-test").get();
+        Set<IdentitySchemaNode> identities = tested.getIdentities();
+        assertEquals(5, identities.size());
+        IdentitySchemaNode cryptoAlg = null;
+        IdentitySchemaNode cryptoBase = null;
+        IdentitySchemaNode cryptoId = null;
+        for (IdentitySchemaNode id : identities) {
+            if (id.getQName().getLocalName().equals("crypto-alg")) {
+                cryptoAlg = id;
+            } else if ("crypto-base".equals(id.getQName().getLocalName())) {
+                cryptoBase = id;
+            } else if ("crypto-id".equals(id.getQName().getLocalName())) {
+                cryptoId = id;
+            }
+        }
+        assertNotNull(cryptoAlg);
+        IdentitySchemaNode baseIdentity = Iterables.getOnlyElement(cryptoAlg.getBaseIdentities());
+        assertEquals("crypto-base", baseIdentity.getQName().getLocalName());
+        assertTrue(cryptoAlg.getDerivedIdentities().isEmpty());
+        assertTrue(baseIdentity.getBaseIdentities().isEmpty());
+
+        assertNotNull(cryptoBase);
+        assertTrue(cryptoBase.getBaseIdentities().isEmpty());
+        assertEquals(3, cryptoBase.getDerivedIdentities().size());
+
+        assertNotNull(cryptoId);
+        assertEquals(1, cryptoId.getUnknownSchemaNodes().size());
+    }
+
+    @Test
+    public void testBitsType1() {
+        Module tested = TestUtils.findModule(context, "custom-types-test").get();
+        LeafSchemaNode leaf = (LeafSchemaNode) tested.getDataChildByName(
+                QName.create(tested.getQNameModule(), "mybits"));
+        BitsTypeDefinition leafType = (BitsTypeDefinition) leaf.getType();
+        List<Bit> bits = leafType.getBits();
+        assertEquals(3, bits.size());
+
+        Bit bit1 = bits.get(0);
+        assertEquals("disable-nagle", bit1.getName());
+        assertEquals(0L, bit1.getPosition());
+
+        Bit bit2 = bits.get(1);
+        assertEquals("auto-sense-speed", bit2.getName());
+        assertEquals(1L, bit2.getPosition());
+
+        Bit bit3 = bits.get(2);
+        assertEquals("only-10-Mb", bit3.getName());
+        assertEquals(2L, bit3.getPosition());
+    }
+
+    @Test
+    public void testBitsType2() {
+        Module tested = TestUtils.findModule(context, "custom-types-test").get();
+        Set<TypeDefinition<?>> typedefs = tested.getTypeDefinitions();
+        TypeDefinition<?> testedType = TestUtils.findTypedef(typedefs, "access-operations-type");
+
+        BitsTypeDefinition bitsType = (BitsTypeDefinition) testedType.getBaseType();
+        List<Bit> bits = bitsType.getBits();
+        assertEquals(5, bits.size());
+
+        Bit bit0 = bits.get(0);
+        assertEquals("create", bit0.getName());
+        assertEquals(0L, bit0.getPosition());
+
+        Bit bit1 = bits.get(1);
+        assertEquals("delete", bit1.getName());
+        assertEquals(365L, bit1.getPosition());
+
+        Bit bit2 = bits.get(2);
+        assertEquals("read", bit2.getName());
+        assertEquals(500L, bit2.getPosition());
+
+        Bit bit3 = bits.get(3);
+        assertEquals("update", bit3.getName());
+        assertEquals(501L, bit3.getPosition());
+
+        Bit bit4 = bits.get(4);
+        assertEquals("exec", bit4.getName());
+        assertEquals(502L, bit4.getPosition());
+    }
+
+    @Test
+    public void testIanaTimezones() {
+        Module tested = TestUtils.findModule(context, "iana-timezones").get();
+        Set<TypeDefinition<?>> typedefs = tested.getTypeDefinitions();
+        TypeDefinition<?> testedType = TestUtils.findTypedef(typedefs, "iana-timezone");
+
+        String expectedDesc = "A timezone location as defined by the IANA timezone";
+        assertTrue(testedType.getDescription().get().contains(expectedDesc));
+        assertFalse(testedType.getReference().isPresent());
+        assertEquals(Status.CURRENT, testedType.getStatus());
+
+        QName testedTypeQName = testedType.getQName();
+        assertEquals(URI.create("urn:ietf:params:xml:ns:yang:iana-timezones"), testedTypeQName.getNamespace());
+        assertEquals(Revision.ofNullable("2012-07-09"), testedTypeQName.getRevision());
+        assertEquals("iana-timezone", testedTypeQName.getLocalName());
+
+        EnumTypeDefinition enumType = (EnumTypeDefinition) testedType.getBaseType();
+        List<EnumPair> values = enumType.getValues();
+        // 0-414
+        assertEquals(415, values.size());
+
+        EnumPair enum168 = values.get(168);
+        assertEquals("America/Danmarkshavn", enum168.getName());
+        assertEquals(168, enum168.getValue());
+        assertEquals(Optional.of("east coast, north of Scoresbysund"), enum168.getDescription());
+
+        EnumPair enum374 = values.get(374);
+        assertEquals("America/Indiana/Winamac", enum374.getName());
+        assertEquals(374, enum374.getValue());
+        assertEquals(Optional.of("Eastern Time - Indiana - Pulaski County"), enum374.getDescription());
+    }
+
+    @Test
+    public void testObjectId128() {
+        Module tested = TestUtils.findModule(context, "ietf-yang-types").get();
+        Set<TypeDefinition<?>> typedefs = tested.getTypeDefinitions();
+        StringTypeDefinition testedType = (StringTypeDefinition) TestUtils.findTypedef(typedefs,
+                "object-identifier-128");
+
+        List<PatternConstraint> patterns = testedType.getPatternConstraints();
+        assertEquals(1, patterns.size());
+        PatternConstraint pattern = patterns.get(0);
+        assertEquals("^(?:\\d*(\\.\\d*){1,127})$", pattern.getJavaPatternString());
+
+        QName testedTypeQName = testedType.getQName();
+        assertEquals(URI.create("urn:ietf:params:xml:ns:yang:ietf-yang-types"), testedTypeQName.getNamespace());
+        assertEquals(Revision.ofNullable("2010-09-24"), testedTypeQName.getRevision());
+        assertEquals("object-identifier-128", testedTypeQName.getLocalName());
+
+        StringTypeDefinition testedTypeBase = testedType.getBaseType();
+        patterns = testedTypeBase.getPatternConstraints();
+        assertEquals(1, patterns.size());
+
+        pattern = patterns.get(0);
+        assertEquals("^(?:(([0-1](\\.[1-3]?[0-9]))|(2\\.(0|([1-9]\\d*))))(\\.(0|([1-9]\\d*)))*)$",
+                pattern.getJavaPatternString());
+
+        QName testedTypeBaseQName = testedTypeBase.getQName();
+        assertEquals(URI.create("urn:ietf:params:xml:ns:yang:ietf-yang-types"), testedTypeBaseQName.getNamespace());
+        assertEquals(Revision.ofNullable("2010-09-24"), testedTypeBaseQName.getRevision());
+        assertEquals("object-identifier", testedTypeBaseQName.getLocalName());
+    }
+
+    @Test
+    public void testIdentityref() {
+        Module tested = TestUtils.findModule(context, "custom-types-test").get();
+        Set<TypeDefinition<?>> typedefs = tested.getTypeDefinitions();
+        TypeDefinition<?> testedType = TestUtils.findTypedef(typedefs, "service-type-ref");
+        IdentityrefTypeDefinition baseType = (IdentityrefTypeDefinition) testedType.getBaseType();
+        QName identity = baseType.getIdentities().iterator().next().getQName();
+        assertEquals(URI.create("urn:custom.types.demo"), identity.getNamespace());
+        assertEquals(Revision.ofNullable("2012-04-16"), identity.getRevision());
+        assertEquals("service-type", identity.getLocalName());
+
+        LeafSchemaNode type = (LeafSchemaNode) tested.getDataChildByName(QName.create(tested.getQNameModule(), "type"));
+        assertNotNull(type);
+    }
+
+    @Test
+    public void testUnionWithExt() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/types/union-with-ext/extdef.yang"))
+                .addSource(sourceForResource("/types/union-with-ext/unionbug.yang"))
+                .addSource(sourceForResource("/ietf/ietf-inet-types@2010-09-24.yang"))
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void testUnionWithBits() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/types/union-with-bits/union-bits-model.yang"))
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void testUnionInList() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/types/union-in-list/unioninlisttest.yang"));
+
+        try {
+            final SchemaContext result = reactor.buildEffective();
+            fail("effective build should fail due to union in list; this is not allowed");
+        } catch (ReactorException e) {
+            assertEquals(SomeModifiersUnresolvedException.class, e.getClass());
+            assertTrue(e.getCause() instanceof SourceException);
+            assertTrue(e.getCause().getMessage().startsWith("union is not a YANG statement or use of extension"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/UsesAugmentTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/UsesAugmentTest.java
new file mode 100644 (file)
index 0000000..a0a9c59
--- /dev/null
@@ -0,0 +1,676 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class UsesAugmentTest {
+
+    private static final QNameModule UG = QNameModule.create(
+        URI.create("urn:opendaylight:params:xml:ns:yang:uses-grouping"), Revision.of("2013-07-30"));
+    private static final QNameModule GD = QNameModule.create(
+        URI.create("urn:opendaylight:params:xml:ns:yang:grouping-definitions"), Revision.of("2013-09-04"));
+
+    private SchemaContext context;
+
+    @Before
+    public void init() throws ReactorException, IOException, YangSyntaxErrorException, URISyntaxException {
+        context = TestUtils.loadModules(getClass().getResource("/grouping-test").toURI());
+    }
+
+    /*
+     * Structure of testing model:
+     *
+     * notification pcreq
+     * |-- leaf version (U)
+     * |-- leaf type (U)
+     * |-- list requests
+     * |-- |-- container rp
+     * |-- |-- |-- leaf priority (U)
+     * |-- |-- |-- container box (U)
+     * |-- |-- |-- |-- container order (A)
+     * |-- |-- |-- |-- |-- leaf delete (U)
+     * |-- |-- |-- |-- |-- |-- leaf setup (U)
+     * |-- |-- |-- leaf processing-rule (U)
+     * |-- |-- |-- leaf ignore (U)
+     * |-- |-- path-key-expansion
+     * |-- |-- |-- container path-key
+     * |-- |-- |-- |-- list path-keys (U)
+     * |-- |-- |-- |-- |-- leaf version (U)
+     * |-- |-- |-- |-- |-- leaf type (U)
+     * |-- |-- |-- |-- |-- leaf processing-rule (U)
+     * |-- |-- |-- |-- |-- leaf ignore (U)
+     * |-- |-- container segment-computation
+     * |-- |-- |-- container p2p
+     * |-- |-- |-- |-- container endpoints
+     * |-- |-- |-- |-- |-- leaf processing-rule (U)
+     * |-- |-- |-- |-- |-- leaf ignore (U)
+     * |-- |-- |-- |-- |-- container box (U)
+     * |-- |-- |-- |-- |-- choice address-family (U)
+     * |-- |-- |-- |-- |-- |-- case ipv4
+     * |-- |-- |-- |-- |-- |-- |-- leaf source-ipv4-address
+     * |-- |-- |-- |-- |-- |-- case ipv6
+     * |-- |-- |-- |-- |-- |-- |-- leaf source-ipv6-address
+     * |-- |-- |-- |-- container reported-route
+     * |-- |-- |-- |-- |-- container bandwidth
+     * |-- |-- |-- |-- |-- list subobjects(U)
+     * |-- |-- |-- |-- |-- leaf processing-rule (U)
+     * |-- |-- |-- |-- |-- leaf ignore (U)
+     * |-- |-- |-- |-- container bandwidth (U)
+     * |-- |-- |-- |-- |-- container bandwidth (U)
+     * |-- |-- |-- |-- |-- leaf processing-rule (U)
+     * |-- |-- |-- |-- |-- leaf ignore (U)
+     * |-- list svec
+     * |-- |-- list metric
+     * |-- |-- |-- leaf metric-type (U)
+     * |-- |-- |-- container box (U)
+     * |-- |-- |-- leaf processing-rule (U)
+     * |-- |-- |-- leaf ignore (U)
+     * |-- |-- leaf link-diverse (U)
+     * |-- |-- leaf processing-rule (U)
+     * |-- |-- leaf ignore (U)
+     *
+     * U = added by uses A = added by augment
+     *
+     * @throws Exception if exception occurs
+     */
+    @Test
+    public void testAugmentInUses() throws Exception {
+        final Module testModule = TestUtils.findModule(context, "uses-grouping").get();
+
+        final Deque<QName> path = new ArrayDeque<>();
+
+        // * notification pcreq
+        final Set<NotificationDefinition> notifications = testModule.getNotifications();
+        assertEquals(1, notifications.size());
+        final NotificationDefinition pcreq = notifications.iterator().next();
+        assertNotNull(pcreq);
+        QName expectedQName = QName.create(UG, "pcreq");
+        path.offer(expectedQName);
+        SchemaPath expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, pcreq.getPath());
+        Collection<DataSchemaNode> childNodes = pcreq.getChildNodes();
+        assertEquals(4, childNodes.size());
+        // * |-- leaf version
+        LeafSchemaNode version = (LeafSchemaNode) pcreq.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "version"));
+        assertNotNull(version);
+        expectedQName = QName.create(UG, "version");
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, version.getPath());
+        expectedQName = QName.create(UG, "version");
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(true, pcreq.getQName(), expectedQName);
+        assertEquals(expectedPath, version.getType().getPath());
+        assertEquals(BaseTypes.uint8Type(), version.getType().getBaseType().getBaseType());
+        assertTrue(version.isAddedByUses());
+        // * |-- leaf type
+        LeafSchemaNode type = (LeafSchemaNode) pcreq.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "type"));
+        assertNotNull(type);
+        expectedQName = QName.create(UG, "type");
+        assertTrue(type.isAddedByUses());
+        path.pollLast();
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, type.getPath());
+        expectedQName = QName.create(GD, "int-ext");
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(true, expectedQName);
+        assertEquals(expectedPath, type.getType().getPath());
+        final UnionTypeDefinition union = (UnionTypeDefinition) type.getType().getBaseType();
+        assertEquals(SchemaPath.create(true, expectedQName, QName.create(expectedQName, "union")), union.getPath());
+        assertEquals(2, union.getTypes().size());
+        // * |-- list requests
+        final ListSchemaNode requests = (ListSchemaNode) pcreq.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "requests"));
+        assertNotNull(requests);
+        expectedQName = QName.create(UG, "requests");
+        assertEquals(expectedQName, requests.getQName());
+        path.pollLast();
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, requests.getPath());
+        assertFalse(requests.isAddedByUses());
+        childNodes = requests.getChildNodes();
+        assertEquals(3, childNodes.size());
+        // * |-- |-- container rp
+        final ContainerSchemaNode rp = (ContainerSchemaNode) requests.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "rp"));
+        assertNotNull(rp);
+        expectedQName = QName.create(UG, "rp");
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, rp.getPath());
+        assertFalse(rp.isAddedByUses());
+        childNodes = rp.getChildNodes();
+        assertEquals(4, childNodes.size());
+        // * |-- |-- |-- leaf processing-rule
+        LeafSchemaNode processingRule = (LeafSchemaNode) rp.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "processing-rule"));
+        assertNotNull(processingRule);
+        expectedQName = QName.create(UG, "processing-rule");
+        assertEquals(expectedQName, processingRule.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, processingRule.getPath());
+        assertEquals(BaseTypes.booleanType(), processingRule.getType());
+        assertTrue(processingRule.isAddedByUses());
+        // * |-- |-- |-- leaf ignore
+        LeafSchemaNode ignore = (LeafSchemaNode) rp.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "ignore"));
+        assertNotNull(ignore);
+        expectedQName = QName.create(UG, "ignore");
+        assertEquals(expectedQName, ignore.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, ignore.getPath());
+        assertEquals(BaseTypes.booleanType(), ignore.getType());
+        assertTrue(ignore.isAddedByUses());
+        // * |-- |-- |-- leaf priority
+        final LeafSchemaNode priority = (LeafSchemaNode) rp.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "priority"));
+        assertNotNull(priority);
+        expectedQName = QName.create(UG, "priority");
+        assertEquals(expectedQName, priority.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, priority.getPath());
+        expectedQName = QName.create(UG, "uint8");
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        // TODO
+        // assertEquals(expectedPath, priority.getType().getPath());
+        assertEquals(BaseTypes.uint8Type(), priority.getType().getBaseType());
+        assertTrue(priority.isAddedByUses());
+        // * |-- |-- |-- container box
+        ContainerSchemaNode box = (ContainerSchemaNode) rp.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "box"));
+        assertNotNull(box);
+        expectedQName = QName.create(UG, "box");
+        assertEquals(expectedQName, box.getQName());
+        path.pollLast();
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, box.getPath());
+        assertTrue(box.isAddedByUses());
+        // * |-- |-- |-- |-- container order
+        final ContainerSchemaNode order = (ContainerSchemaNode) box.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "order"));
+        assertNotNull(order);
+        expectedQName = QName.create(UG, "order");
+        assertEquals(expectedQName, order.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, order.getPath());
+        assertTrue(order.isAddedByUses());
+        assertTrue(order.isAugmenting());
+        assertEquals(2, order.getChildNodes().size());
+        // * |-- |-- |-- |-- |-- leaf delete
+        final LeafSchemaNode delete = (LeafSchemaNode) order.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "delete"));
+        assertNotNull(delete);
+        expectedQName = QName.create(UG, "delete");
+        assertEquals(expectedQName, delete.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, delete.getPath());
+        assertEquals(BaseTypes.uint32Type(), delete.getType());
+        assertTrue(delete.isAddedByUses());
+        // * |-- |-- |-- |-- |-- leaf setup
+        final LeafSchemaNode setup = (LeafSchemaNode) order.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "setup"));
+        assertNotNull(setup);
+        expectedQName = QName.create(UG, "setup");
+        assertEquals(expectedQName, setup.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, setup.getPath());
+        assertEquals(BaseTypes.uint32Type(), setup.getType());
+        assertTrue(setup.isAddedByUses());
+        // * |-- |-- path-key-expansion
+        final ContainerSchemaNode pke = (ContainerSchemaNode) requests.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "path-key-expansion"));
+        assertNotNull(pke);
+        expectedQName = QName.create(UG, "path-key-expansion");
+        assertEquals(expectedQName, pke.getQName());
+        path.pollLast();
+        path.pollLast();
+        path.pollLast();
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, pke.getPath());
+        assertFalse(pke.isAddedByUses());
+        // * |-- |-- |-- path-key
+        final ContainerSchemaNode pathKey = (ContainerSchemaNode) pke.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "path-key"));
+        assertNotNull(pathKey);
+        expectedQName = QName.create(UG, "path-key");
+        assertEquals(expectedQName, pathKey.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, pathKey.getPath());
+        assertFalse(pathKey.isAddedByUses());
+        assertEquals(3, pathKey.getChildNodes().size());
+        // * |-- |-- |-- |-- leaf processing-rule
+        processingRule = (LeafSchemaNode) pathKey.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "processing-rule"));
+        assertNotNull(processingRule);
+        expectedQName = QName.create(UG, "processing-rule");
+        assertEquals(expectedQName, processingRule.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, processingRule.getPath());
+        assertEquals(BaseTypes.booleanType(), processingRule.getType());
+        assertTrue(processingRule.isAddedByUses());
+        // * |-- |-- |-- |-- leaf ignore
+        ignore = (LeafSchemaNode) pathKey.getDataChildByName(QName.create(testModule.getQNameModule(), "ignore"));
+        assertNotNull(ignore);
+        expectedQName = QName.create(UG, "ignore");
+        assertEquals(expectedQName, ignore.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, ignore.getPath());
+        assertEquals(BaseTypes.booleanType(), ignore.getType());
+        assertTrue(ignore.isAddedByUses());
+        // * |-- |-- |-- |-- list path-keys
+        final ListSchemaNode pathKeys = (ListSchemaNode) pathKey.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "path-keys"));
+        assertNotNull(pathKeys);
+        expectedQName = QName.create(UG, "path-keys");
+        assertEquals(expectedQName, pathKeys.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, pathKeys.getPath());
+        assertTrue(pathKeys.isAddedByUses());
+        childNodes = pathKeys.getChildNodes();
+        assertEquals(2, childNodes.size());
+        // * |-- |-- |-- |-- |-- leaf version
+        version = (LeafSchemaNode) pathKeys.getDataChildByName(QName.create(testModule.getQNameModule(), "version"));
+        assertNotNull(version);
+        expectedQName = QName.create(UG, "version");
+        assertEquals(expectedQName, version.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, version.getPath());
+        assertTrue(version.getType() instanceof Uint8TypeDefinition);
+        assertEquals(BaseTypes.uint8Type(), version.getType().getBaseType().getBaseType());
+        assertTrue(version.isAddedByUses());
+        assertTrue(version.isAugmenting());
+        // * |-- |-- |-- |-- |-- leaf type
+        type = (LeafSchemaNode) pathKeys.getDataChildByName(QName.create(testModule.getQNameModule(), "type"));
+        assertNotNull(type);
+        expectedQName = QName.create(UG, "type");
+        assertEquals(expectedQName, type.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, type.getPath());
+        assertTrue(type.getType() instanceof UnionTypeDefinition);
+        assertTrue(type.isAddedByUses());
+        assertTrue(type.isAugmenting());
+        // * |-- |-- container segment-computation
+        final ContainerSchemaNode sc = (ContainerSchemaNode) requests.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "segment-computation"));
+        assertNotNull(sc);
+        expectedQName = QName.create(UG, "segment-computation");
+        assertEquals(expectedQName, sc.getQName());
+        path.pollLast();
+        path.pollLast();
+        path.pollLast();
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, sc.getPath());
+        assertFalse(sc.isAddedByUses());
+        // * |-- |-- |-- container p2p
+        final ContainerSchemaNode p2p = (ContainerSchemaNode) sc.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "p2p"));
+        assertNotNull(p2p);
+        expectedQName = QName.create(UG, "p2p");
+        assertEquals(expectedQName, p2p.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, p2p.getPath());
+        assertFalse(p2p.isAddedByUses());
+        // * |-- |-- |-- |-- container endpoints
+        final ContainerSchemaNode endpoints = (ContainerSchemaNode) p2p.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "endpoints"));
+        assertNotNull(endpoints);
+        expectedQName = QName.create(UG, "endpoints");
+        assertEquals(expectedQName, endpoints.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, endpoints.getPath());
+        assertFalse(endpoints.isAddedByUses());
+        // * |-- |-- |-- |-- |-- leaf processing-rule
+        processingRule = (LeafSchemaNode) endpoints.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "processing-rule"));
+        assertNotNull(processingRule);
+        expectedQName = QName.create(UG, "processing-rule");
+        assertEquals(expectedQName, processingRule.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, processingRule.getPath());
+        assertEquals(BaseTypes.booleanType(), processingRule.getType());
+        assertTrue(processingRule.isAddedByUses());
+        // * |-- |-- |-- |-- |-- leaf ignore
+        ignore = (LeafSchemaNode) endpoints.getDataChildByName(QName.create(testModule.getQNameModule(), "ignore"));
+        assertNotNull(ignore);
+        expectedQName = QName.create(UG, "ignore");
+        assertEquals(expectedQName, ignore.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, ignore.getPath());
+        assertEquals(BaseTypes.booleanType(), ignore.getType());
+        assertTrue(ignore.isAddedByUses());
+        // * |-- |-- |-- |-- |-- container box
+        box = (ContainerSchemaNode) endpoints.getDataChildByName(QName.create(testModule.getQNameModule(), "box"));
+        assertNotNull(box);
+        expectedQName = QName.create(UG, "box");
+        assertEquals(expectedQName, box.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, box.getPath());
+        assertTrue(box.isAddedByUses());
+        // * |-- |-- |-- |-- |-- choice address-family
+        final ChoiceSchemaNode af = (ChoiceSchemaNode) endpoints.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "address-family"));
+        assertNotNull(af);
+        expectedQName = QName.create(UG, "address-family");
+        assertEquals(expectedQName, af.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, af.getPath());
+        assertTrue(af.isAddedByUses());
+        // * |-- |-- |-- |-- container reported-route
+        final ContainerSchemaNode reportedRoute = (ContainerSchemaNode) p2p.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "reported-route"));
+        assertNotNull(reportedRoute);
+        expectedQName = QName.create(UG, "reported-route");
+        assertEquals(expectedQName, reportedRoute.getQName());
+        path.pollLast();
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, reportedRoute.getPath());
+        assertFalse(reportedRoute.isAddedByUses());
+        // * |-- |-- |-- |-- |-- leaf processing-rule
+        processingRule = (LeafSchemaNode) reportedRoute.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "processing-rule"));
+        assertNotNull(processingRule);
+        expectedQName = QName.create(UG, "processing-rule");
+        assertEquals(expectedQName, processingRule.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, processingRule.getPath());
+        assertEquals(BaseTypes.booleanType(), processingRule.getType());
+        assertTrue(processingRule.isAddedByUses());
+        // * |-- |-- |-- |-- |-- leaf ignore
+        ignore = (LeafSchemaNode) reportedRoute.getDataChildByName(QName.create(testModule.getQNameModule(), "ignore"));
+        assertNotNull(ignore);
+        expectedQName = QName.create(UG, "ignore");
+        assertEquals(expectedQName, ignore.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, ignore.getPath());
+        assertEquals(BaseTypes.booleanType(), ignore.getType());
+        assertTrue(ignore.isAddedByUses());
+        // * |-- |-- |-- |-- |-- list subobjects
+        final ListSchemaNode subobjects = (ListSchemaNode) reportedRoute.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "subobjects"));
+        assertNotNull(subobjects);
+        expectedQName = QName.create(UG, "subobjects");
+        assertEquals(expectedQName, subobjects.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, subobjects.getPath());
+        assertTrue(subobjects.isAddedByUses());
+        // * |-- |-- |-- |-- |-- container bandwidth
+        ContainerSchemaNode bandwidth = (ContainerSchemaNode) reportedRoute.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "bandwidth"));
+        assertNotNull(bandwidth);
+        expectedQName = QName.create(UG, "bandwidth");
+        assertEquals(expectedQName, bandwidth.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, bandwidth.getPath());
+        assertFalse(bandwidth.isAddedByUses());
+        // * |-- |-- |-- |-- container bandwidth
+        bandwidth = (ContainerSchemaNode) p2p
+                .getDataChildByName(QName.create(testModule.getQNameModule(), "bandwidth"));
+        assertNotNull(bandwidth);
+        expectedQName = QName.create(UG, "bandwidth");
+        assertEquals(expectedQName, bandwidth.getQName());
+        path.pollLast();
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, bandwidth.getPath());
+        assertTrue(bandwidth.isAddedByUses());
+        // * |-- |-- |-- |-- |-- leaf processing-rule
+        processingRule = (LeafSchemaNode) bandwidth.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "processing-rule"));
+        assertNotNull(processingRule);
+        expectedQName = QName.create(UG, "processing-rule");
+        assertEquals(expectedQName, processingRule.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, processingRule.getPath());
+        assertEquals(BaseTypes.booleanType(), processingRule.getType());
+        assertTrue(processingRule.isAddedByUses());
+        // * |-- |-- |-- |-- |-- leaf ignore
+        ignore = (LeafSchemaNode) bandwidth.getDataChildByName(QName.create(testModule.getQNameModule(), "ignore"));
+        assertNotNull(ignore);
+        expectedQName = QName.create(UG, "ignore");
+        assertEquals(expectedQName, ignore.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, ignore.getPath());
+        assertEquals(BaseTypes.booleanType(), ignore.getType());
+        assertTrue(ignore.isAddedByUses());
+        // * |-- |-- |-- |-- |-- container bandwidth
+        final ContainerSchemaNode bandwidthInner = (ContainerSchemaNode) bandwidth.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "bandwidth"));
+        assertNotNull(bandwidthInner);
+        expectedQName = QName.create(UG, "bandwidth");
+        assertEquals(expectedQName, bandwidth.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, bandwidthInner.getPath());
+        assertTrue(bandwidthInner.isAddedByUses());
+        // * |-- list svec
+        final ListSchemaNode svec = (ListSchemaNode) pcreq.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "svec"));
+        assertNotNull(svec);
+        expectedQName = QName.create(UG, "svec");
+        assertEquals(expectedQName, svec.getQName());
+        path.pollLast();
+        path.pollLast();
+        path.pollLast();
+        path.pollLast();
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, svec.getPath());
+        assertFalse(svec.isAddedByUses());
+        // * |-- |-- leaf link-diverse
+        final LeafSchemaNode linkDiverse = (LeafSchemaNode) svec.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "link-diverse"));
+        assertNotNull(linkDiverse);
+        expectedQName = QName.create(UG, "link-diverse");
+        assertEquals(expectedQName, linkDiverse.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, linkDiverse.getPath());
+        assertEquals(BaseTypes.booleanType(), linkDiverse.getType().getBaseType());
+        assertTrue(linkDiverse.isAddedByUses());
+        // * |-- |-- leaf processing-rule
+        processingRule = (LeafSchemaNode) svec.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "processing-rule"));
+        assertNotNull(processingRule);
+        expectedQName = QName.create(UG, "processing-rule");
+        assertEquals(expectedQName, processingRule.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, processingRule.getPath());
+        assertEquals(BaseTypes.booleanType(), processingRule.getType());
+        assertTrue(processingRule.isAddedByUses());
+        // * |-- |-- leaf ignore
+        ignore = (LeafSchemaNode) svec.getDataChildByName(QName.create(testModule.getQNameModule(), "ignore"));
+        assertNotNull(ignore);
+        expectedQName = QName.create(UG, "ignore");
+        assertEquals(expectedQName, ignore.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, ignore.getPath());
+        assertEquals(BaseTypes.booleanType(), ignore.getType());
+        assertTrue(ignore.isAddedByUses());
+        // * |-- |-- list metric
+        final ListSchemaNode metric = (ListSchemaNode) svec.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "metric"));
+        assertNotNull(metric);
+        expectedQName = QName.create(UG, "metric");
+        assertEquals(expectedQName, metric.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, metric.getPath());
+        assertFalse(metric.isAddedByUses());
+        // * |-- |-- |-- leaf metric-type
+        final LeafSchemaNode metricType = (LeafSchemaNode) metric.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "metric-type"));
+        assertNotNull(metricType);
+        expectedQName = QName.create(UG, "metric-type");
+        assertEquals(expectedQName, metricType.getQName());
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, metricType.getPath());
+        assertEquals(BaseTypes.uint8Type(), metricType.getType());
+        assertTrue(metricType.isAddedByUses());
+        // * |-- |-- |-- box
+        box = (ContainerSchemaNode) metric.getDataChildByName(QName.create(testModule.getQNameModule(), "box"));
+        assertNotNull(box);
+        expectedQName = QName.create(UG, "box");
+        assertEquals(expectedQName, box.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, box.getPath());
+        assertTrue(box.isAddedByUses());
+        // * |-- |-- |-- leaf processing-rule
+        processingRule = (LeafSchemaNode) metric.getDataChildByName(QName.create(testModule.getQNameModule(),
+                "processing-rule"));
+        assertNotNull(processingRule);
+        expectedQName = QName.create(UG, "processing-rule");
+        assertEquals(expectedQName, processingRule.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, processingRule.getPath());
+        assertEquals(BaseTypes.booleanType(), processingRule.getType());
+        assertTrue(processingRule.isAddedByUses());
+        // * |-- |-- |-- leaf ignore
+        ignore = (LeafSchemaNode) metric.getDataChildByName(QName.create(testModule.getQNameModule(), "ignore"));
+        assertNotNull(ignore);
+        expectedQName = QName.create(UG, "ignore");
+        assertEquals(expectedQName, ignore.getQName());
+        path.pollLast();
+        path.offer(expectedQName);
+        expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, ignore.getPath());
+        assertEquals(BaseTypes.booleanType(), ignore.getType());
+        assertTrue(ignore.isAddedByUses());
+    }
+
+    @Test
+    public void testTypedefs() throws Exception {
+        final Module testModule = TestUtils.findModule(context, "grouping-definitions").get();
+        final Set<TypeDefinition<?>> types = testModule.getTypeDefinitions();
+
+        TypeDefinition<?> intExt = null;
+        for (final TypeDefinition<?> td : types) {
+            if ("int-ext".equals(td.getQName().getLocalName())) {
+                intExt = td;
+            }
+        }
+        assertNotNull(intExt);
+
+        SchemaPath expectedPath = SchemaPath.create(true, QName.create(GD, "int-ext"));
+        assertEquals(expectedPath, intExt.getPath());
+
+        final UnionTypeDefinition union = (UnionTypeDefinition) intExt.getBaseType();
+
+        TypeDefinition<?> uint8 = null;
+        TypeDefinition<?> pv = null;
+        for (final TypeDefinition<?> td : union.getTypes()) {
+            if ("uint8".equals(td.getQName().getLocalName())) {
+                uint8 = td;
+            } else if ("protocol-version".equals(td.getQName().getLocalName())) {
+                pv = td;
+            }
+        }
+        assertNotNull(uint8);
+        assertNotNull(pv);
+
+        expectedPath = SchemaPath.create(true, QName.create(GD, "int-ext"), QName.create(GD, "union"));
+        assertEquals(expectedPath, union.getPath());
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT838Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT838Test.java
new file mode 100644 (file)
index 0000000..386bde2
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.stmt;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public class YT838Test {
+    @Test
+    public void testGroupingShadowing() throws Exception {
+        testGrouping("grouping.yang");
+    }
+
+    @Test
+    public void testGroupingPostShadowing() throws Exception {
+        testGrouping("grouping-post.yang");
+    }
+
+    @Test
+    public void testTypedefShadowing() throws Exception {
+        testTypedef("typedef.yang");
+    }
+
+    @Test
+    public void testTypedefPostShadowing() throws Exception {
+        testTypedef("typedef-post.yang");
+    }
+
+    private static void testGrouping(final String model) throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/bugs/YT838/" + model);
+            fail("Expected failure due to grouping identifier shadowing");
+        } catch (ReactorException e) {
+            final Throwable cause = e.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith(
+                "Duplicate name for grouping (grouping?revision=2017-12-20)foo [at "));
+        }
+    }
+
+    private static void testTypedef(final String model) throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/bugs/YT838/" + model);
+            fail("Expected failure due to type identifier shadowing");
+        } catch (ReactorException e) {
+            final Throwable cause = e.getCause();
+            assertTrue(cause instanceof SourceException);
+            assertTrue(cause.getMessage().startsWith(
+                "Duplicate name for typedef (typedef?revision=2017-12-20)foo [at "));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT841Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT841Test.java
new file mode 100644 (file)
index 0000000..24662de
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+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.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class YT841Test {
+    private static final QNameModule FOO = QNameModule.create(URI.create("foo"), Revision.of("2018-01-02"));
+
+    @Test
+    public void testFindDataSchemaNode() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/YT841/");
+        final Module foo = context.findModule(FOO).get();
+
+        final SchemaNode target = SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true,
+            QName.create(FOO, "foo"),
+            QName.create(FOO, "foo"),
+            QName.create(FOO, "foo"),
+            QName.create(FOO, "input")));
+        assertNotNull(target);
+
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT857Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT857Test.java
new file mode 100644 (file)
index 0000000..7bda7a0
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.stmt;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public class YT857Test {
+    @Test
+    public void testConflictDetection() throws Exception {
+        try {
+            StmtTestUtils.parseYangSource("/bugs/YT857/foo.yang");
+            fail("Conflict on models should have been detected");
+        } catch (ReactorException e) {
+            final Throwable cause = e.getCause();
+            assertThat(cause, instanceOf(SourceException.class));
+            assertThat(cause.getMessage(),
+                startsWith("Cannot add data tree child with name (foo)one, a conflicting child already exists"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT893Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT893Test.java
new file mode 100644 (file)
index 0000000..4c40428
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+
+public class YT893Test {
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void testCR() throws Exception {
+        StmtTestUtils.parseYangSource("/bugs/YT893/cr.yang");
+    }
+
+    @Test
+    public void testCRLF() throws Exception {
+        assertNotNull(StmtTestUtils.parseYangSource("/bugs/YT893/crlf.yang"));
+    }
+
+    @Test
+    public void testHTAB() throws Exception {
+        assertNotNull(StmtTestUtils.parseYangSource("/bugs/YT893/ht.yang"));
+    }
+
+    @Test
+    public void testLF() throws Exception {
+        assertNotNull(StmtTestUtils.parseYangSource("/bugs/YT893/lf.yang"));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT911Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT911Test.java
new file mode 100644 (file)
index 0000000..d475d51
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.stmt;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class YT911Test {
+    private static final QName FOO = QName.create("foo", "2018-10-22", "foo");
+    private static final QName BAR = QName.create(FOO, "bar");
+
+    @Test
+    public void testAugmentationConfig() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSource("/bugs/YT911/foo.yang");
+        final DataSchemaNode foo = context.findDataChildByName(FOO).get();
+        assertFalse(foo.isConfiguration());
+        assertTrue(foo instanceof ContainerSchemaNode);
+
+        // Instantiated node
+        final DataSchemaNode bar = ((ContainerSchemaNode) foo).findDataTreeChild(BAR).get();
+        assertFalse(bar.isConfiguration());
+        assertTrue(foo instanceof ContainerSchemaNode);
+
+        // Original augmentation node
+        final AugmentationSchemaNode aug = ((ContainerSchemaNode) foo).getAvailableAugmentations().iterator().next();
+        final DataSchemaNode augBar = aug.findDataTreeChild(BAR).get();
+        assertTrue(augBar.isConfiguration());
+        assertTrue(foo instanceof ContainerSchemaNode);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT956Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT956Test.java
new file mode 100644 (file)
index 0000000..9b232b5
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.stmt;
+
+import static org.hamcrest.Matchers.isA;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+public class YT956Test {
+    private static final QName ANOTHER_CONTAINER = QName.create("http://www.example.com/anothermodule",
+        "another-container");
+    private static final QName FIRST_AUGMENT = QName.create("http://www.example.com/mainmodule", "first-augment");
+
+    @Test
+    public void testAugmentationConditional() throws Exception {
+        final DataSchemaNode another = StmtTestUtils.parseYangSources("/bugs/YT956/")
+                .findDataChildByName(ANOTHER_CONTAINER).get();
+        assertThat(another, isA(ContainerSchemaNode.class));
+        final ContainerSchemaNode anotherContainer = (ContainerSchemaNode) another;
+
+        final DataSchemaNode first = anotherContainer.findDataChildByName(FIRST_AUGMENT).get();
+        assertThat(first, isA(ContainerSchemaNode.class));
+        final ContainerSchemaNode firstAugment = (ContainerSchemaNode) first;
+
+        // Augmentation needs to be added
+        assertEquals(3, firstAugment.getChildNodes().size());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT971Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT971Test.java
new file mode 100644 (file)
index 0000000..ae5fedc
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019 Ericsson AB. 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.stmt;
+
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class YT971Test {
+    private static final QNameModule NAMESPACE = QNameModule.create(URI.create("test"), Revision.of("2019-03-25"));
+
+    @Test
+    public void testEscapeLexer() throws URISyntaxException, IOException, YangSyntaxErrorException, ReactorException {
+        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/bugs/YT971/test.yang");
+        assertNotNull(schemaContext);
+
+        final DataSchemaNode someContainer = schemaContext.findDataChildByName(
+            QName.create(NAMESPACE, "some-container")).get();
+        assertThat(someContainer, instanceOf(ContainerSchemaNode.class));
+        final ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) someContainer;
+
+        final DataSchemaNode someLeaf = containerSchemaNode.findDataChildByName(QName.create(NAMESPACE, "some-leaf"))
+                .get();
+        assertThat(someLeaf, instanceOf(LeafSchemaNode.class));
+        final LeafSchemaNode leafSchemaNode = (LeafSchemaNode) someLeaf;
+        assertEquals(Optional.of("Some string that ends with a backslash (with escape backslash too) \\"),
+                     leafSchemaNode.getDescription());
+        assertThat(leafSchemaNode.getType(), instanceOf(Int16TypeDefinition.class));
+
+        final DataSchemaNode someOtherLeaf = containerSchemaNode.findDataChildByName(
+                QName.create(NAMESPACE, "some-other-leaf")).get();
+        assertThat(someOtherLeaf, instanceOf(LeafSchemaNode.class));
+
+        final LeafSchemaNode otherLeafSchemaNode = (LeafSchemaNode) someOtherLeaf;
+        assertEquals(Optional.of("Some string after the double backslash"), otherLeafSchemaNode.getDescription());
+        assertThat(otherLeafSchemaNode.getType(), instanceOf(Int32TypeDefinition.class));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT983Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YT983Test.java
new file mode 100644 (file)
index 0000000..1a5f6f8
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.stmt;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class YT983Test {
+    private static final QName FOO = QName.create("foo", "2019-04-30", "foo");
+
+    @Test
+    public void testAugmentationConfig() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSource("/bugs/YT983/foo.yang");
+        final DataSchemaNode foo = context.findDataChildByName(FOO).get();
+        assertTrue(foo instanceof LeafSchemaNode);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangFileStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangFileStmtTest.java
new file mode 100644 (file)
index 0000000..a510d3c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.ReactorDeclaredModel;
+
+public class YangFileStmtTest {
+    //basic statements to parse and write
+    private static final StatementStreamSource YANGFILE = sourceForResource("/semantic-statement-parser/test.yang");
+    private static final StatementStreamSource IMPORTEDYANGFILE = sourceForResource(
+        "/semantic-statement-parser/importedtest.yang");
+    private static final StatementStreamSource SIMPLENODES = sourceForResource(
+        "/semantic-statement-parser/simple-nodes-semantic.yang");
+    private static final StatementStreamSource FOOBAR = sourceForResource("/semantic-statement-parser/foobar.yang");
+    //extension statement to parse and write
+    private static final StatementStreamSource EXTFILE = sourceForResource(
+        "/semantic-statement-parser/ext-typedef.yang");
+    private static final StatementStreamSource EXTUSE = sourceForResource("/semantic-statement-parser/ext-use.yang");
+
+
+    private static final StatementStreamSource BAR = sourceForResource("/model-new/bar.yang");
+    private static final StatementStreamSource BAZ = sourceForResource("/model-new/baz.yang");
+    private static final StatementStreamSource FOO = sourceForResource("/model-new/foo.yang");
+    private static final StatementStreamSource SUBFOO = sourceForResource("/model-new/subfoo.yang");
+
+    private static final StatementStreamSource BAR2 = sourceForResource("/model/bar.yang");
+    private static final StatementStreamSource BAZ2 = sourceForResource("/model/baz.yang");
+    private static final StatementStreamSource FOO2 = sourceForResource("/model/foo.yang");
+    private static final StatementStreamSource SUBFOO2 = sourceForResource("/model/subfoo.yang");
+
+    @Test
+    public void readAndParseYangFileTestModel() throws ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(BAZ, FOO, BAR, SUBFOO)
+                .build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void readAndParseYangFileTestModel2() throws ReactorException {
+        ReactorDeclaredModel result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(BAZ2, FOO2, BAR2, SUBFOO2)
+                .build();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void readAndParseYangFileTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(YANGFILE, SIMPLENODES, IMPORTEDYANGFILE, FOOBAR, EXTFILE, EXTUSE)
+                .buildEffective();
+        assertNotNull(result);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserIdentityTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserIdentityTest.java
new file mode 100644 (file)
index 0000000..4b2d78a
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import java.io.IOException;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+
+public class YangParserIdentityTest {
+
+    // base identity name equals identity name
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void testParsingIdentityTestModule() throws IOException, ReactorException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(), "/identity/identitytest.yang");
+        } catch (SomeModifiersUnresolvedException e) {
+            StmtTestUtils.log(e, "      ");
+            throw e;
+        }
+    }
+
+    // same module prefixed base identity name equals identity name
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void testParsingPrefixIdentityTestModule() throws IOException, ReactorException, YangSyntaxErrorException  {
+        try {
+            TestUtils.loadModuleResources(getClass(), "/identity/prefixidentitytest.yang");
+        } catch (SomeModifiersUnresolvedException e) {
+            StmtTestUtils.log(e, "      ");
+            throw e;
+        }
+    }
+
+    // imported module prefixed base identity name equals identity name, but
+    // prefix differs
+    @Test
+    public void testParsingImportPrefixIdentityTestModule() throws Exception {
+        Module module = TestUtils.findModule(TestUtils.loadModules(getClass().getResource("/identity/import").toURI()),
+            "prefiximportidentitytest").get();
+        Set<ModuleImport> imports = module.getImports();
+        assertEquals(imports.size(), 1);
+        ModuleImport dummy = TestUtils.findImport(imports, "dummy");
+        assertNotEquals(dummy.getPrefix(), module.getPrefix());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserNegativeTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserNegativeTest.java
new file mode 100644 (file)
index 0000000..bc688a9
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.Matchers.isA;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Throwables;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public class YangParserNegativeTest {
+
+    @SuppressWarnings("checkstyle:regexpSinglelineJava")
+    private final PrintStream stdout = System.out;
+    private final ByteArrayOutputStream output = new ByteArrayOutputStream();
+    private String testLog;
+
+    @Before
+    public void setUp() throws UnsupportedEncodingException {
+        System.setOut(new PrintStream(output, true, "UTF-8"));
+    }
+
+    @After
+    public void cleanUp() {
+        System.setOut(stdout);
+    }
+
+    @Test
+    public void testInvalidImport() throws IOException, ReactorException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(), "/negative-scenario/testfile1.yang");
+            fail("SomeModifiersUnresolvedException should be thrown");
+        } catch (final SomeModifiersUnresolvedException e) {
+            final Throwable rootCause = Throwables.getRootCause(e);
+            assertThat(rootCause, isA(InferenceException.class));
+            assertThat(rootCause.getMessage(), startsWith("Imported module"));
+            assertThat(rootCause.getMessage(), containsString("was not found."));
+        }
+    }
+
+    @Test
+    public void testTypeNotFound() throws IOException, ReactorException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(), "/negative-scenario/testfile2.yang");
+            fail("InferenceException should be thrown");
+        } catch (final SomeModifiersUnresolvedException e) {
+            final Throwable rootCause = Throwables.getRootCause(e);
+            assertThat(rootCause, isA(InferenceException.class));
+            assertThat(rootCause.getMessage(),
+                startsWith("Type [(urn:simple.types.data.demo?revision=2013-02-27)int-ext] was not found."));
+        }
+    }
+
+    @Test
+    public void testInvalidAugmentTarget() throws IOException, ReactorException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(),
+                "/negative-scenario/testfile0.yang",
+                "/negative-scenario/testfile3.yang");
+            fail("SomeModifiersUnresolvedException should be thrown");
+        } catch (final SomeModifiersUnresolvedException e) {
+            final Throwable rootCause = Throwables.getRootCause(e);
+            assertThat(rootCause, isA(InferenceException.class));
+            assertThat(rootCause.getMessage(), startsWith(
+                "Augment target 'Absolute{path=[(urn:simple.container.demo)unknown]}' not found"));
+        }
+    }
+
+    @Test
+    public void testInvalidRefine() throws IOException, ReactorException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(), "/negative-scenario/testfile4.yang");
+            fail("ReactorException should be thrown");
+        } catch (final ReactorException e) {
+            assertThat(e.getCause().getMessage(), containsString("Error in module 'test4' in the refine of uses "
+                    + "'Relative{path=[(urn:simple.container.demo)node]}': can not perform refine of 'PRESENCE' for"
+                    + " the target 'LEAF_LIST'."));
+        }
+    }
+
+    @Test
+    public void testInvalidLength() throws IOException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(), "/negative-scenario/testfile5.yang");
+            fail("ReactorException should be thrown");
+        } catch (final ReactorException e) {
+            assertThat(e.getCause().getMessage(), containsString("Invalid length constraint [4..10]"));
+        }
+    }
+
+    @Test
+    public void testInvalidRange() throws IOException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(), "/negative-scenario/testfile6.yang");
+            fail("ReactorException should be thrown");
+        } catch (final ReactorException e) {
+            assertThat(e.getCause().getMessage(), startsWith("Invalid range constraint: [[5..20]]"));
+        }
+    }
+
+    @Test
+    public void testDuplicateContainer() throws IOException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(), "/negative-scenario/duplicity/container.yang");
+            fail("SourceException should be thrown");
+        } catch (final ReactorException e) {
+            final String expected = "Error in module 'container': cannot add '(urn:simple.container.demo)foo'. "
+                    + "Node name collision: '(urn:simple.container.demo)foo' already declared";
+            assertThat(e.getCause().getMessage(), containsString(expected));
+        }
+    }
+
+    @Test
+    public void testDuplicateContainerList() throws IOException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(), "/negative-scenario/duplicity/container-list.yang");
+            fail("SourceException should be thrown");
+        } catch (final ReactorException e) {
+            final String expected = "Error in module 'container-list': cannot add '(urn:simple.container.demo)foo'. "
+                    + "Node name collision: '(urn:simple.container.demo)foo' already declared";
+            assertThat(e.getCause().getMessage(), containsString(expected));
+        }
+    }
+
+    @Test
+    public void testDuplicateContainerLeaf() throws IOException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(), "/negative-scenario/duplicity/container-leaf.yang");
+            fail("SourceException should be thrown");
+        } catch (final ReactorException e) {
+            final String expected = "Error in module 'container-leaf': cannot add '(urn:simple.container.demo)foo'. "
+                    + "Node name collision: '(urn:simple.container.demo)foo' already declared";
+            assertThat(e.getCause().getMessage(), containsString(expected));
+        }
+    }
+
+    @Test
+    public void testDuplicateTypedef() throws IOException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(), "/negative-scenario/duplicity/typedef.yang");
+            fail("SourceException should be thrown");
+        } catch (final ReactorException e) {
+            assertThat(e.getCause().getMessage(), startsWith(
+                "Duplicate name for typedef (urn:simple.container.demo)int-ext [at"));
+        }
+    }
+
+    @Test
+    public void testDuplicityInAugmentTarget1() throws IOException, ReactorException, YangSyntaxErrorException {
+        TestUtils.loadModuleResources(getClass(),
+            "/negative-scenario/duplicity/augment0.yang",
+                "/negative-scenario/duplicity/augment1.yang");
+        testLog = output.toString();
+        assertThat(testLog, containsString(
+            "An augment cannot add node named 'id' because this name is already used in target"));
+    }
+
+    @Test
+    public void testDuplicityInAugmentTarget2() throws IOException, ReactorException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(),
+                "/negative-scenario/duplicity/augment0.yang",
+                    "/negative-scenario/duplicity/augment2.yang");
+            fail("Duplicate leaf not detected");
+        } catch (SomeModifiersUnresolvedException e) {
+            final Throwable rootCause = Throwables.getRootCause(e);
+            assertThat(rootCause, isA(SourceException.class));
+            assertThat(rootCause.getMessage(), containsString("Cannot add schema tree child with name "
+                    + "(urn:simple.augment2.demo?revision=2014-06-02)delta, a conflicting child already exists"));
+
+        }
+    }
+
+    @Test
+    public void testMandatoryInAugment() throws IOException, ReactorException, YangSyntaxErrorException {
+        TestUtils.loadModuleResources(getClass(),
+            "/negative-scenario/testfile8.yang",
+                "/negative-scenario/testfile7.yang");
+        testLog = output.toString();
+        assertThat(testLog, containsString(
+            "An augment cannot add node 'linkleaf' because it is mandatory and in module different than target"));
+    }
+
+    @Test
+    public void testInvalidListKeyDefinition() throws IOException, YangSyntaxErrorException {
+        try {
+            TestUtils.loadModuleResources(getClass(), "/negative-scenario/invalid-list-key-def.yang");
+            fail("InferenceException should be thrown");
+        } catch (final ReactorException e) {
+            assertThat(e.getCause().getMessage(),
+                startsWith("Key 'rib-id' misses node 'rib-id' in list '(invalid:list:key:def)application-map'"));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserSimpleTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserSimpleTest.java
new file mode 100644 (file)
index 0000000..328ac01
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+public class YangParserSimpleTest {
+    private static final QNameModule SN = QNameModule.create(URI.create("urn:opendaylight:simple-nodes"),
+        Revision.of("2013-07-30"));
+    private static final QName SN_NODES = QName.create(SN, "nodes");
+    private static final SchemaPath SN_NODES_PATH = SchemaPath.create(true, SN_NODES);
+
+    private SchemaContext context;
+    private Module testModule;
+
+    @Before
+    public void init() throws Exception {
+        context = TestUtils.loadModules(getClass().getResource("/simple-test").toURI());
+        testModule = TestUtils.findModule(context, "simple-nodes").get();
+    }
+
+    @Test
+    public void testParseAnyXml() {
+        final AnyxmlSchemaNode data = (AnyxmlSchemaNode) testModule.getDataChildByName(
+            QName.create(testModule.getQNameModule(), "data"));
+        assertNotNull("'anyxml data not found'", data);
+        assertFalse(data.equals(null));
+        assertEquals("AnyxmlEffectiveStatementImpl[qname=(urn:opendaylight:simple-nodes?revision=2013-07-30)data, "
+                + "path=AbsoluteSchemaPath{path=[(urn:opendaylight:simple-nodes?revision=2013-07-30)data]}]",
+                data.toString());
+
+        // test SchemaNode args
+        assertEquals(QName.create(SN, "data"), data.getQName());
+        assertEquals(Optional.of("anyxml desc"), data.getDescription());
+        assertEquals(Optional.of("data ref"), data.getReference());
+        assertEquals(Status.OBSOLETE, data.getStatus());
+        assertEquals(0, data.getUnknownSchemaNodes().size());
+        // test DataSchemaNode args
+        assertFalse(data.isAugmenting());
+        assertFalse(data.isConfiguration());
+
+        assertTrue(data.isMandatory());
+        assertEquals("class != 'wheel'", data.getWhenCondition().get().getOriginalString());
+        final Collection<MustDefinition> mustConstraints = data.getMustConstraints();
+        assertEquals(2, mustConstraints.size());
+
+        final String must1 = "ifType != 'ethernet' or (ifType = 'ethernet' and ifMTU = 1500)";
+        final String must2 = "ifType != 'atm' or (ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)";
+
+        boolean found1 = false;
+        boolean found2 = false;
+        for (final MustDefinition must : mustConstraints) {
+            if (must1.equals(must.toString())) {
+                found1 = true;
+                assertEquals(Optional.of("An ethernet MTU must be 1500"), must.getErrorMessage());
+            } else if (must2.equals(must.toString())) {
+                found2 = true;
+                assertEquals(Optional.of("An atm MTU must be  64 .. 17966"), must.getErrorMessage());
+                assertEquals(Optional.of("anyxml data error-app-tag"), must.getErrorAppTag());
+                assertEquals(Optional.of("an error occured in data"), must.getDescription());
+                assertEquals(Optional.of("data must ref"), must.getReference());
+            }
+        }
+        assertTrue(found1);
+        assertTrue(found2);
+    }
+
+    @Test
+    public void testParseAnyData() {
+        final AnydataSchemaNode anydata = (AnydataSchemaNode) testModule.findDataChildByName(
+                QName.create(testModule.getQNameModule(), "data2")).orElse(null);
+
+        assertNotNull("'anydata data not found'", anydata);
+        assertEquals("AnydataEffectiveStatementImpl{qname=(urn:opendaylight:simple-nodes?revision=2013-07-30)data2, "
+                        + "path=AbsoluteSchemaPath{path=[(urn:opendaylight:simple-nodes?revision=2013-07-30)data2]}}",
+                anydata.toString());
+
+        // test SchemaNode args
+        assertEquals(QName.create(SN, "data2"), anydata.getQName());
+        assertEquals(Optional.of("anydata desc"), anydata.getDescription());
+        assertEquals(Optional.of("data ref"), anydata.getReference());
+        assertEquals(Status.OBSOLETE, anydata.getStatus());
+        assertEquals(0, anydata.getUnknownSchemaNodes().size());
+        // test DataSchemaNode args
+        assertFalse(anydata.isAugmenting());
+        assertFalse(anydata.isConfiguration());
+
+        assertTrue(anydata.isMandatory());
+        assertTrue(anydata.getWhenCondition().isPresent());
+        assertEquals("class != 'wheel'", anydata.getWhenCondition().get().getOriginalString());
+        final Collection<MustDefinition> mustConstraints = anydata.getMustConstraints();
+        assertEquals(2, mustConstraints.size());
+
+        final String must1 = "ifType != 'ethernet' or (ifType = 'ethernet' and ifMTU = 1500)";
+        final String must2 = "ifType != 'atm' or (ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)";
+
+        boolean found1 = false;
+        boolean found2 = false;
+        for (final MustDefinition must : mustConstraints) {
+            if (must1.equals(must.toString())) {
+                found1 = true;
+                assertEquals(Optional.of("An ethernet MTU must be 1500"), must.getErrorMessage());
+            } else if (must2.equals(must.toString())) {
+                found2 = true;
+                assertEquals(Optional.of("An atm MTU must be  64 .. 17966"), must.getErrorMessage());
+                assertEquals(Optional.of("anydata data error-app-tag"), must.getErrorAppTag());
+                assertEquals(Optional.of("an error occured in data"), must.getDescription());
+                assertEquals(Optional.of("data must ref"), must.getReference());
+            }
+        }
+        assertTrue(found1);
+        assertTrue(found2);
+    }
+
+    @Test
+    public void testParseContainer() {
+        final ContainerSchemaNode nodes = (ContainerSchemaNode) testModule
+                .getDataChildByName(QName.create(testModule.getQNameModule(), "nodes"));
+        // test SchemaNode args
+        assertEquals(SN_NODES, nodes.getQName());
+        assertEquals(SN_NODES_PATH, nodes.getPath());
+        assertEquals(Optional.of("nodes collection"), nodes.getDescription());
+        assertEquals(Optional.of("nodes ref"), nodes.getReference());
+        assertEquals(Status.CURRENT, nodes.getStatus());
+        assertEquals(0, nodes.getUnknownSchemaNodes().size());
+        // test DataSchemaNode args
+        assertFalse(nodes.isAugmenting());
+        assertFalse(nodes.isConfiguration());
+
+        // constraints
+        assertEquals("class != 'wheel'", nodes.getWhenCondition().get().getOriginalString());
+        final Collection<MustDefinition> mustConstraints = nodes.getMustConstraints();
+        assertEquals(2, mustConstraints.size());
+
+        final String must1 = "ifType != 'atm' or (ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)";
+        final String errMsg1 = "An atm MTU must be  64 .. 17966";
+        final String must2 = "ifId != 0";
+
+        boolean found1 = false;
+        boolean found2 = false;
+        for (final MustDefinition must : mustConstraints) {
+            if (must1.equals(must.toString())) {
+                found1 = true;
+                assertEquals(Optional.of(errMsg1), must.getErrorMessage());
+            } else if (must2.equals(must.toString())) {
+                found2 = true;
+                assertFalse(must.getErrorMessage().isPresent());
+                assertFalse(must.getErrorAppTag().isPresent());
+                assertFalse(must.getDescription().isPresent());
+                assertFalse(must.getReference().isPresent());
+            }
+        }
+        assertTrue(found1);
+        assertTrue(found2);
+
+        assertTrue(nodes.isPresenceContainer());
+
+        // typedef
+        final Set<TypeDefinition<?>> typedefs = nodes.getTypeDefinitions();
+        assertEquals(1, typedefs.size());
+        final TypeDefinition<?> nodesType = typedefs.iterator().next();
+        final QName typedefQName = QName.create(SN, "nodes-type");
+        assertEquals(typedefQName, nodesType.getQName());
+        assertEquals(SN_NODES_PATH.createChild(QName.create(SN, "nodes-type")), nodesType.getPath());
+        assertFalse(nodesType.getDescription().isPresent());
+        assertFalse(nodesType.getReference().isPresent());
+        assertEquals(Status.CURRENT, nodesType.getStatus());
+        assertEquals(0, nodesType.getUnknownSchemaNodes().size());
+
+        // child nodes
+        // total size = 8: defined 6, inserted by uses 2
+        assertEquals(8, nodes.getChildNodes().size());
+        final LeafListSchemaNode added = (LeafListSchemaNode)nodes.getDataChildByName(QName.create(
+            testModule.getQNameModule(), "added"));
+        assertEquals(createPath("nodes", "added"), added.getPath());
+        assertEquals(createPath("mytype"), added.getType().getPath());
+
+        final ListSchemaNode links = (ListSchemaNode) nodes.getDataChildByName(QName.create(
+            testModule.getQNameModule(), "links"));
+        assertFalse(links.isUserOrdered());
+
+        final Set<GroupingDefinition> groupings = nodes.getGroupings();
+        assertEquals(1, groupings.size());
+        final GroupingDefinition nodeGroup = groupings.iterator().next();
+        final QName groupQName = QName.create(SN, "node-group");
+        assertEquals(groupQName, nodeGroup.getQName());
+        final SchemaPath nodeGroupPath = SN_NODES_PATH.createChild(groupQName);
+        assertEquals(nodeGroupPath, nodeGroup.getPath());
+
+        final Set<UsesNode> uses = nodes.getUses();
+        assertEquals(1, uses.size());
+        final UsesNode use = uses.iterator().next();
+        assertEquals(nodeGroupPath, use.getGroupingPath());
+    }
+
+
+    private static final URI NS = URI.create("urn:opendaylight:simple-nodes");
+
+    private static SchemaPath createPath(final String... names) {
+        final Revision rev = Revision.of("2013-07-30");
+        final List<QName> path = new ArrayList<>();
+        for (final String name : names) {
+            path.add(QName.create(NS, rev, name));
+        }
+        return SchemaPath.create(path, true);
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserTest.java
new file mode 100644 (file)
index 0000000..44e219c
--- /dev/null
@@ -0,0 +1,757 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeSet;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.SortedMap;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DeviateKind;
+import org.opendaylight.yangtools.yang.model.api.Deviation;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+
+public class YangParserTest {
+    private static final QNameModule FOO = QNameModule.create(URI.create("urn:opendaylight.foo"),
+        Revision.of("2013-02-27"));
+    private static final QNameModule BAR = QNameModule.create(URI.create("urn:opendaylight.bar"),
+        Revision.of("2013-07-03"));
+    private static final QNameModule BAZ = QNameModule.create(URI.create("urn:opendaylight.baz"),
+        Revision.of("2013-02-27"));
+
+    private SchemaContext context;
+    private Module foo;
+    private Module bar;
+    private Module baz;
+
+    @Before
+    public void init() throws Exception {
+        context = TestUtils.loadModules(getClass().getResource("/model").toURI());
+        foo = TestUtils.findModule(context, "foo").get();
+        bar = TestUtils.findModule(context, "bar").get();
+        baz = TestUtils.findModule(context, "baz").get();
+    }
+
+    @Test
+    public void testHeaders() throws ParseException {
+        assertEquals("foo", foo.getName());
+        assertEquals(YangVersion.VERSION_1, foo.getYangVersion());
+        assertEquals(FOO.getNamespace(), foo.getNamespace());
+        assertEquals("foo", foo.getPrefix());
+
+        final Set<ModuleImport> imports = foo.getImports();
+        assertEquals(2, imports.size());
+
+        final ModuleImport import2 = TestUtils.findImport(imports, "br");
+        assertEquals("bar", import2.getModuleName());
+        assertEquals(BAR.getRevision(), import2.getRevision());
+
+        final ModuleImport import3 = TestUtils.findImport(imports, "bz");
+        assertEquals("baz", import3.getModuleName());
+        assertEquals(BAZ.getRevision(), import3.getRevision());
+
+        assertEquals(Optional.of("opendaylight"), foo.getOrganization());
+        assertEquals(Optional.of("http://www.opendaylight.org/"), foo.getContact());
+        assertEquals(Revision.ofNullable("2013-02-27"), foo.getRevision());
+        assertFalse(foo.getReference().isPresent());
+    }
+
+    @Test
+    public void testParseList() {
+        final ContainerSchemaNode interfaces = (ContainerSchemaNode) bar.getDataChildByName(QName.create(
+            bar.getQNameModule(), "interfaces"));
+
+        final ListSchemaNode ifEntry = (ListSchemaNode) interfaces.getDataChildByName(QName.create(bar.getQNameModule(),
+            "ifEntry"));
+        // test SchemaNode args
+        assertEquals(QName.create(BAR, "ifEntry"), ifEntry.getQName());
+
+        final SchemaPath expectedPath = TestUtils.createPath(true, BAR, "interfaces", "ifEntry");
+        assertEquals(expectedPath, ifEntry.getPath());
+        assertFalse(ifEntry.getDescription().isPresent());
+        assertFalse(ifEntry.getReference().isPresent());
+        assertEquals(Status.CURRENT, ifEntry.getStatus());
+        assertEquals(0, ifEntry.getUnknownSchemaNodes().size());
+        // test DataSchemaNode args
+        assertFalse(ifEntry.isAugmenting());
+        assertTrue(ifEntry.isConfiguration());
+        // :TODO augment to ifEntry have when condition and so in consequence
+        // ifEntry should be a context node ?
+        // assertNull(constraints.getWhenCondition());
+        assertEquals(0, ifEntry.getMustConstraints().size());
+        ElementCountConstraint constraints = ifEntry.getElementCountConstraint().get();
+        assertEquals(1, constraints.getMinElements().intValue());
+        assertEquals(11, constraints.getMaxElements().intValue());
+        // test AugmentationTarget args
+        final Set<AugmentationSchemaNode> availableAugmentations = ifEntry.getAvailableAugmentations();
+        assertEquals(2, availableAugmentations.size());
+        // test ListSchemaNode args
+        final List<QName> expectedKey = new ArrayList<>();
+        expectedKey.add(QName.create(BAR, "ifIndex"));
+        assertEquals(expectedKey, ifEntry.getKeyDefinition());
+        assertFalse(ifEntry.isUserOrdered());
+        // test DataNodeContainer args
+        assertEquals(0, ifEntry.getTypeDefinitions().size());
+        assertEquals(4, ifEntry.getChildNodes().size());
+        assertEquals(0, ifEntry.getGroupings().size());
+        assertEquals(0, ifEntry.getUses().size());
+
+        final LeafSchemaNode ifIndex = (LeafSchemaNode) ifEntry.getDataChildByName(QName.create(bar.getQNameModule(),
+            "ifIndex"));
+        assertEquals(ifEntry.getKeyDefinition().get(0), ifIndex.getQName());
+        assertTrue(ifIndex.getType() instanceof Uint32TypeDefinition);
+        assertEquals(Optional.of("minutes"), ifIndex.getType().getUnits());
+        final LeafSchemaNode ifMtu = (LeafSchemaNode) ifEntry.getDataChildByName(QName.create(bar.getQNameModule(),
+            "ifMtu"));
+        assertEquals(BaseTypes.int32Type(), ifMtu.getType());
+    }
+
+    @Test
+    public void testTypedefRangesResolving() throws ParseException {
+        final LeafSchemaNode int32Leaf = (LeafSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+            "int32-leaf"));
+
+        final Int32TypeDefinition leafType = (Int32TypeDefinition) int32Leaf.getType();
+        assertEquals(QName.create(FOO, "int32-ext2"), leafType.getQName());
+        assertEquals(Optional.of("mile"), leafType.getUnits());
+        assertEquals(Optional.of("11"), leafType.getDefaultValue());
+
+        final RangeSet<? extends Number> rangeset = leafType.getRangeConstraint().get().getAllowedRanges();
+        final Set<? extends Range<? extends Number>> ranges = rangeset.asRanges();
+        assertEquals(1, ranges.size());
+
+        final Range<? extends Number> range = ranges.iterator().next();
+        assertEquals(12, range.lowerEndpoint().intValue());
+        assertEquals(20, range.upperEndpoint().intValue());
+
+        final Int32TypeDefinition firstBaseType = leafType.getBaseType();
+        assertEquals(QName.create(BAR, "int32-ext2"), firstBaseType.getQName());
+        assertEquals(Optional.of("mile"), firstBaseType.getUnits());
+        assertEquals(Optional.of("11"), firstBaseType.getDefaultValue());
+
+        final RangeSet<? extends Number> firstRangeset = firstBaseType.getRangeConstraint().get().getAllowedRanges();
+        final Set<? extends Range<? extends Number>> baseRanges = firstRangeset.asRanges();
+        assertEquals(2, baseRanges.size());
+
+        final Iterator<? extends Range<? extends Number>> it = baseRanges.iterator();
+        final Range<? extends Number> baseTypeRange1 = it.next();
+        assertEquals(3, baseTypeRange1.lowerEndpoint().intValue());
+        assertEquals(9, baseTypeRange1.upperEndpoint().intValue());
+        final Range<? extends Number> baseTypeRange2 = it.next();
+        assertEquals(11, baseTypeRange2.lowerEndpoint().intValue());
+        assertEquals(20, baseTypeRange2.upperEndpoint().intValue());
+
+        final Int32TypeDefinition secondBaseType = firstBaseType.getBaseType();
+        final QName baseQName = secondBaseType.getQName();
+        assertEquals("int32-ext1", baseQName.getLocalName());
+        assertEquals(BAR, baseQName.getModule());
+        assertEquals(Optional.empty(), secondBaseType.getUnits());
+        assertEquals(Optional.empty(), secondBaseType.getDefaultValue());
+
+        final Set<? extends Range<? extends Number>> secondRanges = secondBaseType.getRangeConstraint().get()
+                .getAllowedRanges().asRanges();
+        assertEquals(1, secondRanges.size());
+        final Range<? extends Number> secondRange = secondRanges.iterator().next();
+        assertEquals(2, secondRange.lowerEndpoint().intValue());
+        assertEquals(20, secondRange.upperEndpoint().intValue());
+
+        assertEquals(BaseTypes.int32Type(), secondBaseType.getBaseType());
+    }
+
+    @Test
+    public void testTypedefPatternsResolving() {
+        final LeafSchemaNode stringleaf = (LeafSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+            "string-leaf"));
+
+        assertTrue(stringleaf.getType() instanceof StringTypeDefinition);
+        final StringTypeDefinition type = (StringTypeDefinition) stringleaf.getType();
+        final QName typeQName = type.getQName();
+        assertEquals("string-ext4", typeQName.getLocalName());
+        assertEquals(BAR, typeQName.getModule());
+        assertEquals(Optional.empty(), type.getUnits());
+        assertEquals(Optional.empty(), type.getDefaultValue());
+        List<PatternConstraint> patterns = type.getPatternConstraints();
+        assertEquals(1, patterns.size());
+        PatternConstraint pattern = patterns.iterator().next();
+        assertEquals("^(?:[e-z]*)$", pattern.getJavaPatternString());
+        assertEquals(1, type.getLengthConstraint().get().getAllowedRanges().asRanges().size());
+
+        final StringTypeDefinition baseType1 = type.getBaseType();
+        final QName baseType1QName = baseType1.getQName();
+        assertEquals("string-ext3", baseType1QName.getLocalName());
+        assertEquals(BAR, baseType1QName.getModule());
+        assertEquals(Optional.empty(), baseType1.getUnits());
+        assertEquals(Optional.empty(), baseType1.getDefaultValue());
+        patterns = baseType1.getPatternConstraints();
+        assertEquals(1, patterns.size());
+        pattern = patterns.iterator().next();
+        assertEquals("^(?:[b-u]*)$", pattern.getJavaPatternString());
+        assertEquals(1, baseType1.getLengthConstraint().get().getAllowedRanges().asRanges().size());
+
+        final StringTypeDefinition baseType2 = baseType1.getBaseType();
+        final QName baseType2QName = baseType2.getQName();
+        assertEquals("string-ext2", baseType2QName.getLocalName());
+        assertEquals(BAR, baseType2QName.getModule());
+        assertEquals(Optional.empty(), baseType2.getUnits());
+        assertEquals(Optional.empty(), baseType2.getDefaultValue());
+        assertTrue(baseType2.getPatternConstraints().isEmpty());
+        final RangeSet<Integer> baseType2Lengths = baseType2.getLengthConstraint().get().getAllowedRanges();
+        assertEquals(1, baseType2Lengths.asRanges().size());
+        Range<Integer> length = baseType2Lengths.span();
+        assertEquals(6, length.lowerEndpoint().intValue());
+        assertEquals(10, length.upperEndpoint().intValue());
+
+        final StringTypeDefinition baseType3 = baseType2.getBaseType();
+        assertEquals(QName.create(BAR, "string-ext1"), baseType3.getQName());
+        assertEquals(Optional.empty(), baseType3.getUnits());
+        assertEquals(Optional.empty(), baseType3.getDefaultValue());
+        patterns = baseType3.getPatternConstraints();
+        assertEquals(1, patterns.size());
+        pattern = patterns.iterator().next();
+        assertEquals("^(?:[a-k]*)$", pattern.getJavaPatternString());
+        final RangeSet<Integer> baseType3Lengths = baseType3.getLengthConstraint().get().getAllowedRanges();
+        assertEquals(1, baseType3Lengths.asRanges().size());
+        length = baseType3Lengths.span();
+        assertEquals(5, length.lowerEndpoint().intValue());
+        assertEquals(11, length.upperEndpoint().intValue());
+
+        assertEquals(BaseTypes.stringType(), baseType3.getBaseType());
+    }
+
+    @Test
+    public void testTypedefInvalidPatternsResolving() {
+        final LeafSchemaNode invalidPatternStringLeaf = (LeafSchemaNode) foo
+                .getDataChildByName(QName.create(foo.getQNameModule(), "invalid-pattern-string-leaf"));
+        StringTypeDefinition type = (StringTypeDefinition) invalidPatternStringLeaf.getType();
+        assertEquals(QName.create(BAR, "invalid-string-pattern"), type.getQName());
+        assertEquals(Optional.empty(), type.getUnits());
+        assertEquals(Optional.empty(), type.getDefaultValue());
+        List<PatternConstraint> patterns = type.getPatternConstraints();
+        assertTrue(patterns.isEmpty());
+
+        final LeafSchemaNode invalidDirectStringPatternDefLeaf = (LeafSchemaNode) foo
+                .getDataChildByName(QName.create(foo.getQNameModule(), "invalid-direct-string-pattern-def-leaf"));
+        type = (StringTypeDefinition) invalidDirectStringPatternDefLeaf.getType();
+
+        assertEquals(QName.create(YangConstants.RFC6020_YANG_MODULE, "string"), type.getQName());
+        assertEquals(Optional.empty(), type.getUnits());
+        assertEquals(Optional.empty(), type.getDefaultValue());
+        patterns = type.getPatternConstraints();
+        assertTrue(patterns.isEmpty());
+
+        final LeafSchemaNode multiplePatternStringLeaf = (LeafSchemaNode) foo
+                .getDataChildByName(QName.create(foo.getQNameModule(), "multiple-pattern-string-leaf"));
+        type = (StringTypeDefinition) multiplePatternStringLeaf.getType();
+        assertEquals(QName.create(BAR, "multiple-pattern-string"), type.getQName());
+        assertEquals(Optional.empty(), type.getUnits());
+        assertEquals(Optional.empty(), type.getDefaultValue());
+        patterns = type.getPatternConstraints();
+        assertTrue(!patterns.isEmpty());
+        assertEquals(1, patterns.size());
+        final PatternConstraint pattern = patterns.iterator().next();
+        assertEquals("^(?:[e-z]*)$", pattern.getJavaPatternString());
+        assertEquals(1, type.getLengthConstraint().get().getAllowedRanges().asRanges().size());
+
+        final LeafSchemaNode multiplePatternDirectStringDefLeaf = (LeafSchemaNode) foo
+                .getDataChildByName(QName.create(foo.getQNameModule(), "multiple-pattern-direct-string-def-leaf"));
+        type = (StringTypeDefinition) multiplePatternDirectStringDefLeaf.getType();
+        assertEquals(QName.create(FOO, "string"), type.getQName());
+        assertEquals(Optional.empty(), type.getUnits());
+        assertEquals(Optional.empty(), type.getDefaultValue());
+        patterns = type.getPatternConstraints();
+        assertTrue(!patterns.isEmpty());
+        assertEquals(2, patterns.size());
+
+        boolean isEZPattern = false;
+        boolean isADPattern = false;
+        for (final PatternConstraint patternConstraint : patterns) {
+            if (patternConstraint.getJavaPatternString().equals("^(?:[e-z]*)$")) {
+                isEZPattern = true;
+            } else if (patternConstraint.getJavaPatternString().equals("^(?:[a-d]*)$")) {
+                isADPattern = true;
+            }
+        }
+        assertTrue(isEZPattern);
+        assertTrue(isADPattern);
+    }
+
+    @Test
+    public void testTypedefLengthsResolving() {
+        final LeafSchemaNode lengthLeaf = (LeafSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+            "length-leaf"));
+        final StringTypeDefinition type = (StringTypeDefinition) lengthLeaf.getType();
+
+        assertEquals(QName.create(FOO, "string-ext2"), type.getQName());
+        assertEquals(Optional.empty(), type.getUnits());
+        assertEquals(Optional.empty(), type.getDefaultValue());
+        assertTrue(type.getPatternConstraints().isEmpty());
+        final RangeSet<Integer> typeLengths = type.getLengthConstraint().get().getAllowedRanges();
+        assertEquals(1, typeLengths.asRanges().size());
+        Range<Integer> length = typeLengths.span();
+        assertEquals(7, length.lowerEndpoint().intValue());
+        assertEquals(10, length.upperEndpoint().intValue());
+
+        final StringTypeDefinition baseType1 = type.getBaseType();
+        assertEquals(QName.create(BAR, "string-ext2"), baseType1.getQName());
+        assertEquals(Optional.empty(), baseType1.getUnits());
+        assertEquals(Optional.empty(), baseType1.getDefaultValue());
+        assertTrue(baseType1.getPatternConstraints().isEmpty());
+        final RangeSet<Integer> baseType2Lengths = baseType1.getLengthConstraint().get().getAllowedRanges();
+        assertEquals(1, baseType2Lengths.asRanges().size());
+        length = baseType2Lengths.span();
+        assertEquals(6, length.lowerEndpoint().intValue());
+        assertEquals(10, length.upperEndpoint().intValue());
+
+        final StringTypeDefinition baseType2 = baseType1.getBaseType();
+        assertEquals(QName.create(BAR, "string-ext1"), baseType2.getQName());
+        assertEquals(Optional.empty(), baseType2.getUnits());
+        assertEquals(Optional.empty(), baseType2.getDefaultValue());
+        final List<PatternConstraint> patterns = baseType2.getPatternConstraints();
+        assertEquals(1, patterns.size());
+        final PatternConstraint pattern = patterns.iterator().next();
+        assertEquals("^(?:[a-k]*)$", pattern.getJavaPatternString());
+        final RangeSet<Integer> baseType3Lengths = baseType2.getLengthConstraint().get().getAllowedRanges();
+        assertEquals(1, baseType3Lengths.asRanges().size());
+        length = baseType3Lengths.span();
+        assertEquals(5, length.lowerEndpoint().intValue());
+        assertEquals(11, length.upperEndpoint().intValue());
+
+        assertEquals(BaseTypes.stringType(), baseType2.getBaseType());
+    }
+
+    @Test
+    public void testTypedefDecimal1() {
+        final LeafSchemaNode testleaf = (LeafSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+            "decimal-leaf"));
+
+        assertTrue(testleaf.getType() instanceof DecimalTypeDefinition);
+        final DecimalTypeDefinition type = (DecimalTypeDefinition) testleaf.getType();
+        assertEquals(QName.create(BAR, "my-decimal-type"), type.getQName());
+        assertEquals(Optional.empty(), type.getUnits());
+        assertEquals(Optional.empty(), type.getDefaultValue());
+        assertEquals(6, type.getFractionDigits());
+        assertEquals(1, type.getRangeConstraint().get().getAllowedRanges().asRanges().size());
+
+        final DecimalTypeDefinition typeBase = type.getBaseType();
+        assertEquals(QName.create(BAR, "decimal64"), typeBase.getQName());
+        assertEquals(Optional.empty(), typeBase.getUnits());
+        assertEquals(Optional.empty(), typeBase.getDefaultValue());
+        assertEquals(6, typeBase.getFractionDigits());
+        assertEquals(1, typeBase.getRangeConstraint().get().getAllowedRanges().asRanges().size());
+
+        assertNull(typeBase.getBaseType());
+    }
+
+    @Test
+    public void testTypedefDecimal2() {
+        final LeafSchemaNode testleaf = (LeafSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+            "decimal-leaf2"));
+
+        assertTrue(testleaf.getType() instanceof DecimalTypeDefinition);
+        final DecimalTypeDefinition type = (DecimalTypeDefinition) testleaf.getType();
+        assertEquals(QName.create(BAR, "my-decimal-type"), type.getQName());
+        assertEquals(Optional.empty(), type.getUnits());
+        assertEquals(Optional.empty(), type.getDefaultValue());
+        assertEquals(6, type.getFractionDigits());
+        assertEquals(1, type.getRangeConstraint().get().getAllowedRanges().asRanges().size());
+
+        final DecimalTypeDefinition baseTypeDecimal = type.getBaseType();
+        assertEquals(6, baseTypeDecimal.getFractionDigits());
+    }
+
+    @Test
+    public void testTypedefUnion() {
+        final LeafSchemaNode unionleaf = (LeafSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+            "union-leaf"));
+
+        assertTrue(unionleaf.getType() instanceof UnionTypeDefinition);
+        final UnionTypeDefinition type = (UnionTypeDefinition) unionleaf.getType();
+        assertEquals(QName.create(BAR, "my-union-ext"), type.getQName());
+        assertEquals(Optional.empty(), type.getUnits());
+        assertEquals(Optional.empty(), type.getDefaultValue());
+
+        final UnionTypeDefinition baseType = type.getBaseType();
+        assertEquals(QName.create(BAR, "my-union"), baseType.getQName());
+        assertEquals(Optional.empty(), baseType.getUnits());
+        assertEquals(Optional.empty(), baseType.getDefaultValue());
+
+        final UnionTypeDefinition unionType = baseType.getBaseType();
+        final List<TypeDefinition<?>> unionTypes = unionType.getTypes();
+        assertEquals(2, unionTypes.size());
+
+        final Int16TypeDefinition unionType1 = (Int16TypeDefinition) unionTypes.get(0);
+        assertEquals(QName.create(BAR, "my-union"), baseType.getQName());
+        assertEquals(Optional.empty(), unionType1.getUnits());
+        assertEquals(Optional.empty(), unionType1.getDefaultValue());
+
+        final RangeConstraint<?> ranges = unionType1.getRangeConstraint().get();
+        assertEquals(1, ranges.getAllowedRanges().asRanges().size());
+        final Range<?> range = ranges.getAllowedRanges().span();
+        assertEquals((short)1, range.lowerEndpoint());
+        assertEquals((short)100, range.upperEndpoint());
+        assertEquals(BaseTypes.int16Type(), unionType1.getBaseType());
+
+        assertEquals(BaseTypes.int32Type(), unionTypes.get(1));
+    }
+
+    @Test
+    public void testNestedUnionResolving() {
+        final LeafSchemaNode testleaf = (LeafSchemaNode) foo.getDataChildByName(QName.create(foo.getQNameModule(),
+            "custom-union-leaf"));
+
+        assertTrue(testleaf.getType() instanceof UnionTypeDefinition);
+        final UnionTypeDefinition type = (UnionTypeDefinition) testleaf.getType();
+        assertEquals(QName.create(BAZ, "union1"), type.getQName());
+        assertEquals(Optional.empty(), type.getUnits());
+        assertEquals(Optional.empty(), type.getDefaultValue());
+
+        final UnionTypeDefinition typeBase = type.getBaseType();
+        assertEquals(QName.create(BAZ, "union2"), typeBase.getQName());
+        assertEquals(Optional.empty(), typeBase.getUnits());
+        assertEquals(Optional.empty(), typeBase.getDefaultValue());
+
+        final UnionTypeDefinition union = typeBase.getBaseType();
+        final List<TypeDefinition<?>> unionTypes = union.getTypes();
+        assertEquals(2, unionTypes.size());
+        assertEquals(BaseTypes.int32Type(), unionTypes.get(0));
+        assertTrue(unionTypes.get(1) instanceof UnionTypeDefinition);
+
+        final UnionTypeDefinition unionType1 = (UnionTypeDefinition) unionTypes.get(1);
+        assertEquals(QName.create(BAR, "nested-union2"), unionType1.getQName());
+        assertEquals(Optional.empty(), unionType1.getUnits());
+        assertEquals(Optional.empty(), unionType1.getDefaultValue());
+
+        final UnionTypeDefinition nestedUnion = unionType1.getBaseType();
+        final List<TypeDefinition<?>> nestedUnion2Types = nestedUnion.getTypes();
+        assertEquals(2, nestedUnion2Types.size());
+        assertTrue(nestedUnion2Types.get(1) instanceof StringTypeDefinition);
+        assertTrue(nestedUnion2Types.get(0) instanceof UnionTypeDefinition);
+
+        final UnionTypeDefinition myUnionExt = (UnionTypeDefinition) nestedUnion2Types.get(0);
+        assertEquals(QName.create(BAR, "my-union-ext"), myUnionExt.getQName());
+        assertEquals(Optional.empty(), myUnionExt.getUnits());
+        assertEquals(Optional.empty(), myUnionExt.getDefaultValue());
+
+
+        final UnionTypeDefinition myUnion = myUnionExt.getBaseType();
+        assertEquals(QName.create(BAR, "my-union"), myUnion.getQName());
+        assertEquals(Optional.empty(), myUnion.getUnits());
+        assertEquals(Optional.empty(), myUnion.getDefaultValue());
+
+        final UnionTypeDefinition myUnionBase = myUnion.getBaseType();
+        final List<TypeDefinition<?>> myUnionBaseTypes = myUnionBase.getTypes();
+        assertEquals(2, myUnionBaseTypes.size());
+        assertTrue(myUnionBaseTypes.get(0) instanceof Int16TypeDefinition);
+        assertEquals(BaseTypes.int32Type(), myUnionBaseTypes.get(1));
+
+        final Int16TypeDefinition int16Ext = (Int16TypeDefinition) myUnionBaseTypes.get(0);
+        assertEquals(QName.create(BAR, "int16"), int16Ext.getQName());
+        assertEquals(Optional.empty(), int16Ext.getUnits());
+        assertEquals(Optional.empty(), int16Ext.getDefaultValue());
+        final Set<? extends Range<? extends Number>> ranges = int16Ext.getRangeConstraint().get().getAllowedRanges()
+                .asRanges();
+        assertEquals(1, ranges.size());
+        final Range<? extends Number> range = ranges.iterator().next();
+        assertEquals(1, range.lowerEndpoint().intValue());
+        assertEquals(100, range.upperEndpoint().intValue());
+
+        assertEquals(BaseTypes.int16Type(), int16Ext.getBaseType());
+    }
+
+    @Test
+    public void testChoice() {
+        final ContainerSchemaNode transfer = (ContainerSchemaNode) foo.getDataChildByName(
+            QName.create(foo.getQNameModule(), "transfer"));
+        final ChoiceSchemaNode how = (ChoiceSchemaNode) transfer.getDataChildByName(
+            QName.create(foo.getQNameModule(), "how"));
+        final SortedMap<QName, CaseSchemaNode> cases = how.getCases();
+        assertEquals(5, cases.size());
+        CaseSchemaNode input = null;
+        CaseSchemaNode output = null;
+        for (final CaseSchemaNode caseNode : cases.values()) {
+            if ("input".equals(caseNode.getQName().getLocalName())) {
+                input = caseNode;
+            } else if ("output".equals(caseNode.getQName().getLocalName())) {
+                output = caseNode;
+            }
+        }
+        assertNotNull(input);
+        assertNotNull(input.getPath());
+        assertNotNull(output);
+        assertNotNull(output.getPath());
+    }
+
+    @Test
+    public void testDeviation() {
+        final Set<Deviation> deviations = foo.getDeviations();
+        assertEquals(1, deviations.size());
+        final Deviation dev = deviations.iterator().next();
+        assertEquals(Optional.of("system/user ref"), dev.getReference());
+
+        final SchemaPath expectedPath = SchemaPath.create(true,
+            QName.create(BAR, "interfaces"),
+            QName.create(BAR, "ifEntry"));
+
+        assertEquals(expectedPath, dev.getTargetPath());
+        assertEquals(DeviateKind.ADD, dev.getDeviates().iterator().next().getDeviateType());
+    }
+
+    @Test
+    public void testUnknownNode() {
+        final ContainerSchemaNode network = (ContainerSchemaNode) baz.getDataChildByName(
+            QName.create(baz.getQNameModule(), "network"));
+        final List<UnknownSchemaNode> unknownNodes = network.getUnknownSchemaNodes();
+        assertEquals(1, unknownNodes.size());
+        final UnknownSchemaNode unknownNode = unknownNodes.get(0);
+        assertNotNull(unknownNode.getNodeType());
+        assertEquals("point", unknownNode.getNodeParameter());
+    }
+
+    @Test
+    public void testFeature() {
+        final Set<FeatureDefinition> features = baz.getFeatures();
+        assertEquals(3, features.size());
+    }
+
+    @Test
+    public void testExtension() {
+        final List<ExtensionDefinition> extensions = baz.getExtensionSchemaNodes();
+        assertEquals(1, extensions.size());
+        final ExtensionDefinition extension = extensions.get(0);
+        assertEquals("name", extension.getArgument());
+        assertEquals(
+            Optional.of("Takes as argument a name string. Makes the code generator use the given name in the #define."),
+                extension.getDescription());
+        assertTrue(extension.isYinElement());
+    }
+
+    @Test
+    public void testNotification() {
+        final Set<NotificationDefinition> notifications = baz.getNotifications();
+        assertEquals(1, notifications.size());
+
+        final NotificationDefinition notification = notifications.iterator().next();
+        // test SchemaNode args
+        assertEquals(QName.create(BAZ, "event"), notification.getQName());
+        final SchemaPath expectedPath = SchemaPath.create(true,  QName.create(BAZ, "event"));
+        assertEquals(expectedPath, notification.getPath());
+        assertFalse(notification.getDescription().isPresent());
+        assertFalse(notification.getReference().isPresent());
+        assertEquals(Status.CURRENT, notification.getStatus());
+        assertEquals(0, notification.getUnknownSchemaNodes().size());
+        // test DataNodeContainer args
+        assertEquals(0, notification.getTypeDefinitions().size());
+        assertEquals(3, notification.getChildNodes().size());
+        assertEquals(0, notification.getGroupings().size());
+        assertEquals(0, notification.getUses().size());
+
+        final LeafSchemaNode eventClass = (LeafSchemaNode) notification.getDataChildByName(
+            QName.create(baz.getQNameModule(), "event-class"));
+        assertTrue(eventClass.getType() instanceof StringTypeDefinition);
+        final LeafSchemaNode severity = (LeafSchemaNode) notification.getDataChildByName(
+            QName.create(baz.getQNameModule(), "severity"));
+        assertTrue(severity.getType() instanceof StringTypeDefinition);
+    }
+
+    @Test
+    public void testRpc() {
+        final Set<RpcDefinition> rpcs = baz.getRpcs();
+        assertEquals(1, rpcs.size());
+
+        final RpcDefinition rpc = rpcs.iterator().next();
+        assertEquals(Optional.of("Retrieve all or part of a specified configuration."), rpc.getDescription());
+        assertEquals(Optional.of("RFC 6241, Section 7.1"), rpc.getReference());
+    }
+
+    @Test
+    public void testTypePath() throws ParseException {
+        final Set<TypeDefinition<?>> types = bar.getTypeDefinitions();
+
+        // int32-ext1
+        final Int32TypeDefinition int32ext1 = (Int32TypeDefinition) TestUtils.findTypedef(types, "int32-ext1");
+        final QName int32TypedefQName = QName.create(BAR, "int32-ext1");
+        assertEquals(int32TypedefQName, int32ext1.getQName());
+
+        final SchemaPath typeSchemaPath = int32ext1.getPath();
+        final Iterable<QName> typePath = typeSchemaPath.getPathFromRoot();
+        final Iterator<QName> typePathIt = typePath.iterator();
+        assertEquals(int32TypedefQName, typePathIt.next());
+        assertFalse(typePathIt.hasNext());
+
+        // int32-ext1/int32
+        final Int32TypeDefinition int32 = int32ext1.getBaseType();
+        assertEquals(BaseTypes.int32Type(), int32);
+    }
+
+    @Test
+    public void testTypePath2() throws ParseException {
+        final Set<TypeDefinition<?>> types = bar.getTypeDefinitions();
+
+        // my-decimal-type
+        final DecimalTypeDefinition myDecType = (DecimalTypeDefinition) TestUtils.findTypedef(types, "my-decimal-type");
+        final QName myDecTypeQName = myDecType.getQName();
+
+        assertEquals(BAR, myDecTypeQName.getModule());
+        assertEquals("my-decimal-type", myDecTypeQName.getLocalName());
+
+        final SchemaPath typeSchemaPath = myDecType.getPath();
+        final Iterable<QName> typePath = typeSchemaPath.getPathFromRoot();
+        final Iterator<QName> typePathIt = typePath.iterator();
+        assertEquals(myDecTypeQName, typePathIt.next());
+        assertFalse(typePathIt.hasNext());
+
+        // my-base-int32-type/int32
+        final DecimalTypeDefinition dec64 = myDecType.getBaseType();
+        final QName dec64QName = dec64.getQName();
+
+        assertEquals(BAR, dec64QName.getModule());
+        assertEquals("decimal64", dec64QName.getLocalName());
+
+        final SchemaPath dec64SchemaPath = dec64.getPath();
+        final Iterable<QName> dec64Path = dec64SchemaPath.getPathFromRoot();
+        final Iterator<QName> dec64PathIt = dec64Path.iterator();
+        assertEquals(myDecTypeQName, dec64PathIt.next());
+        assertEquals(dec64QName, dec64PathIt.next());
+        assertFalse(dec64PathIt.hasNext());
+    }
+
+    private static void checkOrder(final Collection<Module> modules) {
+        final Iterator<Module> it = modules.iterator();
+        Module module = it.next();
+        assertEquals("m2", module.getName());
+        module = it.next();
+        assertEquals("m4", module.getName());
+        module = it.next();
+        assertEquals("m6", module.getName());
+        module = it.next();
+        assertEquals("m8", module.getName());
+        module = it.next();
+        assertEquals("m7", module.getName());
+        module = it.next();
+        assertEquals("m5", module.getName());
+        module = it.next();
+        assertEquals("m3", module.getName());
+        module = it.next();
+        assertEquals("m1", module.getName());
+    }
+
+    private static void assertSetEquals(final Set<Module> s1, final Set<Module> s2) {
+        assertEquals(s1, s2);
+        final Iterator<Module> it = s1.iterator();
+        for (final Module m : s2) {
+            assertEquals(m, it.next());
+        }
+    }
+
+    @Test
+    public void testSubmodules() {
+        final DataSchemaNode id = foo.getDataChildByName(QName.create(foo.getQNameModule(), "id"));
+        assertNotNull(id);
+        final DataSchemaNode subExt = foo.getDataChildByName(QName.create(foo.getQNameModule(), "sub-ext"));
+        assertNotNull(subExt);
+        final DataSchemaNode subTransfer = foo.getDataChildByName(QName.create(foo.getQNameModule(), "sub-transfer"));
+        assertNotNull(subTransfer);
+
+        assertEquals(2, foo.getExtensionSchemaNodes().size());
+        assertEquals(2, foo.getAugmentations().size());
+    }
+
+    @Test
+    public void unknownStatementInSubmoduleHeaderTest() throws IOException, URISyntaxException, ReactorException {
+        final StatementStreamSource yang1 = sourceForResource("/yang-grammar-test/revisions-extension.yang");
+        final StatementStreamSource yang2 = sourceForResource("/yang-grammar-test/submodule-header-extension.yang");
+        TestUtils.parseYangSources(yang1, yang2);
+    }
+
+    @Test
+    public void unknownStatementBetweenRevisionsTest() throws ReactorException {
+        final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/yang-grammar-test/revisions-extension.yang"))
+                .addSource(sourceForResource("/yang-grammar-test/submodule-header-extension.yang"))
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    @Test
+    public void unknownStatementsInStatementsTest() {
+
+        final StatementStreamSource yangFile1 = sourceForResource(
+                "/yang-grammar-test/stmtsep-in-statements.yang");
+        final StatementStreamSource yangFile2 = sourceForResource(
+                "/yang-grammar-test/stmtsep-in-statements2.yang");
+        final StatementStreamSource yangFile3 = sourceForResource(
+                "/yang-grammar-test/stmtsep-in-statements-sub.yang");
+
+        final BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(yangFile1, yangFile2, yangFile3);
+        // TODO: change test or create new module in order to respect new statement parser validations
+        try {
+            final SchemaContext result = reactor.buildEffective();
+        } catch (final ReactorException e) {
+            assertEquals(SomeModifiersUnresolvedException.class, e.getClass());
+            assertTrue(e.getCause() instanceof SourceException);
+            assertTrue(e.getCause().getMessage().startsWith("aaa is not a YANG statement or use of extension"));
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserWithContextTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangParserWithContextTest.java
new file mode 100644 (file)
index 0000000..259aa1d
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Range;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DeviateKind;
+import org.opendaylight.yangtools.yang.model.api.Deviation;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.MustDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class YangParserWithContextTest {
+    private static final URI T1_NS = URI.create("urn:simple.demo.test1");
+    private static final URI T2_NS = URI.create("urn:simple.demo.test2");
+    private static final URI T3_NS = URI.create("urn:simple.demo.test3");
+    private static final Revision REV = Revision.of("2013-06-18");
+
+    private static final StatementStreamSource BAR = sourceForResource("/model/bar.yang");
+    private static final StatementStreamSource BAZ = sourceForResource("/model/baz.yang");
+    private static final StatementStreamSource FOO = sourceForResource("/model/foo.yang");
+    private static final StatementStreamSource SUBFOO = sourceForResource("/model/subfoo.yang");
+
+    private static final StatementStreamSource[] IETF = new StatementStreamSource[] {
+            sourceForResource("/ietf/iana-afn-safi@2012-06-04.yang"),
+            sourceForResource("/ietf/iana-if-type@2012-06-05.yang"),
+            sourceForResource("/ietf/iana-timezones@2012-07-09.yang"),
+            sourceForResource("/ietf/ietf-inet-types@2010-09-24.yang"),
+            sourceForResource("/ietf/ietf-yang-types@2010-09-24.yang"),
+            sourceForResource("/ietf/network-topology@2013-07-12.yang"),
+            sourceForResource("/ietf/network-topology@2013-10-21.yang") };
+
+    @Test
+    public void testTypeFromContext() throws ReactorException {
+        final SchemaContext context = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(IETF)
+                .addSource(sourceForResource("/types/custom-types-test@2012-04-04.yang"))
+                .addSource(sourceForResource("/context-test/test1.yang"))
+                .buildEffective();
+
+        final Module module = context.findModule("test1", Revision.of("2013-06-18")).get();
+        final LeafSchemaNode leaf = (LeafSchemaNode) module.getDataChildByName(QName.create(module.getQNameModule(),
+                "id"));
+
+        assertTrue(leaf.getType() instanceof Uint16TypeDefinition);
+        final Uint16TypeDefinition leafType = (Uint16TypeDefinition) leaf.getType();
+        QName qname = leafType.getQName();
+        assertEquals(URI.create("urn:simple.demo.test1"), qname.getNamespace());
+        assertEquals(Revision.ofNullable("2013-06-18"), qname.getRevision());
+        assertEquals("port-number", qname.getLocalName());
+
+        final Uint16TypeDefinition leafBaseType = leafType.getBaseType();
+        qname = leafBaseType.getQName();
+        assertEquals(URI.create("urn:ietf:params:xml:ns:yang:ietf-inet-types"), qname.getNamespace());
+        assertEquals(Revision.ofNullable("2010-09-24"), qname.getRevision());
+        assertEquals("port-number", qname.getLocalName());
+
+        final Uint8TypeDefinition dscpExt = (Uint8TypeDefinition) TestUtils.findTypedef(module.getTypeDefinitions(),
+            "dscp-ext");
+        final Set<? extends Range<?>> ranges = dscpExt.getRangeConstraint().get().getAllowedRanges().asRanges();
+        assertEquals(1, ranges.size());
+        final Range<?> range = ranges.iterator().next();
+        assertEquals(Uint8.valueOf(0), range.lowerEndpoint());
+        assertEquals(Uint8.valueOf(63), range.upperEndpoint());
+    }
+
+    @Test
+    public void testUsesFromContext() throws ReactorException {
+        final SchemaContext context = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(BAZ, FOO, BAR, SUBFOO, sourceForResource("/context-test/test2.yang"))
+                .buildEffective();
+
+        final Module testModule = context.findModule("test2", Revision.of("2013-06-18")).get();
+        final Module contextModule = context.findModules(URI.create("urn:opendaylight.baz")).iterator().next();
+        assertNotNull(contextModule);
+        final Set<GroupingDefinition> groupings = contextModule.getGroupings();
+        assertEquals(1, groupings.size());
+        final GroupingDefinition grouping = groupings.iterator().next();
+
+        // get node containing uses
+        final ContainerSchemaNode peer = (ContainerSchemaNode) testModule.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "peer"));
+        final ContainerSchemaNode destination = (ContainerSchemaNode) peer.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "destination"));
+
+        // check uses
+        final Set<UsesNode> uses = destination.getUses();
+        assertEquals(1, uses.size());
+
+        // check uses process
+        final AnyxmlSchemaNode data_u = (AnyxmlSchemaNode) destination.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "data"));
+        assertNotNull(data_u);
+        assertTrue(data_u.isAddedByUses());
+
+        final AnyxmlSchemaNode data_g = (AnyxmlSchemaNode) grouping.getDataChildByName(QName.create(
+                contextModule.getQNameModule(), "data"));
+        assertNotNull(data_g);
+        assertFalse(data_g.isAddedByUses());
+        assertFalse(data_u.equals(data_g));
+
+        final ChoiceSchemaNode how_u = (ChoiceSchemaNode) destination.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "how"));
+        assertNotNull(how_u);
+        assertTrue(how_u.isAddedByUses());
+
+        final ChoiceSchemaNode how_g = (ChoiceSchemaNode) grouping.getDataChildByName(QName.create(
+                contextModule.getQNameModule(), "how"));
+        assertNotNull(how_g);
+        assertFalse(how_g.isAddedByUses());
+        assertFalse(how_u.equals(how_g));
+
+        final LeafSchemaNode address_u = (LeafSchemaNode) destination.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "address"));
+        assertNotNull(address_u);
+        assertTrue(address_u.isAddedByUses());
+
+        final LeafSchemaNode address_g = (LeafSchemaNode) grouping.getDataChildByName(QName.create(
+                contextModule.getQNameModule(), "address"));
+        assertNotNull(address_g);
+        assertFalse(address_g.isAddedByUses());
+        assertFalse(address_u.equals(address_g));
+
+        final ContainerSchemaNode port_u = (ContainerSchemaNode) destination.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "port"));
+        assertNotNull(port_u);
+        assertTrue(port_u.isAddedByUses());
+
+        final ContainerSchemaNode port_g = (ContainerSchemaNode) grouping.getDataChildByName(QName.create(
+                contextModule.getQNameModule(), "port"));
+        assertNotNull(port_g);
+        assertFalse(port_g.isAddedByUses());
+        assertFalse(port_u.equals(port_g));
+
+        final ListSchemaNode addresses_u = (ListSchemaNode) destination.getDataChildByName(QName.create(
+                testModule.getQNameModule(), "addresses"));
+        assertNotNull(addresses_u);
+        assertTrue(addresses_u.isAddedByUses());
+
+        final ListSchemaNode addresses_g = (ListSchemaNode) grouping.getDataChildByName(QName.create(
+                contextModule.getQNameModule(), "addresses"));
+        assertNotNull(addresses_g);
+        assertFalse(addresses_g.isAddedByUses());
+        assertFalse(addresses_u.equals(addresses_g));
+
+        // grouping defined by 'uses'
+        final Set<GroupingDefinition> groupings_u = destination.getGroupings();
+        assertEquals(1, groupings_u.size());
+        final GroupingDefinition grouping_u = groupings_u.iterator().next();
+        assertTrue(grouping_u.isAddedByUses());
+
+        // grouping defined in 'grouping' node
+        final Set<GroupingDefinition> groupings_g = grouping.getGroupings();
+        assertEquals(1, groupings_g.size());
+        final GroupingDefinition grouping_g = groupings_g.iterator().next();
+        assertFalse(grouping_g.isAddedByUses());
+        assertFalse(grouping_u.equals(grouping_g));
+
+        final List<UnknownSchemaNode> nodes_u = destination.getUnknownSchemaNodes();
+        assertEquals(1, nodes_u.size());
+        final UnknownSchemaNode node_u = nodes_u.get(0);
+        assertTrue(node_u.isAddedByUses());
+
+        final List<UnknownSchemaNode> nodes_g = grouping.getUnknownSchemaNodes();
+        assertEquals(1, nodes_g.size());
+        final UnknownSchemaNode node_g = nodes_g.get(0);
+        assertFalse(node_g.isAddedByUses());
+        assertFalse(node_u.equals(node_g));
+    }
+
+    @Test
+    public void testUsesRefineFromContext() throws ReactorException {
+        final SchemaContext context = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(BAZ, FOO, BAR, SUBFOO, sourceForResource("/context-test/test2.yang"))
+                .buildEffective();
+
+        final Module module = context.findModule("test2", Revision.of("2013-06-18")).get();
+        final ContainerSchemaNode peer = (ContainerSchemaNode) module.getDataChildByName(QName.create(
+                module.getQNameModule(), "peer"));
+        final ContainerSchemaNode destination = (ContainerSchemaNode) peer.getDataChildByName(QName.create(
+                module.getQNameModule(), "destination"));
+        final Set<UsesNode> usesNodes = destination.getUses();
+        assertEquals(1, usesNodes.size());
+        final UsesNode usesNode = usesNodes.iterator().next();
+
+        // test grouping path
+        final List<QName> path = new ArrayList<>();
+        final QName qname = QName.create(URI.create("urn:opendaylight.baz"), Revision.of("2013-02-27"), "target");
+        path.add(qname);
+        final SchemaPath expectedPath = SchemaPath.create(path, true);
+        assertEquals(expectedPath, usesNode.getGroupingPath());
+
+        // test refine
+        final Map<SchemaPath, SchemaNode> refines = usesNode.getRefines();
+        assertEquals(3, refines.size());
+
+        LeafSchemaNode refineLeaf = null;
+        ContainerSchemaNode refineContainer = null;
+        ListSchemaNode refineList = null;
+        for (final Map.Entry<SchemaPath, SchemaNode> entry : refines.entrySet()) {
+            final SchemaNode value = entry.getValue();
+            if (value instanceof LeafSchemaNode) {
+                refineLeaf = (LeafSchemaNode) value;
+            } else if (value instanceof ContainerSchemaNode) {
+                refineContainer = (ContainerSchemaNode) value;
+            } else if (value instanceof ListSchemaNode) {
+                refineList = (ListSchemaNode) value;
+            }
+        }
+
+        // leaf address
+        assertNotNull(refineLeaf);
+        assertEquals("address", refineLeaf.getQName().getLocalName());
+        assertEquals(Optional.of("description of address defined by refine"), refineLeaf.getDescription());
+        assertEquals(Optional.of("address reference added by refine"), refineLeaf.getReference());
+        assertFalse(refineLeaf.isConfiguration());
+        assertTrue(refineLeaf.isMandatory());
+        final Collection<MustDefinition> leafMustConstraints = refineLeaf.getMustConstraints();
+        assertEquals(1, leafMustConstraints.size());
+        final MustDefinition leafMust = leafMustConstraints.iterator().next();
+        assertEquals("ifType != 'ethernet' or (ifType = 'ethernet' and ifMTU = 1500)", leafMust.toString());
+
+        // container port
+        assertNotNull(refineContainer);
+        final Collection<MustDefinition> mustConstraints = refineContainer.getMustConstraints();
+        assertTrue(mustConstraints.isEmpty());
+        assertEquals(Optional.of("description of port defined by refine"), refineContainer.getDescription());
+        assertEquals(Optional.of("port reference added by refine"), refineContainer.getReference());
+        assertFalse(refineContainer.isConfiguration());
+        assertTrue(refineContainer.isPresenceContainer());
+
+        // list addresses
+        assertNotNull(refineList);
+        assertEquals(Optional.of("description of addresses defined by refine"), refineList.getDescription());
+        assertEquals(Optional.of("addresses reference added by refine"), refineList.getReference());
+        assertFalse(refineList.isConfiguration());
+        final ElementCountConstraint constraint = refineList.getElementCountConstraint().get();
+        assertEquals(2, constraint.getMinElements().intValue());
+        assertEquals(12, constraint.getMaxElements().intValue());
+    }
+
+    @Test
+    public void testIdentity() throws ReactorException {
+        final SchemaContext context = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(IETF)
+                .addSource(sourceForResource("/types/custom-types-test@2012-04-04.yang"))
+                .addSource(sourceForResource("/context-test/test3.yang"))
+                .buildEffective();
+
+        final Module module = context.findModule("test3", Revision.of("2013-06-18")).get();
+        final Set<IdentitySchemaNode> identities = module.getIdentities();
+        assertEquals(1, identities.size());
+
+        final IdentitySchemaNode identity = identities.iterator().next();
+        final QName idQName = identity.getQName();
+        assertEquals(URI.create("urn:simple.demo.test3"), idQName.getNamespace());
+        assertEquals(Revision.ofNullable("2013-06-18"), idQName.getRevision());
+        assertEquals("pt", idQName.getLocalName());
+
+        final IdentitySchemaNode baseIdentity = Iterables.getOnlyElement(identity.getBaseIdentities());
+        final QName idBaseQName = baseIdentity.getQName();
+        assertEquals(URI.create("urn:custom.types.demo"), idBaseQName.getNamespace());
+        assertEquals(Revision.ofNullable("2012-04-16"), idBaseQName.getRevision());
+        assertEquals("service-type", idBaseQName.getLocalName());
+    }
+
+    @Test
+    public void testUnknownNodes() throws ReactorException {
+        final SchemaContext context = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(IETF)
+                .addSource(sourceForResource("/types/custom-types-test@2012-04-04.yang"))
+                .addSource(sourceForResource("/context-test/test3.yang"))
+                .buildEffective();
+
+        final Module module = context.findModule("test3", Revision.of("2013-06-18")).get();
+        final ContainerSchemaNode network = (ContainerSchemaNode) module.getDataChildByName(QName.create(
+                module.getQNameModule(), "network"));
+        final List<UnknownSchemaNode> unknownNodes = network.getUnknownSchemaNodes();
+        assertEquals(1, unknownNodes.size());
+
+        final UnknownSchemaNode un = unknownNodes.get(0);
+        final QName unType = un.getNodeType();
+        assertEquals(URI.create("urn:custom.types.demo"), unType.getNamespace());
+        assertEquals(Revision.ofNullable("2012-04-16"), unType.getRevision());
+        assertEquals("mountpoint", unType.getLocalName());
+        assertEquals("point", un.getNodeParameter());
+        assertNotNull(un.getExtensionDefinition());
+    }
+
+    @Test
+    public void testAugment() throws ReactorException {
+        final StatementStreamSource resource = sourceForResource("/context-augment-test/test4.yang");
+        final StatementStreamSource test1 = sourceForResource("/context-augment-test/test1.yang");
+        final StatementStreamSource test2 = sourceForResource("/context-augment-test/test2.yang");
+        final StatementStreamSource test3 = sourceForResource("/context-augment-test/test3.yang");
+
+        final SchemaContext context = TestUtils.parseYangSources(resource, test1, test2, test3);
+        final Module t4 = TestUtils.findModule(context, "test4").get();
+        final ContainerSchemaNode interfaces = (ContainerSchemaNode) t4.getDataChildByName(QName.create(
+                t4.getQNameModule(), "interfaces"));
+        final ListSchemaNode ifEntry = (ListSchemaNode) interfaces.getDataChildByName(QName.create(t4.getQNameModule(),
+                "ifEntry"));
+
+        // test augmentation process
+        final ContainerSchemaNode augmentHolder = (ContainerSchemaNode) ifEntry.getDataChildByName(QName.create(T3_NS,
+                REV, "augment-holder"));
+        assertNotNull(augmentHolder);
+        final DataSchemaNode ds0 = augmentHolder.getDataChildByName(QName.create(T2_NS, REV, "ds0ChannelNumber"));
+        assertNotNull(ds0);
+        final DataSchemaNode interfaceId = augmentHolder.getDataChildByName(QName.create(T2_NS, REV, "interface-id"));
+        assertNotNull(interfaceId);
+        final DataSchemaNode higherLayerIf = augmentHolder.getDataChildByName(QName.create(T2_NS, REV,
+                "higher-layer-if"));
+        assertNotNull(higherLayerIf);
+        final ContainerSchemaNode schemas = (ContainerSchemaNode) augmentHolder.getDataChildByName(QName.create(T2_NS,
+                REV, "schemas"));
+        assertNotNull(schemas);
+        assertNotNull(schemas.getDataChildByName(QName.create(T1_NS, REV, "id")));
+
+        // test augment target after augmentation: check if it is same instance
+        final ListSchemaNode ifEntryAfterAugment = (ListSchemaNode) interfaces.getDataChildByName(QName.create(
+                t4.getQNameModule(), "ifEntry"));
+        assertTrue(ifEntry == ifEntryAfterAugment);
+    }
+
+    @Test
+    public void testDeviation() throws ReactorException {
+        final SchemaContext context = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(sourceForResource("/model/bar.yang"))
+                .addSource(sourceForResource("/context-test/deviation-test.yang"))
+                .buildEffective();
+
+        final Module testModule = context.findModule("deviation-test", Revision.of("2013-02-27")).get();
+        final Set<Deviation> deviations = testModule.getDeviations();
+        assertEquals(1, deviations.size());
+        final Deviation dev = deviations.iterator().next();
+
+        assertEquals(Optional.of("system/user ref"), dev.getReference());
+
+        final URI expectedNS = URI.create("urn:opendaylight.bar");
+        final Revision expectedRev = Revision.of("2013-07-03");
+        final List<QName> path = new ArrayList<>();
+        path.add(QName.create(expectedNS, expectedRev, "interfaces"));
+        path.add(QName.create(expectedNS, expectedRev, "ifEntry"));
+        final SchemaPath expectedPath = SchemaPath.create(path, true);
+
+        assertEquals(expectedPath, dev.getTargetPath());
+        assertEquals(DeviateKind.ADD, dev.getDeviates().iterator().next().getDeviateType());
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangTypes2StmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangTypes2StmtTest.java
new file mode 100644 (file)
index 0000000..37cac7a
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class YangTypes2StmtTest {
+
+    private static final StatementStreamSource TYPEFILE1 = sourceForResource("/semantic-statement-parser/types2.yang");
+    private static final StatementStreamSource TYPEFILE2 = sourceForResource("/semantic-statement-parser/types.yang");
+    private static final StatementStreamSource TYPEFILE3 = sourceForResource(
+            "/semantic-statement-parser/simple-types.yang");
+    private static final StatementStreamSource TYPEFILE4 = sourceForResource(
+            "/semantic-statement-parser/identityreftest.yang");
+
+    private static final QNameModule TYPES2_MODULE = QNameModule.create(URI.create("types2"));
+
+    private static final QName LF_DECIMAL = QName.create(TYPES2_MODULE, "lf-decimal");
+    private static final QName LF_MY_STRING = QName.create(TYPES2_MODULE, "lf-my-string");
+    private static final QName LF_INT8 = QName.create(TYPES2_MODULE, "lf-int8");
+    private static final QName LF_INT16 = QName.create(TYPES2_MODULE, "lf-int16");
+    private static final QName LF_INT32 = QName.create(TYPES2_MODULE, "lf-int32");
+    private static final QName LF_INT64 = QName.create(TYPES2_MODULE, "lf-int64");
+    private static final QName LF_UINT8 = QName.create(TYPES2_MODULE, "lf-uint8");
+    private static final QName LF_UINT16 = QName.create(TYPES2_MODULE, "lf-uint16");
+    private static final QName LF_UINT32 = QName.create(TYPES2_MODULE, "lf-uint32");
+    private static final QName LF_UINT64 = QName.create(TYPES2_MODULE, "lf-uint64");
+    private static final QName LF_BOOL = QName.create(TYPES2_MODULE, "lf-bool");
+
+    @Test
+    public void readAndParseYangFileTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(TYPEFILE1, TYPEFILE2, TYPEFILE3, TYPEFILE4)
+                .buildEffective();
+        assertNotNull(result);
+
+        final LeafSchemaNode lfDecimalNode = (LeafSchemaNode) result.getDataChildByName(LF_DECIMAL);
+        assertNotNull(lfDecimalNode);
+
+        assertTrue(lfDecimalNode.getType() instanceof DecimalTypeDefinition);
+        final DecimalTypeDefinition lfDecimalNodeType = (DecimalTypeDefinition) lfDecimalNode.getType();
+        assertEquals(2, lfDecimalNodeType.getFractionDigits());
+
+        final LeafSchemaNode lfInt8Node = (LeafSchemaNode) result.getDataChildByName(LF_INT8);
+        assertNotNull(lfInt8Node);
+        assertEquals(BaseTypes.int8Type().getClass(), lfInt8Node.getType().getClass());
+
+        final LeafSchemaNode lfInt16Node = (LeafSchemaNode) result.getDataChildByName(LF_INT16);
+        assertNotNull(lfInt16Node);
+        assertEquals(BaseTypes.int16Type().getClass(), lfInt16Node.getType().getClass());
+
+        final LeafSchemaNode lfInt32Node = (LeafSchemaNode) result.getDataChildByName(LF_INT32);
+        assertNotNull(lfInt32Node);
+        assertEquals(BaseTypes.int32Type().getClass(), lfInt32Node.getType().getClass());
+
+        final LeafSchemaNode lfInt64Node = (LeafSchemaNode) result.getDataChildByName(LF_INT64);
+        assertNotNull(lfInt64Node);
+        assertEquals(BaseTypes.int64Type().getClass(), lfInt64Node.getType().getClass());
+
+        final LeafSchemaNode lfUInt8Node = (LeafSchemaNode) result.getDataChildByName(LF_UINT8);
+        assertNotNull(lfUInt8Node);
+        assertEquals(BaseTypes.uint8Type().getClass(), lfUInt8Node.getType().getClass());
+
+        final LeafSchemaNode lfUInt16Node = (LeafSchemaNode) result.getDataChildByName(LF_UINT16);
+        assertNotNull(lfUInt16Node);
+        assertEquals(BaseTypes.uint16Type().getClass(), lfUInt16Node.getType().getClass());
+
+        final LeafSchemaNode lfUInt32Node = (LeafSchemaNode) result.getDataChildByName(LF_UINT32);
+        assertNotNull(lfUInt32Node);
+        assertEquals(BaseTypes.uint32Type().getClass(), lfUInt32Node.getType().getClass());
+
+        final LeafSchemaNode lfUInt64Node = (LeafSchemaNode) result.getDataChildByName(LF_UINT64);
+        assertNotNull(lfUInt64Node);
+        assertEquals(BaseTypes.uint64Type().getClass(), lfUInt64Node.getType().getClass());
+
+        final LeafSchemaNode lfBoolNode = (LeafSchemaNode) result.getDataChildByName(LF_BOOL);
+        assertNotNull(lfBoolNode);
+        assertEquals(BaseTypes.booleanType().getClass(), lfBoolNode.getType().getClass());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangTypesStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/YangTypesStmtTest.java
new file mode 100644 (file)
index 0000000..4579104
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016 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.stmt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+
+public class YangTypesStmtTest {
+
+    private static final StatementStreamSource TYPEFILE1 = sourceForResource("/semantic-statement-parser/types.yang");
+    private static final StatementStreamSource TYPEFILE2 = sourceForResource(
+        "/semantic-statement-parser/simple-types.yang");
+    private static final StatementStreamSource TYPEFILE3 = sourceForResource(
+        "/semantic-statement-parser/identityreftest.yang");
+
+    private static final StatementStreamSource FILE1 = sourceForResource("/semantic-statement-parser/model/bar.yang");
+    private static final StatementStreamSource FILE2 = sourceForResource("/semantic-statement-parser/model/baz.yang");
+    private static final StatementStreamSource FILE3 = sourceForResource(
+        "/semantic-statement-parser/model/subfoo.yang");
+    private static final StatementStreamSource FILE4 = sourceForResource("/semantic-statement-parser/model/foo.yang");
+
+    @Test
+    public void readAndParseYangFileTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(TYPEFILE1, TYPEFILE2, TYPEFILE3, FILE1, FILE2, FILE3, FILE4)
+                .buildEffective();
+        assertNotNull(result);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionBorderCaseTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionBorderCaseTest.java
new file mode 100644 (file)
index 0000000..b9ff232
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2016 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.stmt.openconfigver;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class OpenconfigVersionBorderCaseTest {
+
+    @Test
+    public void borderCaseValidMajorTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources(
+            "/openconfig-version/border-case/border-case-valid-major", StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(URI.create("foo")).iterator().next();
+        Module semVer = context.findModules(URI.create("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        Module bar = StmtTestUtils.findImportedModule(context, foo, "bar");
+        assertEquals(SemVer.valueOf("5.5.5"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void borderCaseValidMinorTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources(
+            "/openconfig-version/border-case/border-case-valid-minor", StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(URI.create("foo")).iterator().next();
+        Module semVer = context.findModules(URI.create("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        Module bar = StmtTestUtils.findImportedModule(context, foo, "bar");
+        assertEquals(SemVer.valueOf("5.6.5"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void borderCaseValidPatchTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources(
+            "/openconfig-version/border-case/border-case-valid-patch", StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(URI.create("foo")).iterator().next();
+        Module semVer = context.findModules(URI.create("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        Module bar = StmtTestUtils.findImportedModule(context, foo, "bar");
+        assertEquals(SemVer.valueOf("5.5.6"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void borderCaseInvalidMajorTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/openconfig-version/border-case/border-case-invalid-major",
+                    StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid openconfig version");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage()
+                    .startsWith("Unable to find module compatible with requested import [bar(5.5.5)]."));
+        }
+    }
+
+    @Test
+    public void borderCaseInvalidMinorTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/openconfig-version/border-case/border-case-invalid-minor",
+                    StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid openconfig version");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage()
+                    .startsWith("Unable to find module compatible with requested import [bar(5.5.5)]."));
+        }
+    }
+
+    @Test
+    public void borderCaseInvalidPatchTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/openconfig-version/border-case/border-case-invalid-patch",
+                    StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid openconfig version");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage()
+                    .startsWith("Unable to find module compatible with requested import [bar(5.5.5)]."));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionComplexTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionComplexTest.java
new file mode 100644 (file)
index 0000000..221e1f7
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2016 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.stmt.openconfigver;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.model.util.PathExpressionImpl;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class OpenconfigVersionComplexTest {
+
+    @Test
+    public void complexTest1() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/complex/complex-1",
+                StatementParserMode.SEMVER_MODE);
+        verifySchemaContextTest1(context);
+    }
+
+    @Test
+    public void complexTest1Yang1_1() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/rfc7950/openconfig-version/complex-1",
+                StatementParserMode.SEMVER_MODE);
+        verifySchemaContextTest1(context);
+    }
+
+    private static void verifySchemaContextTest1(final SchemaContext context) {
+        assertNotNull(context);
+
+        final Module foo = context.findModules(URI.create("foo")).iterator().next();
+        final Module semVer = context.findModules(URI.create("http://openconfig.net/yang/openconfig-ext"))
+                .iterator().next();
+
+        // check module versions
+        assertEquals(SemVer.valueOf("1.3.95"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("1.50.2"), foo.getSemanticVersion().get());
+
+        final Module bar = StmtTestUtils.findImportedModule(context, foo, "bar");
+        assertEquals(SemVer.valueOf("1.2.6"), bar.getSemanticVersion().get());
+
+        final Module foobar = StmtTestUtils.findImportedModule(context, bar, "foobar");
+        assertEquals(SemVer.valueOf("2.26.465"), foobar.getSemanticVersion().get());
+
+        // check imported components
+        assertNotNull("This component should be present", SchemaContextUtil.findDataSchemaNode(context, foo,
+                new PathExpressionImpl("/bar:root/bar:test-container/bar:number", true)));
+
+        assertNotNull("This component should be present", SchemaContextUtil.findDataSchemaNode(context, foo,
+                new PathExpressionImpl("/bar:should-present", true)));
+
+        // check not imported components
+        assertNull("This component should not be present", SchemaContextUtil.findDataSchemaNode(context, foo,
+                new PathExpressionImpl("/bar:root/bar:test-container/bar:oldnumber", true)));
+
+        assertNull("This component should not be present", SchemaContextUtil.findDataSchemaNode(context, foo,
+                new PathExpressionImpl("/bar:should-not-be-present", true)));
+    }
+
+    @Test
+    public void complexTest2() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/complex/complex-2",
+                StatementParserMode.SEMVER_MODE);
+        verifySchemaContextTest2(context);
+    }
+
+    @Test
+    public void complexTest2Yang1_1() throws Exception {
+        final SchemaContext context = StmtTestUtils.parseYangSources("/rfc7950/openconfig-version/complex-2",
+                StatementParserMode.SEMVER_MODE);
+        verifySchemaContextTest2(context);
+    }
+
+    private static void verifySchemaContextTest2(final SchemaContext context) {
+        assertNotNull(context);
+
+        final Module foo = context.findModules(URI.create("foo")).iterator().next();
+        final Module semVer = context.findModules(URI.create("http://openconfig.net/yang/openconfig-ext"))
+                .iterator().next();
+
+        // check module versions
+        assertEquals(SemVer.valueOf("2.5.50"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("2.32.2"), foo.getSemanticVersion().get());
+
+        final Module bar = StmtTestUtils.findImportedModule(context, foo, "bar");
+        assertEquals(SemVer.valueOf("4.9.8"), bar.getSemanticVersion().get());
+
+        final Module foobar = StmtTestUtils.findImportedModule(context, bar, "foobar");
+        assertEquals(SemVer.valueOf("7.13.99"), foobar.getSemanticVersion().get());
+
+        // check used augmentations
+        assertNotNull("This component should be present", SchemaContextUtil.findDataSchemaNode(context, bar,
+                new PathExpressionImpl("/foobar:root/foobar:test-container/bar:should-present-leaf-1", true)));
+
+        assertNotNull("This component should be present", SchemaContextUtil.findDataSchemaNode(context, bar,
+                new PathExpressionImpl("/foobar:root/foobar:test-container/bar:should-present-leaf-2", true)));
+
+        // check not used augmentations
+        assertNull("This component should not be present",
+                SchemaContextUtil.findDataSchemaNode(context, bar, new PathExpressionImpl(
+                        "/foobar:root/foobar:test-container/bar:should-not-be-present-leaf-1", true)));
+
+        assertNull("This component should not be present",
+                SchemaContextUtil.findDataSchemaNode(context, bar, new PathExpressionImpl(
+                        "/foobar:root/foobar:test-container/bar:should-not-be-present-leaf-2", true)));
+
+        // check if correct foobar module was included
+        assertNotNull("This component should be present", SchemaContextUtil.findDataSchemaNode(context, bar,
+                new PathExpressionImpl("/foobar:root/foobar:included-correct-mark", true)));
+
+        assertNull("This component should not be present", SchemaContextUtil.findDataSchemaNode(context, bar,
+                new PathExpressionImpl("/foobar:root/foobar:included-not-correct-mark", true)));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionDefaultsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionDefaultsTest.java
new file mode 100644 (file)
index 0000000..988173f
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 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.stmt.openconfigver;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.net.URI;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class OpenconfigVersionDefaultsTest {
+
+    @Test
+    public void defaultsTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/defaults/defaults",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module bar = context.findModules(new URI("bar")).iterator().next();
+
+        assertEquals(Optional.empty(), foo.getSemanticVersion());
+        assertEquals(Optional.empty(), bar.getSemanticVersion());
+    }
+
+    @Test
+    public void defaultMajorValidTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/defaults/default-major-valid",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module bar = context.findModules(new URI("bar")).iterator().next();
+
+        assertEquals(Optional.empty(), foo.getSemanticVersion());
+        assertEquals(SemVer.valueOf("0.99.99"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void defaultMajorInvalidTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/openconfig-version/defaults/default-major-invalid",
+                StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid openconfig version");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage()
+                    .startsWith("Unable to find module compatible with requested import [bar(0.0.1)]."));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionIgnoringRevisionTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionIgnoringRevisionTest.java
new file mode 100644 (file)
index 0000000..65e7506
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 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.stmt.openconfigver;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class OpenconfigVersionIgnoringRevisionTest {
+
+    @Test
+    public void ignoringRevisionTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/ignoring-revision",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module bar = context.findModules(new URI("bar")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.2"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void ignoringRevision2Test() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/ignoring-revision-2",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+        Module bar = StmtTestUtils.findImportedModule(context, foo, "bar");
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.2"), bar.getSemanticVersion().get());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionImportTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionImportTest.java
new file mode 100644 (file)
index 0000000..da4420e
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016 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.stmt.openconfigver;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class OpenconfigVersionImportTest {
+
+    @Test
+    public void importValidTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/import/import-valid",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("1.0.0"), semVer.getSemanticVersion().get());
+    }
+
+    @Test
+    public void importInvalidDeprecatedTest1() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/openconfig-version/import/import-invalid-deprecated-1",
+                    StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid import of openconfig-version module");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage().startsWith(
+                    "Unable to find module compatible with requested import [openconfig-extensions(1.0.0)]."));
+        }
+    }
+
+    @Test
+    public void importInvalidDeprecatedTest2() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/openconfig-version/import/import-invalid-deprecated-2",
+                    StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid import of openconfig-version module");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage().startsWith(
+                    "Unable to find module compatible with requested import [openconfig-extensions(0.9.9)]."));
+        }
+    }
+
+    @Test
+    public void importInvalidNotsufficientTest1() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/openconfig-version/import/import-invalid-notsufficient-1",
+                    StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid import of openconfig-version module");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage().startsWith(
+                    "Unable to find module compatible with requested import [openconfig-extensions(2.0.0)]."));
+        }
+    }
+
+    @Test
+    public void importInvalidNotsufficientTest2() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/openconfig-version/import/import-invalid-notsufficient-2",
+                    StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid import of openconfig-version module");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage().startsWith(
+                    "Unable to find module compatible with requested import [openconfig-extensions(2.0.5)]."));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionMultipleImportTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionMultipleImportTest.java
new file mode 100644 (file)
index 0000000..a26f002
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2016 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.stmt.openconfigver;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.net.URI;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class OpenconfigVersionMultipleImportTest {
+
+    @Test
+    public void multipleInvalidDeprecatedTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/openconfig-version/multiple/multiple-invalid-deprecated",
+                    StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid openconfig version");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage()
+                    .startsWith("Unable to find module compatible with requested import [bar(1.0.0)]."));
+        }
+    }
+
+    @Test
+    public void multipleInvalidNosufficientTest() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/openconfig-version/multiple/multiple-invalid-nosufficient",
+                    StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid openconfig version");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage()
+                    .startsWith("Unable to find module compatible with requested import [bar(2.5.5)]."));
+        }
+    }
+
+    @Test
+    public void multipleValidDefaultsTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/multiple/multiple-valid-defaults",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        Module bar = findImportedModule(context, foo, "bar");
+        assertEquals(SemVer.valueOf("0.9.5"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void multipleValidSpecifiedTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/multiple/multiple-valid-specified",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        Module bar = findImportedModule(context, foo, "bar");
+        assertEquals(SemVer.valueOf("5.5.6"), bar.getSemanticVersion().get());
+    }
+
+    private static Module findImportedModule(final SchemaContext context, final Module rootModule,
+            final String importedModuleName) {
+        ModuleImport requestedModuleImport = null;
+        Set<ModuleImport> rootImports = rootModule.getImports();
+        for (ModuleImport moduleImport : rootImports) {
+            if (moduleImport.getModuleName().equals(importedModuleName)) {
+                requestedModuleImport = moduleImport;
+                break;
+            }
+        }
+
+        return context.findModule(requestedModuleImport.getModuleName(), requestedModuleImport.getRevision()).get();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionPositionTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionPositionTest.java
new file mode 100644 (file)
index 0000000..55dc999
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016 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.stmt.openconfigver;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class OpenconfigVersionPositionTest {
+
+    @Test
+    public void positionHeadTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/position/position-head",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module bar = context.findModules(new URI("bar")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.2"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void positionMiddleTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/position/position-middle",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module bar = context.findModules(new URI("bar")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.2"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void positiontailTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/position/position-tail",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module bar = context.findModules(new URI("bar")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.2"), bar.getSemanticVersion().get());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/OpenconfigVersionTest.java
new file mode 100644 (file)
index 0000000..4b1a020
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2016 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.stmt.openconfigver;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.net.URI;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+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.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class OpenconfigVersionTest {
+    @Test
+    public void basicTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/basic",
+            StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module bar = context.findModules(new URI("bar")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.2"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void basicTest2() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/basic-2",
+            StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module bar = context.findModules(new URI("bar")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.2"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void basicTest3() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/basic-3",
+            StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+    }
+
+    @Test
+    public void basicImportTest1() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/basic-import-1",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        Module bar = StmtTestUtils.findImportedModule(context, foo, "bar");
+        assertEquals(SemVer.valueOf("0.1.2"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void multipleModulesTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/multiple-modules",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        Module bar = StmtTestUtils.findImportedModule(context, foo, "bar");
+        assertEquals(SemVer.valueOf("0.10.4"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void basicImportErrTest1() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/openconfig-version/basic-import-invalid-1",
+                StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid openconfig version");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage()
+                    .startsWith("Unable to find module compatible with requested import [bar(0.1.2)]."));
+        }
+    }
+
+    @Test
+    public void basicImportErrTest2() throws Exception {
+        try {
+            StmtTestUtils.parseYangSources("/openconfig-version/basic-import-invalid-2",
+                StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid openconfig version");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage()
+                    .startsWith("Unable to find module compatible with requested import [bar(0.1.2)]."));
+        }
+    }
+
+    @Test
+    public void nodeTest() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/openconfig-version/node-test",
+            StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(new URI("foo")).iterator().next();
+        Module semVer = context.findModules(new URI("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("2016.1.1"), foo.getSemanticVersion().get());
+        Module bar = StmtTestUtils.findImportedModule(context, foo, "bar");
+        assertEquals(SemVer.valueOf("2016.4.6"), bar.getSemanticVersion().get());
+
+        QName root = QName.create("foo", "2016-01-01", "foo-root");
+        QName container20160404 = QName.create("foo", "2016-01-01", "con20160404");
+        SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, root, container20160404));
+        assertTrue(findDataSchemaNode instanceof ContainerSchemaNode);
+
+        QName container20160405 = QName.create("foo", "2016-01-01", "con20160405");
+        findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, root, container20160405));
+        assertTrue(findDataSchemaNode instanceof ContainerSchemaNode);
+
+        QName container20160406 = QName.create("foo", "2016-01-01", "con20160406");
+        findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, root, container20160406));
+        assertTrue(findDataSchemaNode instanceof ContainerSchemaNode);
+
+        QName container20170406 = QName.create("foo", "2016-01-01", "con20170406");
+        findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context,
+                SchemaPath.create(true, root, container20170406));
+        assertNull(findDataSchemaNode);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/yin/YinOpenconfigVersionTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/openconfigver/yin/YinOpenconfigVersionTest.java
new file mode 100644 (file)
index 0000000..dc0d1f5
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 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.stmt.openconfigver.yin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+import org.xml.sax.SAXException;
+
+public class YinOpenconfigVersionTest {
+
+    @Test
+    public void basicTest() throws URISyntaxException, SAXException, IOException, ReactorException {
+        SchemaContext context = StmtTestUtils.parseYinSources("/openconfig-version/yin-input/basic",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(URI.create("foo")).iterator().next();
+        Module bar = context.findModules(URI.create("bar")).iterator().next();
+        Module semVer = context.findModules(URI.create("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.2"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void basicImportTest1() throws URISyntaxException, SAXException, IOException, ReactorException {
+        SchemaContext context = StmtTestUtils.parseYinSources("/openconfig-version/yin-input/basic-import",
+                StatementParserMode.SEMVER_MODE);
+        assertNotNull(context);
+
+        Module foo = context.findModules(URI.create("foo")).iterator().next();
+        Module semVer = context.findModules(URI.create("http://openconfig.net/yang/openconfig-ext")).iterator().next();
+
+        assertEquals(SemVer.valueOf("0.0.1"), semVer.getSemanticVersion().get());
+        assertEquals(SemVer.valueOf("0.1.1"), foo.getSemanticVersion().get());
+        Module bar = StmtTestUtils.findImportedModule(context, foo, "bar");
+        assertEquals(SemVer.valueOf("0.1.2"), bar.getSemanticVersion().get());
+    }
+
+    @Test
+    public void basicImportErrTest1() throws URISyntaxException, SAXException, IOException {
+        try {
+            StmtTestUtils.parseYinSources("/openconfig-version/yin-input/basic-import-invalid",
+                StatementParserMode.SEMVER_MODE);
+            fail("Test should fail due to invalid openconfig version");
+        } catch (ReactorException e) {
+            assertTrue(e.getCause().getCause().getMessage()
+                    .startsWith("Unable to find module compatible with requested import [bar(0.1.2)]."));
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/test/Bug5200Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/test/Bug5200Test.java
new file mode 100644 (file)
index 0000000..28c3cf9
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016 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.stmt.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
+import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
+
+public class Bug5200Test {
+    private static final String NS = "foo";
+    private static final String REV = "2016-05-05";
+
+    @Test
+    public void test() throws Exception {
+        SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5200");
+        assertNotNull(context);
+
+        QName root = QName.create(NS, REV, "root");
+        QName myLeaf = QName.create(NS, REV, "my-leaf");
+        QName myLeaf2 = QName.create(NS, REV, "my-leaf-2");
+
+        SchemaNode myLeafNode = SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true, root, myLeaf));
+        SchemaNode myLeaf2Node = SchemaContextUtil.findDataSchemaNode(context, SchemaPath.create(true, root, myLeaf2));
+
+        assertTrue(myLeafNode instanceof LeafSchemaNode);
+        assertTrue(myLeaf2Node instanceof LeafSchemaNode);
+
+        TypeDefinition<?> myLeafType = ((LeafSchemaNode) myLeafNode).getType();
+        TypeDefinition<?> myLeaf2Type = ((LeafSchemaNode) myLeaf2Node).getType();
+
+        assertTrue(myLeafType instanceof StringTypeDefinition);
+        assertTrue(myLeaf2Type instanceof Int32TypeDefinition);
+
+        final LengthConstraint lengthConstraint =
+                ((StringTypeDefinition) myLeafType).getLengthConstraint().get();
+        final List<PatternConstraint> patternConstraints = ((StringTypeDefinition) myLeafType).getPatternConstraints();
+
+        assertEquals(1, lengthConstraint.getAllowedRanges().asRanges().size());
+        assertEquals(1, patternConstraints.size());
+
+        assertEquals(Optional.of("lenght constraint error-app-tag"), lengthConstraint.getErrorAppTag());
+        assertEquals(Optional.of("lenght constraint error-app-message"), lengthConstraint.getErrorMessage());
+
+        PatternConstraint patternConstraint = patternConstraints.iterator().next();
+        assertEquals(Optional.of("pattern constraint error-app-tag"), patternConstraint.getErrorAppTag());
+        assertEquals(Optional.of("pattern constraint error-app-message"), patternConstraint.getErrorMessage());
+
+        RangeConstraint<?> rangeConstraint = ((Int32TypeDefinition) myLeaf2Type).getRangeConstraint().get();
+        assertEquals(1, rangeConstraint.getAllowedRanges().asRanges().size());
+
+        assertEquals(Optional.of("range constraint error-app-tag"), rangeConstraint.getErrorAppTag());
+        assertEquals(Optional.of("range constraint error-app-message"), rangeConstraint.getErrorMessage());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/AbstractYinModulesTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/AbstractYinModulesTest.java
new file mode 100644 (file)
index 0000000..40952e4
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.stmt.yin;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+import org.xml.sax.SAXException;
+
+public abstract class AbstractYinModulesTest {
+
+    static SchemaContext context;
+
+    @BeforeClass
+    public static void beforeClass() throws ReactorException, SAXException, IOException, URISyntaxException {
+        context = TestUtils.loadYinModules(
+            AbstractYinModulesTest.class.getResource("/semantic-statement-parser/yin/modules").toURI());
+        assertEquals(9, context.getModules().size());
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        context = null;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileAugmentStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileAugmentStmtTest.java
new file mode 100644 (file)
index 0000000..c842887
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.hamcrest.Matchers.isA;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Iterator;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileAugmentStmtTest extends AbstractYinModulesTest {
+
+    @Test
+    public void testAugment() {
+        final Module testModule = TestUtils.findModule(context, "main-impl").get();
+        assertNotNull(testModule);
+
+        final Set<AugmentationSchemaNode> augmentations = testModule.getAugmentations();
+        assertEquals(1, augmentations.size());
+
+        final Iterator<AugmentationSchemaNode> augmentIterator = augmentations.iterator();
+        final AugmentationSchemaNode augment = augmentIterator.next();
+        assertNotNull(augment);
+        assertTrue(augment.getTargetPath().toString().contains(
+                "(urn:opendaylight:params:xml:ns:yang:controller:config?revision=2013-04-05)modules, "
+                        + "(urn:opendaylight:params:xml:ns:yang:controller:config?revision=2013-04-05)module, "
+                        + "(urn:opendaylight:params:xml:ns:yang:controller:config?revision=2013-04-05)configuration"));
+
+        assertEquals(1, augment.getChildNodes().size());
+        final DataSchemaNode caseNode = augment.findDataChildByName(
+            QName.create(testModule.getQNameModule(), "main-impl")).get();
+        assertThat(caseNode, isA(CaseSchemaNode.class));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileChoiceStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileChoiceStmtTest.java
new file mode 100644 (file)
index 0000000..16a5d16
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Iterator;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileChoiceStmtTest extends AbstractYinModulesTest {
+
+    @Test
+    public void testChoiceAndCases() {
+        final Module testModule = TestUtils.findModule(context, "config").get();
+        assertNotNull(testModule);
+
+        final ListSchemaNode list = (ListSchemaNode) testModule.findDataChildByName(
+            QName.create(testModule.getQNameModule(), "modules"),
+            QName.create(testModule.getQNameModule(), "module")).get();
+
+        ChoiceSchemaNode choice = (ChoiceSchemaNode) list.findDataChildByName(QName.create(testModule.getQNameModule(),
+                "configuration")).get();
+
+        assertEquals("configuration", choice.getQName().getLocalName());
+        assertTrue(choice.isMandatory());
+        assertTrue(choice.isConfiguration());
+        assertEquals(1, choice.getCases().size());
+
+        // this choice is augmented (see main-impl.yang.xml)
+        final Iterator<CaseSchemaNode> casesIterator = choice.getCases().values().iterator();
+        final CaseSchemaNode caseNode = casesIterator.next();
+        assertEquals("main-impl", caseNode.getQName().getLocalName());
+        assertEquals(13, caseNode.getChildNodes().size());
+
+        assertTrue(caseNode.getWhenCondition().isPresent());
+
+        choice = (ChoiceSchemaNode) list.findDataChildByName(QName.create(testModule.getQNameModule(), "state")).get();
+
+        assertEquals("state", choice.getQName().getLocalName());
+        assertFalse(choice.isMandatory());
+        assertFalse(choice.isConfiguration());
+        assertTrue(choice.getCases().isEmpty());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileExtensionStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileExtensionStmtTest.java
new file mode 100644 (file)
index 0000000..7a57d5c
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileExtensionStmtTest extends AbstractYinModulesTest {
+
+    @Test
+    public void testExtensions() {
+        Module testModule = TestUtils.findModule(context, "config").get();
+        assertNotNull(testModule);
+
+        List<ExtensionDefinition> extensions = testModule.getExtensionSchemaNodes();
+        assertEquals(5, extensions.size());
+
+        Iterator<ExtensionDefinition> extIterator = extensions.iterator();
+        ExtensionDefinition extension = extIterator.next();
+        assertEquals("name", extension.getArgument());
+        assertEquals("java-class", extension.getQName().getLocalName());
+        assertEquals(Optional.of("YANG language extension carrying the fully-qualified name of\n"
+                + "a Java class. Code generation tools use the provided reference\n"
+                + "to tie a specific construct to its Java representation."), extension.getDescription());
+
+        extension = extIterator.next();
+        assertEquals("name", extension.getArgument());
+        assertEquals("required-identity", extension.getQName().getLocalName());
+        assertEquals(Optional.of("YANG language extension which indicates that a particular\n"
+                + "leafref, which points to a identityref, should additionally\n"
+                + "require the target node is actually set to a descendant to\n"
+                + "of a particular identity.\n"
+                + "\n"
+                + "This is a workaround to two YANG deficiencies:\n"
+                + "1) not being able to leafref instances of identityref\n"
+                + "2) not being able to refine an identityref\n"
+                + "\n"
+                + "This extension takes one argument, name, which MUST be the name\n"
+                + "of an identity. Furthermore, that identity MUST be based,\n"
+                + "directly or indirectly, on the identity, which is referenced by\n"
+                + "the leaf reference, which is annotated with this extension."), extension.getDescription());
+
+        extension = extIterator.next();
+        assertNull(extension.getArgument());
+        assertEquals("inner-state-bean", extension.getQName().getLocalName());
+        assertEquals(Optional.of("YANG language extension which indicates that a particular\n"
+                + "list located under module's state should be treated as a list\n"
+                + "of child state beans instead of just an ordinary list attribute"), extension.getDescription());
+
+        extension = extIterator.next();
+        assertEquals("name", extension.getArgument());
+        assertEquals("provided-service", extension.getQName().getLocalName());
+        assertEquals(Optional.of("YANG language extension which indicates that a particular\n"
+                + "module provides certain service. This extension can be placed\n"
+                + "on identities that are based on module-type. Zero or more services\n"
+                + "can be provided.\n"
+                + "This extension takes one argument - name - which MUST be the name\n"
+                + "of an identity. Furthermore, this identity MUST be based on\n"
+                + "service-type."), extension.getDescription());
+
+        extension = extIterator.next();
+        assertEquals("java-prefix", extension.getArgument());
+        assertEquals("java-name-prefix", extension.getQName().getLocalName());
+        assertEquals(Optional.of("YANG language extension carrying java simple class name prefix\n"
+                + "that will be taken into account when generating java code from\n"
+                + "identities that are based on module-type."), extension.getDescription());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileFeatureStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileFeatureStmtTest.java
new file mode 100644 (file)
index 0000000..b3c8ce9
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Iterator;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+import org.xml.sax.SAXException;
+
+public class YinFileFeatureStmtTest {
+
+    private SchemaContext context;
+
+    @Before
+    public void init() throws ReactorException, SAXException, IOException, URISyntaxException {
+        context = TestUtils.loadYinModules(getClass().getResource("/semantic-statement-parser/yin/feature-test/")
+            .toURI());
+        assertEquals(1, context.getModules().size());
+    }
+
+    @Test
+    public void testFeature() {
+        Module testModule = TestUtils.findModule(context, "yang-with-features").get();
+        assertNotNull(testModule);
+
+        Set<FeatureDefinition> features = testModule.getFeatures();
+        assertEquals(2, features.size());
+
+        Iterator<FeatureDefinition> featuresIterator = features.iterator();
+        FeatureDefinition feature = featuresIterator.next();
+
+        assertThat(feature.getQName().getLocalName(), anyOf(is("arbitrary-names"), is("pre-provisioning")));
+
+        feature = featuresIterator.next();
+
+        assertThat(feature.getQName().getLocalName(), anyOf(is("arbitrary-names"), is("pre-provisioning")));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileGroupingStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileGroupingStmtTest.java
new file mode 100644 (file)
index 0000000..0b23496
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileGroupingStmtTest extends AbstractYinModulesTest {
+
+    @Test
+    public void testGrouping() throws URISyntaxException {
+        final Module testModule = TestUtils.findModule(context, "config").get();
+        assertNotNull(testModule);
+
+        final Set<GroupingDefinition> groupings = testModule.getGroupings();
+        assertEquals(1, groupings.size());
+
+        final Iterator<GroupingDefinition> groupingsIterator = groupings.iterator();
+        final GroupingDefinition grouping = groupingsIterator.next();
+        assertEquals("service-ref", grouping.getQName().getLocalName());
+        assertEquals(Optional.of("Type of references to a particular service instance. This type\n"
+                + "can be used when defining module configuration to refer to a\n"
+                + "particular service instance. Containers using this grouping\n"
+                + "should not define anything else. The run-time implementation\n"
+                + "is expected to inject a reference to the service as the value\n"
+                + "of the container."), grouping.getDescription());
+
+        final Collection<DataSchemaNode> children = grouping.getChildNodes();
+        assertEquals(2, children.size());
+
+        final LeafSchemaNode leaf1 = (LeafSchemaNode) grouping.findDataChildByName(QName.create(
+                testModule.getQNameModule(), "type")).get();
+        assertTrue(leaf1.isMandatory());
+
+        final LeafSchemaNode leaf2 = (LeafSchemaNode) grouping.findDataChildByName(QName.create(
+                testModule.getQNameModule(), "name")).get();
+        assertTrue(leaf2.isMandatory());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileHeaderStmtsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileHeaderStmtsTest.java
new file mode 100644 (file)
index 0000000..da1de1c
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.junit.Assert.assertEquals;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileHeaderStmtsTest extends AbstractYinModulesTest {
+
+    @Test
+    public void testYinFileHeader() throws URISyntaxException {
+        Module testModule = TestUtils.findModule(context, "config").get();
+
+        assertEquals(YangVersion.VERSION_1, testModule.getYangVersion());
+        assertEquals(new URI("urn:opendaylight:params:xml:ns:yang:controller:config"), testModule.getNamespace());
+        assertEquals("config", testModule.getPrefix());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileIdentityStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileIdentityStmtTest.java
new file mode 100644 (file)
index 0000000..a4a1900
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.net.URISyntaxException;
+import java.util.Iterator;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileIdentityStmtTest extends AbstractYinModulesTest {
+
+    @Test
+    public void testIdentity() throws URISyntaxException {
+        Module testModule = TestUtils.findModule(context, "config").get();
+        assertNotNull(testModule);
+
+        Set<IdentitySchemaNode> identities = testModule.getIdentities();
+        assertEquals(2, identities.size());
+
+        Iterator<IdentitySchemaNode> idIterator = identities.iterator();
+        IdentitySchemaNode id = idIterator.next();
+
+        assertThat(id.getQName().getLocalName(), anyOf(is("module-type"), is("service-type")));
+        assertTrue(id.getBaseIdentities().isEmpty());
+
+        id = idIterator.next();
+        assertThat(id.getQName().getLocalName(), anyOf(is("module-type"), is("service-type")));
+        assertTrue(id.getBaseIdentities().isEmpty());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileImportStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileImportStmtTest.java
new file mode 100644 (file)
index 0000000..abacb5a
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.text.ParseException;
+import java.util.Iterator;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileImportStmtTest extends AbstractYinModulesTest {
+
+    @Test
+    public void testImport() throws ParseException {
+        Module testModule = TestUtils.findModule(context, "ietf-netconf-monitoring").get();
+        assertNotNull(testModule);
+
+        Set<ModuleImport> imports = testModule.getImports();
+        assertEquals(2, imports.size());
+
+        Iterator<ModuleImport> importsIterator = imports.iterator();
+        ModuleImport moduleImport = importsIterator.next();
+
+        assertThat(moduleImport.getModuleName(), anyOf(is("ietf-yang-types"), is("ietf-inet-types")));
+        assertThat(moduleImport.getPrefix(), anyOf(is("yang"), is("inet")));
+
+        moduleImport = importsIterator.next();
+        assertThat(moduleImport.getModuleName(), anyOf(is("ietf-yang-types"), is("ietf-inet-types")));
+        assertThat(moduleImport.getPrefix(), anyOf(is("yang"), is("inet")));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileIncludeStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileIncludeStmtTest.java
new file mode 100644 (file)
index 0000000..ee69341
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Iterator;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+import org.xml.sax.SAXException;
+
+public class YinFileIncludeStmtTest {
+
+    private SchemaContext context;
+
+    @Before
+    public void init() throws URISyntaxException, ReactorException, SAXException, IOException {
+        context = TestUtils.loadYinModules(getClass().getResource(
+            "/semantic-statement-parser/yin/include-belongs-to-test").toURI());
+        assertEquals(1, context.getModules().size());
+    }
+
+    @Test
+    public void testInclude() throws URISyntaxException {
+        Module parentModule = TestUtils.findModule(context, "parent").get();
+        assertNotNull(parentModule);
+
+        assertEquals(1, parentModule.getSubmodules().size());
+        Iterator<Module> submodulesIterator = parentModule.getSubmodules().iterator();
+
+        Module childModule = submodulesIterator.next() ;
+        assertNotNull(childModule);
+        assertEquals("child", childModule.getName());
+        assertEquals(new URI("urn:opendaylight/parent"), childModule.getNamespace());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileLeafListStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileLeafListStmtTest.java
new file mode 100644 (file)
index 0000000..376dac6
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileLeafListStmtTest extends AbstractYinModulesTest {
+
+    @Test
+    public void testLeafList() {
+        final Module testModule = TestUtils.findModule(context, "ietf-netconf-monitoring").get();
+
+        final LeafListSchemaNode leafList = (LeafListSchemaNode) testModule.findDataChildByName(
+            QName.create(testModule.getQNameModule(), "netconf-state"),
+            QName.create(testModule.getQNameModule(), "capabilities"),
+            QName.create(testModule.getQNameModule(), "capability")).get();
+        assertNotNull(leafList);
+        assertEquals("uri", leafList.getType().getQName().getLocalName());
+        assertEquals(Optional.of("List of NETCONF capabilities supported by the server."), leafList.getDescription());
+        assertFalse(leafList.isUserOrdered());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileListStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileListStmtTest.java
new file mode 100644 (file)
index 0000000..45e941f
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.hamcrest.Matchers.isA;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileListStmtTest extends AbstractYinModulesTest {
+    @Test
+    public void testListAndLeaves() {
+        final Module testModule = TestUtils.findModule(context, "config").get();
+        assertNotNull(testModule);
+
+        final ListSchemaNode list = (ListSchemaNode) testModule.findDataChildByName(QName.create(
+                testModule.getQNameModule(), "modules"), QName.create(testModule.getQNameModule(), "module")).get();
+        final List<QName> keys = list.getKeyDefinition();
+        assertEquals(1, keys.size());
+        assertEquals("name", keys.get(0).getLocalName());
+
+        final Collection<DataSchemaNode> children = list.getChildNodes();
+        assertEquals(4, children.size());
+
+        final Iterator<DataSchemaNode> childrenIterator = children.iterator();
+        LeafSchemaNode leaf = (LeafSchemaNode) childrenIterator.next();
+        assertEquals("name", leaf.getQName().getLocalName());
+        assertEquals(Optional.of("Unique module instance name"), leaf.getDescription());
+        assertEquals(BaseTypes.stringType(), leaf.getType());
+        assertTrue(leaf.isMandatory());
+
+        leaf = (LeafSchemaNode) childrenIterator.next();
+        assertEquals("type", leaf.getQName().getLocalName());
+
+        final TypeDefinition<?> leafType = leaf.getType();
+        assertThat(leafType, isA(IdentityrefTypeDefinition.class));
+        assertEquals("module-type", ((IdentityrefTypeDefinition)leafType).getIdentities().iterator().next().getQName()
+            .getLocalName());
+        assertTrue(leaf.isMandatory());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileMetaStmtsTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileMetaStmtsTest.java
new file mode 100644 (file)
index 0000000..e667d1f
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.net.URISyntaxException;
+import java.util.Optional;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileMetaStmtsTest extends AbstractYinModulesTest {
+
+    @Test
+    public void testMetaStatements() throws URISyntaxException {
+        Module testModule = TestUtils.findModule(context, "ietf-netconf-monitoring").get();
+        assertNotNull(testModule);
+
+        assertEquals(Optional.of("IETF NETCONF (Network Configuration) Working Group"), testModule.getOrganization());
+        assertEquals(Optional.of("WG Web:   <http://tools.ietf.org/wg/netconf/>\n"
+                + "WG List:  <mailto:netconf@ietf.org>\n"
+                + "\n"
+                + "WG Chair: Mehmet Ersue\n"
+                + "        <mailto:mehmet.ersue@nsn.com>\n"
+                + "\n"
+                + "WG Chair: Bert Wijnen\n"
+                + "        <mailto:bertietf@bwijnen.net>\n"
+                + "\n"
+                + "Editor:   Mark Scott\n"
+                + "        <mailto:mark.scott@ericsson.com>\n"
+                + "\n"
+                + "Editor:   Martin Bjorklund\n"
+                + "        <mailto:mbj@tail-f.com>"), testModule.getContact());
+        assertEquals(Optional.of("NETCONF Monitoring Module.\n" + "All elements in this module are read-only.\n" + "\n"
+                + "Copyright (c) 2010 IETF Trust and the persons identified as\n" + "authors of the code. All rights "
+                + "reserved.\n" + "\n" + "Redistribution and use in source and binary forms, with or\n" +  "without "
+                + "modification, is permitted pursuant to, and subject\n" + "to the license terms contained in, the "
+                + "Simplified BSD\n" + "License set forth in Section 4.c of the IETF Trust's\n" + "Legal Provisions "
+                + "Relating to IETF Documents\n" + "(http://trustee.ietf.org/license-info).\n" + "\n" + "This version "
+                + "of this YANG module is part of RFC 6022; see\n" + "the RFC itself for full legal notices."),
+                testModule.getDescription());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileRpcStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileRpcStmtTest.java
new file mode 100644 (file)
index 0000000..86884f4
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileRpcStmtTest extends AbstractYinModulesTest {
+
+    @Test
+    public void testRpc() {
+        Module testModule = TestUtils.findModule(context, "ietf-netconf-monitoring").get();
+
+        Set<RpcDefinition> rpcs = testModule.getRpcs();
+        assertEquals(1, rpcs.size());
+
+        RpcDefinition rpc = rpcs.iterator().next();
+        assertEquals("get-schema", rpc.getQName().getLocalName());
+        assertEquals(Optional.of("This operation is used to retrieve a schema from the\n"
+                + "NETCONF server.\n"
+                + "\n"
+                + "Positive Response:\n"
+                + "The NETCONF server returns the requested schema.\n"
+                + "\n"
+                + "Negative Response:\n"
+                + "If requested schema does not exist, the <error-tag> is\n"
+                + "'invalid-value'.\n"
+                + "\n"
+                + "If more than one schema matches the requested parameters, the\n"
+                + "<error-tag> is 'operation-failed', and <error-app-tag> is\n"
+                + "'data-not-unique'."), rpc.getDescription());
+
+        ContainerSchemaNode input = rpc.getInput();
+        assertNotNull(input);
+        assertEquals(3, input.getChildNodes().size());
+
+        ContainerSchemaNode output = rpc.getOutput();
+        assertNotNull(output);
+        assertEquals(1, output.getChildNodes().size());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileStmtTest.java
new file mode 100644 (file)
index 0000000..731c6a1
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.hamcrest.Matchers.isA;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.YinTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YinStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YinTextToDomTransformer;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SomeModifiersUnresolvedException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor.BuildAction;
+import org.xml.sax.SAXException;
+
+public class YinFileStmtTest {
+
+    private static final StatementStreamSource YIN_FILE = createSource("test.yin");
+    private static final StatementStreamSource EXT_FILE = createSource("extension.yin");
+    private static final StatementStreamSource EXT_USE_FILE = createSource("extension-use.yin");
+    private static final StatementStreamSource INVALID_YIN_FILE = createSource("incorrect-foo.yin");
+    private static final StatementStreamSource INVALID_YIN_FILE_2 = createSource("incorrect-bar.yin");
+
+    private static StatementStreamSource createSource(final String name) {
+        try {
+            return YinStatementStreamSource.create(YinTextToDomTransformer.transformSource(
+                YinTextSchemaSource.forResource(YinFileStmtTest.class, "/semantic-statement-parser/yin/" + name)));
+        } catch (SAXException | IOException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    @Test
+    public void readAndParseYinFileTestModel() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSources(YIN_FILE, EXT_FILE, EXT_USE_FILE)
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    // parsing yin file whose import statement references a module which does not exist
+    @Test(expected = SomeModifiersUnresolvedException.class)
+    public void readAndParseInvalidYinFileTest() throws ReactorException {
+        SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+                .addSource(INVALID_YIN_FILE)
+                .buildEffective();
+        assertNotNull(result);
+    }
+
+    // parsing yin file with duplicate key name in a list statement
+    @Test
+    public void readAndParseInvalidYinFileTest2() {
+        BuildAction reactor = RFC7950Reactors.defaultReactor().newBuild().addSource(INVALID_YIN_FILE_2);
+
+        try {
+            reactor.buildEffective();
+            fail("Reactor exception should have been thrown");
+        } catch (ReactorException e) {
+            final Throwable cause = e.getCause();
+            assertThat(cause, isA(SourceException.class));
+            assertTrue(cause.getMessage().startsWith(
+                "Key argument 'testing-string testing-string' contains duplicates"));
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileTypeDefStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileTypeDefStmtTest.java
new file mode 100644 (file)
index 0000000..0faec82
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileTypeDefStmtTest extends AbstractYinModulesTest {
+
+    @Test
+    public void testTypedef() {
+        Module testModule = TestUtils.findModule(context, "config").get();
+        assertNotNull(testModule);
+
+        Set<TypeDefinition<?>> typeDefs = testModule.getTypeDefinitions();
+        assertEquals(1, typeDefs.size());
+
+        Iterator<TypeDefinition<?>> typeDefIterator = typeDefs.iterator();
+        TypeDefinition<?> typeDef = typeDefIterator.next();
+        assertEquals("service-type-ref", typeDef.getQName().getLocalName());
+        assertEquals(Optional.of("Internal type of references to service type identity."), typeDef.getDescription());
+        assertEquals("identityref", typeDef.getBaseType().getQName().getLocalName());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileUsesStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/yin/YinFileUsesStmtTest.java
new file mode 100644 (file)
index 0000000..40d7055
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 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.stmt.yin;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.util.Iterator;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+import org.opendaylight.yangtools.yang.stmt.TestUtils;
+
+public class YinFileUsesStmtTest extends AbstractYinModulesTest {
+
+    @Test
+    public void testUses() {
+        final Module testModule = TestUtils.findModule(context, "main-impl").get();
+
+        final Set<AugmentationSchemaNode> augmentations = testModule.getAugmentations();
+        assertEquals(1, augmentations.size());
+
+        final Iterator<AugmentationSchemaNode> augmentIterator = augmentations.iterator();
+        final AugmentationSchemaNode augment = augmentIterator.next();
+
+        final ContainerSchemaNode container = (ContainerSchemaNode) augment.findDataChildByName(
+            QName.create(testModule.getQNameModule(), "main-impl"),
+            QName.create(testModule.getQNameModule(), "notification-service")).get();
+
+        assertEquals(1, container.getUses().size());
+        final UsesNode usesNode = container.getUses().iterator().next();
+        assertNotNull(usesNode);
+        assertThat(usesNode.getGroupingPath().toString(), containsString(
+            "[(urn:opendaylight:params:xml:ns:yang:controller:config?revision=2013-04-05)service-ref]"));
+        assertEquals(1, usesNode.getRefines().size());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/CustomInferencePipeline.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/CustomInferencePipeline.java
new file mode 100644 (file)
index 0000000..7b02c60
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 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.thirdparty.plugin;
+
+import static org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.sourceLocal;
+
+import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+
+public final class CustomInferencePipeline {
+    public static final CrossSourceStatementReactor CUSTOM_REACTOR = RFC7950Reactors.defaultReactorBuilder()
+            .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, ThirdPartyExtensionSupport.getInstance())
+            .addNamespaceSupport(ModelProcessingPhase.FULL_DECLARATION, sourceLocal(ThirdPartyNamespace.class))
+            .build();
+
+    private CustomInferencePipeline() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionEffectiveStatement.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionEffectiveStatement.java
new file mode 100644 (file)
index 0000000..a4d4aaa
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.thirdparty.plugin;
+
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Public interface exposed to the effective world.
+ *
+ * @author Robert Varga
+ */
+public interface ThirdPartyExtensionEffectiveStatement
+    extends EffectiveStatement<String, ThirdPartyExtensionStatement> {
+
+    String getValueFromNamespace();
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionEffectiveStatementImpl.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionEffectiveStatementImpl.java
new file mode 100644 (file)
index 0000000..76af2f2
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016 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.thirdparty.plugin;
+
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.UnknownEffectiveStatementBase;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+final class ThirdPartyExtensionEffectiveStatementImpl
+        extends UnknownEffectiveStatementBase<String, ThirdPartyExtensionStatement>
+        implements ThirdPartyExtensionEffectiveStatement {
+
+    private final @NonNull SchemaPath path;
+    private final String valueFromNamespace;
+
+    ThirdPartyExtensionEffectiveStatementImpl(final StmtContext<String, ThirdPartyExtensionStatement, ?> ctx) {
+        super(ctx);
+        path = ctx.coerceParentContext().getSchemaPath().get().createChild(getNodeType());
+        valueFromNamespace = ctx.getFromNamespace(ThirdPartyNamespace.class, ctx);
+    }
+
+    @Override
+    public String getValueFromNamespace() {
+        return valueFromNamespace;
+    }
+
+    @Override
+    public QName getQName() {
+        return getNodeType();
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return path;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(path, getNodeType(), getNodeParameter());
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ThirdPartyExtensionEffectiveStatementImpl other = (ThirdPartyExtensionEffectiveStatementImpl) obj;
+        if (!Objects.equals(path, other.path)) {
+            return false;
+        }
+        if (!Objects.equals(getNodeType(), other.getNodeType())) {
+            return false;
+        }
+        if (!Objects.equals(getNodeParameter(), other.getNodeParameter())) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionPluginTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionPluginTest.java
new file mode 100644 (file)
index 0000000..bd756fc
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 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.thirdparty.plugin;
+
+import static org.hamcrest.Matchers.isA;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+
+public class ThirdPartyExtensionPluginTest {
+    private static final String NS = "urn:opendaylight:yang:extension:third-party";
+    private static final String REV = "2016-06-09";
+
+    @Test
+    public void test() throws URISyntaxException, ReactorException, IOException, YangSyntaxErrorException {
+        final CrossSourceStatementReactor.BuildAction reactor = CustomInferencePipeline.CUSTOM_REACTOR.newBuild();
+        reactor.addSource(YangStatementStreamSource.create(YangTextSchemaSource.forResource("/plugin-test/foo.yang")));
+
+        final SchemaContext schema = reactor.buildEffective();
+        final DataSchemaNode dataChildByName = schema.getDataChildByName(QName.create(NS, REV, "root"));
+        assertThat(dataChildByName, isA(ContainerSchemaNode.class));
+
+        final ContainerSchemaNode root = (ContainerSchemaNode) dataChildByName;
+
+        final List<UnknownSchemaNode> unknownSchemaNodes = root.getUnknownSchemaNodes();
+        assertEquals(1, unknownSchemaNodes.size());
+
+        final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.get(0);
+        assertThat(unknownSchemaNode, isA(ThirdPartyExtensionEffectiveStatement.class));
+        final ThirdPartyExtensionEffectiveStatement thirdPartyExtensionStmt =
+                (ThirdPartyExtensionEffectiveStatement) unknownSchemaNode;
+        assertEquals("Third-party namespace test.", thirdPartyExtensionStmt.getValueFromNamespace());
+        assertEquals("plugin test", thirdPartyExtensionStmt.argument());
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionStatement.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionStatement.java
new file mode 100644 (file)
index 0000000..75173df
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.thirdparty.plugin;
+
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+
+/**
+ * Public declared statement definition that this plugin supports. This interface should generally depend only on
+ * concepts defined in yang-model-api.
+ */
+public interface ThirdPartyExtensionStatement extends UnknownStatement<String> {
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionStatementImpl.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionStatementImpl.java
new file mode 100644 (file)
index 0000000..686492e
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2016 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.thirdparty.plugin;
+
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Private implementation of {@link ThirdPartyExtensionStatement}. This class can rely on parser internals and should
+ * never be exposed to the world.
+ */
+final class ThirdPartyExtensionStatementImpl extends AbstractDeclaredStatement<String> implements
+        ThirdPartyExtensionStatement {
+
+    ThirdPartyExtensionStatementImpl(final StmtContext<String, ThirdPartyExtensionStatement, ?> context) {
+        super(context);
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionSupport.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionSupport.java
new file mode 100644 (file)
index 0000000..d77f07e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.thirdparty.plugin;
+
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
+
+public final class ThirdPartyExtensionSupport extends AbstractStatementSupport<String, ThirdPartyExtensionStatement,
+        EffectiveStatement<String, ThirdPartyExtensionStatement>> {
+
+    private static final ThirdPartyExtensionSupport INSTANCE = new ThirdPartyExtensionSupport();
+
+    private ThirdPartyExtensionSupport() {
+        super(ThirdPartyExtensionsMapping.THIRD_PARTY_EXTENSION);
+    }
+
+    public static ThirdPartyExtensionSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return value;
+    }
+
+    @Override
+    public void onFullDefinitionDeclared(final Mutable<String, ThirdPartyExtensionStatement,
+            EffectiveStatement<String, ThirdPartyExtensionStatement>> stmt) {
+        super.onFullDefinitionDeclared(stmt);
+        stmt.addToNs(ThirdPartyNamespace.class, stmt, "Third-party namespace test.");
+    }
+
+    @Override
+    public ThirdPartyExtensionStatement createDeclared(final StmtContext<String, ThirdPartyExtensionStatement, ?> ctx) {
+        return new ThirdPartyExtensionStatementImpl(ctx);
+    }
+
+    @Override
+    public EffectiveStatement<String, ThirdPartyExtensionStatement> createEffective(final StmtContext<String,
+            ThirdPartyExtensionStatement, EffectiveStatement<String, ThirdPartyExtensionStatement>> ctx) {
+        return new ThirdPartyExtensionEffectiveStatementImpl(ctx);
+    }
+
+    @Override
+    protected SubstatementValidator getSubstatementValidator() {
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionsMapping.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyExtensionsMapping.java
new file mode 100644 (file)
index 0000000..829ae3e
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016 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.thirdparty.plugin;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+@Beta
+public enum ThirdPartyExtensionsMapping implements StatementDefinition {
+    THIRD_PARTY_EXTENSION("urn:opendaylight:yang:extension:third-party", "2016-06-09",
+            ThirdPartyExtensionStatementImpl.class, ThirdPartyExtensionEffectiveStatementImpl.class,
+            "third-party-extension", "argument-name", false);
+
+    private final @NonNull Class<? extends DeclaredStatement<?>> type;
+    private final @NonNull Class<? extends EffectiveStatement<?, ?>> effectiveType;
+    private final @NonNull QName name;
+    private final QName argument;
+    private final boolean yinElement;
+
+    ThirdPartyExtensionsMapping(final String namespace, final String revision,
+            final Class<? extends DeclaredStatement<?>> declared,
+            final Class<? extends EffectiveStatement<?, ?>> effective, final String nameStr, final String argumentStr,
+            final boolean yinElement) {
+        type = requireNonNull(declared);
+        effectiveType = requireNonNull(effective);
+        name = createQName(namespace, revision, nameStr);
+        argument = createQName(namespace, revision, argumentStr);
+        this.yinElement = yinElement;
+    }
+
+    ThirdPartyExtensionsMapping(final String namespace, final Class<? extends DeclaredStatement<?>> declared,
+            final Class<? extends EffectiveStatement<?, ?>> effective, final String nameStr, final String argumentStr,
+            final boolean yinElement) {
+        type = requireNonNull(declared);
+        effectiveType = requireNonNull(effective);
+        name = createQName(namespace, nameStr);
+        argument = createQName(namespace, argumentStr);
+        this.yinElement = yinElement;
+    }
+
+    private static @NonNull QName createQName(final String namespace, final String localName) {
+        return QName.create(namespace, localName).intern();
+    }
+
+    private static @NonNull QName createQName(final String namespace, final String revision, final String localName) {
+        return QName.create(namespace, revision, localName).intern();
+    }
+
+    @Override
+    public QName getStatementName() {
+        return name;
+    }
+
+    @Override
+    public @NonNull Optional<ArgumentDefinition> getArgumentDefinition() {
+        return ArgumentDefinition.ofNullable(argument, yinElement);
+    }
+
+    @Override
+    public Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+        return type;
+    }
+
+    @Override
+    public Class<? extends EffectiveStatement<?, ?>> getEffectiveRepresentationClass() {
+        return effectiveType;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyNamespace.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/thirdparty/plugin/ThirdPartyNamespace.java
new file mode 100644 (file)
index 0000000..d61d2de
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2015 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.thirdparty.plugin;
+
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * ThirdPartyNamespace.
+ */
+public interface ThirdPartyNamespace extends IdentifierNamespace<StmtContext<?, ?, ?>, String> {
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/added-by-uses-leaf-test/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/added-by-uses-leaf-test/foo.yang
new file mode 100644 (file)
index 0000000..413d7c6
--- /dev/null
@@ -0,0 +1,20 @@
+module foo {
+    prefix foo;
+    namespace "namespace-foo";
+
+    import import-module { prefix imp; revision-date 1970-01-02; } 
+    
+    grouping grp {
+        leaf my-leaf {
+            type imp:imp-type;
+        }
+        
+    }
+    
+    container my-container {
+        uses grp;
+        uses imp:imp_grp;
+    }
+    
+}
+
diff --git a/yang/yang-parser-rfc7950/src/test/resources/added-by-uses-leaf-test/import-module.yang b/yang/yang-parser-rfc7950/src/test/resources/added-by-uses-leaf-test/import-module.yang
new file mode 100644 (file)
index 0000000..6cb9f69
--- /dev/null
@@ -0,0 +1,35 @@
+module import-module {
+    prefix imp;
+    namespace "import-module";
+
+    revision 1970-01-02 {
+        description "Initial revision.";
+    }
+
+    typedef imp-type {
+        type string {
+            length "0..100";
+        }
+    }
+
+    grouping imp_grp {
+        
+        typedef grp-type {
+            type string {
+                length "20..30";
+            }
+        }
+
+        leaf my-leaf2 {
+            type grp-type;
+        }
+        
+        leaf union-leaf {
+            type union {
+                type int16;
+                type string;
+            }
+        }
+    }
+}
+
diff --git a/yang/yang-parser-rfc7950/src/test/resources/augment-test/augment-in-augment/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/augment-test/augment-in-augment/bar.yang
new file mode 100644 (file)
index 0000000..514bc5e
--- /dev/null
@@ -0,0 +1,55 @@
+module bar {
+    yang-version 1;
+    namespace "urn:opendaylight.bar";
+    prefix "bar";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-10-14" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    extension opendaylight {
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+    container interfaces {
+        grouping ifEntry {
+            container augment-holder;
+        }
+        list ifEntry {
+            key "ifIndex";
+
+            leaf ifIndex {
+                type uint32;
+                units minutes;
+            }
+
+            leaf ifMtu {
+                type int32;
+            }
+
+            min-elements 1;
+            max-elements 11;
+        }
+    }
+
+    container controller {
+        choice switch {
+            case start {
+                leaf start {
+                    type int8;
+                }
+            }
+            case stop {
+                leaf stop {
+                    type int16;
+                }
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/augment-test/augment-in-augment/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/augment-test/augment-in-augment/baz.yang
new file mode 100644 (file)
index 0000000..e7b20a9
--- /dev/null
@@ -0,0 +1,94 @@
+module baz {
+    yang-version 1;
+    namespace "urn:opendaylight.baz";
+    prefix "baz";
+
+    import bar {
+        prefix "br";
+        revision-date 2013-10-14;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-10-15" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+
+    augment "/br:interfaces/br:ifEntry" {
+        when "if:ifType='ds0'";
+        container augment-holder {
+            description "Description for augment holder";
+        }
+    }
+
+    augment "/br:interfaces/br:ifEntry" {
+        when "if:ifType='ds2'";
+        container augment-holder2 {
+            description "Description for augment holder";
+        }
+    }
+
+    augment "/br:controller/br:switch" {
+        leaf pause {
+            type int32;
+        }
+    }
+
+    grouping target {
+        anyxml data {
+            config true;
+            description "Copy of the source datastore subset.";
+            mandatory false;
+            must "test-condition-text";
+            reference "test-no-reference";
+            status "obsolete";
+            when "test-when-text";
+        }
+        choice how {
+            description "test choice description";
+            default interval;
+            case interval {
+                leaf interval {
+                    type uint16;
+                    default 30;
+                    units minutes;
+                }
+            }
+            case daily {
+                leaf daily {
+                    type empty;
+                }
+                leaf time-of-day {
+                    type string;
+                    units 24-hour-clock;
+                    default 1am;
+                }
+            }
+        }
+        leaf address {
+            type string;
+            description "Target IP address";
+            mandatory true;
+        }
+        container port {
+            description "Target port container";
+        }
+        list addresses {
+            key "id";
+            leaf id {
+                type int8;
+            }
+        }
+        grouping target-inner {
+            description "target-inner default description";
+            leaf inner-grouping-id {
+                type int8;
+            }
+        }
+        
+        br:opendaylight awesome;
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/augment-test/augment-in-augment/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/augment-test/augment-in-augment/foo.yang
new file mode 100644 (file)
index 0000000..836eb57
--- /dev/null
@@ -0,0 +1,62 @@
+module foo {
+    yang-version 1;
+    namespace "urn:opendaylight.foo";
+    prefix "foo";
+
+    import bar {
+        prefix "br";
+        revision-date 2013-10-14;
+    }
+
+    import baz {
+        prefix "bz";
+        revision-date 2013-10-15;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-10-13" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+
+    augment "/br:interfaces/br:ifEntry/bz:augment-holder" {
+        when "if:ifType='ds0'";
+        leaf ds0ChannelNumber {
+            type string;
+        }
+        leaf interface-id {
+            type leafref {
+                path "/br:interfaces/br:ifEntry/br:ifIndex";
+            }
+        }
+        container schemas {
+        }
+        choice odl {
+            leaf id {
+                type int8;
+            }
+            case node1 {
+                description "node1";
+            }
+            case node2 {
+                description "node2";
+            }
+            container node3 {
+                description "node3";
+            }
+        }
+    }
+
+
+    uses bz:target {
+        augment "how/interval" {
+            description "inner augment";
+            leaf name {
+                type string;
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/augment-test/augment-in-uses/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/augment-test/augment-in-uses/foo.yang
new file mode 100644 (file)
index 0000000..0dca0bc
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module foo {
+    yang-version 1;
+    namespace "urn:yang.foo";
+    prefix "f";
+
+    revision "2014-03-10" {
+    }
+
+    container links {
+        container link {
+            uses g1 {
+                augment nodes/node {
+                    leaf id {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+    grouping g1 {
+        list nodes {
+            container node {
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/augment-test/rpc/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/augment-test/rpc/bar.yang
new file mode 100644 (file)
index 0000000..0d1ffb9
--- /dev/null
@@ -0,0 +1,46 @@
+module bar {
+    yang-version 1;
+    namespace "urn:opendaylight:bar";
+    prefix "bar";
+
+    revision "2013-10-11" {
+    }
+
+    rpc cancel {
+        input {
+            leaf id {
+                type string;
+            }
+        }
+        output {
+            leaf failure {
+                type string;
+            }
+        }
+    }
+
+
+    rpc submit {
+        input {
+            leaf id {
+                type string;
+            }
+            leaf type {
+                type string;
+            }
+            choice arguments {
+            }
+        }
+
+        output {
+            choice result {
+                case failure {
+                    leaf failure {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/augment-test/rpc/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/augment-test/rpc/foo.yang
new file mode 100644 (file)
index 0000000..7ee2c1b
--- /dev/null
@@ -0,0 +1,50 @@
+module foo {
+    yang-version 1;
+    namespace "urn:opendaylight:foo";
+    prefix "foo";
+
+    import bar { prefix b; revision-date 2013-10-11; }
+
+    revision "2013-10-11" {
+    }
+
+    augment "/b:submit/b:input/b:arguments" {
+        when "../type = attach-path-instruction";
+
+        case attach {
+            container attach {
+                leaf link-id {
+                    type string;
+                }
+                leaf path-id {
+                    type string;
+                }
+            }
+        }
+    }
+
+    augment "/b:submit/b:input/b:arguments" {
+        when "../type = create-path-instruction";
+
+        case create {
+            container create {
+                leaf id {
+                    type string;
+                }
+            }
+        }
+    }
+
+    augment "/b:submit/b:input/b:arguments" {
+        when "../type = destroy-path-instruction";
+
+        case destroy {
+            container destroy {
+                leaf id {
+                    type string;
+                }
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/correct-augment/augment-module.yang b/yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/correct-augment/augment-module.yang
new file mode 100644 (file)
index 0000000..05ec41b
--- /dev/null
@@ -0,0 +1,39 @@
+module augment-module {
+    yang-version 1;
+    namespace "uri:augment-module";
+    prefix aug;
+
+    import extension-module { prefix ext; }
+
+    revision 2014-10-07 {
+        description
+                "Yang model with augment into extension";
+    }
+
+    grouping my-grouping {
+        ext:my-extension my-extension-name {
+            description
+                        "Extension.";
+            input {
+                leaf my-leaf {
+                    type string;
+                    description
+                                        "my-leaf in extension body.";
+                }
+            }
+        }
+        container my-container-in-gruping {
+        }
+    }
+
+    container my-container {
+        uses my-grouping {
+            augment "my-container-in-gruping" {
+                leaf-list my-leaf-list {
+                    type string;
+                }
+            }
+        }
+    }
+}
+
diff --git a/yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/correct-augment/extension-module.yang b/yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/correct-augment/extension-module.yang
new file mode 100644 (file)
index 0000000..d2046d1
--- /dev/null
@@ -0,0 +1,15 @@
+module extension-module {
+    yang-version 1;
+    namespace "uri:extension-module";
+    prefix ext;
+
+    revision 2014-10-07 {
+        description
+                "Yang model with extension definition";
+    }
+
+    extension my-extension {
+        argument name {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/correct-path-into-unsupported-target/augment-module.yang b/yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/correct-path-into-unsupported-target/augment-module.yang
new file mode 100644 (file)
index 0000000..814004e
--- /dev/null
@@ -0,0 +1,39 @@
+module augment-module {
+    yang-version 1;
+    namespace "uri:augment-module";
+    prefix aug;
+
+    import extension-module { prefix ext; }
+
+    revision 2014-10-07 {
+        description
+                "Yang model with augment into extension";
+    }
+
+    grouping my-grouping {
+        ext:my-extension my-extension-name {
+            description
+                        "Extension.";
+            input {
+                leaf my-leaf {
+                    type string;
+                    description
+                                        "my-leaf in extension body.";
+                }
+            }
+        }
+        container my-container-in-gruping {
+        }
+    }
+
+    container my-container {
+        uses my-grouping {
+            augment "my-extension-name/input" {
+                leaf-list my-leaf-list {
+                    type string;
+                }
+            }
+        }
+    }
+}
+
diff --git a/yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/correct-path-into-unsupported-target/extension-module.yang b/yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/correct-path-into-unsupported-target/extension-module.yang
new file mode 100644 (file)
index 0000000..d2046d1
--- /dev/null
@@ -0,0 +1,15 @@
+module extension-module {
+    yang-version 1;
+    namespace "uri:extension-module";
+    prefix ext;
+
+    revision 2014-10-07 {
+        description
+                "Yang model with extension definition";
+    }
+
+    extension my-extension {
+        argument name {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/incorrect-path/augment-module.yang b/yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/incorrect-path/augment-module.yang
new file mode 100644 (file)
index 0000000..7370e12
--- /dev/null
@@ -0,0 +1,39 @@
+module augment-module {
+    yang-version 1;
+    namespace "uri:augment-module";
+    prefix aug;
+
+    import extension-module { prefix ext; }
+
+    revision 2014-10-07 {
+        description
+                "Yang model with augment into extension";
+    }
+
+    grouping my-grouping {
+        ext:my-extension my-extension-name {
+            description
+                        "Extension.";
+            input {
+                leaf my-leaf {
+                    type string;
+                    description
+                                        "my-leaf in extension body.";
+                }
+            }
+        }
+        container my-container-in-gruping {
+        }
+    }
+
+    container my-container {
+        uses my-grouping {
+            augment "my-extension-name-a/input" {
+                leaf-list my-leaf-list {
+                    type string;
+                }
+            }
+        }
+    }
+}
+
diff --git a/yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/incorrect-path/extension-module.yang b/yang/yang-parser-rfc7950/src/test/resources/augment-to-extension-test/incorrect-path/extension-module.yang
new file mode 100644 (file)
index 0000000..d2046d1
--- /dev/null
@@ -0,0 +1,15 @@
+module extension-module {
+    yang-version 1;
+    namespace "uri:extension-module";
+    prefix ext;
+
+    revision 2014-10-07 {
+        description
+                "Yang model with extension definition";
+    }
+
+    extension my-extension {
+        argument name {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT827/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT827/foo.yang
new file mode 100644 (file)
index 0000000..c096177
--- /dev/null
@@ -0,0 +1,8 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    revision "2010-10-10";
+
+    revision "2014-12-24";
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT838/grouping-post.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT838/grouping-post.yang
new file mode 100644 (file)
index 0000000..316cee7
--- /dev/null
@@ -0,0 +1,18 @@
+module grouping {
+    namespace grouping;
+    prefix grp;
+
+    revision "2017-12-20";
+
+    container top {
+        container nested {
+            grouping foo {
+
+            }
+        }
+
+        grouping foo {
+
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT838/grouping.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT838/grouping.yang
new file mode 100644 (file)
index 0000000..9f5526a
--- /dev/null
@@ -0,0 +1,18 @@
+module grouping {
+    namespace grouping;
+    prefix grp;
+
+    revision "2017-12-20";
+
+    container top {
+        grouping foo {
+
+        }
+
+        container nested {
+            grouping foo {
+
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT838/typedef-post.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT838/typedef-post.yang
new file mode 100644 (file)
index 0000000..f82295b
--- /dev/null
@@ -0,0 +1,18 @@
+module typedef {
+    namespace typedef;
+    prefix tdef;
+
+    revision "2017-12-20";
+
+    container top {
+        container nested {
+            typedef foo {
+                type string;
+            }
+        }
+
+        typedef foo {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT838/typedef.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT838/typedef.yang
new file mode 100644 (file)
index 0000000..70fb9f7
--- /dev/null
@@ -0,0 +1,18 @@
+module typedef {
+    namespace typedef;
+    prefix tdef;
+
+    revision "2017-12-20";
+
+    container top {
+        typedef foo {
+            type string;
+        }
+
+        container nested {
+            typedef foo {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT841/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT841/foo.yang
new file mode 100644 (file)
index 0000000..966f430
--- /dev/null
@@ -0,0 +1,19 @@
+module foo {
+    yang-version 1.1;
+    namespace foo;
+    prefix foo;
+
+    revision "2018-01-02";
+
+    container foo {
+        list foo {
+            leaf bar {
+                type string;
+            }
+            key bar;
+
+            action foo {
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT857/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT857/foo.yang
new file mode 100644 (file)
index 0000000..51d97ed
--- /dev/null
@@ -0,0 +1,28 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    grouping flags {
+        leaf one {
+            type boolean;
+        }
+        leaf two {
+            type boolean;
+        }
+    }
+
+    grouping choice-grp {
+        choice flags {
+            case first {
+                uses flags;
+            }
+            case second {
+                uses flags;
+            }
+        }
+    }
+
+    container cont {
+        uses choice-grp;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT893/cr.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT893/cr.yang
new file mode 100644 (file)
index 0000000..ed724ac
--- /dev/null
@@ -0,0 +1,15 @@
+module cr {
+    namespace "urn:cr";
+    prefix cr;
+
+    list foo {
+        unique "a\rb";
+
+        leaf a {
+            type string;
+        }
+        leaf b {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT893/crlf.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT893/crlf.yang
new file mode 100644 (file)
index 0000000..063f54a
--- /dev/null
@@ -0,0 +1,16 @@
+module crlf {
+    namespace "urn:crlf";
+    prefix crlf;
+
+    list foo {
+        unique "a\r
+                b";
+
+        leaf a {
+            type string;
+        }
+        leaf b {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT893/ht.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT893/ht.yang
new file mode 100644 (file)
index 0000000..8e035fe
--- /dev/null
@@ -0,0 +1,15 @@
+module ht {
+    namespace "urn:ht";
+    prefix ht;
+
+    list foo {
+        unique "a      b";
+
+        leaf a {
+            type string;
+        }
+        leaf b {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT893/lf.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT893/lf.yang
new file mode 100644 (file)
index 0000000..b1d559f
--- /dev/null
@@ -0,0 +1,16 @@
+module lf {
+    namespace "urn:lf";
+    prefix lf;
+
+    list foo {
+        unique "a
+                b";
+
+        leaf a {
+            type string;
+        }
+        leaf b {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT911/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT911/foo.yang
new file mode 100644 (file)
index 0000000..ee9224d
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    revision "2018-10-22";
+
+    container foo {
+        config false;
+    }
+
+    augment /foo {
+        container bar;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT956/another-module.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT956/another-module.yang
new file mode 100644 (file)
index 0000000..da47533
--- /dev/null
@@ -0,0 +1,12 @@
+module another-module {
+    yang-version 1.1;
+    namespace "http://www.example.com/anothermodule";
+    prefix am;
+
+    container another-container {
+        leaf type {
+            type string;
+            mandatory true;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT956/mainmodule.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT956/mainmodule.yang
new file mode 100644 (file)
index 0000000..4fa4166
--- /dev/null
@@ -0,0 +1,8 @@
+module mainmodule {
+    yang-version 1.1;
+    namespace "http://www.example.com/mainmodule";
+    prefix mm;
+
+    include sub-module-1;
+    include sub-module-2;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT956/sub-module-1.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT956/sub-module-1.yang
new file mode 100644 (file)
index 0000000..6e401c7
--- /dev/null
@@ -0,0 +1,18 @@
+submodule sub-module-1 {
+  yang-version 1.1;
+
+  belongs-to mainmodule {
+      prefix mm;
+  }
+
+  import another-module {
+      prefix am;
+  }
+
+  augment '/am:another-container' {
+      when "am:type = 'test'";
+      container first-augment {
+
+      }
+  }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT956/sub-module-2.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT956/sub-module-2.yang
new file mode 100644 (file)
index 0000000..3e6f036
--- /dev/null
@@ -0,0 +1,43 @@
+submodule sub-module-2 {
+    yang-version 1.1;
+
+    belongs-to mainmodule {
+        prefix mm;
+    }
+
+    import another-module {
+        prefix am;
+    }
+
+    include sub-module-1;
+
+    grouping dummygrouping {
+        leaf dummyleaf {
+            type string;
+            mandatory true;
+        }
+    }
+
+    grouping first {
+        leaf first-leaf {
+            type string;
+            mandatory true;
+        }
+    }
+
+    grouping second {
+        uses first;
+        leaf second-leaf {
+            type string;
+            mandatory true;
+        }
+    }
+
+    augment '/am:another-container/mm:first-augment' {
+        uses dummygrouping;
+    }
+
+    augment '/am:another-container/mm:first-augment' {
+        uses second;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT971/test.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT971/test.yang
new file mode 100644 (file)
index 0000000..c105d8a
--- /dev/null
@@ -0,0 +1,18 @@
+module test {
+    namespace test;
+    prefix test;
+    yang-version 1.1;
+
+    revision 2019-03-25;
+
+    container some-container {
+        leaf some-leaf {
+            description "Some string that ends with a backslash (with escape backslash too) \\";
+            type int16;
+        }
+        leaf some-other-leaf {
+            description "Some string after the double backslash";
+            type int32;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/YT983/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/YT983/foo.yang
new file mode 100644 (file)
index 0000000..a39a3f1
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    revision "2019-04-30";
+
+    extension ext;
+
+    leaf foo {
+        type enumeration {
+            enum "10" {
+                foo:ext;
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug1412/bug1412.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug1412/bug1412.yang
new file mode 100644 (file)
index 0000000..b85e057
--- /dev/null
@@ -0,0 +1,28 @@
+module bug1412 {
+    yang-version 1;
+    namespace 'urn:test:bug1412';
+    prefix "b1412";
+
+    import extensions {
+        prefix ext;
+        revision-date "2014-07-25";
+    }
+
+    revision "2014-07-25" {
+    }
+
+
+    container node {
+        ext:action hello {
+            ext:info "greeting";
+            description "say greeting";
+            ext:actionpoint entry {
+                ext:internal;
+            }
+            output {
+                uses params;
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug1412/extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug1412/extensions.yang
new file mode 100644 (file)
index 0000000..0a2c123
--- /dev/null
@@ -0,0 +1,28 @@
+module extensions {
+    yang-version 1;
+    namespace "urn:test:bug1412:ext:definitions";
+    prefix "ext";
+
+    revision "2014-07-25" {
+    }
+
+    extension action {
+        argument hello {
+
+        }
+    }
+
+    extension info {
+        argument string;
+    }
+
+    extension actionpoint {
+        argument entry {
+
+        }
+    }
+
+    extension internal {
+
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug1413/bug1413.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug1413/bug1413.yang
new file mode 100644 (file)
index 0000000..f2297f6
--- /dev/null
@@ -0,0 +1,23 @@
+module bug1413 {
+    yang-version 1;
+    namespace "odl:test:bug1413";
+    prefix "b1413";
+
+    import extensions {
+        prefix ext;
+        revision-date "2014-07-25";
+    }
+
+    revision "2014-07-25" {
+    }
+
+    extension info {
+        argument text {
+            yin-element true;
+            ext:arg-type {
+                type string;
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug1413/extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug1413/extensions.yang
new file mode 100644 (file)
index 0000000..679a113
--- /dev/null
@@ -0,0 +1,12 @@
+module extensions {
+    yang-version 1;
+    namespace "urn:test:bug1412:ext:definitions";
+    prefix "ext";
+
+    revision "2014-07-25" {
+    }
+
+    extension arg-type {
+
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug2291/bug2291-ext.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug2291/bug2291-ext.yang
new file mode 100644 (file)
index 0000000..f059a8d
--- /dev/null
@@ -0,0 +1,13 @@
+module "bug2291-ext" {
+    yang-version 1;
+    namespace "urn:test:bug:bug2291-ext";
+    prefix "bug2291-ext";
+
+    revision 2012-04-16 {
+    }
+
+    extension migration {
+        argument level;
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug2291/bug2291.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug2291/bug2291.yang
new file mode 100644 (file)
index 0000000..90962b4
--- /dev/null
@@ -0,0 +1,22 @@
+module bug2291 {
+    yang-version 1;
+    namespace "urn:test:bug:bug2291";
+    prefix "bug2291";
+
+    import bug2291-ext {
+        prefix bug2291-ext;
+    }
+
+    import ietf-inet-types {
+        prefix "inet";
+    }
+
+    revision 2012-04-16 {
+        description "Initial revision";
+        bug2291-ext:migration 1;
+    }
+
+    leaf test {
+        type string;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug2872/bug2872.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug2872/bug2872.yang
new file mode 100644 (file)
index 0000000..623ec44
--- /dev/null
@@ -0,0 +1,16 @@
+module bug2872 {
+    yang-version 1;
+    namespace "bug2872";
+    prefix bug2872;
+
+    revision 2016-06-08 {
+        description "Bug 2872 test model";
+    }
+
+    leaf bar {
+        type enumeration {
+            enum "value-one";
+            enum "value-two";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug3799/test_module.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug3799/test_module.yang
new file mode 100644 (file)
index 0000000..f0b30e3
--- /dev/null
@@ -0,0 +1,25 @@
+module test_module {\r
+\r
+    namespace "http://www.example.com/test";\r
+    prefix "test";\r
+\r
+    include "test_submodule";\r
+\r
+    organization "some organisation";\r
+\r
+    description\r
+        "Testing including a submodule with a different revision number";\r
+\r
+    revision 2014-06-15 {\r
+        description "some more changes";\r
+    }\r
+    revision 2013-06-15 {\r
+        description "some changes";\r
+    }\r
+\r
+    container some_container {\r
+\r
+        description\r
+                "some container";\r
+    }\r
+}\r
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug3799/test_submodule.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug3799/test_submodule.yang
new file mode 100644 (file)
index 0000000..346f139
--- /dev/null
@@ -0,0 +1,25 @@
+submodule test_submodule {\r
+\r
+    belongs-to "test_module" {\r
+        prefix "sub";\r
+    }\r
+\r
+    organization "some organisation";\r
+\r
+    revision 2015-05-05 {\r
+        description\r
+                "submodule for testing with grouping";\r
+    }\r
+\r
+    grouping foo {\r
+        leaf bar {\r
+            description "some description";\r
+            type uint64;\r
+        }\r
+    }\r
+\r
+    notification baz {\r
+        description "container using grouping";\r
+        uses foo;\r
+    }\r
+}\r
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug3859/bug3859.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug3859/bug3859.yang
new file mode 100644 (file)
index 0000000..1535645
--- /dev/null
@@ -0,0 +1,31 @@
+module reference-in-unknown {
+  namespace "urn:ref:unknown";
+  prefix "riu";
+
+  extension test-extension {
+    argument test;
+  }
+
+  riu:test-extension test {
+    container cont {
+      description
+        "This is just a plain text";
+
+      list mylist {
+        description
+          "This is a just another plain text";
+
+        reference "refers somewhere...";
+
+        leaf myleaf1 {
+          type string;
+        }
+
+        leaf myleaf2 {
+          type string;
+        }
+      }
+    }
+  }
+}
+
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug394-retest/bug394-ext.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug394-retest/bug394-ext.yang
new file mode 100644 (file)
index 0000000..ba4055d
--- /dev/null
@@ -0,0 +1,27 @@
+module bug394-ext {
+    yang-version 1;
+    namespace "urn:test:bug394-ext";
+    prefix "b394e";
+
+    revision "2014-03-04" {
+    }
+
+
+    extension info {
+        description "Takes string as argument. Provide short info message";
+        argument text {
+            yin-element true;
+        }
+    }
+
+    extension action {
+        description "Define new action.";
+        argument "show" {
+            yin-element "true";
+        }
+    }
+
+    extension cli-drop-node-name {
+            description "Drop node name";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug394-retest/bug394.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug394-retest/bug394.yang
new file mode 100644 (file)
index 0000000..849cdc7
--- /dev/null
@@ -0,0 +1,48 @@
+module bug394 {
+    yang-version 1;
+    namespace "urn:test:bug394";
+    prefix "b394";
+
+    import bug394-ext {
+        prefix ext;
+        revision-date 2014-03-04;
+    }
+
+    import ietf-yang-types {
+        prefix yang;
+        revision-date 2010-09-24;
+    }
+
+    revision "2014-03-04" {
+    }
+
+
+    container logrecords {
+        ext:info "Shows logrecords information";
+        ext:action show {
+
+            ext:info "Shows the log records";
+
+            input {
+                leaf from {
+                    type yang:date-and-time;
+                    default "2000-01-01T00:00:00-00:00";
+                    description "Shows the log records from the given time";
+                }
+                leaf to {
+                    type yang:date-and-time;
+                    default "2099-01-01T00:00:00-00:00";
+                    description "Shows the log records up to the given time";
+                }
+            }
+            output {
+                leaf data {
+                    ext:cli-drop-node-name;
+                    type string;
+                    description "Shows the log records according to the given input";
+                }
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug394-retest/ietf-yang-types@2010-09-24.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug394-retest/ietf-yang-types@2010-09-24.yang
new file mode 100644 (file)
index 0000000..51d9f8b
--- /dev/null
@@ -0,0 +1,396 @@
+ module ietf-yang-types {
+
+   namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+   prefix "yang";
+
+   organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+   contact
+    "WG Web:   <http://tools.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+
+     WG Chair: David Partain
+               <mailto:david.partain@ericsson.com>
+
+     WG Chair: David Kessens
+               <mailto:david.kessens@nsn.com>
+
+     Editor:   Juergen Schoenwaelder
+               <mailto:j.schoenwaelder@jacobs-university.de>";
+
+   description
+    "This module contains a collection of generally useful derived
+     YANG data types.
+
+     Copyright (c) 2010 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or without
+     modification, is permitted pursuant to, and subject to the license
+     terms contained in, the Simplified BSD License set forth in Section
+     4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6021; see
+     the RFC itself for full legal notices.";
+
+   revision 2010-09-24 {
+     description
+      "Initial revision.";
+     reference
+      "RFC 6021: Common YANG Data Types";
+   }
+
+   /*** collection of counter and gauge types ***/
+
+   typedef counter32 {
+     type uint32;
+     description
+      "The counter32 type represents a non-negative integer
+       that monotonically increases until it reaches a
+       maximum value of 2^32-1 (4294967295 decimal), when it
+       wraps around and starts increasing again from zero.
+
+       Counters have no defined 'initial' value, and thus, a
+       single value of a counter has (in general) no information
+       content.  Discontinuities in the monotonically increasing
+       value normally occur at re-initialization of the
+       management system, and at other times as specified in the
+       description of a schema node using this type.  If such
+       other times can occur, for example, the creation of
+       a schema node of type counter32 at times other than
+       re-initialization, then a corresponding schema node
+       should be defined, with an appropriate type, to indicate
+       the last discontinuity.
+
+       The counter32 type should not be used for configuration
+       schema nodes.  A default statement SHOULD NOT be used in
+       combination with the type counter32.
+
+       In the value set and its semantics, this type is equivalent
+       to the Counter32 type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef zero-based-counter32 {
+     type yang:counter32;
+     default "0";
+     description
+      "The zero-based-counter32 type represents a counter32
+       that has the defined 'initial' value zero.
+
+       A schema node of this type will be set to zero (0) on creation
+       and will thereafter increase monotonically until it reaches
+       a maximum value of 2^32-1 (4294967295 decimal), when it
+       wraps around and starts increasing again from zero.
+
+       Provided that an application discovers a new schema node
+       of this type within the minimum time to wrap, it can use the
+       'initial' value as a delta.  It is important for a management
+       station to be aware of this minimum time and the actual time
+       between polls, and to discard data if the actual time is too
+       long or there is no defined minimum time.
+
+       In the value set and its semantics, this type is equivalent
+       to the ZeroBasedCounter32 textual convention of the SMIv2.";
+     reference
+       "RFC 4502: Remote Network Monitoring Management Information
+                  Base Version 2";
+   }
+
+   typedef counter64 {
+     type uint64;
+     description
+      "The counter64 type represents a non-negative integer
+       that monotonically increases until it reaches a
+       maximum value of 2^64-1 (18446744073709551615 decimal),
+       when it wraps around and starts increasing again from zero.
+
+       Counters have no defined 'initial' value, and thus, a
+       single value of a counter has (in general) no information
+       content.  Discontinuities in the monotonically increasing
+       value normally occur at re-initialization of the
+       management system, and at other times as specified in the
+       description of a schema node using this type.  If such
+       other times can occur, for example, the creation of
+       a schema node of type counter64 at times other than
+       re-initialization, then a corresponding schema node
+       should be defined, with an appropriate type, to indicate
+       the last discontinuity.
+
+       The counter64 type should not be used for configuration
+       schema nodes.  A default statement SHOULD NOT be used in
+       combination with the type counter64.
+
+       In the value set and its semantics, this type is equivalent
+       to the Counter64 type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef zero-based-counter64 {
+     type yang:counter64;
+     default "0";
+     description
+      "The zero-based-counter64 type represents a counter64 that
+       has the defined 'initial' value zero.
+
+       A schema node of this type will be set to zero (0) on creation
+       and will thereafter increase monotonically until it reaches
+       a maximum value of 2^64-1 (18446744073709551615 decimal),
+       when it wraps around and starts increasing again from zero.
+
+       Provided that an application discovers a new schema node
+       of this type within the minimum time to wrap, it can use the
+       'initial' value as a delta.  It is important for a management
+       station to be aware of this minimum time and the actual time
+       between polls, and to discard data if the actual time is too
+       long or there is no defined minimum time.
+
+       In the value set and its semantics, this type is equivalent
+       to the ZeroBasedCounter64 textual convention of the SMIv2.";
+     reference
+      "RFC 2856: Textual Conventions for Additional High Capacity
+                 Data Types";
+   }
+
+   typedef gauge32 {
+     type uint32;
+     description
+      "The gauge32 type represents a non-negative integer, which
+       may increase or decrease, but shall never exceed a maximum
+       value, nor fall below a minimum value.  The maximum value
+       cannot be greater than 2^32-1 (4294967295 decimal), and
+       the minimum value cannot be smaller than 0.  The value of
+       a gauge32 has its maximum value whenever the information
+       being modeled is greater than or equal to its maximum
+       value, and has its minimum value whenever the information
+       being modeled is smaller than or equal to its minimum value.
+       If the information being modeled subsequently decreases
+       below (increases above) the maximum (minimum) value, the
+       gauge32 also decreases (increases).
+
+       In the value set and its semantics, this type is equivalent
+       to the Gauge32 type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef gauge64 {
+     type uint64;
+     description
+      "The gauge64 type represents a non-negative integer, which
+       may increase or decrease, but shall never exceed a maximum
+       value, nor fall below a minimum value.  The maximum value
+       cannot be greater than 2^64-1 (18446744073709551615), and
+       the minimum value cannot be smaller than 0.  The value of
+       a gauge64 has its maximum value whenever the information
+       being modeled is greater than or equal to its maximum
+       value, and has its minimum value whenever the information
+       being modeled is smaller than or equal to its minimum value.
+       If the information being modeled subsequently decreases
+       below (increases above) the maximum (minimum) value, the
+       gauge64 also decreases (increases).
+
+       In the value set and its semantics, this type is equivalent
+       to the CounterBasedGauge64 SMIv2 textual convention defined
+       in RFC 2856";
+     reference
+      "RFC 2856: Textual Conventions for Additional High Capacity
+                 Data Types";
+   }
+
+   /*** collection of identifier related types ***/
+
+   typedef object-identifier {
+     type string {
+       pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+             + '(\.(0|([1-9]\d*)))*';
+     }
+     description
+      "The object-identifier type represents administratively
+       assigned names in a registration-hierarchical-name tree.
+
+       Values of this type are denoted as a sequence of numerical
+       non-negative sub-identifier values.  Each sub-identifier
+       value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers
+       are separated by single dots and without any intermediate
+       whitespace.
+
+       The ASN.1 standard restricts the value space of the first
+       sub-identifier to 0, 1, or 2.  Furthermore, the value space
+       of the second sub-identifier is restricted to the range
+       0 to 39 if the first sub-identifier is 0 or 1.  Finally,
+       the ASN.1 standard requires that an object identifier
+       has always at least two sub-identifier.  The pattern
+       captures these restrictions.
+
+       Although the number of sub-identifiers is not limited,
+       module designers should realize that there may be
+       implementations that stick with the SMIv2 limit of 128
+       sub-identifiers.
+
+       This type is a superset of the SMIv2 OBJECT IDENTIFIER type
+       since it is not restricted to 128 sub-identifiers.  Hence,
+       this type SHOULD NOT be used to represent the SMIv2 OBJECT
+       IDENTIFIER type, the object-identifier-128 type SHOULD be
+       used instead.";
+     reference
+      "ISO9834-1: Information technology -- Open Systems
+       Interconnection -- Procedures for the operation of OSI
+       Registration Authorities: General procedures and top
+       arcs of the ASN.1 Object Identifier tree";
+   }
+
+
+
+
+   typedef object-identifier-128 {
+     type object-identifier {
+       pattern '\d*(\.\d*){1,127}';
+     }
+     description
+      "This type represents object-identifiers restricted to 128
+       sub-identifiers.
+
+       In the value set and its semantics, this type is equivalent
+       to the OBJECT IDENTIFIER type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   /*** collection of date and time related types ***/
+
+   typedef date-and-time {
+     type string {
+       pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+             + '(Z|[\+\-]\d{2}:\d{2})';
+     }
+     description
+      "The date-and-time type is a profile of the ISO 8601
+       standard for representation of dates and times using the
+       Gregorian calendar.  The profile is defined by the
+       date-time production in Section 5.6 of RFC 3339.
+
+       The date-and-time type is compatible with the dateTime XML
+       schema type with the following notable exceptions:
+
+       (a) The date-and-time type does not allow negative years.
+
+       (b) The date-and-time time-offset -00:00 indicates an unknown
+           time zone (see RFC 3339) while -00:00 and +00:00 and Z all
+           represent the same time zone in dateTime.
+
+       (c) The canonical format (see below) of data-and-time values
+           differs from the canonical format used by the dateTime XML
+           schema type, which requires all times to be in UTC using the
+           time-offset 'Z'.
+
+       This type is not equivalent to the DateAndTime textual
+       convention of the SMIv2 since RFC 3339 uses a different
+       separator between full-date and full-time and provides
+       higher resolution of time-secfrac.
+
+       The canonical format for date-and-time values with a known time
+       zone uses a numeric time zone offset that is calculated using
+       the device's configured known offset to UTC time.  A change of
+       the device's offset to UTC time will cause date-and-time values
+       to change accordingly.  Such changes might happen periodically
+       in case a server follows automatically daylight saving time
+       (DST) time zone offset changes.  The canonical format for
+       date-and-time values with an unknown time zone (usually referring
+       to the notion of local time) uses the time-offset -00:00.";
+     reference
+      "RFC 3339: Date and Time on the Internet: Timestamps
+       RFC 2579: Textual Conventions for SMIv2
+       XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
+   }
+
+   typedef timeticks {
+     type uint32;
+     description
+      "The timeticks type represents a non-negative integer that
+       represents the time, modulo 2^32 (4294967296 decimal), in
+       hundredths of a second between two epochs.  When a schema
+       node is defined that uses this type, the description of
+       the schema node identifies both of the reference epochs.
+
+       In the value set and its semantics, this type is equivalent
+       to the TimeTicks type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef timestamp {
+     type yang:timeticks;
+     description
+      "The timestamp type represents the value of an associated
+       timeticks schema node at which a specific occurrence happened.
+       The specific occurrence must be defined in the description
+       of any schema node defined using this type.  When the specific
+       occurrence occurred prior to the last time the associated
+       timeticks attribute was zero, then the timestamp value is
+       zero.  Note that this requires all timestamp values to be
+       reset to zero when the value of the associated timeticks
+       attribute reaches 497+ days and wraps around to zero.
+
+       The associated timeticks schema node must be specified
+       in the description of any schema node using this type.
+
+       In the value set and its semantics, this type is equivalent
+       to the TimeStamp textual convention of the SMIv2.";
+     reference
+      "RFC 2579: Textual Conventions for SMIv2";
+   }
+
+   /*** collection of generic address types ***/
+
+   typedef phys-address {
+     type string {
+       pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+     }
+     description
+      "Represents media- or physical-level addresses represented
+       as a sequence octets, each octet represented by two hexadecimal
+       numbers.  Octets are separated by colons.  The canonical
+       representation uses lowercase characters.
+
+       In the value set and its semantics, this type is equivalent
+       to the PhysAddress textual convention of the SMIv2.";
+     reference
+      "RFC 2579: Textual Conventions for SMIv2";
+   }
+
+   typedef mac-address {
+     type string {
+       pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+     }
+     description
+      "The mac-address type represents an IEEE 802 MAC address.
+       The canonical representation uses lowercase characters.
+
+       In the value set and its semantics, this type is equivalent
+       to the MacAddress textual convention of the SMIv2.";
+     reference
+      "IEEE 802: IEEE Standard for Local and Metropolitan Area
+                 Networks: Overview and Architecture
+       RFC 2579: Textual Conventions for SMIv2";
+   }
+
+   /*** collection of XML specific types ***/
+
+   typedef xpath1.0 {
+     type string;
+     description
+      "This type represents an XPATH 1.0 expression.
+
+       When a schema node is defined that uses this type, the
+       description of the schema node MUST specify the XPath
+       context in which the XPath expression is evaluated.";
+     reference
+      "XPATH: XML Path Language (XPath) Version 1.0";
+   }
+
+ }
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug394/bug394-ext.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug394/bug394-ext.yang
new file mode 100644 (file)
index 0000000..9af32ad
--- /dev/null
@@ -0,0 +1,21 @@
+module bug394-ext {
+    yang-version 1;
+    namespace "urn:test:bug394-ext";
+    prefix "b394e";
+
+    revision "2014-03-04" {
+    }
+
+
+    extension info {
+        description "Takes string as argument. Provide short info message";
+    }
+
+    extension action {
+        description "Define new action.";
+        argument "show" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug394/bug394.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug394/bug394.yang
new file mode 100644 (file)
index 0000000..470eba3
--- /dev/null
@@ -0,0 +1,44 @@
+module bug394 {
+    yang-version 1;
+    namespace "urn:test:bug394";
+    prefix "b394";
+
+    import bug394-ext {
+        prefix ext;
+        revision-date 2014-03-04;
+    }
+
+    revision "2014-03-04" {
+    }
+
+
+    container logrecords {
+        ext:info "Shows logrecords information";
+        ext:action show {
+
+            info "Shows the log records";
+            actionpoint logging-logrecord-show;
+
+            input {
+                leaf from {
+                    type yang:date-and-time;
+                    default 2000-01-01T00:00:00-00:00;
+                    description "Shows the log records from the given time";
+                }
+                leaf to {
+                    type yang:date-and-time;
+                    default 2099-01-01T00:00:00-00:00;
+                    description "Shows the log records up to the given time";
+                }
+            }
+            output {
+                leaf data {
+                    cli-drop-node-name;
+                    type string;
+                    description "Shows the log records according to the given input";
+                }
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4079/bug4079-test.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4079/bug4079-test.yang
new file mode 100644 (file)
index 0000000..a93b254
--- /dev/null
@@ -0,0 +1,14 @@
+module bug4079-test {
+
+    yang-version "1";
+    namespace "urn:opendaylight.org/bug4079-test";
+    prefix "b4079t";
+
+    container test-container {
+        leaf test-leaf {
+            type string {
+                pattern "(\p{IsBasicLatin}|\p{IsLatin-1Supplement})*";
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4231/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4231/foo.yang
new file mode 100644 (file)
index 0000000..d994c03
--- /dev/null
@@ -0,0 +1,38 @@
+module foo {
+    namespace "foo";
+    prefix "foo";
+    yang-version 1;
+
+    revision 2015-09-02 {
+        description "Test";
+    }
+
+    grouping my-grouping {
+        container my-container-in-grouping {
+        }
+    }
+
+    augment /augment-target {
+        uses my-grouping {
+            augment my-container-in-grouping {
+                leaf-list l2 {
+                    type string;
+                    ordered-by "user";
+                    foo:bar "argument";
+                    foo:baz "$X(/a/a/a/@@)";
+                }
+            }
+        }
+    }
+
+    container augment-target {
+    }
+
+    extension bar {
+        argument "arg";
+    }
+
+    extension baz {
+        argument "arg";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4410/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4410/foo.yang
new file mode 100644 (file)
index 0000000..a733b46
--- /dev/null
@@ -0,0 +1,13 @@
+module foo {
+    namespace "foo";
+    prefix "foo";
+    yang-version 1;
+
+    typedef foo {
+        type bar;
+    }
+
+    typedef bar {
+        type foo;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4456/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4456/foo.yang
new file mode 100644 (file)
index 0000000..2ff2010
--- /dev/null
@@ -0,0 +1,35 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    extension a {
+        foo:b {
+            foo:c;
+        }
+    }
+
+    extension b {
+        foo:c {
+            foo:a;
+        }
+    }
+
+    extension c {
+        foo:a {
+            foo:b;
+        }
+    }
+
+    extension r {
+        foo:r {
+            foo:r2;
+        }
+    }
+
+    extension r2 {
+        foo:r2 {
+            foo:r;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4459/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4459/foo.yang
new file mode 100644 (file)
index 0000000..31ee8ea
--- /dev/null
@@ -0,0 +1,27 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1;
+
+    extension e {
+        argument a {
+            foo:e2 a {
+                type enumeration {
+                    enum "?";
+                    enum "*";
+                }
+            }
+        }
+    }
+
+    extension e2 {
+        argument name;
+    }
+
+    leaf my-leaf {
+        type enumeration {
+            enum "?-&-(-)-?-?-&";
+            enum "*";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4610/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4610/bar.yang
new file mode 100644 (file)
index 0000000..6eb5139
--- /dev/null
@@ -0,0 +1,18 @@
+module bar {
+    yang-version 1;
+    namespace "bar";
+    prefix bar;
+
+    revision 2015-12-12 {
+        description "Bug 4610 test model.";
+    }
+
+    grouping g1 {
+        container c1 {
+        }
+    }
+
+    grouping g2 {
+        uses g1;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4610/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4610/foo.yang
new file mode 100644 (file)
index 0000000..63828c3
--- /dev/null
@@ -0,0 +1,19 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix foo;
+
+    import bar { prefix bar; revision-date 2015-12-12; }
+
+    revision 2015-12-12 {
+        description "Bug 4610 test model";
+    }
+
+    grouping g3 {
+        uses bar:g2;
+    }
+
+    container root {
+        uses g3;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4623/extension-def.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4623/extension-def.yang
new file mode 100644 (file)
index 0000000..a7521e4
--- /dev/null
@@ -0,0 +1,9 @@
+module extension-def {
+    namespace "urn:simple.extension.typedefs";
+    prefix "ext";
+
+    extension unknown {
+        argument "ext-arg";
+        description "Extension with single argument definition.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4623/string-with-ext.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4623/string-with-ext.yang
new file mode 100644 (file)
index 0000000..8aa0b30
--- /dev/null
@@ -0,0 +1,30 @@
+module types {
+    namespace "urn:custom.types.demo";
+    prefix "types";
+
+    import extension-def { prefix "ext"; }
+
+    leaf leaf-unknown-length-pattern {
+        type string {
+            ext:unknown "unknown";
+            length "2..10";
+            pattern "[0-9a-fA-F]";
+        }
+    }
+
+    leaf leaf-length-unknown-pattern {
+        type string {
+            length "2..10";
+            ext:unknown "unknown";
+            pattern "[0-9a-fA-F]";
+        }
+    }
+
+    leaf leaf-length-pattern-unknown {
+        type string {
+            length "2..10";
+            pattern "[0-9a-fA-F]";
+            ext:unknown "unknown";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4933/correct/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4933/correct/bar.yang
new file mode 100644 (file)
index 0000000..19746fc
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace bar-ns;
+    prefix bar;
+
+    container my-container {
+        leaf-list my-leaf-list {
+            type string;
+            max-elements 5;
+            must "0 != 1";
+        }
+    }
+
+    container my-container2 {
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4933/correct/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4933/correct/foo.yang
new file mode 100644 (file)
index 0000000..0ae8d61
--- /dev/null
@@ -0,0 +1,31 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix foo;
+
+    import bar {
+        prefix bar;
+    }
+
+    deviation /bar:my-container/bar:my-leaf-list {
+        deviate add {
+            min-elements 1;
+        }
+    }
+
+    deviation /bar:my-container/bar:my-leaf-list {
+        deviate replace {
+            max-elements 10;
+        }
+    }
+
+    deviation /bar:my-container/bar:my-leaf-list {
+        deviate delete {
+            must "0 != 1";
+        }
+    }
+
+    deviation /bar:my-container2 {
+        deviate not-supported;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4933/incorrect/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4933/incorrect/bar.yang
new file mode 100644 (file)
index 0000000..19746fc
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace bar-ns;
+    prefix bar;
+
+    container my-container {
+        leaf-list my-leaf-list {
+            type string;
+            max-elements 5;
+            must "0 != 1";
+        }
+    }
+
+    container my-container2 {
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4933/incorrect/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug4933/incorrect/foo.yang
new file mode 100644 (file)
index 0000000..51ff1fe
--- /dev/null
@@ -0,0 +1,31 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix foo;
+
+    import bar {
+        prefix bar;
+    }
+
+    deviation /bar:my-container/bar:my-leaf-list {
+        deviate add {
+            min-elements 1;
+        }
+    }
+
+    deviation /bar:my-container/bar:my-leaf-list {
+        deviate replace {
+            max-elements 10;
+        }
+    }
+
+    deviation /bar:my-container/bar:my-leaf-list {
+        deviate delete {
+            must "0 != 1";
+        }
+    }
+
+    deviation /bar:my-container2 {
+        deviate not_supported;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5059/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5059/foo.yang
new file mode 100644 (file)
index 0000000..3a35eec
--- /dev/null
@@ -0,0 +1,33 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-01-25 {
+        description "test";
+    }
+
+    grouping my-grouping {
+        container my-container {
+            foo:e2 "e2";
+        }
+        foo:e1 "e1";
+    }
+
+    container root {
+        uses my-grouping {
+            refine e1 {
+                description "new description for e1";
+            }
+            refine my-container/e2 {
+                description "new description for e2";
+            }
+        }
+    }
+
+    extension e1 {
+        argument name;
+    }
+    extension e2 {
+        argument name;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5101/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5101/foo.yang
new file mode 100644 (file)
index 0000000..206ec74
--- /dev/null
@@ -0,0 +1,21 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-01-29 {
+        description "test";
+    }
+
+    grouping my-grouping {
+        status deprecated;
+        container my-container {
+            status deprecated;
+        }
+    }
+
+    container root {
+        uses my-grouping;
+    }
+
+    uses my-grouping;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5200/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5200/foo.yang
new file mode 100644 (file)
index 0000000..d007127
--- /dev/null
@@ -0,0 +1,32 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-05-05 {
+        description "Bug 5200 test.";
+    }
+
+    container root {
+        leaf my-leaf {
+            type string {
+                length "1..255" {
+                    error-app-tag "lenght constraint error-app-tag";
+                    error-message "lenght constraint error-app-message";
+                }
+                pattern "[a-z]+" {
+                    error-app-tag "pattern constraint error-app-tag";
+                    error-message "pattern constraint error-app-message";
+                }
+            }
+        }
+        leaf my-leaf-2 {
+            type int32 {
+                range "1..100" {
+                    error-app-tag "range constraint error-app-tag";
+                    error-message "range constraint error-app-message";
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-1/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-1/bar.yang
new file mode 100644 (file)
index 0000000..998063a
--- /dev/null
@@ -0,0 +1,17 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import foo { prefix foo; revision-date 2016-03-04; }
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    augment "/foo:root" {
+        container presence-container {
+            presence "presence-container";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-1/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-1/foo.yang
new file mode 100644 (file)
index 0000000..793790d
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-1/sub-bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-1/sub-bar.yang
new file mode 100644 (file)
index 0000000..4ffccd0
--- /dev/null
@@ -0,0 +1,14 @@
+submodule sub-bar {
+    belongs-to bar {
+        prefix bar;
+    }
+
+    import foo { prefix foo; revision-date 2016-03-04; }
+
+    augment "/foo:root/bar:presence-container" {
+        leaf mandatory-leaf {
+            type string;
+            mandatory true;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-2/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-2/bar.yang
new file mode 100644 (file)
index 0000000..32b8d38
--- /dev/null
@@ -0,0 +1,22 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import foo { prefix foo; revision-date 2016-03-04; }
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    augment "/foo:root" {
+        container presence-container {
+            presence "presence-container";
+        }
+    }
+
+    augment "/foo:root/bar:presence-container" {
+        container non-presence-container {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-2/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-2/foo.yang
new file mode 100644 (file)
index 0000000..793790d
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-2/sub-bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-2/sub-bar.yang
new file mode 100644 (file)
index 0000000..4e6b7b6
--- /dev/null
@@ -0,0 +1,14 @@
+submodule sub-bar {
+    belongs-to bar {
+        prefix bar;
+    }
+
+    import foo { prefix foo; revision-date 2016-03-04; }
+
+    augment "/foo:root/bar:presence-container/bar:non-presence-container" {
+        leaf mandatory-leaf {
+            type string;
+            mandatory true;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-3/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-3/bar.yang
new file mode 100644 (file)
index 0000000..ba83b97
--- /dev/null
@@ -0,0 +1,29 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import foo { prefix foo; revision-date 2016-03-04; }
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    augment "/foo:root" {
+        container presence-container {
+            presence "presence-container";
+        }
+    }
+
+    augment "/foo:root/bar:presence-container" {
+        container non-presence-container {
+        }
+    }
+
+    augment "/foo:root/bar:presence-container/bar:non-presence-container" {
+        leaf mandatory-leaf {
+            type string;
+            mandatory true;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-3/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-3/foo.yang
new file mode 100644 (file)
index 0000000..793790d
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-4/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/correct/case-4/foo.yang
new file mode 100644 (file)
index 0000000..848b47f
--- /dev/null
@@ -0,0 +1,24 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    container root {
+    }
+
+    augment "/root" {
+        container non-presence-container {
+        }
+    }
+
+    augment "/root/non-presence-container" {
+        leaf mandatory-leaf {
+            type string;
+            mandatory true;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-1/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-1/bar.yang
new file mode 100644 (file)
index 0000000..756c59f
--- /dev/null
@@ -0,0 +1,16 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import foo { prefix foo; revision-date 2016-03-04; }
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    augment "/foo:root" {
+        container non-presence-container {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-1/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-1/foo.yang
new file mode 100644 (file)
index 0000000..793790d
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-1/sub-bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-1/sub-bar.yang
new file mode 100644 (file)
index 0000000..caf317f
--- /dev/null
@@ -0,0 +1,14 @@
+submodule sub-bar {
+    belongs-to bar {
+        prefix bar;
+    }
+
+    import foo { prefix foo; revision-date 2016-03-04; }
+
+    augment "/foo:root/bar:non-presence-container" {
+        leaf mandatory-leaf {
+            type string;
+            mandatory true;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-2/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-2/bar.yang
new file mode 100644 (file)
index 0000000..a47749c
--- /dev/null
@@ -0,0 +1,18 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import foo { prefix foo; revision-date 2016-03-04; }
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    augment "/foo:root/foo:presence-container" {
+        leaf mandatory-leaf {
+            type string;
+            mandatory true;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-2/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-2/foo.yang
new file mode 100644 (file)
index 0000000..fd2ce12
--- /dev/null
@@ -0,0 +1,18 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    container root {
+    }
+
+    augment "/root" {
+        container presence-container {
+            presence "presence-container";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-3/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-3/bar.yang
new file mode 100644 (file)
index 0000000..c14830c
--- /dev/null
@@ -0,0 +1,16 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import foo { prefix foo; revision-date 2016-03-04; }
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    augment "/foo:root/foo:presence-container" {
+        container non-presence-container {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-3/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-3/foo.yang
new file mode 100644 (file)
index 0000000..fd2ce12
--- /dev/null
@@ -0,0 +1,18 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    revision "2016-03-04" {
+        description "test";
+    }
+
+    container root {
+    }
+
+    augment "/root" {
+        container presence-container {
+            presence "presence-container";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-3/sub-bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5335/incorrect/case-3/sub-bar.yang
new file mode 100644 (file)
index 0000000..ce22474
--- /dev/null
@@ -0,0 +1,14 @@
+submodule sub-bar {
+    belongs-to bar {
+        prefix bar;
+    }
+
+    import foo { prefix foo; revision-date 2016-03-04; }
+
+    augment "/foo:root/foo:presence-container/bar:non-presence-container" {
+        leaf mandatory-leaf {
+            type string;
+            mandatory true;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5396/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5396/foo.yang
new file mode 100644 (file)
index 0000000..ca419ee
--- /dev/null
@@ -0,0 +1,28 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix "foo";
+
+    container root {
+        leaf my-leaf2 {
+            type my-type;
+        }
+    }
+
+    typedef my-type {
+        type union {
+            type string {
+                pattern "dp[0-9]+o[0-9]+(d[0-9]+)?";
+            }
+            type string {
+                pattern "dp[0-9]+s[0-9]+(f[0-9]+)?(d[0-9]+)?";
+            }
+            type string {
+                pattern "dp[0-9]+(P[0-9]+)?p[0-9]{1,3}s[0-9]{1,3}(f[0-9]+)?(d[0-9]+)?";
+            }
+            type string {
+                pattern "dp[0-9]+p[0-9]+p[0-9]+";
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5410/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5410/foo.yang
new file mode 100644 (file)
index 0000000..8c47a13
--- /dev/null
@@ -0,0 +1,10 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    leaf leaf-with-pattern {
+        type string {
+            pattern "$0$.*|$1$[a-zA-Z0-9./]{1,8}$[a-zA-Z0-9./]{22}|$5$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{43}|$6$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{86}";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5437/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5437/foo.yang
new file mode 100644 (file)
index 0000000..9d30edc
--- /dev/null
@@ -0,0 +1,47 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    revision "2016-03-01" {
+        description "test";
+    }
+
+    grouping grp {
+        container con-grp {
+            leaf l {
+                type int16;
+            }
+            leaf leaf-ref {
+                type leafref {
+                    path "../../con2/l2";
+                }
+            }
+        }
+    }
+
+    container root {
+        uses grp;
+        container con2 {
+            leaf l2 {
+                type binary;
+            }
+        }
+    }
+
+    augment "/root" {
+        leaf leaf-ref-2 {
+            type leaf-ref-type2;
+        }
+    }
+
+    typedef leaf-ref-type2 {
+        type leaf-ref-type;
+    }
+
+    typedef leaf-ref-type {
+        type leafref {
+            path "../con-grp/l";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5481/module1.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5481/module1.yang
new file mode 100644 (file)
index 0000000..5ded8cf
--- /dev/null
@@ -0,0 +1,18 @@
+module module1 {
+    yang-version 1;
+
+    namespace "http://example.com/module1";
+    prefix "module1";
+
+    description "Module 1";
+
+    revision "2016-03-09" {
+        description "Initial version.";
+    }
+
+    container top {
+        leaf top-leaf {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5481/module2.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5481/module2.yang
new file mode 100644 (file)
index 0000000..f6b1fa3
--- /dev/null
@@ -0,0 +1,28 @@
+module module2 {
+    yang-version 1;
+
+    namespace "http://example.com/module2";
+    prefix "module2";
+
+    import module1 { prefix "module1"; }
+
+    description "Module 2";
+
+    revision "2016-03-09" {
+        description "Initial version.";
+    }
+
+    augment "module1:top" {
+        when "module1:top = 'extended'";
+        description "text";
+        status deprecated;
+        reference "ref";
+        leaf extended-leaf {
+            when "module1:top = 'extended'";
+            description "text";
+            status deprecated;
+            reference "ref";
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5518/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5518/foo.yang
new file mode 100644 (file)
index 0000000..dbe89e0
--- /dev/null
@@ -0,0 +1,8 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    container root {
+        must "not(deref(.)/../same-pass)";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5550/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5550/foo.yang
new file mode 100644 (file)
index 0000000..3ad0916
--- /dev/null
@@ -0,0 +1,28 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    revision "2016-03-18" {
+        description "test";
+    }
+
+    grouping grouping-1 {
+        container container-in-grouping {
+        }
+    }
+
+    container root {
+    }
+
+    augment "/root" {
+        uses grouping-1 {
+            augment "container-in-grouping" {
+                leaf leaf-1 {
+                    type string;
+                    mandatory true;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5693/foo.yin b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5693/foo.yin
new file mode 100644 (file)
index 0000000..239e7dd
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:foo="foo" name="foo">
+    <yang-version value="1"></yang-version>
+    <namespace uri="foo"></namespace>
+    <prefix value="foo"></prefix>
+    <organization>
+        <text></text>
+    </organization>
+    <contact>
+        <text></text>
+    </contact>
+    <revision date="2016-04-01"></revision>
+    <feature name="test-input-stream-not-closed">
+        <description>
+            <text>
+                Presence of this feature in Module indicates that parsing of this file was successful.
+                Input stream was not closed after completing the first phase of parsing, so
+                statements defined in the last phase (like feature) were successfully parsed.
+            </text>
+        </description>
+    </feature>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5712/bad.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5712/bad.yang
new file mode 100644 (file)
index 0000000..21995ac
--- /dev/null
@@ -0,0 +1,15 @@
+module bad {
+    namespace "urn:opendaylight:bad";
+    prefix "bad";
+
+    revision 2016-04-11 {
+        description "Initial revision";
+    }
+    typedef thing {
+        type uint32;
+    }
+    typedef thing2 {
+        type bad:thing; // Error on this line
+        //type thing;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5884/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5884/foo.yang
new file mode 100644 (file)
index 0000000..5ae1bd6
--- /dev/null
@@ -0,0 +1,78 @@
+module foo {
+    namespace "urn:yang.foo";
+    prefix "foo";
+
+    revision "2016-01-01";
+
+    container main-container {
+        uses test-grouping {
+            augment "test/test-choice" {
+                leaf short-aug-uses-inside {
+                    type string;
+                }
+            }
+
+            augment "test/test-choice" {
+                case normal-aug-uses-inside {
+                    leaf normal-aug-uses-inside {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+    grouping test-grouping {
+        container test {
+            choice test-choice {
+                leaf short {
+                    type string;
+                }
+                case normal {
+                    leaf normal {
+                        type string;
+                    }
+                }
+            }
+        }
+    }
+
+    choice test-choice {
+        leaf short {
+            type string;
+        }
+        case normal {
+            leaf normal {
+                type string;
+            }
+        }
+    }
+
+    augment "/test-choice" {
+        leaf short-aug-uses {
+            type string;
+        }
+    }
+
+    augment "/test-choice" {
+        case normal-aug-uses {
+            leaf normal-aug-uses {
+                type string;
+            }
+        }
+    }
+
+    augment "/main-container/test/test-choice" {
+        leaf short-aug-uses-outside {
+            type string;
+        }
+    }
+
+    augment "/main-container/test/test-choice" {
+        case normal-aug-uses-outside {
+            leaf normal-aug-uses-outside {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5899/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5899/foo.yang
new file mode 100644 (file)
index 0000000..f302af2
--- /dev/null
@@ -0,0 +1,32 @@
+module foo {
+    yang-version 1;
+
+    namespace "org.namespace.test.foo";
+    prefix "foo";
+
+    description "foo";
+
+    revision "2016-01-01" {
+        description "Initial version.";
+    }
+
+    grouping test-grouping {
+        leaf test-leaf {
+            type string;
+        }
+    }
+
+    container test-container {
+        uses test-grouping {
+            refine test-leaf {
+                description "refined description";
+                must "test-leaf != 'not test message 1'" {
+                    error-message "not test message 1";
+                }
+                must "test-leaf != 'not test message 2'" {
+                    error-message "not test message 2";
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5942/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5942/foo.yang
new file mode 100644 (file)
index 0000000..006277d
--- /dev/null
@@ -0,0 +1,25 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    revision "2016-06-02";
+
+    grouping grp {
+        container bar;
+    }
+
+    container root {
+        uses grp {
+            description "uses description";
+            reference "uses reference";
+            status "deprecated";
+            when "0!=1";
+            foo:e "argument";
+        }
+    }
+
+    extension e {
+        argument name;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5946/foo-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5946/foo-invalid.yang
new file mode 100644 (file)
index 0000000..c5cdf73
--- /dev/null
@@ -0,0 +1,20 @@
+module foo-invalid {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    revision 2016-05-26 {
+        description "test";
+    }
+
+    list simple-unique {
+        key "k";
+        leaf k {
+            type string;
+        }
+        unique "/simple-unique/l1";
+        leaf l1 {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5946/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug5946/foo.yang
new file mode 100644 (file)
index 0000000..28abb46
--- /dev/null
@@ -0,0 +1,56 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    revision 2016-05-26 {
+        description "test";
+    }
+
+    list without-unique {
+        key "k";
+        leaf k {
+            type string;
+        }
+    }
+
+    list simple-unique {
+        key "k";
+        leaf k {
+            type string;
+        }
+        unique "l1 c/l3";
+        leaf l1 {
+            type string;
+        }
+        leaf l2 {
+            type string;
+        }
+        container c {
+            leaf l3 {
+                type string;
+            }
+        }
+    }
+
+    list multiple-unique {
+        key "k";
+        leaf k {
+            type string;
+        }
+        unique "l1 c/l3";
+        unique "c/l3 l2";
+        unique "l1 l2";
+        leaf l1 {
+            type string;
+        }
+        leaf l2 {
+            type string;
+        }
+        container c {
+            leaf l3 {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6131/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6131/foo.yang
new file mode 100644 (file)
index 0000000..acf8845
--- /dev/null
@@ -0,0 +1,2 @@
+container foo {
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6150/aug-first.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6150/aug-first.yang
new file mode 100644 (file)
index 0000000..faeb5b3
--- /dev/null
@@ -0,0 +1,21 @@
+module aug-first {
+
+  namespace "odl:test:bug6150:aug-first";
+  prefix "af";
+  revision 2016-11-10;
+
+  import target {
+    prefix tf;
+  }
+  import target {
+    prefix ts;
+  }
+
+  // With Bug present, this fails because tf got overwritten.
+  augment "/tf:target" {
+    leaf added-leaf {
+      type string;
+    }
+  }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6150/aug-second.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6150/aug-second.yang
new file mode 100644 (file)
index 0000000..5551af5
--- /dev/null
@@ -0,0 +1,21 @@
+module aug-second {
+
+  namespace "odl:test:bug6150:aug-second";
+  prefix "as";
+  revision 2016-11-10;
+
+  import target {
+    prefix tf;
+  }
+  import target {
+    prefix ts;
+  }
+
+  // With Bug present, this works just because tf is not involved.
+  augment "/ts:target" {
+    leaf added-leaf {
+      type string;
+    }
+  }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6150/target.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6150/target.yang
new file mode 100644 (file)
index 0000000..157bf38
--- /dev/null
@@ -0,0 +1,11 @@
+module target {
+
+  namespace "odl:test:bug6150:target";
+  prefix "t";
+  revision 2016-11-10;
+
+  container target {
+    presence "Augmentations go here.";
+  }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6180/double-quotes-single-inside.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6180/double-quotes-single-inside.yang
new file mode 100644 (file)
index 0000000..6745046
--- /dev/null
@@ -0,0 +1,19 @@
+module double-quotes {
+    namespace "foo";
+    prefix "foo";
+    revision 2016-07-11;
+
+    typedef double-quoted {
+        type string {
+            pattern "'.*'";
+        }
+    }
+
+    leaf foo {
+        type string;
+    }
+
+    container bar {
+        when "/foo != 'bar'";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6180/double-quotes.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6180/double-quotes.yang
new file mode 100644 (file)
index 0000000..030bb6e
--- /dev/null
@@ -0,0 +1,19 @@
+module double-quotes {
+    namespace "foo";
+    prefix "foo";
+    revision 2016-07-11;
+
+    typedef double-quoted {
+        type string {
+            pattern "\".*\"";
+        }
+    }
+
+    leaf foo {
+        type string;
+    }
+
+    container bar {
+        when "/foo != \"bar\"";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6180/single-quotes.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6180/single-quotes.yang
new file mode 100644 (file)
index 0000000..3f7ddf2
--- /dev/null
@@ -0,0 +1,18 @@
+module single-quotes {
+    namespace "foo";
+    prefix "foo";
+    revision 2016-07-11;
+
+    typedef double-quoted {
+        type string {
+            pattern '".*"';
+        }
+    }
+
+    leaf foo {
+        type string;
+    }
+    container bar {
+        when '/foo != "bar"';
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6180/string-test.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6180/string-test.yang
new file mode 100644 (file)
index 0000000..ae1e15a
--- /dev/null
@@ -0,0 +1,8 @@
+module string-test {
+    namespace "test";
+    prefix test;
+
+    description "    1. this text contains \"string enclosed in double quotes\" and special characters: \\,\n,\t      " +
+                "    2. this text contains \"string enclosed in double quotes\" and special characters: \\,\n,
+,                     3. this text contains \"string enclosed in double quotes\" and special characters: \\,\n,\t      ";
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6183/yang10/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6183/yang10/foo.yang
new file mode 100644 (file)
index 0000000..1b1961b
--- /dev/null
@@ -0,0 +1,62 @@
+module foo {
+    namespace "foo";
+    prefix "foo";
+
+    container before {
+    }
+
+    choice my-choice {
+        container implicit-case-container {
+            container declared-container-in-case-container {
+                leaf declared-leaf {
+                    type empty;
+                }
+            }
+            leaf declared-leaf-in-case-container {
+                type empty;
+            }
+        }
+        case declared-case-one {
+            leaf leaf-in-declare-case-one {
+                type empty;
+            }
+        }
+        container second-implicit-case-container {
+            leaf declared-leaf-in-second-case-container {
+                type empty;
+            }
+        }
+        case declared-case-two {
+            leaf leaf-in-declare-case-two {
+                type empty;
+            }
+        }
+    }
+
+    container after {
+    }
+
+    augment "/my-choice/implicit-case-container" {
+        leaf leaf-after-container {
+            type empty;
+        }
+    }
+
+    augment "/my-choice/implicit-case-container/implicit-case-container" {
+        leaf leaf-inside-container {
+            type empty;
+        }
+    }
+
+    augment "/my-choice/second-implicit-case-container" {
+        leaf leaf-after-second-container {
+            type empty;
+        }
+    }
+
+    augment "/my-choice/second-implicit-case-container/second-implicit-case-container" {
+        leaf leaf-inside-second-container {
+            type empty;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6183/yang11/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6183/yang11/foo.yang
new file mode 100644 (file)
index 0000000..f1aa472
--- /dev/null
@@ -0,0 +1,63 @@
+module foo {
+    namespace "foo";
+    prefix "foo";
+    yang-version 1.1;
+
+    container before {
+    }
+
+    choice my-choice {
+        container implicit-case-container {
+            container declared-container-in-case-container {
+                leaf declared-leaf {
+                    type empty;
+                }
+            }
+            leaf declared-leaf-in-case-container {
+                type empty;
+            }
+        }
+        case declared-case-one {
+            leaf leaf-in-declare-case-one {
+                type empty;
+            }
+        }
+        container second-implicit-case-container {
+            leaf declared-leaf-in-second-case-container {
+                type empty;
+            }
+        }
+        case declared-case-two {
+            leaf leaf-in-declare-case-two {
+                type empty;
+            }
+        }
+    }
+
+    container after {
+    }
+
+    augment "/my-choice/implicit-case-container" {
+        leaf leaf-after-container {
+            type empty;
+        }
+    }
+
+    augment "/my-choice/implicit-case-container/implicit-case-container" {
+        leaf leaf-inside-container {
+            type empty;
+        }
+    }
+
+    augment "/my-choice/second-implicit-case-container" {
+        leaf leaf-after-second-container {
+            type empty;
+        }
+    }
+
+    augment "/my-choice/second-implicit-case-container/second-implicit-case-container" {
+        leaf leaf-inside-second-container {
+            type empty;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/correct/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/correct/bar.yang
new file mode 100644 (file)
index 0000000..0136d58
--- /dev/null
@@ -0,0 +1,14 @@
+module bar {
+    namespace "bar";
+    prefix "bar";
+
+    include sub-bar {
+        revision-date 2016-07-19;
+    }
+    import foo { prefix foo; revision-date 2016-07-19; }
+
+    revision "2016-07-19";
+
+    uses foo:sub-foo-grp;
+    uses foo:foo-grp;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/correct/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/correct/foo.yang
new file mode 100644 (file)
index 0000000..7fe73c4
--- /dev/null
@@ -0,0 +1,15 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    include sub-foo {
+        revision-date 2016-07-19;
+    }
+
+    revision 2016-07-19;
+    
+    grouping foo-grp {
+        container foo-grp-con {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/correct/sub-bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/correct/sub-bar.yang
new file mode 100644 (file)
index 0000000..1b21c45
--- /dev/null
@@ -0,0 +1,14 @@
+submodule sub-bar {
+    belongs-to bar {
+        prefix bar;
+    }
+    
+    import foo { prefix foo; revision-date 2016-07-19; }
+    
+    revision 2016-07-19;
+
+    container sub-bar-con {
+        uses foo:sub-foo-grp;
+        uses foo:foo-grp;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/correct/sub-foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/correct/sub-foo.yang
new file mode 100644 (file)
index 0000000..d3e858b
--- /dev/null
@@ -0,0 +1,12 @@
+submodule sub-foo {
+    belongs-to foo {
+        prefix foo;
+    }
+
+    revision 2016-07-19;
+
+    grouping sub-foo-grp {
+        container sub-foo-grp-con {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/incorrect/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/incorrect/bar.yang
new file mode 100644 (file)
index 0000000..9ece862
--- /dev/null
@@ -0,0 +1,12 @@
+module bar {
+    namespace "bar";
+    prefix "bar";
+
+    import foo { prefix foo; revision-date 2016-07-19; }
+
+    revision "2016-07-19";
+
+    uses foo:sub-foo-grp;
+    uses foo:foo-grp;
+    uses foo-imp-grp;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/incorrect/foo-imp.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/incorrect/foo-imp.yang
new file mode 100644 (file)
index 0000000..e524961
--- /dev/null
@@ -0,0 +1,11 @@
+module foo-imp {
+    namespace "foo-imp";
+    prefix foo-imp;
+
+    revision 2016-07-19;
+
+    grouping foo-imp-grp {
+        container foo-imp-grp-con {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/incorrect/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/incorrect/foo.yang
new file mode 100644 (file)
index 0000000..7fe73c4
--- /dev/null
@@ -0,0 +1,15 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    include sub-foo {
+        revision-date 2016-07-19;
+    }
+
+    revision 2016-07-19;
+    
+    grouping foo-grp {
+        container foo-grp-con {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/incorrect/sub-foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6240/incorrect/sub-foo.yang
new file mode 100644 (file)
index 0000000..d3e858b
--- /dev/null
@@ -0,0 +1,12 @@
+submodule sub-foo {
+    belongs-to foo {
+        prefix foo;
+    }
+
+    revision 2016-07-19;
+
+    grouping sub-foo-grp {
+        container sub-foo-grp-con {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6316/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6316/foo.yang
new file mode 100644 (file)
index 0000000..1a56c05
--- /dev/null
@@ -0,0 +1,40 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    typedef my-enumeration {
+        type enumeration {
+            enum zero;
+            enum twenty {
+                value 20;
+            }
+            enum twenty-one;
+            enum two {
+                value 2;
+            }
+            enum twenty-two;
+        }
+    }
+
+    typedef my-bits {
+        type bits {
+            bit zero;
+            bit twenty {
+                position 20;
+            }
+            bit twenty-one;
+            bit two {
+                position 2;
+            }
+            bit twenty-two;
+        }
+    }
+
+    leaf enum-leaf {
+        type my-enumeration;
+    }
+
+    leaf bits-leaf {
+        type my-bits;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6410/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6410/bar.yang
new file mode 100644 (file)
index 0000000..aff0f9b
--- /dev/null
@@ -0,0 +1,21 @@
+module bar {
+    namespace bar-namespace;
+    prefix bar-prefix;
+
+    revision 2016-08-16;
+
+    rpc test-rpc {
+        typedef test-string-typedef {
+            type string {
+                length "5..15";
+            }
+            default "def-val";
+        }
+        typedef test-string-typedef {
+            type string {
+                range "5..15";
+            }
+            default "def-val";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6410/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6410/foo.yang
new file mode 100644 (file)
index 0000000..7dccbe1
--- /dev/null
@@ -0,0 +1,21 @@
+module foo {
+    namespace foo-namespace;
+    prefix foo-prefix;
+
+    revision 2016-08-16;
+
+    rpc test-rpc {
+        typedef test-string-typedef {
+            type string {
+                length "5..15";
+            }
+            default "def-val";
+        }
+        typedef test-int-typedef {
+            type int32 {
+                range "10..100";
+            }
+            default 50;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/importedModuleRevisionOnly/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/importedModuleRevisionOnly/bar.yang
new file mode 100644 (file)
index 0000000..ee4c238
--- /dev/null
@@ -0,0 +1,12 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    import foo {
+        prefix foo;
+    }
+
+    leaf bar-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/importedModuleRevisionOnly/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/importedModuleRevisionOnly/foo.yang
new file mode 100644 (file)
index 0000000..410b573
--- /dev/null
@@ -0,0 +1,9 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    revision 2016-01-01;
+
+    leaf foo-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/moduleRevisionOnly/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/moduleRevisionOnly/bar.yang
new file mode 100644 (file)
index 0000000..0732c41
--- /dev/null
@@ -0,0 +1,13 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    revision 2016-01-01;
+
+    import foo {
+        prefix foo;
+    }
+
+    leaf bar-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/moduleRevisionOnly/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/moduleRevisionOnly/foo.yang
new file mode 100644 (file)
index 0000000..31ccc34
--- /dev/null
@@ -0,0 +1,8 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    leaf foo-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/withRevision/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/withRevision/bar.yang
new file mode 100644 (file)
index 0000000..0732c41
--- /dev/null
@@ -0,0 +1,13 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    revision 2016-01-01;
+
+    import foo {
+        prefix foo;
+    }
+
+    leaf bar-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/withRevision/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/withRevision/foo.yang
new file mode 100644 (file)
index 0000000..410b573
--- /dev/null
@@ -0,0 +1,9 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    revision 2016-01-01;
+
+    leaf foo-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/withoutRevision/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/withoutRevision/bar.yang
new file mode 100644 (file)
index 0000000..ee4c238
--- /dev/null
@@ -0,0 +1,12 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    import foo {
+        prefix foo;
+    }
+
+    leaf bar-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/withoutRevision/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6491/withoutRevision/foo.yang
new file mode 100644 (file)
index 0000000..31ccc34
--- /dev/null
@@ -0,0 +1,8 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    leaf foo-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test1/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test1/bar.yang
new file mode 100644 (file)
index 0000000..5fea1b1
--- /dev/null
@@ -0,0 +1,19 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    import foo { prefix foo; revision-date 2016-09-08; }
+
+    revision 2016-09-08;
+
+    augment /foo:root {
+        container bar {
+            container bar1 {
+                leaf m {
+                    mandatory true;
+                    type empty;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test1/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test1/foo.yang
new file mode 100644 (file)
index 0000000..3c1f031
--- /dev/null
@@ -0,0 +1,9 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-09-08;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test2/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test2/bar.yang
new file mode 100644 (file)
index 0000000..197ed30
--- /dev/null
@@ -0,0 +1,30 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    import foo { prefix foo; revision-date 2016-09-08; }
+
+    revision 2016-09-08;
+
+    augment /foo:root {
+        container bar {
+        }
+    }
+
+    augment /foo:root/bar:bar {
+        container bar1 {
+        }
+    }
+
+    augment /foo:root/bar:bar/bar:bar1 {
+        container bar2 {
+        }
+    }
+
+    augment /foo:root/bar:bar/bar:bar1/bar:bar2 {
+        leaf m {
+            mandatory true;
+            type empty;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test2/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test2/foo.yang
new file mode 100644 (file)
index 0000000..3c1f031
--- /dev/null
@@ -0,0 +1,9 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-09-08;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test3/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test3/bar.yang
new file mode 100644 (file)
index 0000000..3bbc371
--- /dev/null
@@ -0,0 +1,29 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    import foo { prefix foo; revision-date 2016-09-08; }
+
+    revision 2016-09-08;
+
+    augment /foo:root {
+        container bar {
+        }
+    }
+
+    augment /foo:root/bar:bar {
+        container bar1 {
+        }
+    }
+
+    augment /foo:root/bar:bar/bar:bar1 {
+        container bar2 {
+        }
+    }
+
+    augment /foo:root/bar:bar/bar:bar1/bar:bar2 {
+        list l {
+            min-elements 1;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test3/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/invalid/test3/foo.yang
new file mode 100644 (file)
index 0000000..3c1f031
--- /dev/null
@@ -0,0 +1,9 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-09-08;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test1/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test1/bar.yang
new file mode 100644 (file)
index 0000000..1f9aa87
--- /dev/null
@@ -0,0 +1,20 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    import foo { prefix foo; revision-date 2016-09-08; }
+
+    revision 2016-09-08;
+
+    augment /foo:root {
+        container bar {
+            container bar1 {
+                presence "presence container";
+                leaf m {
+                    mandatory true;
+                    type empty;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test1/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test1/foo.yang
new file mode 100644 (file)
index 0000000..3c1f031
--- /dev/null
@@ -0,0 +1,9 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-09-08;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test2/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test2/bar.yang
new file mode 100644 (file)
index 0000000..5a9cd60
--- /dev/null
@@ -0,0 +1,31 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    import foo { prefix foo; revision-date 2016-09-08; }
+
+    revision 2016-09-08;
+
+    augment /foo:root {
+        container bar {
+        }
+    }
+
+    augment /foo:root/bar:bar {
+        container bar1 {
+            presence "presence container";
+        }
+    }
+
+    augment /foo:root/bar:bar/bar:bar1 {
+        container bar2 {
+        }
+    }
+
+    augment /foo:root/bar:bar/bar:bar1/bar:bar2 {
+        leaf m {
+            mandatory true;
+            type empty;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test2/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test2/foo.yang
new file mode 100644 (file)
index 0000000..3c1f031
--- /dev/null
@@ -0,0 +1,9 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-09-08;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test3/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test3/bar.yang
new file mode 100644 (file)
index 0000000..52107de
--- /dev/null
@@ -0,0 +1,30 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    import foo { prefix foo; revision-date 2016-09-08; }
+
+    revision 2016-09-08;
+
+    augment /foo:root {
+        container bar {
+        }
+    }
+
+    augment /foo:root/bar:bar {
+        container bar1 {
+            presence "presence container";
+        }
+    }
+
+    augment /foo:root/bar:bar/bar:bar1 {
+        container bar2 {
+        }
+    }
+
+    augment /foo:root/bar:bar/bar:bar1/bar:bar2 {
+        list l {
+            min-elements 1;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test3/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6669/valid/test3/foo.yang
new file mode 100644 (file)
index 0000000..3c1f031
--- /dev/null
@@ -0,0 +1,9 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    revision 2016-09-08;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6771/augment/typedef-bug.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6771/augment/typedef-bug.yang
new file mode 100644 (file)
index 0000000..d006c70
--- /dev/null
@@ -0,0 +1,26 @@
+module typedef-bug {
+    namespace "http://www.example.com/typedef-bug";
+    prefix tdb;
+
+    container root {
+    }
+
+    augment "/root" {
+        container container-b {
+
+            leaf leaf-container-b {
+                type type-container-b;
+            }
+
+            typedef type-container-b {
+                type uint32;
+            }
+
+            container inner-container {
+                leaf leaf-container-b {
+                    type type-container-b;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6771/choice-case/typedef-bug.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6771/choice-case/typedef-bug.yang
new file mode 100644 (file)
index 0000000..4d1e0d2
--- /dev/null
@@ -0,0 +1,45 @@
+module typedef-bug {
+    namespace "http://www.example.com/typedef-bug";
+    prefix tdb;
+
+    container root {
+        choice my-choice {
+            case one {
+                container container-one {
+
+                    typedef type-container-b {
+                        type uint32;
+                    }
+
+                    leaf leaf-container-b {
+                        type type-container-b;
+                    }
+                }
+            }
+            case two {
+                container container-two {
+
+                    leaf leaf-container-b {
+                        type type-container-b;
+                    }
+
+                    typedef type-container-b {
+                        type uint32;
+                    }
+                }
+            }
+            case three {
+                container container-three {
+                    container inner-container {
+                        leaf leaf-container-b {
+                            type type-container-b;
+                        }
+                    }
+                    typedef type-container-b {
+                        type uint32;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6771/grouping/typedef-bug.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6771/grouping/typedef-bug.yang
new file mode 100644 (file)
index 0000000..51037a6
--- /dev/null
@@ -0,0 +1,21 @@
+module typedef-bug {
+    namespace "http://www.example.com/typedef-bug";
+    prefix tdb;
+
+    container root {
+        uses grp;
+    }
+
+    grouping grp {
+        container container-b {
+
+            typedef type-container-b {
+                type uint32;
+            }
+
+            leaf leaf-container-b {
+                type type-container-b;
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/bar-sub1.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/bar-sub1.yang
new file mode 100644 (file)
index 0000000..ea1d323
--- /dev/null
@@ -0,0 +1,11 @@
+submodule sub1-bar {
+    belongs-to bar {
+        prefix bar;
+    }
+
+    revision 2016-01-01;
+
+    leaf sub1-bar-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/bar.yang
new file mode 100644 (file)
index 0000000..19d905c
--- /dev/null
@@ -0,0 +1,11 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    revision 2016-01-01;
+
+    include sub1-bar;
+
+    leaf bar-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/baz.yang
new file mode 100644 (file)
index 0000000..1b82bce
--- /dev/null
@@ -0,0 +1,9 @@
+module baz {
+    namespace "baz";
+    prefix baz;
+    revision 2016-01-01;
+
+    leaf baz-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/foo-sub1.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/foo-sub1.yang
new file mode 100644 (file)
index 0000000..d942464
--- /dev/null
@@ -0,0 +1,11 @@
+submodule sub1-foo {
+    belongs-to foo {
+        prefix foo;
+    }
+
+    revision 2016-01-01;
+
+    leaf sub1-foo-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/foo-sub2.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/foo-sub2.yang
new file mode 100644 (file)
index 0000000..dfef551
--- /dev/null
@@ -0,0 +1,11 @@
+submodule sub2-foo {
+    belongs-to foo {
+        prefix foo;
+    }
+
+    revision 2016-01-01;
+
+    leaf sub2-foo-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6961/foo.yang
new file mode 100644 (file)
index 0000000..8b82d0d
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    revision 2016-01-01;
+
+    include sub1-foo;
+    include sub2-foo;
+
+    leaf foo-leaf {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6972/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6972/bar.yang
new file mode 100644 (file)
index 0000000..ed7a56c
--- /dev/null
@@ -0,0 +1,18 @@
+module bar {
+     namespace bar-ns;
+     prefix bar-prefix;
+
+     import foo {
+         prefix foo;
+         revision-date 2016-10-20;
+     }
+
+     revision 2016-10-20;
+
+     container bar-export {
+         uses foo:export;
+     }
+     container bar-foo {
+         uses foo:foo;
+     }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6972/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6972/baz.yang
new file mode 100644 (file)
index 0000000..91cec2b
--- /dev/null
@@ -0,0 +1,18 @@
+module baz {
+     namespace baz-ns;
+     prefix baz-prefix;
+
+     import foo {
+         prefix foo;
+         revision-date 2016-10-20;
+     }
+
+     revision 2016-10-20;
+
+     container baz-export {
+         uses foo:export;
+     }
+     container baz-foo {
+         uses foo:foo;
+     }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6972/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug6972/foo.yang
new file mode 100644 (file)
index 0000000..0460125
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prefix;
+
+    revision 2016-10-20;
+
+    grouping foo {
+        leaf foo {
+            type string;
+            units foo;
+        }
+    }
+
+    grouping export {
+        uses foo;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7037/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7037/bar.yang
new file mode 100644 (file)
index 0000000..99f7acd
--- /dev/null
@@ -0,0 +1,11 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    extension bar-ext {
+        argument arg;
+    }
+
+    extension bar-ext-2 {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7037/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7037/foo.yang
new file mode 100644 (file)
index 0000000..5393312
--- /dev/null
@@ -0,0 +1,33 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar { prefix bar; }
+
+    extension foo-ext {
+        argument arg;
+    }
+
+    bar:bar-ext "first" {
+        container bar-ext-con {
+        }
+    }
+
+    container root {
+        bar:bar-ext "bar" {
+            container bar-ext-con-2 {
+            }
+            bar:bar-ext "sub-bar" {
+                container bar-ext-con-3 {
+                }
+            }
+            bar:bar-ext-2 {
+                container bar-ext-2-con {
+                }
+            }
+        }
+        foo:foo-ext "foo" {
+            container foo-ext-con;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7038/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7038/bar.yang
new file mode 100644 (file)
index 0000000..c9cd57b
--- /dev/null
@@ -0,0 +1,8 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    import foo { prefix decimal64; }
+
+    decimal64:decimal64 decimal64;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7038/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7038/foo.yang
new file mode 100644 (file)
index 0000000..7ffabf5
--- /dev/null
@@ -0,0 +1,8 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    extension decimal64 {
+        argument arg;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7038/yang10/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7038/yang10/foo.yang
new file mode 100644 (file)
index 0000000..c40e633
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1;
+
+    container root {
+        leaf my-leafref {
+            type leafref {
+                path "../target";
+                require-instance false;
+            }
+        }
+        leaf target {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7038/yang11/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7038/yang11/foo.yang
new file mode 100644 (file)
index 0000000..ee77e86
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    container root {
+        leaf my-leafref {
+            type leafref {
+                path "../target";
+                require-instance false;
+            }
+        }
+        leaf target {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7146/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7146/foo.yang
new file mode 100644 (file)
index 0000000..a2a8dcb
--- /dev/null
@@ -0,0 +1,9 @@
+module foo {
+    namespace foo-namespace;
+    prefix foo-prefix;
+
+    revision 2016-11-23;
+
+    #
+    container foo-cont {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7424/foo-data.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7424/foo-data.yang
new file mode 100644 (file)
index 0000000..1c719b8
--- /dev/null
@@ -0,0 +1,11 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    container root {
+        list name {
+        }
+        container name {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7424/foo-notification.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7424/foo-notification.yang
new file mode 100644 (file)
index 0000000..39f2ff6
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    container root {
+        notification name {
+        }
+        action name {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7424/foo-rpc-uses.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7424/foo-rpc-uses.yang
new file mode 100644 (file)
index 0000000..b4ff7fe
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    uses grp;
+
+    rpc name {
+    }
+
+    grouping grp {
+        container name {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7424/foo-rpc.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7424/foo-rpc.yang
new file mode 100644 (file)
index 0000000..10828f9
--- /dev/null
@@ -0,0 +1,11 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    container name {
+    }
+
+    rpc name {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7440/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7440/bar.yang
new file mode 100644 (file)
index 0000000..883b4a7
--- /dev/null
@@ -0,0 +1,10 @@
+module bar {
+    namespace bar-ns;
+    prefix bar;
+
+    revision 2016-12-23;
+
+    leaf test-leaf {
+            type int32;
+        }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7440/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7440/foo.yang
new file mode 100644 (file)
index 0000000..7ca75a1
--- /dev/null
@@ -0,0 +1,19 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prfx;
+
+    import bar {
+        prefix bar;
+        revision-date 2016-12-23;
+    }
+
+    revision 2016-12-23;
+
+    deviation "/bar:test-leaf" {
+        deviate replace {
+            type uint32 {
+                range "5..50";
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files-2/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files-2/bar.yang
new file mode 100644 (file)
index 0000000..9fae14e
--- /dev/null
@@ -0,0 +1,10 @@
+module bar {
+    namespace bar;
+    prefix bar;
+    yang-version 1;
+
+    revision 2017-01-25;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files-2/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files-2/foo.yang
new file mode 100644 (file)
index 0000000..d130ce6
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1;
+
+    import foo-imp-1 { prefix foo-imp-1; revision-date 2017-01-23; }
+    import foo-imp-2 { prefix foo-imp-2; revision-date 2017-01-23; }
+    include sub-foo {
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-24;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files/bar.yang
new file mode 100644 (file)
index 0000000..9fae14e
--- /dev/null
@@ -0,0 +1,10 @@
+module bar {
+    namespace bar;
+    prefix bar;
+    yang-version 1;
+
+    revision 2017-01-25;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files/baz.yang
new file mode 100644 (file)
index 0000000..351dfb3
--- /dev/null
@@ -0,0 +1,7 @@
+module baz {
+    namespace baz;
+    prefix baz;
+    yang-version 1.1;
+
+    import baz-imp { prefix baz-imp; }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files/foo.yang
new file mode 100644 (file)
index 0000000..d130ce6
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1;
+
+    import foo-imp-1 { prefix foo-imp-1; revision-date 2017-01-23; }
+    import foo-imp-2 { prefix foo-imp-2; revision-date 2017-01-23; }
+    include sub-foo {
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-24;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files/sub-mod.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/files/sub-mod.yang
new file mode 100644 (file)
index 0000000..3c7cce0
--- /dev/null
@@ -0,0 +1,11 @@
+submodule sub-mod {
+    belongs-to parent-mod {
+        prefix pm;
+    }
+
+    container my-container {
+        leaf my-leaf {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/foo-imp-1.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/foo-imp-1.yang
new file mode 100644 (file)
index 0000000..0e8cb51
--- /dev/null
@@ -0,0 +1,13 @@
+module foo-imp-1 {
+    namespace foo-imp;
+    prefix foo-imp;
+    yang-version 1.1;
+    
+    import foo-imp-imp-1 { prefix foo-imp-imp-1; revision-date 2017-01-22; }
+    import missing-lib { prefix missing; revision-date 2000-01-01; }
+
+    revision 2017-01-23;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/foo-imp-1@2017-01-22.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/foo-imp-1@2017-01-22.yang
new file mode 100644 (file)
index 0000000..5b71ad4
--- /dev/null
@@ -0,0 +1,10 @@
+module foo-imp-1 {
+    namespace foo-imp;
+    prefix foo-imp;
+    yang-version 1;
+
+    revision 2017-01-22;
+    
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/foo-imp-2.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/foo-imp-2.yang
new file mode 100644 (file)
index 0000000..9aa6406
--- /dev/null
@@ -0,0 +1,10 @@
+module foo-imp-2 {
+    namespace foo-imp-2;
+    prefix foo-imp-2;
+    yang-version 1;
+
+    revision 2017-01-23;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/foo-imp-imp-1.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/foo-imp-imp-1.yang
new file mode 100644 (file)
index 0000000..8fd7ec9
--- /dev/null
@@ -0,0 +1,10 @@
+module foo-imp-imp-1 {
+    namespace foo-imp-imp;
+    prefix foo-imp-imp;
+    yang-version 1;
+
+    revision 2017-01-22;
+
+    container root {
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/lib-1.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/lib-1.yang
new file mode 100644 (file)
index 0000000..26cbcaf
--- /dev/null
@@ -0,0 +1,12 @@
+module lib-1 {
+    namespace lib-1;
+    prefix lib-1;
+
+    import lib-2 { prefix lib-2; revision-date 2000-01-01; }
+    import missing-lib { prefix missing; revision-date 2000-01-01; }
+
+    revision 2000-01-01;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/lib-2.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/lib-2.yang
new file mode 100644 (file)
index 0000000..1cefe02
--- /dev/null
@@ -0,0 +1,9 @@
+module lib-2 {
+    namespace lib-2;
+    prefix lib-2;
+
+    revision 2000-01-01;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/sub-foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib-2/sub-foo.yang
new file mode 100644 (file)
index 0000000..0efef75
--- /dev/null
@@ -0,0 +1,10 @@
+submodule sub-foo {
+    belongs-to foo {
+        prefix foo;
+    }
+
+    revision 2017-01-20;
+
+    container sub-root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/baz-imp@2000-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/baz-imp@2000-01-01.yang
new file mode 100644 (file)
index 0000000..e358046
--- /dev/null
@@ -0,0 +1,7 @@
+module baz-imp {
+    namespace baz-imp;
+    prefix baz-imp;
+
+    revision 2000-01-01 {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/baz-imp@2001-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/baz-imp@2001-01-01.yang
new file mode 100644 (file)
index 0000000..b83b171
--- /dev/null
@@ -0,0 +1,8 @@
+module baz-imp {
+    namespace baz-imp;
+    prefix baz-imp;
+    yang-version 1.1;
+
+    revision 2001-01-01 {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/baz-imp@2002-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/baz-imp@2002-01-01.yang
new file mode 100644 (file)
index 0000000..ead8e5a
--- /dev/null
@@ -0,0 +1,7 @@
+module baz-imp {
+    namespace baz-imp;
+    prefix baz-imp;
+
+    revision 2002-01-01 {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/foo-imp-1.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/foo-imp-1.yang
new file mode 100644 (file)
index 0000000..b896f48
--- /dev/null
@@ -0,0 +1,12 @@
+module foo-imp-1 {
+    namespace foo-imp;
+    prefix foo-imp;
+    yang-version 1.1;
+    
+    import foo-imp-imp-1 { prefix foo-imp-imp-1; revision-date 2017-01-22; }
+
+    revision 2017-01-23;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/foo-imp-1@2017-01-22.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/foo-imp-1@2017-01-22.yang
new file mode 100644 (file)
index 0000000..5b71ad4
--- /dev/null
@@ -0,0 +1,10 @@
+module foo-imp-1 {
+    namespace foo-imp;
+    prefix foo-imp;
+    yang-version 1;
+
+    revision 2017-01-22;
+    
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/foo-imp-2.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/foo-imp-2.yang
new file mode 100644 (file)
index 0000000..9aa6406
--- /dev/null
@@ -0,0 +1,10 @@
+module foo-imp-2 {
+    namespace foo-imp-2;
+    prefix foo-imp-2;
+    yang-version 1;
+
+    revision 2017-01-23;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/foo-imp-imp-1.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/foo-imp-imp-1.yang
new file mode 100644 (file)
index 0000000..8fd7ec9
--- /dev/null
@@ -0,0 +1,10 @@
+module foo-imp-imp-1 {
+    namespace foo-imp-imp;
+    prefix foo-imp-imp;
+    yang-version 1;
+
+    revision 2017-01-22;
+
+    container root {
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/lib-1.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/lib-1.yang
new file mode 100644 (file)
index 0000000..26cbcaf
--- /dev/null
@@ -0,0 +1,12 @@
+module lib-1 {
+    namespace lib-1;
+    prefix lib-1;
+
+    import lib-2 { prefix lib-2; revision-date 2000-01-01; }
+    import missing-lib { prefix missing; revision-date 2000-01-01; }
+
+    revision 2000-01-01;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/lib-2.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/lib-2.yang
new file mode 100644 (file)
index 0000000..a3b34c4
--- /dev/null
@@ -0,0 +1,10 @@
+module lib-2 {
+    namespace lib-2;
+    prefix lib-2;
+    yang-version 1.1;
+
+    revision 2000-01-01;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/lib-3.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/lib-3.yang
new file mode 100644 (file)
index 0000000..4c287af
--- /dev/null
@@ -0,0 +1,13 @@
+module lib-3 {
+    namespace lib-3;
+    prefix lib-3;
+    yang-version 1.1;
+
+    import lib-2 { prefix lib-2; revision-date 2000-01-01; }
+    import missing-lib { prefix missing; revision-date 2000-01-01; }
+
+    revision 2000-01-01;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/parent-mod.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/parent-mod.yang
new file mode 100644 (file)
index 0000000..ef8ad8a
--- /dev/null
@@ -0,0 +1,10 @@
+module parent-mod {
+    namespace parent-mod-ns;
+    prefix pm;
+
+    include sub-mod;
+
+    revision 2017-09-07;
+
+    container my-parent-container {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/sub-foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/lib/sub-foo.yang
new file mode 100644 (file)
index 0000000..0efef75
--- /dev/null
@@ -0,0 +1,10 @@
+submodule sub-foo {
+    belongs-to foo {
+        prefix foo;
+    }
+
+    revision 2017-01-20;
+
+    container sub-root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/main-source-lib-source-conflict-test/child-module.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/main-source-lib-source-conflict-test/child-module.yang
new file mode 100644 (file)
index 0000000..10c1e21
--- /dev/null
@@ -0,0 +1,7 @@
+submodule child-module {
+    belongs-to parent-module {
+        prefix pm;
+    }
+
+    container child-mod-cont {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/main-source-lib-source-conflict-test/parent-module.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7480/main-source-lib-source-conflict-test/parent-module.yang
new file mode 100644 (file)
index 0000000..677cb02
--- /dev/null
@@ -0,0 +1,8 @@
+module parent-module {
+    namespace pm-ns;
+    prefix pm;
+
+    include child-module;
+
+    container parent-mod-cont {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7865/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7865/foo.yang
new file mode 100644 (file)
index 0000000..47febbc
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    container root {
+        foo:p "/bar:a/bar:b" {
+            foo:p "baz:a/baz:b" {
+                foo:p "p";
+            }
+        }
+    }
+
+    extension p {
+        argument "path";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7879/my-event-types.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7879/my-event-types.yang
new file mode 100644 (file)
index 0000000..f43538c
--- /dev/null
@@ -0,0 +1,16 @@
+module my-event-types {
+
+  namespace my-event-types-ns;
+  prefix my-event-types-pfx;
+
+  grouping my-event-with-value {
+    leaf my-event-value {
+      type string;
+    }
+  }
+
+  grouping my-event-with-container {
+    container my-event-container;
+  }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7879/my-model-alarms.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7879/my-model-alarms.yang
new file mode 100644 (file)
index 0000000..2560ccc
--- /dev/null
@@ -0,0 +1,33 @@
+submodule my-model-alarms {
+
+  belongs-to my-model {
+    prefix my-model-pfx;
+  }
+
+  import my-notification-types {
+    prefix my-notif-types-pfx;
+  }
+
+  import my-event-types {
+    prefix my-event-types-pfx;
+  }
+
+  notification my-alarm {
+
+    uses my-notif-types-pfx:my-notification {
+      augment my-content {
+
+        uses my-event-types-pfx:my-event-with-value {
+          refine my-event-value {
+              description "new description";
+          }
+        }
+
+        uses my-event-types-pfx:my-event-with-container {
+          augment my-event-container;
+        }
+
+      }
+    }
+  }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7879/my-model.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7879/my-model.yang
new file mode 100644 (file)
index 0000000..0d6f017
--- /dev/null
@@ -0,0 +1,8 @@
+module my-model {
+
+  namespace my-model-ns;
+  prefix my-model-pfx;
+
+  include my-model-alarms;
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7879/my-notification-types.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7879/my-notification-types.yang
new file mode 100644 (file)
index 0000000..ad853cf
--- /dev/null
@@ -0,0 +1,10 @@
+module my-notification-types {
+
+  namespace my-notif-types-ns;
+  prefix my-notif-types-pfx;
+
+  grouping my-notification {
+    container my-content;
+  }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7954/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7954/bar.yang
new file mode 100644 (file)
index 0000000..d1c2734
--- /dev/null
@@ -0,0 +1,16 @@
+module bar {
+    namespace bar-ns;
+    prefix bar-prefix;
+
+    include subbar {
+        revision-date 2017-03-14;
+    }
+
+    revision 2017-03-14;
+
+    container my-cont {
+        leaf my-leaf {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7954/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7954/foo.yang
new file mode 100644 (file)
index 0000000..9e73917
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prefix;
+
+    revision 2017-03-14;
+
+    container my-cont {
+        leaf my-leaf {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7954/subbar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug7954/subbar.yang
new file mode 100644 (file)
index 0000000..4bbcf93
--- /dev/null
@@ -0,0 +1,13 @@
+submodule subbar {
+    belongs-to bar {
+        prefix bar-prefix;
+    }
+
+    revision 2017-03-14;
+
+    container sub-container {
+        leaf sub-leaf {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8126/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8126/bar.yang
new file mode 100644 (file)
index 0000000..c142cbe
--- /dev/null
@@ -0,0 +1,72 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    import foo { prefix foo; }
+
+    //valid augments (non-mandatory choice)
+    augment "/foo:root" {
+        container my-container {
+            choice my-choice {
+                case one {
+                }
+            }
+        }
+    }
+
+    augment "/foo:root/my-container/my-choice/one" {
+        container one {
+            leaf mandatory-leaf {
+                mandatory true;
+                type empty;
+            }
+        }
+    }
+
+    //valid augments (non-mandatory list)
+    augment "/foo:root" {
+        list my-list {
+            min-elements 0;
+        }
+    }
+
+    augment "/foo:root/my-list" {
+        container two {
+            leaf mandatory-leaf-2 {
+                mandatory true;
+                type empty;
+            }
+        }
+    }
+
+    //invalid augment (mandatory choice)
+    augment "/foo:root" {
+        container mandatory-container {
+            choice mandatory-choice {
+                mandatory true;
+            }
+        }
+    }
+
+    //invalid augment (mandatory list)    
+    augment "/foo:root" {
+        list mandatory-list {
+            min-elements 1;
+        }
+    }
+
+    //invalid augments (mandatory container)
+    augment "/foo:root" {
+        container mandatory-container-2 {
+        }
+    }
+
+    augment "/foo:root/mandatory-container-2" {
+        container one {
+            leaf mandatory-leaf-3 {
+                mandatory true;
+                type empty;
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8126/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8126/foo.yang
new file mode 100644 (file)
index 0000000..b1b0df7
--- /dev/null
@@ -0,0 +1,7 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/bar-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/bar-invalid.yang
new file mode 100644 (file)
index 0000000..d49ab16
--- /dev/null
@@ -0,0 +1,19 @@
+module bar {
+    namespace bar-ns;
+    prefix bar;
+
+    import baz {
+        prefix baz;
+        revision-date 2017-05-16;
+    }
+
+    revision 2017-05-16;
+
+    deviation "/baz:my-baz-cont/bar:my-aug-cont" {
+        deviate not-supported;
+    }
+
+    augment "/baz:my-baz-cont" {
+        container my-aug-cont {}
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/bar.yang
new file mode 100644 (file)
index 0000000..46e15df
--- /dev/null
@@ -0,0 +1,19 @@
+module bar {
+    namespace bar-ns;
+    prefix bar-prefix;
+
+    import foo {
+        prefix foo;
+        revision-date 2017-05-16;
+    }
+
+    revision 2017-05-16;
+
+    deviation "/foo:my-foo-cont-a" {
+        deviate not-supported;
+    }
+
+    container my-bar-cont-a {}
+
+    container my-bar-cont-b {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/baz-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/baz-invalid.yang
new file mode 100644 (file)
index 0000000..39d1cfe
--- /dev/null
@@ -0,0 +1,8 @@
+module baz {
+    namespace baz-ns;
+    prefix baz;
+
+    revision 2017-05-16;
+
+    container my-baz-cont {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/baz.yang
new file mode 100644 (file)
index 0000000..05df3ad
--- /dev/null
@@ -0,0 +1,26 @@
+module baz {
+    namespace baz-ns;
+    prefix baz-prefix;
+
+    import foo {
+        prefix foo;
+        revision-date 2017-05-16;
+    }
+
+    import bar {
+        prefix bar;
+        revision-date 2017-05-16;
+    }
+
+    revision 2017-05-16;
+
+    deviation "/foo:my-foo-cont-b" {
+        deviate not-supported;
+    }
+
+    deviation "/bar:my-bar-cont-a" {
+        deviate not-supported;
+    }
+
+    container my-baz-cont {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/foo-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/foo-invalid.yang
new file mode 100644 (file)
index 0000000..b1cad54
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace foo-ns;
+    prefix foo;
+
+    revision 2017-05-16;
+
+    deviation "/my-foo-cont" {
+        deviate not-supported;
+    }
+
+    container my-foo-cont {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/foo.yang
new file mode 100644 (file)
index 0000000..4027fee
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prefix;
+
+    revision 2017-05-16;
+
+    container my-foo-cont-a {}
+
+    container my-foo-cont-b {}
+
+    container my-foo-cont-c {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/foobar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8307/foobar.yang
new file mode 100644 (file)
index 0000000..97d4478
--- /dev/null
@@ -0,0 +1,24 @@
+module foobar {
+    namespace foobar-ns;
+    prefix foobar-prefix;
+
+    import foo {
+        prefix foo;
+        revision-date 2017-05-16;
+    }
+
+    import bar {
+        prefix bar;
+        revision-date 2017-05-16;
+    }
+
+    revision 2017-05-16;
+
+    deviation "/foo:my-foo-cont-c" {
+        deviate not-supported;
+    }
+
+    deviation "/bar:my-bar-cont-b" {
+        deviate not-supported;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8597/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8597/bar.yang
new file mode 100644 (file)
index 0000000..f1187ee
--- /dev/null
@@ -0,0 +1,6 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    revision 1970-01-01;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8597/bar@2010-10-10.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8597/bar@2010-10-10.yang
new file mode 100644 (file)
index 0000000..8fabd5d
--- /dev/null
@@ -0,0 +1,6 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    revision 2010-10-10;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8597/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8597/baz.yang
new file mode 100644 (file)
index 0000000..43436cf
--- /dev/null
@@ -0,0 +1,6 @@
+module baz {
+    namespace "baz";
+    prefix baz;
+
+    revision 2010-10-10;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8597/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8597/foo.yang
new file mode 100644 (file)
index 0000000..a35fcdd
--- /dev/null
@@ -0,0 +1,8 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    import bar { prefix bar; revision-date 1970-01-01; reference "bar-ref"; description "bar-desc"; }
+    import baz { prefix baz; reference "baz-ref"; description "baz-desc"; }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8831/invalid/inv-model.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8831/invalid/inv-model.yang
new file mode 100644 (file)
index 0000000..6b8e724
--- /dev/null
@@ -0,0 +1,29 @@
+module inv-model {
+    yang-version 1.1;
+    namespace "http://www.inv-model.com";
+    prefix ex;
+
+    revision 2017-07-10;
+
+    feature my-feature {
+        description "my feature";
+    }
+
+    typedef enum-last {
+        type union {
+            type uint16;
+            type enumeration {
+                enum "any" {
+                    if-feature my-feature;
+                }
+            }
+        }
+    }
+
+    container top {
+        leaf enum-last-leaf {
+            type enum-last;
+            default "any";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8831/invalid/inv-model2.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8831/invalid/inv-model2.yang
new file mode 100644 (file)
index 0000000..246976c
--- /dev/null
@@ -0,0 +1,29 @@
+module inv-model2 {
+    yang-version 1.1;
+    namespace "http://www.inv-model2.com";
+    prefix ex;
+
+    revision 2017-07-10;
+
+    feature my-feature {
+        description "my feature";
+    }
+
+    typedef enum-first {
+        type union {
+            type enumeration {
+                enum "any" {
+                    if-feature my-feature;
+                }
+            }
+            type uint16;
+        }
+    }
+
+    container top {
+        leaf enum-first-leaf {
+            type enum-first;
+            default "any";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8831/valid/example-model.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8831/valid/example-model.yang
new file mode 100644 (file)
index 0000000..715f87b
--- /dev/null
@@ -0,0 +1,36 @@
+module example-model {
+    yang-version 1.1;
+    namespace "http://www.example.com";
+    prefix ex;
+
+    revision 2017-07-10;
+
+    typedef enum-first {
+        type union {
+            type enumeration {
+                enum "any";
+            }
+            type uint16;
+        }
+    }
+
+    typedef enum-last {
+        type union {
+            type uint16;
+            type enumeration {
+                enum "any";
+            }
+        }
+    }
+
+    container top {
+        leaf enum-first-leaf {
+            type enum-first;
+            default "any";
+        }
+        leaf enum-last-leaf {
+            type enum-last;
+            default "any";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8831/valid/example-model2.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8831/valid/example-model2.yang
new file mode 100644 (file)
index 0000000..3b39c75
--- /dev/null
@@ -0,0 +1,29 @@
+module example-model2 {
+    yang-version 1.1;
+    namespace "http://www.example2.com";
+    prefix ex2;
+
+    revision 2017-07-10;
+
+    feature my-feature {
+        description "my feature";
+    }
+
+    typedef enum-last {
+        type union {
+            type uint16;
+            type enumeration {
+                enum "any" {
+                    if-feature my-feature;
+                }
+            }
+        }
+    }
+
+    container top {
+        leaf enum-last-leaf {
+            type enum-last;
+            default "16";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8922/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug8922/foo.yang
new file mode 100644 (file)
index 0000000..891d6bb
--- /dev/null
@@ -0,0 +1,25 @@
+module foo {
+    namespace foo;
+    prefix f;
+
+    feature my-feature;
+
+    container target {
+    }
+
+    augment "/target" {
+        if-feature my-feature;
+        uses my-grp {
+            refine "my-con" {
+                description
+                                "New description";
+            }
+        }
+    }
+
+    grouping my-grp {
+        container my-con {
+            if-feature my-feature;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9005/bar-1@2000-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9005/bar-1@2000-01-01.yang
new file mode 100644 (file)
index 0000000..79e7d8b
--- /dev/null
@@ -0,0 +1,6 @@
+module bar-1 {
+    namespace bar-1;
+    prefix bar;
+
+    revision 2000-01-01;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9005/bar-2@2000-01-02.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9005/bar-2@2000-01-02.yang
new file mode 100644 (file)
index 0000000..cd3ff10
--- /dev/null
@@ -0,0 +1,6 @@
+module bar-2 {
+    namespace bar-2;
+    prefix bar;
+
+    revision 2000-01-02;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9005/foo@2017-07-07.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9005/foo@2017-07-07.yang
new file mode 100644 (file)
index 0000000..6ab063a
--- /dev/null
@@ -0,0 +1,10 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar-2 { prefix bar; }
+
+    include sub-foo;
+
+    revision 2017-07-07;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9005/sub-foo@2017-07-07.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9005/sub-foo@2017-07-07.yang
new file mode 100644 (file)
index 0000000..5214745
--- /dev/null
@@ -0,0 +1,9 @@
+submodule sub-foo {
+    belongs-to foo {
+        prefix foo;
+    }
+
+    import bar-1 { prefix bar; }
+
+    revision 2017-07-07;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9242/imp-module-2.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9242/imp-module-2.yang
new file mode 100644 (file)
index 0000000..0636c31
--- /dev/null
@@ -0,0 +1,12 @@
+module imp-module-2 {
+    namespace imp2-ns;
+    prefix imp2;
+
+    revision 2017-10-13;
+
+    typedef new-type {
+        type string {
+            length 10..15;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9242/imp-module.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9242/imp-module.yang
new file mode 100644 (file)
index 0000000..b26b6c5
--- /dev/null
@@ -0,0 +1,14 @@
+module imp-module {
+    namespace imp-ns;
+    prefix imp;
+
+    revision 2017-10-13;
+
+    leaf my-leaf {
+        type string;
+    }
+
+    leaf my-leaf-2 {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9242/root-module.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9242/root-module.yang
new file mode 100644 (file)
index 0000000..b5c0005
--- /dev/null
@@ -0,0 +1,32 @@
+module root-module {
+    namespace rm-ns;
+    prefix rm;
+
+    import imp-module {
+        prefix im;
+    }
+
+    import imp-module-2 {
+        prefix im2;
+    }
+
+    revision 2017-10-13;
+
+    deviation "/im:my-leaf" {
+        deviate replace {
+            type im2:new-type;
+        }
+    }
+
+    deviation "/im:my-leaf-2" {
+        deviate replace {
+            type new-type;
+        }
+    }
+
+    typedef new-type {
+        type int32 {
+            range 10..15;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9244/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9244/bar.yang
new file mode 100644 (file)
index 0000000..636f0f0
--- /dev/null
@@ -0,0 +1,18 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-10-13;
+
+    container bar-cont {
+
+    }
+
+    leaf-list bar-leaf-list {
+        type int32;
+    }
+
+    leaf bar-leaf {
+        type int32;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9244/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/bug9244/foo.yang
new file mode 100644 (file)
index 0000000..076ce79
--- /dev/null
@@ -0,0 +1,29 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+    }
+
+    revision 2017-10-13;
+
+    deviation "/bar:bar-cont" {
+        deviate replace {
+            config false;
+        }
+    }
+
+    deviation "/bar:bar-leaf-list" {
+        deviate replace {
+            min-elements 5;
+            max-elements 10;
+        }
+    }
+
+    deviation "/bar:bar-leaf" {
+        deviate replace {
+            mandatory true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/identity-ietf-routing-test/ietf-interfaces@2014-05-08.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/identity-ietf-routing-test/ietf-interfaces@2014-05-08.yang
new file mode 100644 (file)
index 0000000..d498da6
--- /dev/null
@@ -0,0 +1,656 @@
+module ietf-interfaces {
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces";
+  prefix if;
+
+  import ietf-yang-types {
+    prefix yang;
+  }
+
+  organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+    "WG Web:   <http://tools.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+     WG Chair: Thomas Nadeau
+               <mailto:tnadeau@lucidvision.com>
+     WG Chair: Juergen Schoenwaelder
+               <mailto:j.schoenwaelder@jacobs-university.de>
+     Editor:   Martin Bjorklund
+               <mailto:mbj@tail-f.com>";
+
+  description
+    "This module contains a collection of YANG definitions for
+     managing network interfaces.
+     Copyright (c) 2014 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD License
+     set forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+     This version of this YANG module is part of RFC 7223; see
+     the RFC itself for full legal notices.";
+
+  revision 2014-05-08 {
+    description
+      "Initial revision.";
+    reference
+      "RFC 7223: A YANG Data Model for Interface Management";
+  }
+
+  /*
+   * Typedefs
+   */
+
+  typedef interface-ref {
+    type leafref {
+      path "/if:interfaces/if:interface/if:name";
+    }
+    description
+      "This type is used by data models that need to reference
+       configured interfaces.";
+  }
+
+  typedef interface-state-ref {
+    type leafref {
+      path "/if:interfaces-state/if:interface/if:name";
+    }
+    description
+      "This type is used by data models that need to reference
+       the operationally present interfaces.";
+  }
+
+  /*
+   * Identities
+   */
+
+  identity interface-type {
+    description
+      "Base identity from which specific interface types are
+       derived.";
+  }
+
+  /*
+   * Features
+   */
+
+  feature arbitrary-names {
+    description
+      "This feature indicates that the device allows user-controlled
+       interfaces to be named arbitrarily.";
+  }
+  feature pre-provisioning {
+    description
+      "This feature indicates that the device supports
+       pre-provisioning of interface configuration, i.e., it is
+       possible to configure an interface whose physical interface
+       hardware is not present on the device.";
+  }
+
+  feature if-mib {
+    description
+      "This feature indicates that the device implements
+       the IF-MIB.";
+    reference
+      "RFC 2863: The Interfaces Group MIB";
+  }
+
+  /*
+   * Configuration data nodes
+   */
+
+  container interfaces {
+    description
+      "Interface configuration parameters.";
+
+    list interface {
+      key "name";
+
+      description
+        "The list of configured interfaces on the device.
+         The operational state of an interface is available in the
+         /interfaces-state/interface list.  If the configuration of a
+         system-controlled interface cannot be used by the system
+         (e.g., the interface hardware present does not match the
+         interface type), then the configuration is not applied to
+         the system-controlled interface shown in the
+         /interfaces-state/interface list.  If the configuration
+         of a user-controlled interface cannot be used by the system,
+         the configured interface is not instantiated in the
+         /interfaces-state/interface list.";
+
+     leaf name {
+        type string;
+        description
+          "The name of the interface.
+           A device MAY restrict the allowed values for this leaf,
+           possibly depending on the type of the interface.
+           For system-controlled interfaces, this leaf is the
+           device-specific name of the interface.  The 'config false'
+           list /interfaces-state/interface contains the currently
+           existing interfaces on the device.
+           If a client tries to create configuration for a
+           system-controlled interface that is not present in the
+           /interfaces-state/interface list, the server MAY reject
+           the request if the implementation does not support
+           pre-provisioning of interfaces or if the name refers to
+           an interface that can never exist in the system.  A
+           NETCONF server MUST reply with an rpc-error with the
+           error-tag 'invalid-value' in this case.
+           If the device supports pre-provisioning of interface
+           configuration, the 'pre-provisioning' feature is
+           advertised.
+           If the device allows arbitrarily named user-controlled
+           interfaces, the 'arbitrary-names' feature is advertised.
+           When a configured user-controlled interface is created by
+           the system, it is instantiated with the same name in the
+           /interface-state/interface list.";
+      }
+
+      leaf description {
+        type string;
+        description
+          "A textual description of the interface.
+           A server implementation MAY map this leaf to the ifAlias
+           MIB object.  Such an implementation needs to use some
+           mechanism to handle the differences in size and characters
+           allowed between this leaf and ifAlias.  The definition of
+           such a mechanism is outside the scope of this document.
+           Since ifAlias is defined to be stored in non-volatile
+           storage, the MIB implementation MUST map ifAlias to the
+           value of 'description' in the persistently stored
+           datastore.
+           Specifically, if the device supports ':startup', when
+           ifAlias is read the device MUST return the value of
+           'description' in the 'startup' datastore, and when it is
+           written, it MUST be written to the 'running' and 'startup'
+           datastores.  Note that it is up to the implementation to
+           decide whether to modify this single leaf in 'startup' or
+           perform an implicit copy-config from 'running' to
+           'startup'.
+           If the device does not support ':startup', ifAlias MUST
+           be mapped to the 'description' leaf in the 'running'
+           datastore.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifAlias";
+      }
+
+      leaf type {
+        type identityref {
+          base interface-type;
+        }
+        mandatory true;
+        description
+          "The type of the interface.
+           When an interface entry is created, a server MAY
+           initialize the type leaf with a valid value, e.g., if it
+           is possible to derive the type from the name of the
+           interface.
+           If a client tries to set the type of an interface to a
+           value that can never be used by the system, e.g., if the
+           type is not supported or if the type does not match the
+           name of the interface, the server MUST reject the request.
+           A NETCONF server MUST reply with an rpc-error with the
+           error-tag 'invalid-value' in this case.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifType";
+      }
+
+      leaf enabled {
+        type boolean;
+        default "true";
+        description
+          "This leaf contains the configured, desired state of the
+           interface.
+           Systems that implement the IF-MIB use the value of this
+           leaf in the 'running' datastore to set
+           IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry
+           has been initialized, as described in RFC 2863.
+           Changes in this leaf in the 'running' datastore are
+           reflected in ifAdminStatus, but if ifAdminStatus is
+           changed over SNMP, this leaf is not affected.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+      }
+
+      leaf link-up-down-trap-enable {
+        if-feature if-mib;
+        type enumeration {
+          enum enabled {
+            value 1;
+          }
+          enum disabled {
+            value 2;
+          }
+        }
+        description
+          "Controls whether linkUp/linkDown SNMP notifications
+           should be generated for this interface.
+           If this node is not configured, the value 'enabled' is
+           operationally used by the server for interfaces that do
+           not operate on top of any other interface (i.e., there are
+           no 'lower-layer-if' entries), and 'disabled' otherwise.";
+        reference
+          "RFC 2863: The Interfaces Group MIB -
+                     ifLinkUpDownTrapEnable";
+      }
+    }
+  }
+
+  /*
+   * Operational state data nodes
+   */
+
+  container interfaces-state {
+    config false;
+    description
+      "Data nodes for the operational state of interfaces.";
+
+    list interface {
+      key "name";
+
+      description
+        "The list of interfaces on the device.
+         System-controlled interfaces created by the system are
+         always present in this list, whether they are configured or
+         not.";
+
+      leaf name {
+        type string;
+        description
+          "The name of the interface.
+           A server implementation MAY map this leaf to the ifName
+           MIB object.  Such an implementation needs to use some
+           mechanism to handle the differences in size and characters
+           allowed between this leaf and ifName.  The definition of
+           such a mechanism is outside the scope of this document.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifName";
+      }
+
+      leaf type {
+        type identityref {
+          base interface-type;
+        }
+        mandatory true;
+        description
+          "The type of the interface.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifType";
+      }
+
+      leaf admin-status {
+        if-feature if-mib;
+        type enumeration {
+          enum up {
+            value 1;
+            description
+              "Ready to pass packets.";
+          }
+          enum down {
+            value 2;
+            description
+              "Not ready to pass packets and not in some test mode.";
+          }
+
+          enum testing {
+            value 3;
+            description
+              "In some test mode.";
+          }
+        }
+        mandatory true;
+        description
+          "The desired state of the interface.
+           This leaf has the same read semantics as ifAdminStatus.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+      }
+
+      leaf oper-status {
+        type enumeration {
+          enum up {
+            value 1;
+            description
+              "Ready to pass packets.";
+          }
+          enum down {
+            value 2;
+            description
+              "The interface does not pass any packets.";
+          }
+          enum testing {
+            value 3;
+            description
+              "In some test mode.  No operational packets can
+               be passed.";
+          }
+          enum unknown {
+            value 4;
+            description
+              "Status cannot be determined for some reason.";
+          }
+          enum dormant {
+            value 5;
+            description
+              "Waiting for some external event.";
+          }
+          enum not-present {
+            value 6;
+            description
+              "Some component (typically hardware) is missing.";
+          }
+          enum lower-layer-down {
+            value 7;
+            description
+              "Down due to state of lower-layer interface(s).";
+          }
+        }
+        mandatory true;
+        description
+          "The current operational state of the interface.
+           This leaf has the same semantics as ifOperStatus.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifOperStatus";
+      }
+
+      leaf last-change {
+        type yang:date-and-time;
+        description
+          "The time the interface entered its current operational
+           state.  If the current state was entered prior to the
+           last re-initialization of the local network management
+           subsystem, then this node is not present.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifLastChange";
+      }
+
+      leaf if-index {
+        if-feature if-mib;
+        type int32 {
+          range "1..2147483647";
+        }
+        mandatory true;
+        description
+          "The ifIndex value for the ifEntry represented by this
+           interface.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifIndex";
+      }
+
+      leaf phys-address {
+        type yang:phys-address;
+        description
+          "The interface's address at its protocol sub-layer.  For
+           example, for an 802.x interface, this object normally
+           contains a Media Access Control (MAC) address.  The
+           interface's media-specific modules must define the bit
+           and byte ordering and the format of the value of this
+           object.  For interfaces that do not have such an address
+           (e.g., a serial line), this node is not present.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifPhysAddress";
+      }
+
+      leaf-list higher-layer-if {
+        type interface-state-ref;
+        description
+          "A list of references to interfaces layered on top of this
+           interface.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifStackTable";
+      }
+
+      leaf-list lower-layer-if {
+        type interface-state-ref;
+        description
+          "A list of references to interfaces layered underneath this
+           interface.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifStackTable";
+      }
+
+      leaf speed {
+        type yang:gauge64;
+        units "bits/second";
+        description
+            "An estimate of the interface's current bandwidth in bits
+             per second.  For interfaces that do not vary in
+             bandwidth or for those where no accurate estimation can
+             be made, this node should contain the nominal bandwidth.
+             For interfaces that have no concept of bandwidth, this
+             node is not present.";
+        reference
+          "RFC 2863: The Interfaces Group MIB -
+                     ifSpeed, ifHighSpeed";
+      }
+
+      container statistics {
+        description
+          "A collection of interface-related statistics objects.";
+
+        leaf discontinuity-time {
+          type yang:date-and-time;
+          mandatory true;
+          description
+            "The time on the most recent occasion at which any one or
+             more of this interface's counters suffered a
+             discontinuity.  If no such discontinuities have occurred
+             since the last re-initialization of the local management
+             subsystem, then this node contains the time the local
+             management subsystem re-initialized itself.";
+        }
+
+        leaf in-octets {
+          type yang:counter64;
+          description
+            "The total number of octets received on the interface,
+             including framing characters.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifHCInOctets";
+        }
+
+        leaf in-unicast-pkts {
+          type yang:counter64;
+          description
+            "The number of packets, delivered by this sub-layer to a
+             higher (sub-)layer, that were not addressed to a
+             multicast or broadcast address at this sub-layer.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts";
+        }
+
+        leaf in-broadcast-pkts {
+          type yang:counter64;
+          description
+            "The number of packets, delivered by this sub-layer to a
+             higher (sub-)layer, that were addressed to a broadcast
+             address at this sub-layer.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB -
+                       ifHCInBroadcastPkts";
+        }
+
+        leaf in-multicast-pkts {
+          type yang:counter64;
+          description
+            "The number of packets, delivered by this sub-layer to a
+             higher (sub-)layer, that were addressed to a multicast
+             address at this sub-layer.  For a MAC-layer protocol,
+             this includes both Group and Functional addresses.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB -
+                       ifHCInMulticastPkts";
+        }
+
+        leaf in-discards {
+          type yang:counter32;
+          description
+            "The number of inbound packets that were chosen to be
+             discarded even though no errors had been detected to
+             prevent their being deliverable to a higher-layer
+             protocol.  One possible reason for discarding such a
+             packet could be to free up buffer space.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifInDiscards";
+        }
+
+        leaf in-errors {
+          type yang:counter32;
+          description
+            "For packet-oriented interfaces, the number of inbound
+             packets that contained errors preventing them from being
+             deliverable to a higher-layer protocol.  For character-
+             oriented or fixed-length interfaces, the number of
+             inbound transmission units that contained errors
+             preventing them from being deliverable to a higher-layer
+             protocol.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifInErrors";
+        }
+
+        leaf in-unknown-protos {
+          type yang:counter32;
+          description
+            "For packet-oriented interfaces, the number of packets
+             received via the interface that were discarded because
+             of an unknown or unsupported protocol.  For
+             character-oriented or fixed-length interfaces that
+             support protocol multiplexing, the number of
+             transmission units received via the interface that were
+             discarded because of an unknown or unsupported protocol.
+             For any interface that does not support protocol
+             multiplexing, this counter is not present.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos";
+        }
+
+        leaf out-octets {
+          type yang:counter64;
+          description
+            "The total number of octets transmitted out of the
+             interface, including framing characters.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifHCOutOctets";
+        }
+
+        leaf out-unicast-pkts {
+          type yang:counter64;
+          description
+            "The total number of packets that higher-level protocols
+             requested be transmitted, and that were not addressed
+             to a multicast or broadcast address at this sub-layer,
+             including those that were discarded or not sent.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts";
+        }
+
+        leaf out-broadcast-pkts {
+          type yang:counter64;
+          description
+            "The total number of packets that higher-level protocols
+             requested be transmitted, and that were addressed to a
+             broadcast address at this sub-layer, including those
+             that were discarded or not sent.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB -
+                       ifHCOutBroadcastPkts";
+        }
+
+        leaf out-multicast-pkts {
+          type yang:counter64;
+          description
+            "The total number of packets that higher-level protocols
+             requested be transmitted, and that were addressed to a
+             multicast address at this sub-layer, including those
+             that were discarded or not sent.  For a MAC-layer
+             protocol, this includes both Group and Functional
+             addresses.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB -
+                       ifHCOutMulticastPkts";
+        }
+
+        leaf out-discards {
+          type yang:counter32;
+          description
+            "The number of outbound packets that were chosen to be
+             discarded even though no errors had been detected to
+             prevent their being transmitted.  One possible reason
+             for discarding such a packet could be to free up buffer
+             space.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifOutDiscards";
+        }
+
+        leaf out-errors {
+          type yang:counter32;
+          description
+            "For packet-oriented interfaces, the number of outbound
+             packets that could not be transmitted because of errors.
+             For character-oriented or fixed-length interfaces, the
+             number of outbound transmission units that could not be
+             transmitted because of errors.
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifOutErrors";
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/identity-ietf-routing-test/ietf-routing@2016-11-04.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/identity-ietf-routing-test/ietf-routing@2016-11-04.yang
new file mode 100644 (file)
index 0000000..16aca6f
--- /dev/null
@@ -0,0 +1,547 @@
+module ietf-routing {
+
+  yang-version "1.1";
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-routing";
+
+  prefix "rt";
+
+  import ietf-yang-types {
+    prefix "yang";
+  }
+
+  import ietf-interfaces {
+    prefix "if";
+  }
+
+  organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+    "WG Web:   <https://datatracker.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+     WG Chair: Lou Berger
+               <mailto:lberger@labn.net>
+     WG Chair: Kent Watsen
+               <mailto:kwatsen@juniper.net>
+     Editor:   Ladislav Lhotka
+               <mailto:lhotka@nic.cz>
+     Editor:   Acee Lindem
+               <mailto:acee@cisco.com>";
+
+  description
+    "This YANG module defines essential components for the management
+     of a routing subsystem.
+     Copyright (c) 2016 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject to
+     the license terms contained in, the Simplified BSD License set
+     forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+     The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
+     NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'MAY', and
+     'OPTIONAL' in the module text are to be interpreted as described
+     in RFC 2119.
+     This version of this YANG module is part of RFC 8022;
+     see the RFC itself for full legal notices.";
+
+  revision 2016-11-04 {
+    description
+      "Initial revision.";
+    reference
+      "RFC 8022: A YANG Data Model for Routing Management";
+  }
+
+  /* Features */
+
+  feature multiple-ribs {
+    description
+      "This feature indicates that the server supports user-defined
+       RIBs.
+       Servers that do not advertise this feature SHOULD provide
+       exactly one system-controlled RIB per supported address family
+       and make it also the default RIB.  This RIB then appears as an
+       entry of the list /routing-state/ribs/rib.";
+  }
+
+  feature router-id {
+    description
+      "This feature indicates that the server supports configuration
+       of an explicit 32-bit router ID that is used by some routing
+       protocols.
+       Servers that do not advertise this feature set a router ID
+       algorithmically, usually to one of the configured IPv4
+       addresses.  However, this algorithm is implementation
+       specific.";
+  }
+
+  /* Identities */
+
+  identity address-family {
+    description
+      "Base identity from which identities describing address
+       families are derived.";
+  }
+
+  identity ipv4 {
+    base address-family;
+    description
+      "This identity represents IPv4 address family.";
+  }
+
+  identity ipv6 {
+    base address-family;
+    description
+      "This identity represents IPv6 address family.";
+  }
+
+  identity control-plane-protocol {
+    description
+      "Base identity from which control-plane protocol identities are
+       derived.";
+  }
+
+  identity routing-protocol {
+    base control-plane-protocol;
+    description
+      "Identity from which Layer 3 routing protocol identities are
+       derived.";
+  }
+
+  identity direct {
+    base routing-protocol;
+    description
+      "Routing pseudo-protocol that provides routes to directly
+       connected networks.";
+  }
+
+  identity static {
+    base routing-protocol;
+    description
+      "Static routing pseudo-protocol.";
+  }
+
+  /* Type Definitions */
+
+  typedef route-preference {
+    type uint32;
+    description
+      "This type is used for route preferences.";
+  }
+
+  /* Groupings */
+
+  grouping address-family {
+    description
+      "This grouping provides a leaf identifying an address
+       family.";
+    leaf address-family {
+      type identityref {
+        base address-family;
+      }
+      mandatory "true";
+      description
+        "Address family.";
+    }
+  }
+
+  grouping router-id {
+    description
+      "This grouping provides router ID.";
+    leaf router-id {
+      type yang:dotted-quad;
+      description
+        "A 32-bit number in the form of a dotted quad that is used by
+         some routing protocols identifying a router.";
+      reference
+        "RFC 2328: OSPF Version 2.";
+    }
+  }
+
+  grouping special-next-hop {
+    description
+      "This grouping provides a leaf with an enumeration of special
+       next hops.";
+    leaf special-next-hop {
+      type enumeration {
+        enum blackhole {
+          description
+            "Silently discard the packet.";
+        }
+        enum unreachable {
+          description
+            "Discard the packet and notify the sender with an error
+             message indicating that the destination host is
+             unreachable.";
+        }
+        enum prohibit {
+          description
+            "Discard the packet and notify the sender with an error
+             message indicating that the communication is
+             administratively prohibited.";
+        }
+        enum receive {
+          description
+            "The packet will be received by the local system.";
+        }
+      }
+      description
+        "Options for special next hops.";
+    }
+  }
+
+  grouping next-hop-content {
+    description
+      "Generic parameters of next hops in static routes.";
+    choice next-hop-options {
+      mandatory "true";
+      description
+        "Options for next hops in static routes.
+         It is expected that further cases will be added through
+         augments from other modules.";
+      case simple-next-hop {
+        description
+          "This case represents a simple next hop consisting of the
+           next-hop address and/or outgoing interface.
+           Modules for address families MUST augment this case with a
+           leaf containing a next-hop address of that address
+           family.";
+        leaf outgoing-interface {
+          type if:interface-ref;
+          description
+            "Name of the outgoing interface.";
+        }
+      }
+      case special-next-hop {
+        uses special-next-hop;
+      }
+      case next-hop-list {
+        container next-hop-list {
+          description
+            "Container for multiple next-hops.";
+          list next-hop {
+            key "index";
+            description
+              "An entry of a next-hop list.
+               Modules for address families MUST augment this list
+               with a leaf containing a next-hop address of that
+               address family.";
+            leaf index {
+              type string;
+              description
+                "A user-specified identifier utilized to uniquely
+                 reference the next-hop entry in the next-hop list.
+                 The value of this index has no semantic meaning
+                 other than for referencing the entry.";
+            }
+            leaf outgoing-interface {
+              type if:interface-ref;
+              description
+                "Name of the outgoing interface.";
+            }
+          }
+        }
+      }
+    }
+  }
+
+  grouping next-hop-state-content {
+    description
+      "Generic parameters of next hops in state data.";
+    choice next-hop-options {
+      mandatory "true";
+      description
+        "Options for next hops in state data.
+         It is expected that further cases will be added through
+         augments from other modules, e.g., for recursive
+         next hops.";
+      case simple-next-hop {
+        description
+          "This case represents a simple next hop consisting of the
+           next-hop address and/or outgoing interface.
+           Modules for address families MUST augment this case with a
+           leaf containing a next-hop address of that address
+           family.";
+        leaf outgoing-interface {
+          type if:interface-state-ref;
+          description
+            "Name of the outgoing interface.";
+        }
+      }
+      case special-next-hop {
+        uses special-next-hop;
+      }
+      case next-hop-list {
+        container next-hop-list {
+          description
+            "Container for multiple next hops.";
+          list next-hop {
+            description
+              "An entry of a next-hop list.
+               Modules for address families MUST augment this list
+               with a leaf containing a next-hop address of that
+               address family.";
+            leaf outgoing-interface {
+              type if:interface-state-ref;
+              description
+                "Name of the outgoing interface.";
+            }
+          }
+        }
+      }
+    }
+  }
+
+  grouping route-metadata {
+    description
+      "Common route metadata.";
+    leaf source-protocol {
+      type identityref {
+        base routing-protocol;
+      }
+      mandatory "true";
+      description
+        "Type of the routing protocol from which the route
+         originated.";
+    }
+    leaf active {
+      type empty;
+      description
+        "Presence of this leaf indicates that the route is preferred
+         among all routes in the same RIB that have the same
+         destination prefix.";
+    }
+    leaf last-updated {
+      type yang:date-and-time;
+      description
+        "Time stamp of the last modification of the route.  If the
+         route was never modified, it is the time when the route was
+         inserted into the RIB.";
+    }
+  }
+
+  /* State data */
+
+  container routing-state {
+    config "false";
+    description
+      "State data of the routing subsystem.";
+    uses router-id {
+      description
+        "Global router ID.
+         It may be either configured or assigned algorithmically by
+         the implementation.";
+    }
+    container interfaces {
+      description
+        "Network-layer interfaces used for routing.";
+      leaf-list interface {
+        type if:interface-state-ref;
+        description
+          "Each entry is a reference to the name of a configured
+           network-layer interface.";
+      }
+    }
+    container control-plane-protocols {
+      description
+        "Container for the list of routing protocol instances.";
+      list control-plane-protocol {
+        key "type name";
+        description
+          "State data of a control-plane protocol instance.
+           An implementation MUST provide exactly one
+           system-controlled instance of the 'direct'
+           pseudo-protocol.  Instances of other control-plane
+           protocols MAY be created by configuration.";
+        leaf type {
+          type identityref {
+            base control-plane-protocol;
+          }
+          description
+            "Type of the control-plane protocol.";
+        }
+        leaf name {
+          type string;
+          description
+            "The name of the control-plane protocol instance.
+             For system-controlled instances this name is persistent,
+             i.e., it SHOULD NOT change across reboots.";
+        }
+      }
+    }
+    container ribs {
+      description
+        "Container for RIBs.";
+      list rib {
+        key "name";
+        min-elements "1";
+        description
+          "Each entry represents a RIB identified by the 'name' key.
+           All routes in a RIB MUST belong to the same address
+           family.
+           An implementation SHOULD provide one system-controlled
+           default RIB for each supported address family.";
+        leaf name {
+          type string;
+          description
+            "The name of the RIB.";
+        }
+        uses address-family;
+        leaf default-rib {
+          if-feature "multiple-ribs";
+          type boolean;
+          default "true";
+          description
+            "This flag has the value of 'true' if and only if the RIB
+             is the default RIB for the given address family.
+             By default, control-plane protocols place their routes
+             in the default RIBs.";
+        }
+        container routes {
+          description
+            "Current content of the RIB.";
+          list route {
+            description
+              "A RIB route entry.  This data node MUST be augmented
+               with information specific for routes of each address
+               family.";
+            leaf route-preference {
+              type route-preference;
+              description
+                "This route attribute, also known as administrative
+                 distance, allows for selecting the preferred route
+                 among routes with the same destination prefix.  A
+                 smaller value means a more preferred route.";
+            }
+            container next-hop {
+              description
+                "Route's next-hop attribute.";
+              uses next-hop-state-content;
+            }
+            uses route-metadata;
+          }
+        }
+        action active-route {
+          description
+            "Return the active RIB route that is used for the
+             destination address.
+             Address-family-specific modules MUST augment input
+             parameters with a leaf named 'destination-address'.";
+          output {
+            container route {
+              description
+                "The active RIB route for the specified destination.
+                 If no route exists in the RIB for the destination
+                 address, no output is returned.
+                 Address-family-specific modules MUST augment this
+                 container with appropriate route contents.";
+              container next-hop {
+                description
+                  "Route's next-hop attribute.";
+                uses next-hop-state-content;
+              }
+              uses route-metadata;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /* Configuration Data */
+
+  container routing {
+    description
+      "Configuration parameters for the routing subsystem.";
+    uses router-id {
+      if-feature "router-id";
+      description
+        "Configuration of the global router ID.  Routing protocols
+         that use router ID can use this parameter or override it
+         with another value.";
+    }
+    container control-plane-protocols {
+      description
+        "Configuration of control-plane protocol instances.";
+      list control-plane-protocol {
+        key "type name";
+        description
+          "Each entry contains configuration of a control-plane
+           protocol instance.";
+        leaf type {
+          type identityref {
+            base control-plane-protocol;
+          }
+          description
+            "Type of the control-plane protocol - an identity derived
+             from the 'control-plane-protocol' base identity.";
+        }
+        leaf name {
+          type string;
+          description
+            "An arbitrary name of the control-plane protocol
+             instance.";
+        }
+        leaf description {
+          type string;
+          description
+            "Textual description of the control-plane protocol
+             instance.";
+        }
+        container static-routes {
+          when "derived-from-or-self(../type, 'rt:static')" {
+            description
+              "This container is only valid for the 'static' routing
+               protocol.";
+          }
+          description
+            "Configuration of the 'static' pseudo-protocol.
+             Address-family-specific modules augment this node with
+             their lists of routes.";
+        }
+      }
+    }
+    container ribs {
+      description
+        "Configuration of RIBs.";
+      list rib {
+        key "name";
+        description
+          "Each entry contains configuration for a RIB identified by
+           the 'name' key.
+           Entries having the same key as a system-controlled entry
+           of the list /routing-state/ribs/rib are used for
+           configuring parameters of that entry.  Other entries
+           define additional user-controlled RIBs.";
+        leaf name {
+          type string;
+          description
+            "The name of the RIB.
+             For system-controlled entries, the value of this leaf
+             must be the same as the name of the corresponding entry
+             in state data.
+             For user-controlled entries, an arbitrary name can be
+             used.";
+        }
+        uses address-family {
+          description
+            "Address family of the RIB.
+             It is mandatory for user-controlled RIBs.  For
+             system-controlled RIBs it can be omitted; otherwise, it
+             must match the address family of the corresponding state
+             entry.";
+          refine "address-family" {
+            mandatory "false";
+          }
+        }
+        leaf description {
+          type string;
+          description
+            "Textual description of the RIB.";
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/identity-ietf-routing-test/ietf-yang-types@2013-07-15.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/identity-ietf-routing-test/ietf-yang-types@2013-07-15.yang
new file mode 100644 (file)
index 0000000..a144941
--- /dev/null
@@ -0,0 +1,435 @@
+module ietf-yang-types {
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+  prefix "yang";
+
+  organization
+   "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+   "WG Web:   <http://tools.ietf.org/wg/netmod/>
+    WG List:  <mailto:netmod@ietf.org>
+    WG Chair: David Kessens
+              <mailto:david.kessens@nsn.com>
+    WG Chair: Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>
+    Editor:   Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>";
+
+  description
+   "This module contains a collection of generally useful derived
+    YANG data types.
+    Copyright (c) 2013 IETF Trust and the persons identified as
+    authors of the code.  All rights reserved.
+    Redistribution and use in source and binary forms, with or
+    without modification, is permitted pursuant to, and subject
+    to the license terms contained in, the Simplified BSD License
+    set forth in Section 4.c of the IETF Trust's Legal Provisions
+    Relating to IETF Documents
+    (http://trustee.ietf.org/license-info).
+    This version of this YANG module is part of RFC 6991; see
+    the RFC itself for full legal notices.";
+
+  revision 2013-07-15 {
+    description
+     "This revision adds the following new data types:
+      - yang-identifier
+      - hex-string
+      - uuid
+      - dotted-quad";
+    reference
+     "RFC 6991: Common YANG Data Types";
+  }
+
+  revision 2010-09-24 {
+    description
+     "Initial revision.";
+    reference
+     "RFC 6021: Common YANG Data Types";
+  }
+
+  /*** collection of counter and gauge types ***/
+
+  typedef counter32 {
+    type uint32;
+    description
+     "The counter32 type represents a non-negative integer
+      that monotonically increases until it reaches a
+      maximum value of 2^32-1 (4294967295 decimal), when it
+      wraps around and starts increasing again from zero.
+      Counters have no defined 'initial' value, and thus, a
+      single value of a counter has (in general) no information
+      content.  Discontinuities in the monotonically increasing
+      value normally occur at re-initialization of the
+      management system, and at other times as specified in the
+      description of a schema node using this type.  If such
+      other times can occur, for example, the creation of
+      a schema node of type counter32 at times other than
+      re-initialization, then a corresponding schema node
+      should be defined, with an appropriate type, to indicate
+      the last discontinuity.
+      The counter32 type should not be used for configuration
+      schema nodes.  A default statement SHOULD NOT be used in
+      combination with the type counter32.
+      In the value set and its semantics, this type is equivalent
+      to the Counter32 type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef zero-based-counter32 {
+    type yang:counter32;
+    default "0";
+    description
+     "The zero-based-counter32 type represents a counter32
+      that has the defined 'initial' value zero.
+      A schema node of this type will be set to zero (0) on creation
+      and will thereafter increase monotonically until it reaches
+      a maximum value of 2^32-1 (4294967295 decimal), when it
+      wraps around and starts increasing again from zero.
+      Provided that an application discovers a new schema node
+      of this type within the minimum time to wrap, it can use the
+      'initial' value as a delta.  It is important for a management
+      station to be aware of this minimum time and the actual time
+      between polls, and to discard data if the actual time is too
+      long or there is no defined minimum time.
+      In the value set and its semantics, this type is equivalent
+      to the ZeroBasedCounter32 textual convention of the SMIv2.";
+    reference
+      "RFC 4502: Remote Network Monitoring Management Information
+                 Base Version 2";
+  }
+
+  typedef counter64 {
+    type uint64;
+    description
+     "The counter64 type represents a non-negative integer
+      that monotonically increases until it reaches a
+      maximum value of 2^64-1 (18446744073709551615 decimal),
+      when it wraps around and starts increasing again from zero.
+      Counters have no defined 'initial' value, and thus, a
+      single value of a counter has (in general) no information
+      content.  Discontinuities in the monotonically increasing
+      value normally occur at re-initialization of the
+      management system, and at other times as specified in the
+      description of a schema node using this type.  If such
+      other times can occur, for example, the creation of
+      a schema node of type counter64 at times other than
+      re-initialization, then a corresponding schema node
+      should be defined, with an appropriate type, to indicate
+      the last discontinuity.
+      The counter64 type should not be used for configuration
+      schema nodes.  A default statement SHOULD NOT be used in
+      combination with the type counter64.
+      In the value set and its semantics, this type is equivalent
+      to the Counter64 type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef zero-based-counter64 {
+    type yang:counter64;
+    default "0";
+    description
+     "The zero-based-counter64 type represents a counter64 that
+      has the defined 'initial' value zero.
+      A schema node of this type will be set to zero (0) on creation
+      and will thereafter increase monotonically until it reaches
+      a maximum value of 2^64-1 (18446744073709551615 decimal),
+      when it wraps around and starts increasing again from zero.
+      Provided that an application discovers a new schema node
+      of this type within the minimum time to wrap, it can use the
+      'initial' value as a delta.  It is important for a management
+      station to be aware of this minimum time and the actual time
+      between polls, and to discard data if the actual time is too
+      long or there is no defined minimum time.
+      In the value set and its semantics, this type is equivalent
+      to the ZeroBasedCounter64 textual convention of the SMIv2.";
+    reference
+     "RFC 2856: Textual Conventions for Additional High Capacity
+                Data Types";
+  }
+
+  typedef gauge32 {
+    type uint32;
+    description
+     "The gauge32 type represents a non-negative integer, which
+      may increase or decrease, but shall never exceed a maximum
+      value, nor fall below a minimum value.  The maximum value
+      cannot be greater than 2^32-1 (4294967295 decimal), and
+      the minimum value cannot be smaller than 0.  The value of
+      a gauge32 has its maximum value whenever the information
+      being modeled is greater than or equal to its maximum
+      value, and has its minimum value whenever the information
+      being modeled is smaller than or equal to its minimum value.
+      If the information being modeled subsequently decreases
+      below (increases above) the maximum (minimum) value, the
+      gauge32 also decreases (increases).
+      In the value set and its semantics, this type is equivalent
+      to the Gauge32 type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef gauge64 {
+    type uint64;
+    description
+     "The gauge64 type represents a non-negative integer, which
+      may increase or decrease, but shall never exceed a maximum
+      value, nor fall below a minimum value.  The maximum value
+      cannot be greater than 2^64-1 (18446744073709551615), and
+      the minimum value cannot be smaller than 0.  The value of
+      a gauge64 has its maximum value whenever the information
+      being modeled is greater than or equal to its maximum
+      value, and has its minimum value whenever the information
+      being modeled is smaller than or equal to its minimum value.
+      If the information being modeled subsequently decreases
+      below (increases above) the maximum (minimum) value, the
+      gauge64 also decreases (increases).
+      In the value set and its semantics, this type is equivalent
+      to the CounterBasedGauge64 SMIv2 textual convention defined
+      in RFC 2856";
+    reference
+     "RFC 2856: Textual Conventions for Additional High Capacity
+                Data Types";
+  }
+
+  /*** collection of identifier-related types ***/
+
+  typedef object-identifier {
+    type string {
+      pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+            + '(\.(0|([1-9]\d*)))*';
+    }
+    description
+     "The object-identifier type represents administratively
+      assigned names in a registration-hierarchical-name tree.
+      Values of this type are denoted as a sequence of numerical
+      non-negative sub-identifier values.  Each sub-identifier
+      value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers
+      are separated by single dots and without any intermediate
+      whitespace.
+      The ASN.1 standard restricts the value space of the first
+      sub-identifier to 0, 1, or 2.  Furthermore, the value space
+      of the second sub-identifier is restricted to the range
+      0 to 39 if the first sub-identifier is 0 or 1.  Finally,
+      the ASN.1 standard requires that an object identifier
+      has always at least two sub-identifiers.  The pattern
+      captures these restrictions.
+      Although the number of sub-identifiers is not limited,
+      module designers should realize that there may be
+      implementations that stick with the SMIv2 limit of 128
+      sub-identifiers.
+      This type is a superset of the SMIv2 OBJECT IDENTIFIER type
+      since it is not restricted to 128 sub-identifiers.  Hence,
+      this type SHOULD NOT be used to represent the SMIv2 OBJECT
+      IDENTIFIER type; the object-identifier-128 type SHOULD be
+      used instead.";
+    reference
+     "ISO9834-1: Information technology -- Open Systems
+      Interconnection -- Procedures for the operation of OSI
+      Registration Authorities: General procedures and top
+      arcs of the ASN.1 Object Identifier tree";
+  }
+
+  typedef object-identifier-128 {
+    type object-identifier {
+      pattern '\d*(\.\d*){1,127}';
+    }
+    description
+     "This type represents object-identifiers restricted to 128
+      sub-identifiers.
+      In the value set and its semantics, this type is equivalent
+      to the OBJECT IDENTIFIER type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef yang-identifier {
+    type string {
+      length "1..max";
+      pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
+      pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*';
+    }
+    description
+      "A YANG identifier string as defined by the 'identifier'
+       rule in Section 12 of RFC 6020.  An identifier must
+       start with an alphabetic character or an underscore
+       followed by an arbitrary sequence of alphabetic or
+       numeric characters, underscores, hyphens, or dots.
+       A YANG identifier MUST NOT start with any possible
+       combination of the lowercase or uppercase character
+       sequence 'xml'.";
+    reference
+      "RFC 6020: YANG - A Data Modeling Language for the Network
+                 Configuration Protocol (NETCONF)";
+  }
+
+  /*** collection of types related to date and time***/
+
+  typedef date-and-time {
+    type string {
+      pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+            + '(Z|[\+\-]\d{2}:\d{2})';
+    }
+    description
+     "The date-and-time type is a profile of the ISO 8601
+      standard for representation of dates and times using the
+      Gregorian calendar.  The profile is defined by the
+      date-time production in Section 5.6 of RFC 3339.
+      The date-and-time type is compatible with the dateTime XML
+      schema type with the following notable exceptions:
+      (a) The date-and-time type does not allow negative years.
+      (b) The date-and-time time-offset -00:00 indicates an unknown
+          time zone (see RFC 3339) while -00:00 and +00:00 and Z
+          all represent the same time zone in dateTime.
+      (c) The canonical format (see below) of data-and-time values
+          differs from the canonical format used by the dateTime XML
+          schema type, which requires all times to be in UTC using
+          the time-offset 'Z'.
+      This type is not equivalent to the DateAndTime textual
+      convention of the SMIv2 since RFC 3339 uses a different
+      separator between full-date and full-time and provides
+      higher resolution of time-secfrac.
+      The canonical format for date-and-time values with a known time
+      zone uses a numeric time zone offset that is calculated using
+      the device's configured known offset to UTC time.  A change of
+      the device's offset to UTC time will cause date-and-time values
+      to change accordingly.  Such changes might happen periodically
+      in case a server follows automatically daylight saving time
+      (DST) time zone offset changes.  The canonical format for
+      date-and-time values with an unknown time zone (usually
+      referring to the notion of local time) uses the time-offset
+      -00:00.";
+    reference
+     "RFC 3339: Date and Time on the Internet: Timestamps
+      RFC 2579: Textual Conventions for SMIv2
+      XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
+  }
+
+  typedef timeticks {
+    type uint32;
+    description
+     "The timeticks type represents a non-negative integer that
+      represents the time, modulo 2^32 (4294967296 decimal), in
+      hundredths of a second between two epochs.  When a schema
+      node is defined that uses this type, the description of
+      the schema node identifies both of the reference epochs.
+      In the value set and its semantics, this type is equivalent
+      to the TimeTicks type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef timestamp {
+    type yang:timeticks;
+    description
+     "The timestamp type represents the value of an associated
+      timeticks schema node at which a specific occurrence
+      happened.  The specific occurrence must be defined in the
+      description of any schema node defined using this type.  When
+      the specific occurrence occurred prior to the last time the
+      associated timeticks attribute was zero, then the timestamp
+      value is zero.  Note that this requires all timestamp values
+      to be reset to zero when the value of the associated timeticks
+      attribute reaches 497+ days and wraps around to zero.
+      The associated timeticks schema node must be specified
+      in the description of any schema node using this type.
+      In the value set and its semantics, this type is equivalent
+      to the TimeStamp textual convention of the SMIv2.";
+    reference
+     "RFC 2579: Textual Conventions for SMIv2";
+  }
+
+  /*** collection of generic address types ***/
+
+  typedef phys-address {
+    type string {
+      pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+    }
+
+    description
+     "Represents media- or physical-level addresses represented
+      as a sequence octets, each octet represented by two hexadecimal
+      numbers.  Octets are separated by colons.  The canonical
+      representation uses lowercase characters.
+      In the value set and its semantics, this type is equivalent
+      to the PhysAddress textual convention of the SMIv2.";
+    reference
+     "RFC 2579: Textual Conventions for SMIv2";
+  }
+
+  typedef mac-address {
+    type string {
+      pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+    }
+    description
+     "The mac-address type represents an IEEE 802 MAC address.
+      The canonical representation uses lowercase characters.
+      In the value set and its semantics, this type is equivalent
+      to the MacAddress textual convention of the SMIv2.";
+    reference
+     "IEEE 802: IEEE Standard for Local and Metropolitan Area
+                Networks: Overview and Architecture
+      RFC 2579: Textual Conventions for SMIv2";
+  }
+
+  /*** collection of XML-specific types ***/
+
+  typedef xpath1.0 {
+    type string;
+    description
+     "This type represents an XPATH 1.0 expression.
+      When a schema node is defined that uses this type, the
+      description of the schema node MUST specify the XPath
+      context in which the XPath expression is evaluated.";
+    reference
+     "XPATH: XML Path Language (XPath) Version 1.0";
+  }
+
+  /*** collection of string types ***/
+
+  typedef hex-string {
+    type string {
+      pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+    }
+    description
+     "A hexadecimal string with octets represented as hex digits
+      separated by colons.  The canonical representation uses
+      lowercase characters.";
+  }
+
+  typedef uuid {
+    type string {
+      pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-'
+            + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
+    }
+    description
+     "A Universally Unique IDentifier in the string representation
+      defined in RFC 4122.  The canonical representation uses
+      lowercase characters.
+      The following is an example of a UUID in string representation:
+      f81d4fae-7dec-11d0-a765-00a0c91e6bf6
+      ";
+    reference
+     "RFC 4122: A Universally Unique IDentifier (UUID) URN
+                Namespace";
+  }
+
+  typedef dotted-quad {
+    type string {
+      pattern
+        '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+      + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
+    }
+    description
+      "An unsigned 32-bit number expressed in the dotted-quad
+       notation, i.e., four octets written as decimal numbers
+       and separated with the '.' (full stop) character.";
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/name-collision-within-case/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/name-collision-within-case/bar.yang
new file mode 100644 (file)
index 0000000..634e2bb
--- /dev/null
@@ -0,0 +1,29 @@
+module bar {
+    yang-version 1.1;
+    namespace bar;
+    prefix bar;
+
+    revision "2018-02-11";
+
+    grouping foo {
+      container bar {
+
+      }
+    }
+
+    container cont {
+      choice bar {
+        case foo {
+          uses foo;
+        }
+      }
+    }
+
+    augment "/cont" {
+      choice foo {
+        case foo {
+          uses foo;
+        }
+      }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/name-collision-within-case/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/name-collision-within-case/baz.yang
new file mode 100644 (file)
index 0000000..49df16b
--- /dev/null
@@ -0,0 +1,25 @@
+module baz {
+    yang-version 1.1;
+    namespace baz;
+    prefix baz;
+
+    revision "2018-02-28";
+
+    grouping foo {
+      container bar {
+
+      }
+    }
+
+    container cont {
+      choice bar {
+        case foo {
+          uses foo;
+        }
+      }
+
+      container bar {
+
+      }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/name-collision-within-case/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/name-collision-within-case/foo.yang
new file mode 100644 (file)
index 0000000..21f661f
--- /dev/null
@@ -0,0 +1,27 @@
+module foo {
+    yang-version 1.1;
+    namespace foo;
+    prefix foo;
+
+    revision "2018-02-11";
+
+    grouping foo {
+      container bar {
+
+      }
+    }
+
+    container cont {
+      choice bar {
+        case foo {
+          uses foo;
+        }
+      }
+    }
+
+    augment "/cont/bar" {
+      case foo2 {
+        uses foo;
+      }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/qnameDuplicity/augment/two-cases.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/qnameDuplicity/augment/two-cases.yang
new file mode 100644 (file)
index 0000000..e18cefa
--- /dev/null
@@ -0,0 +1,31 @@
+module two-cases {
+
+    namespace "urn:test:two:cases";
+    prefix "two";
+
+    organization "OPEN DAYLIGHT";
+    contact "http://www.opendaylight.org/";
+
+    revision 2013-07-09 {
+
+    }
+
+    container cont {
+        choice choice {
+            default second;
+            case first {
+                leaf leaf-case1 {
+                    type int32;
+                }
+            }
+        }
+    }
+
+    augment "/cont/choice/" {
+        case forth {
+            leaf leaf-case1 {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/qnameDuplicity/two-same-node-in-choice/two-same-nodes-in-choice-case.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/qnameDuplicity/two-same-node-in-choice/two-same-nodes-in-choice-case.yang
new file mode 100644 (file)
index 0000000..1f1db32
--- /dev/null
@@ -0,0 +1,28 @@
+module two-same-nodes-in-choice-case {
+
+    namespace "urn:test:two:same-nodes-in-choice-case";
+    prefix "two";
+
+    organization "OPEN DAYLIGHT";
+    contact "http://www.opendaylight.org/";
+
+    revision 2013-07-09 {
+
+    }
+
+    container cont3 {
+        choice choice3 {
+            default second;
+            case first {
+                leaf leaf-case1 {
+                    type int32;
+                }
+            }
+            case second {
+                leaf leaf-case1 {
+                    type int32;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/bugs/yangtools826/example.yang b/yang/yang-parser-rfc7950/src/test/resources/bugs/yangtools826/example.yang
new file mode 100644 (file)
index 0000000..fb9e8fc
--- /dev/null
@@ -0,0 +1,39 @@
+module example {
+    yang-version 1.1;
+
+    namespace "http://www.example.com";
+
+    prefix "ex";
+
+    revision "2017-10-11";
+
+    identity interface-type;
+
+    identity ethernet {
+       base interface-type;
+    }
+
+    list interface {
+       key name;
+
+       leaf name {
+           type string;
+       }
+
+       leaf type {
+           type identityref {
+              base interface-type;
+          }
+       }
+    }
+
+    augment "/interface" {
+        when 'derived-from-or-self (type, "ex:ethernet")';
+
+        container extension {
+            leaf speed {
+                type int32;
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/case-test/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/case-test/bar.yang
new file mode 100644 (file)
index 0000000..d90a570
--- /dev/null
@@ -0,0 +1,168 @@
+module bar {
+    yang-version 1;
+    namespace "bar";
+    prefix "br";
+
+    revision "2015-09-09" {
+            reference "NO REF";
+    }
+
+    container sh-root-fff {
+        config false;
+        choice sh-choice-fff {
+            config false;
+            container sh-c-fff {
+                config false;
+            }
+        }
+    }
+
+    container sh-root-ffn {
+        config false;
+        choice sh-choice-ffn {
+            config false;
+            container sh-c-ffn {
+            }
+        }
+    }
+
+    container sh-root-fnf {
+        config false;
+        choice sh-choice-fnf {
+            container sh-c-fnf {
+                config false;
+            }
+        }
+    }
+
+    container sh-root-nff {
+        choice sh-choice-nff {
+            config false;
+            container sh-c-nff {
+                config false;
+            }
+        }
+    }
+
+    container sh-root-nnf {
+        choice sh-choice-nnf {
+            container sh-c-nnf {
+                config false;
+            }
+        }
+    }
+
+    container sh-root-nfn {
+        choice sh-choice-nfn {
+            config false;
+            container sh-c-nfn {
+            }
+        }
+    }
+
+    container sh-root-fnn {
+        config false;
+        choice sh-choice-fnn {
+            container sh-c-fnn {
+            }
+        }
+    }
+
+    container sh-root-ttt {
+        config true;
+        choice sh-choice-ttt {
+            config true;
+            container sh-c-ttt {
+                config true;
+            }
+        }
+    }
+
+    container sh-root-ntt {
+        choice sh-choice-ntt {
+            config true;
+            container sh-c-ntt {
+                config true;
+            }
+        }
+    }
+
+    container sh-root-tnt {
+        config true;
+        choice sh-choice-tnt {
+            container sh-c-tnt {
+                config true;
+            }
+        }
+    }
+
+    container sh-root-ttn {
+        config true;
+        choice sh-choice-ttt {
+            config true;
+            container sh-c-ttt {
+            }
+        }
+    }
+
+    container sh-root-tnn {
+        config true;
+        choice sh-choice-tnn {
+            container sh-c-tnn {
+            }
+        }
+    }
+
+    container sh-root-ntn {
+        choice sh-choice-ntn {
+            config true;
+            container sh-c-ntn {
+            }
+        }
+    }
+
+    container sh-root-nnt {
+        choice sh-choice-nnt {
+            container sh-c-nnt {
+                config true;
+            }
+        }
+    }
+
+    container sh-root-tff {
+        config true;
+        choice sh-choice-tff {
+            config false;
+            container c-tff {
+                config false;
+            }
+        }
+    }
+
+    container sh-root-tfn {
+        config true;
+        choice sh-choice-tfn {
+            config false;
+            container sh-c-tfn {
+            }
+        }
+    }
+
+    container sh-root-tnf {
+        config true;
+        choice sh-choice-tnf {
+            container sh-c-tnf {
+                config false;
+            }
+        }
+    }
+
+    container sh-root-ntf {
+        choice sh-choice-ntf {
+            config true;
+            container sh-c-ntf {
+                config false;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/case-test/case-test-exceptions/case/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/case-test/case-test-exceptions/case/foo.yang
new file mode 100644 (file)
index 0000000..0d33a9b
--- /dev/null
@@ -0,0 +1,20 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix "fo";
+
+    revision "2015-09-09" {
+            reference "NO REF";
+    }
+
+    container root-fnt {
+        config false;
+        choice choice-fnt {
+            case case-fnt {
+                container c-fnt {
+                    config true;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/case-test/case-test-exceptions/choice/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/case-test/case-test-exceptions/choice/foo.yang
new file mode 100644 (file)
index 0000000..febd3ad
--- /dev/null
@@ -0,0 +1,20 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix "fo";
+
+    revision "2015-09-09" {
+            reference "NO REF";
+    }
+
+    container root-ftn {
+        config false;
+        choice choice-ftn {
+            config true;
+            case case-ftn {
+                container c-ftn {
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/case-test/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/case-test/foo.yang
new file mode 100644 (file)
index 0000000..269fee7
--- /dev/null
@@ -0,0 +1,206 @@
+module foo {
+    yang-version 1;
+    namespace "foo";
+    prefix "fo";
+
+    import bar { prefix br; }
+
+    revision "2015-09-09" {
+            reference "NO REF";
+    }
+
+    container root-fff {
+        config false;
+        choice choice-fff {
+            config false;
+            case case-fff {
+                container c-fff {
+                    config false;
+                }
+            }
+        }
+    }
+
+    container root-ffn {
+        config false;
+        choice choice-ffn {
+            config false;
+            case case-ffn {
+                container c-ffn {
+                }
+            }
+        }
+    }
+
+    container root-fnf {
+        config false;
+        choice choice-fnf {
+            case case-fnf {
+                container c-fnf {
+                    config false;
+                }
+            }
+        }
+    }
+
+    container root-nff {
+        choice choice-nff {
+            config false;
+            case case-nff {
+                container c-nff {
+                    config false;
+                }
+            }
+        }
+    }
+
+    container root-nnf {
+        choice choice-nnf {
+            case case-nnf {
+                container c-nnf {
+                    config false;
+                }
+            }
+        }
+    }
+
+    container root-nfn {
+        choice choice-nfn {
+            config false;
+            case case-nfn {
+                container c-nfn {
+                }
+            }
+        }
+    }
+
+    container root-fnn {
+        config false;
+        choice choice-fnn {
+            case case-fnn {
+                container c-fnn {
+                }
+            }
+        }
+    }
+
+    container root-ttt {
+        config true;
+        choice choice-ttt {
+            config true;
+            case case-ttt {
+                container c-ttt {
+                    config true;
+                }
+            }
+        }
+    }
+
+    container root-ntt {
+        choice choice-ntt {
+            config true;
+            case case-ntt {
+                container c-ntt {
+                    config true;
+                }
+            }
+        }
+    }
+
+    container root-tnt {
+        config true;
+        choice choice-tnt {
+            case case-tnt {
+                container c-tnt {
+                    config true;
+                }
+            }
+        }
+    }
+
+    container root-ttn {
+        config true;
+        choice choice-ttt {
+            config true;
+            case case-ttt {
+                container c-ttt {
+                }
+            }
+        }
+    }
+
+    container root-tnn {
+        config true;
+        choice choice-tnn {
+            case case-tnn {
+                container c-tnn {
+                }
+            }
+        }
+    }
+
+    container root-ntn {
+        choice choice-ntn {
+            config true;
+            case case-ntn {
+                container c-ntn {
+                }
+            }
+        }
+    }
+
+    container root-nnt {
+        choice choice-nnt {
+            case case-nnt {
+                container c-nnt {
+                    config true;
+                }
+            }
+        }
+    }
+
+    container root-tff {
+        config true;
+        choice choice-tff {
+            config false;
+            case case-tff {
+                container c-tff {
+                    config false;
+                }
+            }
+        }
+    }
+
+    container root-tfn {
+        config true;
+        choice choice-tfn {
+            config false;
+            case case-tfn {
+                container c-tfn {
+                }
+            }
+        }
+    }
+
+    container root-tnf {
+        config true;
+        choice choice-tnf {
+            case case-tnf {
+                container c-tnf {
+                    config false;
+                }
+            }
+        }
+    }
+
+    container root-ntf {
+        choice choice-ntf {
+            config true;
+            case case-ntf {
+                container c-ntf {
+                    config false;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/choice-case-type-test-models/augment-monitoring@2013-07-01.yang b/yang/yang-parser-rfc7950/src/test/resources/choice-case-type-test-models/augment-monitoring@2013-07-01.yang
new file mode 100644 (file)
index 0000000..5c48c1c
--- /dev/null
@@ -0,0 +1,25 @@
+module augment-monitoring {
+    yang-version 1;
+    namespace "urn:ietf:params:xml:ns:yang:augment-monitoring";
+    prefix "amon";
+
+    import choice-monitoring { prefix nm; }
+
+    revision "2013-07-01" {
+            reference "NO REF";
+    }
+
+    augment "/nm:netconf-state/nm:datastores/nm:datastore/nm:locks/nm:lock-type" {
+
+        case anonymous-lock {
+            leaf lock-time {
+                type uint32;
+            }
+        }
+
+        leaf leaf-aug-case {
+            type string;
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/choice-case-type-test-models/choice-monitoring@2013-07-01.yang b/yang/yang-parser-rfc7950/src/test/resources/choice-case-type-test-models/choice-monitoring@2013-07-01.yang
new file mode 100644 (file)
index 0000000..03a518c
--- /dev/null
@@ -0,0 +1,30 @@
+module choice-monitoring {
+  yang-version 1;
+  namespace "urn:ietf:params:xml:ns:yang:choice-monitoring";
+  prefix "ncm";
+
+  revision 2013-07-01 {
+  }
+
+  container netconf-state {
+    config false;
+
+    container datastores {
+      list datastore {
+        key name;
+
+        leaf name {
+          type string;
+        }
+        container locks {
+          choice lock-type {
+
+            case original-lock {
+            }
+            
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/constraint-definitions-test/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/constraint-definitions-test/foo.yang
new file mode 100644 (file)
index 0000000..395e8b5
--- /dev/null
@@ -0,0 +1,73 @@
+module foo {
+    namespace foo-namespace;
+    prefix foo-prefix;
+
+    revision 2016-09-20;
+
+    leaf mandatory-leaf-1 {
+        type int32;
+        mandatory true;
+        when "foo = 'bar'";
+        must "bar != 'foo'";
+    }
+
+    leaf mandatory-leaf-2 {
+        type int32;
+        mandatory true;
+        when "foo = 'bar'";
+        must "bar != 'foo'";
+    }
+
+    leaf mandatory-leaf-3 {
+        type int32;
+        mandatory false;
+        when "foo = 'bar'";
+        must "bar != 'foo'";
+    }
+
+    leaf mandatory-leaf-4 {
+        type int32;
+        mandatory false;
+        when "foo = 'baz'";
+        must "bar != 'foo'";
+    }
+
+    leaf mandatory-leaf-5 {
+        type int32;
+        mandatory false;
+        when "foo = 'baz'";
+        must "baz != 'foo'";
+    }
+
+    leaf-list constrained-leaf-list-1 {
+        type int32;
+        when "foo = 'bar'";
+        must "bar != 'foo'";
+        min-elements 10;
+        max-elements 100;
+    }
+
+    leaf-list constrained-leaf-list-2 {
+        type int32;
+        when "foo = 'bar'";
+        must "bar != 'foo'";
+        min-elements 10;
+        max-elements 100;
+    }
+
+    leaf-list constrained-leaf-list-3 {
+        type int32;
+        when "foo = 'bar'";
+        must "bar != 'foo'";
+        min-elements 50;
+        max-elements 500;
+    }
+
+    leaf-list constrained-leaf-list-4 {
+        type int32;
+        when "foo = 'bar'";
+        must "bar != 'foo'";
+        min-elements 50;
+        max-elements 100;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/context-augment-test/test1.yang b/yang/yang-parser-rfc7950/src/test/resources/context-augment-test/test1.yang
new file mode 100644 (file)
index 0000000..18d81a8
--- /dev/null
@@ -0,0 +1,34 @@
+module test1 {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test1";
+    prefix "t1";
+
+    import test3 {
+        prefix "t3";
+        revision-date 2013-06-18;
+    }
+
+    import test2 {
+        prefix "t2";
+        revision-date 2013-06-18;
+    }
+
+    import test4 {
+        prefix "t4";
+        revision-date 2013-06-18;
+    }
+
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+        revision 2013-06-18 {
+    }
+
+    augment "/t4:interfaces/t4:ifEntry/t3:augment-holder/t2:schemas" {
+        when "if:ifType='ds0'";
+        leaf id {
+            type string;
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/context-augment-test/test2.yang b/yang/yang-parser-rfc7950/src/test/resources/context-augment-test/test2.yang
new file mode 100644 (file)
index 0000000..d8430c1
--- /dev/null
@@ -0,0 +1,41 @@
+module test2 {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test2";
+    prefix "t2";
+
+    import test3 {
+        prefix "t3";
+        revision-date 2013-06-18;
+    }
+
+    import test4 {
+        prefix "t4";
+        revision-date 2013-06-18;
+    }
+
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+        revision 2013-06-18 {
+    }
+
+    augment "/t4:interfaces/t4:ifEntry/t3:augment-holder" {
+        when "if:ifType='ds0'";
+        leaf ds0ChannelNumber {
+            type string;
+        }
+        leaf interface-id {
+            type leafref {
+                path "/t4:interfaces/t4:ifEntry/t4:ifIndex";
+            }
+        }
+        leaf-list higher-layer-if {
+            type leafref {
+                path "/t4:interfaces/t4:ifEntry/t4:ifMtu";
+            }
+        }
+        container schemas {
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/context-augment-test/test3.yang b/yang/yang-parser-rfc7950/src/test/resources/context-augment-test/test3.yang
new file mode 100644 (file)
index 0000000..f954153
--- /dev/null
@@ -0,0 +1,24 @@
+module test3 {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test3";
+    prefix "t3";
+
+    import test4 {
+        prefix "t4";
+        revision-date 2013-06-18;
+    }
+
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+        revision 2013-06-18 {
+    }
+
+    augment "/t4:interfaces/t4:ifEntry" {
+        when "if:ifType='ds0'";
+        container augment-holder {
+            description "Description for augment holder";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/context-augment-test/test4.yang b/yang/yang-parser-rfc7950/src/test/resources/context-augment-test/test4.yang
new file mode 100644 (file)
index 0000000..9d57a9e
--- /dev/null
@@ -0,0 +1,27 @@
+module test4 {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test4";
+    prefix "t4";
+
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+        revision 2013-06-18 {
+    }
+
+    container interfaces {
+         list ifEntry {
+             key "ifIndex";
+             leaf ifIndex {
+                 type uint32;
+                 units minutes;
+             }
+             leaf ifMtu {
+                 type int32;
+             }
+             min-elements 1;
+             max-elements 11;
+         }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/context-test/deviation-test.yang b/yang/yang-parser-rfc7950/src/test/resources/context-test/deviation-test.yang
new file mode 100644 (file)
index 0000000..98b5e0c
--- /dev/null
@@ -0,0 +1,26 @@
+module deviation-test {
+    yang-version 1;
+    namespace "urn:simple.deviation.test";
+    prefix "dev";
+
+    import bar {
+        prefix "t";
+        revision-date 2013-07-03;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+
+    deviation /t:interfaces/t:ifEntry {
+        deviate add {
+            config "true";
+        }
+        reference "system/user ref";
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/context-test/test1.yang b/yang/yang-parser-rfc7950/src/test/resources/context-test/test1.yang
new file mode 100644 (file)
index 0000000..8a4743f
--- /dev/null
@@ -0,0 +1,29 @@
+module test1 {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test1";
+    prefix "t1";
+    
+    import ietf-inet-types {
+        prefix "inet";
+        revision-date 2010-09-24;
+    }
+    
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+        revision 2013-06-18 {
+    }
+    
+    typedef dscp-ext {
+        type inet:dscp {
+            range "min..max";
+        }
+    }
+
+    leaf id {
+        type inet:port-number {
+            range "0..65535";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/context-test/test2.yang b/yang/yang-parser-rfc7950/src/test/resources/context-test/test2.yang
new file mode 100644 (file)
index 0000000..48c0e09
--- /dev/null
@@ -0,0 +1,47 @@
+module test2 {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test2";
+    prefix "t2";
+
+    import baz {
+        prefix "data";
+    }
+
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+        revision 2013-06-18 {
+    }
+
+    container peer {
+        container destination {
+            uses data:target {
+                refine address {
+                    default "1.2.3.4";
+                    description "description of address defined by refine";
+                    reference "address reference added by refine";
+                    config false;
+                    mandatory true;
+                    must "ifType != 'ethernet' or " +
+                            "(ifType = 'ethernet' and ifMTU = 1500)" {
+                        error-message "An ethernet MTU must be 1500";
+                    }
+                }
+                refine port {
+                    description "description of port defined by refine";
+                    reference "port reference added by refine";
+                    config false;
+                    presence "presence is required";
+                }
+                refine addresses {
+                    description "description of addresses defined by refine";
+                    reference "addresses reference added by refine";
+                    config false;
+                    min-elements 2;
+                    max-elements 12;
+                }
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/context-test/test3.yang b/yang/yang-parser-rfc7950/src/test/resources/context-test/test3.yang
new file mode 100644 (file)
index 0000000..f694682
--- /dev/null
@@ -0,0 +1,31 @@
+module test3 {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test3";
+    prefix "t3";
+    
+    import custom-types-test {
+        prefix "custom";
+    }
+    
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+    revision 2013-06-18 {
+    }
+
+    identity pt {
+        base custom:service-type;
+    }
+    
+    container network {
+        custom:mountpoint point {
+        }
+        
+        description "network-description";
+        reference "network-reference";
+        status obsolete;
+        config true;
+        presence "some presence text";
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/anyxml-declared-test.yang b/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/anyxml-declared-test.yang
new file mode 100644 (file)
index 0000000..6111b93
--- /dev/null
@@ -0,0 +1,25 @@
+module anyxml-declared-test {
+    namespace "anyxml-declared-test";
+    prefix "axdt";
+
+    feature foobar-feature;
+
+    anyxml foobar {
+        when "foo = 'bar'" {
+            description "when description";
+            reference "when reference";
+        }
+        if-feature foobar-feature;
+        must "bar != 'foo'" {
+            description "must description";
+            reference "must reference";
+            error-app-tag "error";
+            error-message "error";
+        }
+        config false;
+        status current;
+        description "anyxml description";
+        reference "anyxml reference";
+        mandatory "false";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/augment-declared-test.yang b/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/augment-declared-test.yang
new file mode 100644 (file)
index 0000000..4c3cf3f
--- /dev/null
@@ -0,0 +1,20 @@
+module augment-declared-test {
+    namespace "augment-declared-test";
+    prefix "adt";
+
+    augment "/cont/sub-cont" {
+        list lst {
+            key key-leaf;
+
+            leaf key-leaf {
+                type string;
+            }
+        }
+    }
+
+    container cont {
+        container sub-cont {
+
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/child-module-declared-test.yang b/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/child-module-declared-test.yang
new file mode 100644 (file)
index 0000000..0dfb4c3
--- /dev/null
@@ -0,0 +1,7 @@
+submodule child-module-declared-test {
+    belongs-to "parent-module-declared-test" {
+        prefix "prnt";
+    }
+
+    yang-version "1";
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/choice-declared-test.yang b/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/choice-declared-test.yang
new file mode 100644 (file)
index 0000000..01aec1e
--- /dev/null
@@ -0,0 +1,42 @@
+module choice-declared-test {
+    namespace "choice-declared-test";
+    prefix "chdt";
+
+    feature foo-feature;
+    feature foobar-feature;
+
+    choice test-choice {
+        default case-two;
+        config false;
+        mandatory false;
+        when "bar = 'baz'";
+        if-feature foo-feature;
+        status current;
+        description "choice description";
+        reference "choice reference";
+
+        case case-one {
+            when "foo = 'bar'";
+            if-feature foobar-feature;
+            status current;
+            description "case description";
+            reference "case reference";
+
+            leaf case-one-leaf {
+                type string;
+            }
+        }
+
+        case case-two {
+            leaf case-two-leaf {
+                type string;
+            }
+        }
+
+        case case-three {
+            leaf case-three-leaf {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/container-declared-test.yang b/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/container-declared-test.yang
new file mode 100644 (file)
index 0000000..01842ed
--- /dev/null
@@ -0,0 +1,33 @@
+module container-declared-test {
+    namespace "container-declared-test";
+    prefix "cdt";
+
+    feature foo-feature;
+
+    container test-container {
+        when "foo = 'bar'";
+        if-feature foo-feature;
+        must "bar != 'foo'";
+        presence "container presence";
+        config false;
+        status current;
+        description "container description";
+        reference "container reference";
+
+        typedef tiny-integer {
+            type int8 {
+                range "1..10";
+            }
+        }
+
+        grouping grpng {
+            leaf-list lflst {
+                type string;
+            }
+        }
+
+        leaf lf {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/imported-module-declared-test.yang b/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/imported-module-declared-test.yang
new file mode 100644 (file)
index 0000000..66890fa
--- /dev/null
@@ -0,0 +1,6 @@
+module imported-module-declared-test {
+    namespace imported-module-declared-test;
+    prefix imdt;
+
+    revision 2016-09-28;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/parent-module-declared-test.yang b/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/parent-module-declared-test.yang
new file mode 100644 (file)
index 0000000..894ee2a
--- /dev/null
@@ -0,0 +1,7 @@
+module parent-module-declared-test {
+    namespace "parent-module-declared-test";
+    prefix "pmdt";
+    yang-version 1;
+
+    include child-module-declared-test;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/root-module-declared-test.yang b/yang/yang-parser-rfc7950/src/test/resources/declared-statements-test/root-module-declared-test.yang
new file mode 100644 (file)
index 0000000..555f1b9
--- /dev/null
@@ -0,0 +1,57 @@
+module root-module-declared-test {
+    namespace root-module-declared-test;
+    prefix rmdt;
+
+    import imported-module-declared-test {
+        prefix imdt;
+        revision-date 2016-09-28;
+    }
+
+    description "test description";
+    reference "test reference";
+    organization "test organization";
+    contact "test contact";
+
+    revision 2016-09-28 {
+        description "test description";
+        reference "test reference";
+    }
+
+    extension test-extension {
+        argument ext-argument {
+            yin-element true;
+        }
+        status current;
+        description "test description";
+        reference "test reference";
+    }
+
+    feature test-feature {
+        if-feature required-feature;
+        status current;
+        description "test description";
+        reference "test reference";
+    }
+
+    feature required-feature;
+
+    identity test-base-id;
+
+    identity test-id {
+        base test-base-id;
+        status current;
+        description "test description";
+        reference "test reference";
+    }
+
+    typedef test-typedef {
+        type int32 {
+            range "5..10";
+        }
+
+        status current;
+        description "test description";
+        reference "test reference";
+        units "meter";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/depinfo-malformed/malformed-import-rev.yang b/yang/yang-parser-rfc7950/src/test/resources/depinfo-malformed/malformed-import-rev.yang
new file mode 100644 (file)
index 0000000..64cf208
--- /dev/null
@@ -0,0 +1,5 @@
+module malformed-import-rev {
+    namespace "urn:test:foo";
+    prefix aug;
+    import foo { revision-date; prefix foo; }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/depinfo-malformed/malformed-import.yang b/yang/yang-parser-rfc7950/src/test/resources/depinfo-malformed/malformed-import.yang
new file mode 100644 (file)
index 0000000..709980e
--- /dev/null
@@ -0,0 +1,5 @@
+module malformed-import {
+    namespace "urn:test:foo";
+    prefix aug;
+    import;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/depinfo-malformed/malformed-module.yang b/yang/yang-parser-rfc7950/src/test/resources/depinfo-malformed/malformed-module.yang
new file mode 100644 (file)
index 0000000..35b3bbc
--- /dev/null
@@ -0,0 +1,4 @@
+module {
+    namespace "urn:test:foo";
+    prefix aug;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/depinfo-malformed/malformed-rev.yang b/yang/yang-parser-rfc7950/src/test/resources/depinfo-malformed/malformed-rev.yang
new file mode 100644 (file)
index 0000000..cc6098d
--- /dev/null
@@ -0,0 +1,6 @@
+module malformed-import {
+    namespace "urn:test:opendaylight-mdsal45-aug";
+    prefix aug;
+
+    revision;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/bar.yang
new file mode 100644 (file)
index 0000000..76e70f6
--- /dev/null
@@ -0,0 +1,10 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-01-20;
+
+    container my-cont {
+
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar-invalid-2.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar-invalid-2.yang
new file mode 100644 (file)
index 0000000..dc93a72
--- /dev/null
@@ -0,0 +1,11 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-01-20;
+
+    leaf my-leaf {
+        type int32;
+        default 100;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar-invalid-3.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar-invalid-3.yang
new file mode 100644 (file)
index 0000000..2b0ec52
--- /dev/null
@@ -0,0 +1,10 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-01-20;
+
+    leaf my-leaf {
+        type int32;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar-invalid-4.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar-invalid-4.yang
new file mode 100644 (file)
index 0000000..98bd9f9
--- /dev/null
@@ -0,0 +1,20 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-02-01;
+
+    container my-cont {
+        uses my-grouping {
+            refine my-used-leaf {
+                default "def-val-added-by-refine";
+            }
+        }
+    }
+
+    grouping my-grouping {
+        leaf my-used-leaf {
+            type int32;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar-invalid.yang
new file mode 100644 (file)
index 0000000..b3a2fa1
--- /dev/null
@@ -0,0 +1,11 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-01-20;
+
+    leaf my-leaf {
+        type int32;
+        config false;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar.yang
new file mode 100644 (file)
index 0000000..1fefbc0
--- /dev/null
@@ -0,0 +1,78 @@
+module bar {
+    namespace bar;
+    prefix bar;
+    yang-version 1.1;
+
+    revision 2017-01-20;
+
+    leaf-list my-leaf-list {
+        type int32;
+        default 50;
+    }
+
+    list my-list {
+        key key-leaf;
+
+        leaf key-leaf {
+            type string;
+        }
+
+        leaf my-leaf-a {
+            type string;
+        }
+
+        leaf my-leaf-b {
+            type string;
+        }
+
+        leaf my-leaf-c {
+            type string;
+        }
+
+        leaf my-leaf-d {
+            type string;
+        }
+    }
+
+    choice my-choice {
+        case c1 {
+            leaf c1-leaf {
+                type string;
+                default "heaven";
+            }
+        }
+
+        case c2 {
+            leaf c2-leaf {
+                type string;
+                default "hell";
+            }
+        }
+    }
+
+    rpc my-rpc {
+        input {
+            leaf my-rpc-input-leaf {
+                type string;
+            }
+        }
+
+        output {
+            leaf my-rpc-output-leaf {
+                type string;
+            }
+        }
+    }
+
+    notification my-notification {
+
+    }
+
+    anyxml my-anyxml {
+
+    }
+
+    anydata my-anydata {
+
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar10-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/bar10-invalid.yang
new file mode 100644 (file)
index 0000000..9892f6a
--- /dev/null
@@ -0,0 +1,10 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-01-20;
+
+    leaf-list my-leaf-list {
+        type int32;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo-invalid-2.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo-invalid-2.yang
new file mode 100644 (file)
index 0000000..2efe0f1
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-leaf" {
+        deviate add {
+            default 200;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo-invalid-3.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo-invalid-3.yang
new file mode 100644 (file)
index 0000000..219614a
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-leaf" {
+        deviate add {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo-invalid-4.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo-invalid-4.yang
new file mode 100644 (file)
index 0000000..2070d77
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-02-01;
+    }
+
+    revision 2017-02-01;
+
+    deviation "/bar:my-cont/bar:my-used-leaf" {
+        deviate add {
+            default 50;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo-invalid.yang
new file mode 100644 (file)
index 0000000..1916b6e
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-leaf" {
+        deviate add {
+            config true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo.yang
new file mode 100644 (file)
index 0000000..85b31f2
--- /dev/null
@@ -0,0 +1,77 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-leaf-list" {
+        deviate add {
+            config false;
+            min-elements 5;
+            max-elements 10;
+            default 100;
+            default 200;
+            units "hours";
+        }
+    }
+
+    deviation "/bar:my-list" {
+        deviate add {
+            unique "my-leaf-a my-leaf-b";
+            unique "my-leaf-c my-leaf-d";
+        }
+    }
+
+    deviation "/bar:my-choice" {
+        deviate add {
+            default c2;
+        }
+    }
+
+    deviation "/bar:my-rpc/bar:input" {
+        deviate add {
+            must "day or night";
+            must "black and white";
+        }
+    }
+
+    deviation "/bar:my-rpc/bar:output" {
+        deviate add {
+            must "day or night";
+            must "black and white";
+        }
+    }
+
+    deviation "/bar:my-notification" {
+        deviate add {
+            must "day or night";
+            must "black and white";
+        }
+    }
+
+    deviation "/bar:my-anyxml" {
+        deviate add {
+            mandatory true;
+            foo:custom-property "arg";
+            foo:custom-property "another_arg";
+        }
+    }
+
+    deviation "/bar:my-anydata" {
+        deviate add {
+            mandatory true;
+            foo:custom-property "arg";
+            foo:custom-property "another_arg";
+        }
+    }
+
+    extension custom-property {
+        argument name;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo10-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-add/foo10-invalid.yang
new file mode 100644 (file)
index 0000000..410f5b6
--- /dev/null
@@ -0,0 +1,18 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-leaf-list" {
+        deviate add {
+            default 10;
+            default 100;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/bar-invalid-2.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/bar-invalid-2.yang
new file mode 100644 (file)
index 0000000..b3a2fa1
--- /dev/null
@@ -0,0 +1,11 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-01-20;
+
+    leaf my-leaf {
+        type int32;
+        config false;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/bar-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/bar-invalid.yang
new file mode 100644 (file)
index 0000000..2b0ec52
--- /dev/null
@@ -0,0 +1,10 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-01-20;
+
+    leaf my-leaf {
+        type int32;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/bar.yang
new file mode 100644 (file)
index 0000000..c66768d
--- /dev/null
@@ -0,0 +1,84 @@
+module bar {
+    namespace bar;
+    prefix bar;
+    yang-version 1.1;
+
+    revision 2017-01-20;
+
+    leaf my-leaf {
+        type int32;
+        units seconds;
+        default 100;
+        bar:custom-property-without-arg;
+    }
+
+    leaf-list my-leaf-list {
+        type string;
+        default "def-val-1";
+        default "def-val-2";
+        must "a = 1 or b = 2";
+        must "x != y";
+    }
+
+    list my-list {
+        key key-leaf;
+
+        unique "my-leaf-a my-leaf-b";
+        unique "my-leaf-c my-leaf-d";
+        bar:custom-property "arg";
+        bar:custom-property "another arg";
+
+        leaf key-leaf {
+            type string;
+        }
+
+        leaf my-leaf-a {
+            type string;
+        }
+
+        leaf my-leaf-b {
+            type string;
+        }
+
+        leaf my-leaf-c {
+            type string;
+        }
+
+        leaf my-leaf-d {
+            type string;
+        }
+    }
+
+    container my-cont {
+        uses my-grouping {
+            refine my-used-leaf {
+                default "def-val-added-by-refine";
+                bar:custom-property-without-arg;
+            }
+        }
+    }
+
+    augment "/my-cont" {
+        leaf my-aug-leaf {
+            type int32;
+            default "def-val";
+            units "minutes";
+            must "x = 1";
+            bar:custom-property-without-arg;
+        }
+    }
+
+    grouping my-grouping {
+        leaf my-used-leaf {
+            type int32;
+            units "days";
+            must "y = 5";
+        }
+    }
+
+    extension custom-property {
+        argument name;
+    }
+
+    extension custom-property-without-arg;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/bar10-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/bar10-invalid.yang
new file mode 100644 (file)
index 0000000..9892f6a
--- /dev/null
@@ -0,0 +1,10 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-01-20;
+
+    leaf-list my-leaf-list {
+        type int32;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/foo-invalid-2.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/foo-invalid-2.yang
new file mode 100644 (file)
index 0000000..4c4a555
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/my-leaf" {
+        deviate delete {
+            config false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/foo-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/foo-invalid.yang
new file mode 100644 (file)
index 0000000..87b1272
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-leaf" {
+        deviate delete {
+            units "seconds";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/foo.yang
new file mode 100644 (file)
index 0000000..57abb1f
--- /dev/null
@@ -0,0 +1,56 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-leaf" {
+        deviate delete {
+            units seconds;
+            default 100;
+            bar:custom-property-without-arg;
+        }
+    }
+
+    deviation "/bar:my-leaf-list" {
+        deviate delete {
+            default "def-val-1";
+            default "def-val-2";
+            must "a = 1 or b = 2";
+            must "x != y";
+        }
+    }
+
+    deviation "/bar:my-list" {
+        deviate delete {
+            unique "my-leaf-a my-leaf-b";
+            unique "my-leaf-c my-leaf-d";
+            bar:custom-property "arg";
+            bar:custom-property "another arg";
+        }
+    }
+
+    deviation "/bar:my-cont/bar:my-aug-leaf" {
+        deviate delete {
+            default "def-val";
+            units "minutes";
+            must "x = 1";
+            bar:custom-property-without-arg;
+        }
+    }
+
+    deviation "/bar:my-cont/bar:my-used-leaf" {
+        deviate delete {
+            default "def-val-added-by-refine";
+            bar:custom-property-without-arg;
+            units "days";
+            must "y = 5";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/foo10-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-delete/foo10-invalid.yang
new file mode 100644 (file)
index 0000000..b5f76dc
--- /dev/null
@@ -0,0 +1,18 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-leaf-list" {
+        deviate delete {
+            default 10;
+            default 100;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-not-supported/imported.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-not-supported/imported.yang
new file mode 100644 (file)
index 0000000..559fec2
--- /dev/null
@@ -0,0 +1,48 @@
+module imported {
+    namespace imp;
+    prefix imp;
+
+    revision 2017-01-20;
+
+    container my-cont-a {
+        leaf my-leaf-a1 {
+            type int8;
+        }
+
+        leaf my-leaf-a2 {
+            type int16;
+        }
+
+        leaf my-leaf-a3 {
+            type int32;
+        }
+    }
+
+    container my-cont-b {
+        leaf my-leaf-b1 {
+            type int8;
+        }
+
+        leaf my-leaf-b2 {
+            type int16;
+        }
+
+        leaf my-leaf-b3 {
+            type int32;
+        }
+    }
+
+    container my-cont-c {
+        leaf my-leaf-c1 {
+            type int8;
+        }
+
+        leaf my-leaf-c2 {
+            type int16;
+        }
+
+        leaf my-leaf-c3 {
+            type int32;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-not-supported/root.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-not-supported/root.yang
new file mode 100644 (file)
index 0000000..6a19cbc
--- /dev/null
@@ -0,0 +1,27 @@
+module root {
+    namespace root;
+    prefix root;
+
+    import imported {
+        prefix imp;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/imp:my-cont-a/imp:my-leaf-a1" {
+        deviate not-supported;
+    }
+
+    deviation "/imp:my-cont-a/imp:my-leaf-a2" {
+        deviate not-supported;
+    }
+
+    deviation "/imp:my-cont-b" {
+        deviate not-supported;
+    }
+
+    deviation "/imp:my-cont-c/imp:my-leaf-c3" {
+        deviate not-supported;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/bar-invalid-2.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/bar-invalid-2.yang
new file mode 100644 (file)
index 0000000..290267a
--- /dev/null
@@ -0,0 +1,13 @@
+module bar {
+    namespace bar;
+    prefix bar;
+    yang-version 1.1;
+
+    revision 2017-01-20;
+
+    leaf-list my-leaf-list {
+        type string;
+        default "default value one";
+        default "default value two";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/bar-invalid-3.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/bar-invalid-3.yang
new file mode 100644 (file)
index 0000000..0e4f1fe
--- /dev/null
@@ -0,0 +1,11 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-01-20;
+
+    leaf my-leaf {
+        type int32;
+        must "a != b";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/bar-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/bar-invalid.yang
new file mode 100644 (file)
index 0000000..2b0ec52
--- /dev/null
@@ -0,0 +1,10 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-01-20;
+
+    leaf my-leaf {
+        type int32;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/bar.yang
new file mode 100644 (file)
index 0000000..24a5e42
--- /dev/null
@@ -0,0 +1,65 @@
+module bar {
+    namespace bar;
+    prefix bar;
+    yang-version 1.1;
+
+    revision 2017-01-20;
+
+    leaf my-leaf {
+        type int32;
+        default 100;
+        units kilobytes;
+    }
+
+    leaf-list my-leaf-list-test {
+        type string;
+        min-elements 5;
+        max-elements 10;
+        config false;
+    }
+
+    choice my-choice {
+        mandatory true;
+        bar:custom-property "original arg";
+        case c1 {
+            leaf c1-leaf {
+                type string;
+            }
+        }
+
+        case c2 {
+            leaf c2-leaf {
+                type string;
+            }
+        }
+    }
+
+    augment "/my-cont" {
+        leaf my-aug-leaf {
+            type int32;
+            default "def-val";
+            units "minutes";
+            bar:custom-property "arg";
+        }
+    }
+
+    container my-cont {
+        uses my-grouping {
+            refine my-used-leaf {
+                default "def-val-added-by-refine";
+                bar:custom-property "arg";
+            }
+        }
+    }
+
+    grouping my-grouping {
+        leaf my-used-leaf {
+            type int32;
+            units "days";
+        }
+    }
+
+    extension custom-property {
+        argument name;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/foo-invalid-2.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/foo-invalid-2.yang
new file mode 100644 (file)
index 0000000..9ebf435
--- /dev/null
@@ -0,0 +1,18 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-leaf-list" {
+        deviate replace {
+            default "new default value";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/foo-invalid-3.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/foo-invalid-3.yang
new file mode 100644 (file)
index 0000000..f3a9ab7
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-leaf" {
+        deviate replace {
+            must "a != b";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/foo-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/foo-invalid.yang
new file mode 100644 (file)
index 0000000..04f110c
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-leaf" {
+        deviate replace {
+            units "seconds";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/deviation-replace/foo.yang
new file mode 100644 (file)
index 0000000..5375af4
--- /dev/null
@@ -0,0 +1,52 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-leaf" {
+        deviate replace {
+            type uint32;
+            default 10;
+            units bytes;
+        }
+    }
+
+    deviation "/bar:my-leaf-list-test" {
+        deviate replace {
+            min-elements 3;
+            max-elements 6;
+            config true;
+        }
+    }
+
+    deviation "/bar:my-choice" {
+        deviate replace {
+            mandatory false;
+            bar:custom-property "new arg";
+        }
+    }
+
+    deviation "/bar:my-cont/bar:my-aug-leaf" {
+        deviate replace {
+            type uint32;
+            default "new-def-val";
+            units "seconds";
+            bar:custom-property "new arg";
+        }
+    }
+
+    deviation "/bar:my-cont/bar:my-used-leaf" {
+        deviate replace {
+            type uint32;
+            default "new-def-val";
+            bar:custom-property "new arg";
+            units "weeks";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/foo-invalid-deviation-path.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/foo-invalid-deviation-path.yang
new file mode 100644 (file)
index 0000000..21ef959
--- /dev/null
@@ -0,0 +1,19 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:invalid/bar:path" {
+        deviate not-supported;
+    }
+
+    container my-cont {
+
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/foo-invalid-deviation-target.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-resolution-test/foo-invalid-deviation-target.yang
new file mode 100644 (file)
index 0000000..9618058
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar {
+        prefix bar;
+        revision-date 2017-01-20;
+    }
+
+    revision 2017-01-20;
+
+    deviation "/bar:my-cont" {
+        deviate add {
+            max-elements 5;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-stmt-test/bar-imp.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-stmt-test/bar-imp.yang
new file mode 100644 (file)
index 0000000..38652e0
--- /dev/null
@@ -0,0 +1,20 @@
+module bar-imp {
+    namespace "bimp-ns";
+    prefix "bimp";
+
+    revision 2016-09-22;
+
+    container bar-container-1 {
+        leaf bar-leaf-1 {
+            type string;
+        }
+    }
+
+    container bar-container-2 {
+        must "a = b";
+
+        leaf bar-leaf-2 {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-stmt-test/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-stmt-test/bar.yang
new file mode 100644 (file)
index 0000000..6b52bf3
--- /dev/null
@@ -0,0 +1,60 @@
+module bar {
+    namespace bar-namespace;
+    prefix bar-prefix;
+
+    import bar-imp {
+        prefix bimp;
+        revision-date 2016-09-22;
+    }
+
+    revision "2016-09-22";
+
+    deviation "/bimp:bar-container-1" {
+        deviate add {
+            must "a != b";
+        }
+        bar-prefix:bar-extension;
+    }
+
+    deviation "/bimp:bar-container-2" {
+        deviate delete {
+            must "a = b";
+        }
+    }
+
+    deviation "/bimp:bar-container-2" {
+        deviate add {
+            config false;
+        }
+    }
+
+    deviation "/bimp:bar-container-1/bimp:bar-leaf-1" {
+        deviate add {
+            default "def-val";
+        }
+        description "desc";
+    }
+
+    deviation "/bimp:bar-container-1/bimp:bar-leaf-1" {
+        deviate add {
+            config false;
+        }
+        description "another desc";
+    }
+
+    deviation "/bimp:bar-container-2/bimp:bar-leaf-2" {
+        deviate add {
+            default "def-val";
+        }
+        reference "ref";
+    }
+
+    deviation "/bimp:bar-container-2/bimp:bar-leaf-2" {
+        deviate add {
+            config false;
+        }
+        reference "another ref";
+    }
+
+    extension bar-extension {}
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-stmt-test/foo-imp.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-stmt-test/foo-imp.yang
new file mode 100644 (file)
index 0000000..0164671
--- /dev/null
@@ -0,0 +1,50 @@
+module foo-imp {
+    namespace "fimp-ns";
+    prefix "fimp";
+
+    revision 2016-06-23;
+
+    container test-container {
+        leaf test-leaf {
+            type string;
+        }
+
+        leaf test-leaf-2 {
+            type string;
+        }
+
+        leaf-list test-leaf-list {
+            type int32;
+            min-elements 3;
+            must "daytime or time";
+            must "time or daytime";
+            units minutes;
+        }
+
+        list test-list {
+            key key-leaf;
+            unique "list-leaf-1 list-leaf-2";
+            unique "list-leaf-3 list-leaf-4";
+
+            leaf key-leaf {
+                type string;
+            }
+
+            leaf list-leaf-1 {
+                type string;
+            }
+
+            leaf list-leaf-2 {
+                type string;
+            }
+
+            leaf list-leaf-3 {
+                type string;
+            }
+
+            leaf list-leaf-4 {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/deviation-stmt-test/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/deviation-stmt-test/foo.yang
new file mode 100644 (file)
index 0000000..bb45673
--- /dev/null
@@ -0,0 +1,50 @@
+module foo {
+    namespace "foo-namespace";
+    prefix "foo-prefix";
+
+    import foo-imp {
+        prefix fimp;
+        revision-date 2016-06-23;
+    }
+
+    revision "2016-06-23" {
+        description "Initial revision";
+    }
+
+    deviation "/fimp:test-container/fimp:test-leaf" {
+        description "test-leaf is not supported";
+        deviate not-supported;
+    }
+
+    deviation "/fimp:test-container/fimp:test-leaf-2" {
+        deviate add {
+            default "added-def-val";
+            config false;
+            mandatory true;
+        }
+    }
+
+    deviation "/fimp:test-container/fimp:test-leaf-list" {
+        deviate add {
+            max-elements 12;
+        }
+
+        deviate replace {
+            min-elements 5;
+            type uint32;
+        }
+
+        deviate delete {
+            must "daytime or time";
+            must "time or daytime";
+            units minutes;
+        }
+    }
+
+    deviation "/fimp:test-container/fimp:test-list" {
+        deviate delete {
+            unique "list-leaf-1 list-leaf-2";
+            unique "list-leaf-3 list-leaf-4";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/effective-schema-context-test/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/effective-schema-context-test/bar.yang
new file mode 100644 (file)
index 0000000..0ff13dc
--- /dev/null
@@ -0,0 +1,16 @@
+module bar {
+    namespace bar-namespace;
+    prefix bar-prefix;
+
+    revision 2016-09-21;
+
+    container bar-container {}
+
+    notification bar-notification {}
+
+    rpc bar-rpc {}
+
+    extension bar-extension {}
+
+    bar-prefix:bar-extension;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/effective-schema-context-test/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/effective-schema-context-test/baz.yang
new file mode 100644 (file)
index 0000000..419c718
--- /dev/null
@@ -0,0 +1,16 @@
+module baz {
+    namespace baz-namespace;
+    prefix baz-prefix;
+
+    revision 2016-09-21;
+
+    container baz-container {}
+
+    notification baz-notification {}
+
+    rpc baz-rpc {}
+
+    extension baz-extension {}
+
+    baz-prefix:baz-extension;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/effective-schema-context-test/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/effective-schema-context-test/foo.yang
new file mode 100644 (file)
index 0000000..d4aaba9
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace foo-namespace;
+    prefix foo-prefix;
+
+    revision 2016-09-21;
+
+    container foo-container {}
+
+    notification foo-notification {}
+
+    rpc foo-rpc {}
+
+    extension foo-extension {}
+
+    foo-prefix:foo-extension;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/extensions/ext-typedef.yang b/yang/yang-parser-rfc7950/src/test/resources/extensions/ext-typedef.yang
new file mode 100644 (file)
index 0000000..36046f8
--- /dev/null
@@ -0,0 +1,19 @@
+module ext-typedef {
+    yang-version 1;
+    namespace "urn:simple.extension.typedefs";
+    prefix "ext";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    description "Model for testing and resolving of extension typedefs.";
+
+    revision "2014-08-07" {
+        reference "Initial revision.";
+    }
+
+    extension simple-ext-w-arg {
+        argument "ext-arg";
+        description "Extension with single argument definition.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/extensions/ext-use.yang b/yang/yang-parser-rfc7950/src/test/resources/extensions/ext-use.yang
new file mode 100644 (file)
index 0000000..01c1dd9
--- /dev/null
@@ -0,0 +1,53 @@
+module ext-use {
+    ext:id "http://opendaylight.org";
+    yang-version 1;
+    ext:id "http://opendaylight.org";
+
+    ext:id2 "73354";
+    ext:name "test-name";
+
+    namespace "urn:simple.extension.use";
+    ext:id "http://opendaylight.org";
+
+    ext:name "test-name";
+
+    prefix "ext-use";
+    ext:id "http://opendaylight.org";
+    ext:name "test-name";
+
+    import ext-typedef { prefix "ext"; }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    description "Model for testing of used extensions across model.";
+
+    revision "2014-08-07" {
+        reference "Initial revision.";
+    }
+
+    leaf value {
+      ext:simple-ext-w-arg "key:value";
+      type uint32;
+      description "value";
+    }
+
+    grouping inner-ext {
+        ext:simple-ext-w-arg "value:test";
+
+        leaf inner-value {
+          ext:simple-ext-w-arg "key:inner-value";
+          type uint32;
+          description "value";
+        }
+    }
+
+    container cont-ext {
+        ext:simple-ext-w-arg "value:test";
+
+        leaf inner-value {
+            ext:simple-ext-w-arg "value:test";
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/grouping-test/cascade-uses.yang b/yang/yang-parser-rfc7950/src/test/resources/grouping-test/cascade-uses.yang
new file mode 100644 (file)
index 0000000..d539142
--- /dev/null
@@ -0,0 +1,55 @@
+module cascade-uses {
+    
+    namespace "urn:grouping:cascade-uses";
+    prefix "cu";
+
+    organization "OPEN DAYLIGHT";
+    contact "http://www.opendaylight.org/";
+
+    revision 2013-07-18 {
+    }
+  
+    grouping grouping-U {
+        leaf leaf-grouping-U {
+            type string;
+        }
+        uses grouping-V;
+        uses grouping-X;
+    }
+
+    grouping grouping-V {
+        leaf leaf-grouping-V {
+            type string;
+        }
+        container container-grouping-V {
+            uses grouping-X;
+        }
+        uses grouping-Z;
+        uses grouping-ZZ;
+    }
+
+    grouping grouping-X {
+        leaf leaf-grouping-X {
+            type string;
+        }
+        uses grouping-Y;
+    }
+
+    grouping grouping-Y {
+        leaf leaf-grouping-Y {
+            type string;
+        }
+    }
+    
+    grouping grouping-Z {
+        leaf leaf-grouping-Z {
+            type string;
+        }
+    }
+    
+    grouping grouping-ZZ {
+        leaf leaf-grouping-ZZ {
+            type string;
+        }
+    }    
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/grouping-test/grouping-definitions.yang b/yang/yang-parser-rfc7950/src/test/resources/grouping-test/grouping-definitions.yang
new file mode 100644 (file)
index 0000000..2cc7bbd
--- /dev/null
@@ -0,0 +1,89 @@
+module grouping-definitions {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:grouping-definitions";
+    prefix "gd";
+
+    organization "opendaylight";
+    contact "asdf";
+
+    revision "2013-09-04" {
+    }
+
+
+    typedef int-ext {
+        type union {
+            type uint8;
+            type protocol-version;
+        }
+    }
+
+    typedef protocol-version {
+        type uint8 {
+            range 1..7;
+        }
+    }
+
+    grouping base-header {
+        leaf delete {
+            type uint32;
+            mandatory true;
+        }
+        leaf setup {
+            type uint32;
+            mandatory true;
+        }
+    }
+
+    grouping ieee754-32 {
+        reference "IEEE 754-2008";
+        leaf fraction {
+            type uint32 {
+                range 0..1677215;
+            }
+            mandatory true;
+        }
+    }
+
+    grouping message-header {
+        leaf version {
+            type protocol-version;
+            default 1;
+        }
+        leaf type {
+            type int-ext;
+        }
+    }
+
+    grouping object {
+        uses object-header;
+
+        container box {
+        }
+    }
+
+    grouping object-header {
+        leaf processing-rule {
+            type boolean;
+        }
+        leaf ignore {
+            type boolean;
+        }
+    }
+
+    grouping rp-object {
+        uses object {
+            augment "box" {
+                container order {
+                    uses base-header;
+                }
+            }
+        }
+
+        leaf priority {
+            type uint8 {
+                range 1..7;
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/grouping-test/uses-grouping.yang b/yang/yang-parser-rfc7950/src/test/resources/grouping-test/uses-grouping.yang
new file mode 100644 (file)
index 0000000..d314fa6
--- /dev/null
@@ -0,0 +1,127 @@
+module uses-grouping {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:uses-grouping";
+    prefix "ug";
+
+    import grouping-definitions { prefix gd; revision-date 2013-09-04; }
+
+    organization "opendaylight";
+    contact "asdf";
+
+    revision "2013-07-30" {
+    }
+
+
+    grouping bandwidth-object {
+        uses gd:object-header;
+
+        container bandwidth {
+            uses gd:ieee754-32;
+        }
+    }
+
+    grouping endpoints-object {
+        uses gd:object;
+
+        choice address-family {
+            case ipv4 {
+                leaf source-ipv4-address {
+                    type string;
+                    mandatory true;
+                }
+            }
+            case ipv6 {
+                leaf source-ipv6-address {
+                    type string;
+                    mandatory true;
+                }
+            }
+        }
+    }
+
+    grouping lsp-attributes {
+        container bandwidth {
+            uses bandwidth-object;
+        }
+    }
+
+    grouping metric-object {
+        uses gd:object;
+        leaf metric-type {
+            type uint8;
+            mandatory true;
+        }
+    }
+
+    grouping path-key-object {
+        uses gd:object-header;
+
+        list path-keys {
+        }
+    }
+
+    grouping route-object {
+        uses gd:object-header;
+
+        list subobjects {
+        }
+    }
+
+    grouping svec-object {
+        uses gd:object-header;
+
+        leaf link-diverse {
+            type boolean;
+            default false;
+        }
+    }
+
+
+    notification pcreq {
+        uses gd:message-header;
+
+        list requests {
+            container rp {
+                uses gd:rp-object;
+            }
+            container path-key-expansion {
+                when "rp/path-key = true";
+                container path-key {
+                    uses path-key-object {
+                        augment path-keys {
+                            uses gd:message-header;
+                        }
+                    }
+                }
+            }
+            container segment-computation {
+                when "rp/path-key = false";
+
+                container p2p {
+                    when "../rp/p2mp = false";
+
+                    container endpoints {
+                        uses endpoints-object;
+                    }
+                    container reported-route {
+                        uses route-object;
+
+                        container bandwidth {
+                            uses bandwidth-object;
+                        }
+                    }
+
+                    uses lsp-attributes;
+                }
+            }
+        }
+
+        list svec {
+            uses svec-object;
+            list metric {
+                uses metric-object;
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/identity/duplicate-identity-test.yang b/yang/yang-parser-rfc7950/src/test/resources/identity/duplicate-identity-test.yang
new file mode 100644 (file)
index 0000000..ec8ed98
--- /dev/null
@@ -0,0 +1,13 @@
+module duplicate-identity-test {
+    namespace "urn:opendaylight.org/duplicate-identity-test";
+    prefix "didt";
+
+    identity first-identity {
+        description "this identity is not unique";
+    }
+
+    identity first-identity {
+        description "this identity is not unique";
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/identity/identitytest.yang b/yang/yang-parser-rfc7950/src/test/resources/identity/identitytest.yang
new file mode 100644 (file)
index 0000000..141256f
--- /dev/null
@@ -0,0 +1,16 @@
+module identitytest {
+    yang-version 1;
+    namespace "urn:test.identitytest";
+    prefix "idtest";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org";
+
+    revision "2014-09-17" {
+        description "Initial import";
+    }
+
+    identity test {
+        base test;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/identity/illegal-chained-identity-test.yang b/yang/yang-parser-rfc7950/src/test/resources/identity/illegal-chained-identity-test.yang
new file mode 100644 (file)
index 0000000..9e5d783
--- /dev/null
@@ -0,0 +1,16 @@
+module illegal-chained-identity-test {
+    namespace "urn:opendaylight.org/illegal-chained-identity-test";
+    prefix icidt;
+
+    identity first-identity {
+        base second-identity;
+    }
+
+    identity second-identity {
+        base third-identity;
+    }
+
+    identity third-identity {
+        base first-identity;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/identity/import/dummy.yang b/yang/yang-parser-rfc7950/src/test/resources/identity/import/dummy.yang
new file mode 100644 (file)
index 0000000..31f7705
--- /dev/null
@@ -0,0 +1,15 @@
+module dummy {
+    yang-version 1;
+    namespace "urn:test.dummy";
+    prefix "dummy";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org";
+
+    revision "2014-09-24" {
+        description "Initial import";
+    }
+
+    identity prefiximportidentitytest {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/identity/import/prefiximportidentitytest.yang b/yang/yang-parser-rfc7950/src/test/resources/identity/import/prefiximportidentitytest.yang
new file mode 100644 (file)
index 0000000..401aab2
--- /dev/null
@@ -0,0 +1,18 @@
+module prefiximportidentitytest {
+    yang-version 1;
+    namespace "urn:test.prefiximportidentitytest";
+    prefix "prefiximportidentitytest";
+
+    import dummy { prefix dummy; revision-date 2014-09-24; }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org";
+
+    revision "2014-09-24" {
+        description "Initial import";
+    }
+
+    identity prefiximportidentitytest {
+        base dummy:prefiximportidentitytest;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/identity/legal-chained-identity-test.yang b/yang/yang-parser-rfc7950/src/test/resources/identity/legal-chained-identity-test.yang
new file mode 100644 (file)
index 0000000..9e59c0c
--- /dev/null
@@ -0,0 +1,20 @@
+module legal-chained-identity-test {
+    namespace "urn:opendaylight.org/legal-chained-identity-test";
+    prefix lcidt;
+
+    identity first-identity {
+        base second-identity;
+    }
+
+    identity second-identity {
+        base third-identity;
+    }
+
+    identity third-identity {
+        base fourth-identity;
+    }
+
+    identity fourth-identity {
+        description "the highest identity";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/identity/prefixidentitytest.yang b/yang/yang-parser-rfc7950/src/test/resources/identity/prefixidentitytest.yang
new file mode 100644 (file)
index 0000000..08f0e08
--- /dev/null
@@ -0,0 +1,16 @@
+module prefixidentitytest {
+    yang-version 1;
+    namespace "urn:test.prefixidentitytest";
+    prefix prefixtest;
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org";
+
+    revision "2014-09-24" {
+        description "Initial import";
+    }
+
+    identity prefixtest {
+        base prefixtest:prefixtest;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/ietf/iana-afn-safi@2012-06-04.yang b/yang/yang-parser-rfc7950/src/test/resources/ietf/iana-afn-safi@2012-06-04.yang
new file mode 100644 (file)
index 0000000..5d5bdd0
--- /dev/null
@@ -0,0 +1,387 @@
+module iana-afn-safi {
+  namespace "urn:ietf:params:xml:ns:yang:iana-afn-safi";
+  prefix "ianaaf";
+
+  organization
+    "IANA";
+  contact
+    "        Internet Assigned Numbers Authority
+
+     Postal: ICANN
+             4676 Admiralty Way, Suite 330
+             Marina del Rey, CA 90292
+
+     Tel:    +1 310 823 9358
+     E-Mail: iana&iana.org";
+  description
+    "This YANG module provides two typedefs containing YANG
+     definitions for the following IANA-registered enumerations:
+
+     - Address Family Numbers (AFN)
+
+     - Subsequent Address Family Identifiers (SAFI)
+
+     The latest revision of this YANG module can be obtained from the
+     IANA web site.
+
+     Copyright (c) 2012 IETF Trust and the persons identified as
+     authors of the code. All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject to
+     the license terms contained in, the Simplified BSD License set
+     forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC XXXX; see the
+     RFC itself for full legal notices.";
+  // RFC Ed.: replace XXXX with actual RFC number and remove this
+  // note.
+
+  // RFC Ed.: update the date below with the date of RFC publication
+  // and remove this note.
+  revision 2012-06-04 {
+    description
+      "Initial revision.";
+    reference
+      "RFC XXXX: TITLE";
+  }
+
+  typedef address-family {
+    type enumeration {
+      enum other {
+        value "0";
+        description
+          "none of the following";
+      }
+      enum ipv4 {
+        value "1";
+        description
+          "IP version 4";
+      }
+      enum ipv6 {
+        value "2";
+        description
+          "IP version 6";
+      }
+      enum nsap {
+        value "3";
+        description
+          "NSAP";
+      }
+      enum hdlc {
+        value "4";
+        description
+          "HDLC (8-bit multidrop)";
+      }
+      enum bbn1822 {
+        value "5";
+        description
+          "BBN 1822";
+      }
+      enum all802 {
+        value "6";
+        description
+          "802 (includes all 802 media plus Ethernet 'canonical
+           format')";
+      }
+      enum e163 {
+        value "7";
+        description
+          "E.163";
+      }
+      enum e164 {
+        value "8";
+        description
+          "E.164 (SMDS, FrameRelay, ATM)";
+      }
+      enum f69 {
+        value "9";
+        description
+          "F.69 (Telex)";
+      }
+      enum x121 {
+        value "10";
+        description
+          "X.121 (X.25, Frame Relay)";
+      }
+      enum ipx {
+        value "11";
+        description
+          "IPX (Internetwork Packet Exchange)";
+      }
+      enum appletalk {
+        value "12";
+        description
+          "Appletalk";
+      }
+      enum decnetIV {
+        value "13";
+        description
+          "DECnet IV";
+      }
+      enum banyanVines {
+        value "14";
+        description
+          "Banyan Vines";
+      }
+      enum e164withNsap {
+        value "15";
+        description
+          "E.164 with NSAP format subaddress";
+        reference
+          "ATM Forum UNI 3.1";
+      }
+      enum dns {
+        value "16";
+        description
+          "DNS (Domain Name System)";
+      }
+      enum distinguishedName {
+        value "17";
+        description
+          "Distinguished Name (per X.500)";
+      }
+      enum asNumber {
+        value "18";
+        description
+          "Autonomous System Number";
+      }
+      enum xtpOverIPv4 {
+        value "19";
+        description
+          "XTP over IP version 4";
+      }
+      enum xtpOverIpv6 {
+        value "20";
+        description
+          "XTP over IP version 6";
+      }
+      enum xtpNativeModeXTP {
+        value "21";
+        description
+          "XTP native mode XTP";
+      }
+      enum fibreChannelWWPN {
+        value "22";
+        description
+          "Fibre Channel World-Wide Port Name";
+      }
+      enum fibreChannelWWNN {
+        value "23";
+        description
+          "Fibre Channel World-Wide Node Name";
+      }
+      enum gwid {
+        value "24";
+        description
+          "Gateway Identifier";
+      }
+      enum l2vpn {
+        value "25";
+        description
+          "AFI for L2VPN information";
+        reference
+          "RFC 4761: Virtual Private LAN Service (VPLS): Using BGP
+           for Auto-Discovery and Signaling
+
+           RFC 6074: Provisioning, Auto-Discovery, and Signaling in
+           Layer 2 Virtual Private Networks (L2VPNs)
+          ";
+      }
+      enum eigrpCommon {
+        value "16384";
+        description
+          "EIGRP Common Service Family";
+      }
+      enum eigrpIPv4 {
+        value "16385";
+        description
+          "EIGRP IPv4 Service Family";
+      }
+      enum eigrpIPv6 {
+        value "16386";
+        description
+          "EIGRP IPv6 Service Family";
+      }
+      enum lcaf {
+        value "16387";
+        description
+          "LISP Canonical Address Format";
+      }
+    }
+    description
+      "This typedef is a YANG enumeration of IANA-registered address
+       family numbers (AFN).";
+    reference
+      "Address Family Numbers. IANA, 2011-01-20.
+       <http://www.iana.org/assignments/address-family-numbers/
+       address-family-numbers.xml>
+      ";
+  }
+
+  typedef subsequent-address-family {
+    type enumeration {
+      enum nlri-unicast {
+        value "1";
+        description
+          "Network Layer Reachability Information used for unicast
+           forwarding";
+        reference
+          "RFC 4760: Multiprotocol Extensions for BGP-4";
+      }
+      enum nlri-multicast {
+        value "2";
+        description
+          "Network Layer Reachability Information used for multicast
+           forwarding";
+        reference
+          "RFC 4760: Multiprotocol Extensions for BGP-4";
+      }
+      enum nlri-mpls {
+        value "4";
+        description
+          "Network Layer Reachability Information (NLRI) with MPLS
+           Labels";
+        reference
+          "RFC 3107: Carrying Label Information in BGP-4";
+      }
+      enum mcast-vpn {
+        value "5";
+        description
+          "MCAST-VPN";
+        reference
+          "RFC 6514: BGP Encodings and Procedures for Multicast in
+           MPLS/BGP IP VPNs";
+      }
+      enum nlri-dynamic-ms-pw {
+        value "6";
+        status "obsolete";
+        description
+          "Network Layer Reachability Information used for Dynamic
+           Placement of Multi-Segment Pseudowires (TEMPORARY -
+           Expires 2008-08-23)";
+        reference
+          "draft-ietf-pwe3-dynamic-ms-pw: Dynamic Placement of Multi
+           Segment Pseudowires";
+      }
+      enum encapsulation {
+        value "7";
+        description
+          "Encapsulation SAFI";
+        reference
+          "RFC 5512: The BGP Encapsulation Subsequent Address Family
+           Identifier (SAFI) and the BGP Tunnel Encapsulation
+           Attribute";
+      }
+      enum tunnel-safi {
+        value "64";
+        status "obsolete";
+        description
+          "Tunnel SAFI";
+        reference
+          "draft-nalawade-kapoor-tunnel-safi: BGP Tunnel SAFI";
+      }
+      enum vpls {
+        value "65";
+        description
+          "Virtual Private LAN Service (VPLS)";
+        reference
+          "RFC 4761: Virtual Private LAN Service (VPLS): Using BGP
+           for Auto-Discovery and Signaling
+
+           RFC 6074: Provisioning, Auto-Discovery, and Signaling in
+           Layer 2 Virtual Private Networks (L2VPNs)
+          ";
+      }
+      enum bgp-mdt {
+        value "66";
+        description
+          "BGP MDT SAFI";
+        reference
+          "RFC 6037: Cisco Systems' Solution for Multicast in
+           BGP/MPLS IP VPNs";
+      }
+      enum bgp-4over6 {
+        value "67";
+        description
+          "BGP 4over6 SAFI";
+        reference
+          "RFC 5747: 4over6 Transit Solution Using IP Encapsulation
+           and MP-BGP Extensions";
+      }
+      enum bgp-6over4 {
+        value "68";
+        description
+          "BGP 6over4 SAFI";
+      }
+      enum l1vpn-auto-discovery {
+        value "69";
+        description
+          "Layer-1 VPN auto-discovery information";
+        reference
+          "RFC 5195: BGP-Based Auto-Discovery for Layer-1 VPNs";
+      }
+      enum mpls-vpn {
+        value "128";
+        description
+          "MPLS-labeled VPN address";
+        reference
+          "RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs)";
+      }
+      enum multicast-bgp-mpls-vpn {
+        value "129";
+        description
+          "Multicast for BGP/MPLS IP Virtual Private Networks
+           (VPNs)";
+        reference
+          "RFC 6513: Multicast in MPLS/BGP IP VPNs
+
+           RFC 6514: BGP Encodings and Procedures for Multicast in
+           MPLS/BGP IP VPNs
+          ";
+      }
+      enum route-target-constraints {
+        value "132";
+        description
+          "Route Target constraints";
+        reference
+          "RFC 4684: Constrained Route Distribution for Border
+           Gateway Protocol/MultiProtocol Label Switching (BGP/MPLS)
+           Internet Protocol (IP) Virtual Private Networks (VPNs)";
+      }
+      enum ipv4-diss-flow {
+        value "133";
+        description
+          "IPv4 dissemination of flow specification rules";
+        reference
+          "RFC 5575: Dissemination of Flow Specification Rules";
+      }
+      enum vpnv4-diss-flow {
+        value "134";
+        description
+          "IPv4 dissemination of flow specification rules";
+        reference
+          "RFC 5575: Dissemination of Flow Specification Rules";
+      }
+      enum vpn-auto-discovery {
+        value "140";
+        status "obsolete";
+        description
+          "VPN auto-discovery";
+        reference
+          "draft-ietf-l3vpn-bgpvpn-auto: Using BGP as an
+           Auto-Discovery Mechanism for VR-based Layer-3 VPNs";
+      }
+    }
+    description
+      "This typedef is a YANG enumeration of IANA-registered
+       subsequent address family identifiers (SAFI).";
+    reference
+      "Subsequent Address Family Identifiers (SAFI) Parameters. IANA,
+       2012-02-22. <http://www.iana.org/assignments/safi-namespace/
+       safi-namespace.xml>
+      ";
+  }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/ietf/iana-if-type@2012-06-05.yang b/yang/yang-parser-rfc7950/src/test/resources/ietf/iana-if-type@2012-06-05.yang
new file mode 100644 (file)
index 0000000..c5ce80c
--- /dev/null
@@ -0,0 +1,1516 @@
+module iana-if-type {
+  namespace "urn:ietf:params:xml:ns:yang:iana-if-type";
+  prefix ianaift;
+
+  organization "IANA";
+  contact
+    "        Internet Assigned Numbers Authority
+
+     Postal: ICANN
+             4676 Admiralty Way, Suite 330
+             Marina del Rey, CA 90292
+
+     Tel:    +1 310 823 9358
+     E-Mail: iana&iana.org";
+  description
+    "This YANG module defines the iana-if-type typedef, which
+     contains YANG definitions for IANA-registered interface types.
+
+     This YANG module is maintained by IANA, and reflects the
+     'ifType definitions' registry.
+
+     The latest revision of this YANG module can be obtained from
+     the IANA web site.
+
+     Copyright (c) 2011 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD License
+     set forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC XXXX; see
+     the RFC itself for full legal notices.";
+  // RFC Ed.: replace XXXX with actual RFC number and remove this
+  // note.
+
+  // RFC Ed.: update the date below with the date of RFC publication
+  // and remove this note.
+  revision 2012-06-05 {
+    description
+      "Initial revision.";
+    reference
+      "RFC XXXX: TITLE";
+  }
+
+  typedef iana-if-type {
+    type enumeration {
+      enum "other" {
+        value 1;
+        description
+          "None of the following";
+      }
+      enum "regular1822" {
+        value 2;
+      }
+      enum "hdh1822" {
+        value 3;
+      }
+      enum "ddnX25" {
+        value 4;
+      }
+      enum "rfc877x25" {
+        value 5;
+        reference
+          "RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer";
+      }
+      enum "ethernetCsmacd" {
+        value 6;
+        description
+          "For all ethernet-like interfaces, regardless of speed,
+           as per RFC3635.";
+        reference
+          "RFC 3635 - Definitions of Managed Objects for the
+                      Ethernet-like Interface Types.";
+      }
+      enum "iso88023Csmacd" {
+        value 7;
+        status deprecated;
+        description
+          "Deprecated via RFC3635.
+           Use ethernetCsmacd(6) instead.";
+        reference
+          "RFC 3635 - Definitions of Managed Objects for the
+                      Ethernet-like Interface Types.";
+      }
+      enum "iso88024TokenBus" {
+        value 8;
+      }
+      enum "iso88025TokenRing" {
+        value 9;
+      }
+      enum "iso88026Man" {
+        value 10;
+      }
+      enum "starLan" {
+        value 11;
+        status deprecated;
+        description
+          "Deprecated via RFC3635.
+           Use ethernetCsmacd(6) instead.";
+        reference
+          "RFC 3635 - Definitions of Managed Objects for the
+                      Ethernet-like Interface Types.";
+      }
+      enum "proteon10Mbit" {
+        value 12;
+      }
+      enum "proteon80Mbit" {
+        value 13;
+      }
+      enum "hyperchannel" {
+        value 14;
+      }
+      enum "fddi" {
+        value 15;
+        reference
+          "RFC 1512 - FDDI Management Information Base";
+      }
+      enum "lapb" {
+        value 16;
+        reference
+          "RFC 1381 - SNMP MIB Extension for X.25 LAPB";
+      }
+      enum "sdlc" {
+        value 17;
+      }
+      enum "ds1" {
+        value 18;
+        description
+          "DS1-MIB";
+        reference
+          "RFC 4805 - Definitions of Managed Objects for the
+                      DS1, J1, E1, DS2, and E2 Interface Types";
+      }
+      enum "e1" {
+        value 19;
+        status obsolete;
+        description
+          "Obsolete see DS1-MIB";
+        reference
+          "RFC 4805 - Definitions of Managed Objects for the
+                      DS1, J1, E1, DS2, and E2 Interface Types";
+      }
+      enum "basicISDN" {
+        value 20;
+        description
+          "see also RFC2127";
+      }
+      enum "primaryISDN" {
+        value 21;
+      }
+      enum "propPointToPointSerial" {
+        value 22;
+        description
+          "proprietary serial";
+      }
+      enum "ppp" {
+        value 23;
+      }
+      enum "softwareLoopback" {
+        value 24;
+      }
+      enum "eon" {
+        value 25;
+        description
+          "CLNP over IP";
+      }
+      enum "ethernet3Mbit" {
+        value 26;
+      }
+      enum "nsip" {
+        value 27;
+        description
+          "XNS over IP";
+      }
+      enum "slip" {
+        value 28;
+        description
+          "generic SLIP";
+      }
+      enum "ultra" {
+        value 29;
+        description
+          "ULTRA technologies";
+      }
+      enum "ds3" {
+        value 30;
+        description
+          "DS3-MIB";
+        reference
+          "RFC 3896 - Definitions of Managed Objects for the
+                      DS3/E3 Interface Type";
+      }
+      enum "sip" {
+        value 31;
+        description
+          "SMDS, coffee";
+        reference
+          "RFC 1694 - Definitions of Managed Objects for SMDS
+                      Interfaces using SMIv2";
+      }
+      enum "frameRelay" {
+        value 32;
+        description
+          "DTE only.";
+        reference
+          "RFC 2115 - Management Information Base for Frame Relay
+                      DTEs Using SMIv2";
+      }
+      enum "rs232" {
+        value 33;
+        reference
+          "RFC 1659 - Definitions of Managed Objects for RS-232-like
+                      Hardware Devices using SMIv2";
+      }
+      enum "para" {
+        value 34;
+        description
+          "parallel-port";
+        reference
+          "RFC 1660 - Definitions of Managed Objects for
+                      Parallel-printer-like Hardware Devices using
+                      SMIv2";
+      }
+      enum "arcnet" {
+        value 35;
+        description
+          "arcnet";
+      }
+      enum "arcnetPlus" {
+        value 36;
+        description
+          "arcnet plus";
+      }
+      enum "atm" {
+        value 37;
+        description
+          "ATM cells";
+      }
+      enum "miox25" {
+        value 38;
+        reference
+          "RFC 1461 - SNMP MIB extension for Multiprotocol
+                      Interconnect over X.25";
+      }
+      enum "sonet" {
+        value 39;
+        description
+          "SONET or SDH";
+      }
+      enum "x25ple" {
+        value 40;
+        reference
+          "RFC 2127 - ISDN Management Information Base using SMIv2";
+      }
+      enum "iso88022llc" {
+        value 41;
+      }
+      enum "localTalk" {
+        value 42;
+      }
+      enum "smdsDxi" {
+        value 43;
+      }
+      enum "frameRelayService" {
+        value 44;
+        description
+          "FRNETSERV-MIB";
+        reference
+          "RFC 2954 - Definitions of Managed Objects for Frame
+                      Relay Service";
+      }
+      enum "v35" {
+        value 45;
+      }
+      enum "hssi" {
+        value 46;
+      }
+      enum "hippi" {
+        value 47;
+      }
+      enum "modem" {
+        value 48;
+        description
+          "Generic modem";
+      }
+      enum "aal5" {
+        value 49;
+        description
+          "AAL5 over ATM";
+      }
+      enum "sonetPath" {
+        value 50;
+      }
+      enum "sonetVT" {
+        value 51;
+      }
+      enum "smdsIcip" {
+        value 52;
+        description
+          "SMDS InterCarrier Interface";
+      }
+      enum "propVirtual" {
+        value 53;
+        description
+          "proprietary virtual/internal";
+        reference
+          "RFC 2863 - The Interfaces Group MIB";
+      }
+      enum "propMultiplexor" {
+        value 54;
+        description
+          "proprietary multiplexing";
+        reference
+          "RFC 2863 - The Interfaces Group MIB";
+      }
+      enum "ieee80212" {
+        value 55;
+        description
+          "100BaseVG";
+      }
+      enum "fibreChannel" {
+        value 56;
+        description
+          "Fibre Channel";
+      }
+      enum "hippiInterface" {
+        value 57;
+        description
+          "HIPPI interfaces";
+      }
+      enum "frameRelayInterconnect" {
+        value 58;
+        status obsolete;
+        description
+          "Obsolete use either
+           frameRelay(32) or frameRelayService(44).";
+      }
+      enum "aflane8023" {
+        value 59;
+        description
+          "ATM Emulated LAN for 802.3";
+      }
+      enum "aflane8025" {
+        value 60;
+        description
+          "ATM Emulated LAN for 802.5";
+      }
+      enum "cctEmul" {
+        value 61;
+        description
+         "ATM Emulated circuit";
+      }
+      enum "fastEther" {
+        value 62;
+        status deprecated;
+        description
+          "Obsoleted via RFC3635.
+          ethernetCsmacd(6) should be used instead";
+        reference
+          "RFC 3635 - Definitions of Managed Objects for the
+                      Ethernet-like Interface Types.";
+      }
+      enum "isdn" {
+        value 63;
+        description
+          "ISDN and X.25";
+        reference
+          "RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN
+                      in the Packet Mode";
+      }
+      enum "v11" {
+        value 64;
+        description
+         "CCITT V.11/X.21";
+      }
+      enum "v36" {
+        value 65;
+        description
+          "CCITT V.36";
+      }
+      enum "g703at64k" {
+        value 66;
+        description
+          "CCITT G703 at 64Kbps";
+      }
+      enum "g703at2mb" {
+        value 67;
+        status obsolete;
+        description
+          "Obsolete see DS1-MIB";
+      }
+      enum "qllc" {
+        value 68;
+        description
+          "SNA QLLC";
+      }
+      enum "fastEtherFX" {
+        value 69;
+        status deprecated;
+        description
+          "Obsoleted via RFC3635
+          ethernetCsmacd(6) should be used instead";
+        reference
+          "RFC 3635 - Definitions of Managed Objects for the
+                      Ethernet-like Interface Types.";
+      }
+      enum "channel" {
+        value 70;
+        description
+          "channel";
+      }
+      enum "ieee80211" {
+        value 71;
+        description
+          "radio spread spectrum";
+      }
+      enum "ibm370parChan" {
+        value 72;
+        description
+          "IBM System 360/370 OEMI Channel";
+      }
+      enum "escon" {
+        value 73;
+        description
+          "IBM Enterprise Systems Connection";
+      }
+      enum "dlsw" {
+        value 74;
+        description
+          "Data Link Switching";
+      }
+      enum "isdns" {
+        value 75;
+        description
+          "ISDN S/T interface";
+      }
+      enum "isdnu" {
+        value 76;
+        description
+          "ISDN U interface";
+      }
+      enum "lapd" {
+        value 77;
+        description
+          "Link Access Protocol D";
+      }
+      enum "ipSwitch" {
+        value 78;
+        description
+          "IP Switching Objects";
+      }
+      enum "rsrb" {
+        value 79;
+        description
+          "Remote Source Route Bridging";
+      }
+      enum "atmLogical" {
+        value 80;
+        description
+          "ATM Logical Port";
+        reference
+          "RFC 3606 - Definitions of Supplemental Managed Objects
+                      for ATM Interface";
+      }
+      enum "ds0" {
+        value 81;
+        description
+          "Digital Signal Level 0";
+        reference
+          "RFC 2494 - Definitions of Managed Objects for the DS0
+                      and DS0 Bundle Interface Type";
+      }
+      enum "ds0Bundle" {
+        value 82;
+        description
+          "group of ds0s on the same ds1";
+        reference
+          "RFC 2494 - Definitions of Managed Objects for the DS0
+                      and DS0 Bundle Interface Type";
+      }
+      enum "bsc" {
+        value 83;
+        description
+          "Bisynchronous Protocol";
+      }
+      enum "async" {
+        value 84;
+        description
+          "Asynchronous Protocol";
+      }
+      enum "cnr" {
+        value 85;
+        description
+          "Combat Net Radio";
+      }
+      enum "iso88025Dtr" {
+        value 86;
+        description
+          "ISO 802.5r DTR";
+      }
+      enum "eplrs" {
+        value 87;
+        description
+          "Ext Pos Loc Report Sys";
+      }
+      enum "arap" {
+        value 88;
+        description
+          "Appletalk Remote Access Protocol";
+      }
+      enum "propCnls" {
+        value 89;
+        description
+          "Proprietary Connectionless Protocol";
+      }
+      enum "hostPad" {
+        value 90;
+        description
+          "CCITT-ITU X.29 PAD Protocol";
+      }
+      enum "termPad" {
+        value 91;
+        description
+          "CCITT-ITU X.3 PAD Facility";
+      }
+      enum "frameRelayMPI" {
+        value 92;
+        description
+          "Multiproto Interconnect over FR";
+      }
+      enum "x213" {
+        value 93;
+        description
+          "CCITT-ITU X213";
+      }
+      enum "adsl" {
+        value 94;
+        description
+          "Asymmetric Digital Subscriber Loop";
+      }
+      enum "radsl" {
+        value 95;
+        description
+          "Rate-Adapt. Digital Subscriber Loop";
+      }
+      enum "sdsl" {
+        value 96;
+        description
+          "Symmetric Digital Subscriber Loop";
+      }
+      enum "vdsl" {
+        value 97;
+        description
+          "Very H-Speed Digital Subscrib. Loop";
+      }
+      enum "iso88025CRFPInt" {
+        value 98;
+        description
+          "ISO 802.5 CRFP";
+      }
+      enum "myrinet" {
+        value 99;
+        description
+          "Myricom Myrinet";
+      }
+      enum "voiceEM" {
+        value 100;
+        description
+          "voice recEive and transMit";
+      }
+      enum "voiceFXO" {
+        value 101;
+        description
+          "voice Foreign Exchange Office";
+      }
+      enum "voiceFXS" {
+        value 102;
+        description
+          "voice Foreign Exchange Station";
+      }
+      enum "voiceEncap" {
+        value 103;
+        description
+          "voice encapsulation";
+      }
+      enum "voiceOverIp" {
+        value 104;
+        description
+          "voice over IP encapsulation";
+      }
+      enum "atmDxi" {
+        value 105;
+        description
+          "ATM DXI";
+      }
+      enum "atmFuni" {
+        value 106;
+        description
+          "ATM FUNI";
+      }
+      enum "atmIma" {
+        value 107;
+        description
+          "ATM IMA";
+      }
+      enum "pppMultilinkBundle" {
+        value 108;
+        description
+          "PPP Multilink Bundle";
+      }
+      enum "ipOverCdlc" {
+        value 109;
+        description
+          "IBM ipOverCdlc";
+      }
+      enum "ipOverClaw" {
+        value 110;
+        description
+          "IBM Common Link Access to Workstn";
+      }
+      enum "stackToStack" {
+        value 111;
+        description
+          "IBM stackToStack";
+      }
+      enum "virtualIpAddress" {
+        value 112;
+        description
+          "IBM VIPA";
+      }
+      enum "mpc" {
+        value 113;
+        description
+          "IBM multi-protocol channel support";
+      }
+      enum "ipOverAtm" {
+        value 114;
+        description
+          "IBM ipOverAtm";
+        reference
+          "RFC 2320 - Definitions of Managed Objects for Classical IP
+                      and ARP Over ATM Using SMIv2 (IPOA-MIB)";
+      }
+      enum "iso88025Fiber" {
+        value 115;
+        description
+          "ISO 802.5j Fiber Token Ring";
+      }
+      enum "tdlc" {
+        value 116;
+        description
+          "IBM twinaxial data link control";
+      }
+      enum "gigabitEthernet" {
+        value 117;
+        status deprecated;
+        description
+          "Obsoleted via RFC3635
+           ethernetCsmacd(6) should be used instead";
+        reference
+          "RFC 3635 - Definitions of Managed Objects for the
+                      Ethernet-like Interface Types.";
+      }
+      enum "hdlc" {
+        value 118;
+        description
+          "HDLC";
+      }
+      enum "lapf" {
+        value 119;
+        description
+          "LAP F";
+      }
+      enum "v37" {
+        value 120;
+        description
+          "V.37";
+      }
+      enum "x25mlp" {
+        value 121;
+        description
+          "Multi-Link Protocol";
+      }
+      enum "x25huntGroup" {
+        value 122;
+        description
+          "X25 Hunt Group";
+      }
+      enum "transpHdlc" {
+        value 123;
+        description
+          "Transp HDLC";
+      }
+      enum "interleave" {
+        value 124;
+        description
+          "Interleave channel";
+      }
+      enum "fast" {
+        value 125;
+        description
+          "Fast channel";
+      }
+      enum "ip" {
+        value 126;
+        description
+          "IP (for APPN HPR in IP networks)";
+      }
+      enum "docsCableMaclayer" {
+        value 127;
+        description
+          "CATV Mac Layer";
+      }
+      enum "docsCableDownstream" {
+        value 128;
+        description
+          "CATV Downstream interface";
+      }
+      enum "docsCableUpstream" {
+        value 129;
+        description
+          "CATV Upstream interface";
+      }
+      enum "a12MppSwitch" {
+        value 130;
+        description
+          "Avalon Parallel Processor";
+      }
+      enum "tunnel" {
+        value 131;
+        description
+          "Encapsulation interface";
+      }
+      enum "coffee" {
+        value 132;
+        description
+          "coffee pot";
+        reference
+          "RFC 2325 - Coffee MIB";
+      }
+      enum "ces" {
+        value 133;
+        description
+          "Circuit Emulation Service";
+      }
+      enum "atmSubInterface" {
+        value 134;
+        description
+          "ATM Sub Interface";
+      }
+      enum "l2vlan" {
+        value 135;
+        description
+          "Layer 2 Virtual LAN using 802.1Q";
+      }
+      enum "l3ipvlan" {
+        value 136;
+        description
+          "Layer 3 Virtual LAN using IP";
+      }
+      enum "l3ipxvlan" {
+        value 137;
+        description
+          "Layer 3 Virtual LAN using IPX";
+      }
+      enum "digitalPowerline" {
+        value 138;
+        description
+          "IP over Power Lines";
+      }
+      enum "mediaMailOverIp" {
+        value 139;
+        description
+          "Multimedia Mail over IP";
+      }
+      enum "dtm" {
+        value 140;
+        description
+          "Dynamic syncronous Transfer Mode";
+      }
+      enum "dcn" {
+        value 141;
+        description
+          "Data Communications Network";
+      }
+      enum "ipForward" {
+        value 142;
+        description
+          "IP Forwarding Interface";
+      }
+      enum "msdsl" {
+        value 143;
+        description
+          "Multi-rate Symmetric DSL";
+      }
+      enum "ieee1394" {
+        value 144;
+        description
+          "IEEE1394 High Performance Serial Bus";
+      }
+      enum "if-gsn" {
+        value 145;
+        description
+          "HIPPI-6400";
+      }
+      enum "dvbRccMacLayer" {
+        value 146;
+        description
+          "DVB-RCC MAC Layer";
+      }
+      enum "dvbRccDownstream" {
+        value 147;
+        description
+          "DVB-RCC Downstream Channel";
+      }
+      enum "dvbRccUpstream" {
+        value 148;
+        description
+          "DVB-RCC Upstream Channel";
+      }
+      enum "atmVirtual" {
+        value 149;
+        description
+          "ATM Virtual Interface";
+      }
+      enum "mplsTunnel" {
+        value 150;
+        description
+          "MPLS Tunnel Virtual Interface";
+      }
+      enum "srp" {
+        value 151;
+        description
+          "Spatial Reuse Protocol       ";
+      }
+      enum "voiceOverAtm" {
+        value 152;
+        description
+          "Voice Over ATM";
+      }
+      enum "voiceOverFrameRelay" {
+        value 153;
+        description
+          "Voice Over Frame Relay";
+      }
+      enum "idsl" {
+        value 154;
+        description
+          "Digital Subscriber Loop over ISDN";
+      }
+      enum "compositeLink" {
+        value 155;
+        description
+          "Avici Composite Link Interface";
+      }
+      enum "ss7SigLink" {
+        value 156;
+        description
+          "SS7 Signaling Link";
+      }
+      enum "propWirelessP2P" {
+        value 157;
+        description
+          "Prop. P2P wireless interface";
+      }
+      enum "frForward" {
+        value 158;
+        description
+          "Frame Forward Interface";
+      }
+      enum "rfc1483" {
+        value 159;
+        description
+          "Multiprotocol over ATM AAL5";
+        reference
+          "RFC 1483 - Multiprotocol Encapsulation over ATM
+                      Adaptation Layer 5";
+      }
+      enum "usb" {
+        value 160;
+        description
+          "USB Interface";
+      }
+      enum "ieee8023adLag" {
+        value 161;
+        description
+          "IEEE 802.3ad Link Aggregate";
+      }
+      enum "bgppolicyaccounting" {
+        value 162;
+        description
+          "BGP Policy Accounting";
+      }
+      enum "frf16MfrBundle" {
+        value 163;
+        description
+          "FRF .16 Multilink Frame Relay";
+      }
+      enum "h323Gatekeeper" {
+        value 164;
+        description
+          "H323 Gatekeeper";
+      }
+      enum "h323Proxy" {
+        value 165;
+        description
+          "H323 Voice and Video Proxy";
+      }
+      enum "mpls" {
+        value 166;
+        description
+          "MPLS";
+      }
+      enum "mfSigLink" {
+        value 167;
+        description
+          "Multi-frequency signaling link";
+      }
+      enum "hdsl2" {
+        value 168;
+        description
+          "High Bit-Rate DSL - 2nd generation";
+      }
+      enum "shdsl" {
+        value 169;
+        description
+          "Multirate HDSL2";
+      }
+      enum "ds1FDL" {
+        value 170;
+        description
+          "Facility Data Link 4Kbps on a DS1";
+      }
+      enum "pos" {
+        value 171;
+        description
+          "Packet over SONET/SDH Interface";
+      }
+      enum "dvbAsiIn" {
+        value 172;
+        description
+          "DVB-ASI Input";
+      }
+      enum "dvbAsiOut" {
+        value 173;
+        description
+          "DVB-ASI Output";
+      }
+      enum "plc" {
+        value 174;
+        description
+          "Power Line Communtications";
+      }
+      enum "nfas" {
+        value 175;
+        description
+          "Non Facility Associated Signaling";
+      }
+      enum "tr008" {
+        value 176;
+        description
+          "TR008";
+      }
+      enum "gr303RDT" {
+        value 177;
+        description
+          "Remote Digital Terminal";
+      }
+      enum "gr303IDT" {
+        value 178;
+        description
+          "Integrated Digital Terminal";
+      }
+      enum "isup" {
+        value 179;
+        description
+          "ISUP";
+      }
+      enum "propDocsWirelessMaclayer" {
+        value 180;
+        description
+          "Cisco proprietary Maclayer";
+      }
+      enum "propDocsWirelessDownstream" {
+        value 181;
+        description
+          "Cisco proprietary Downstream";
+      }
+      enum "propDocsWirelessUpstream" {
+        value 182;
+        description
+          "Cisco proprietary Upstream";
+      }
+      enum "hiperlan2" {
+        value 183;
+        description
+          "HIPERLAN Type 2 Radio Interface";
+      }
+      enum "propBWAp2Mp" {
+        value 184;
+        description
+          "PropBroadbandWirelessAccesspt2multipt use of this value
+           for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f
+           is deprecated and ieee80216WMAN(237) should be used
+           instead.";
+      }
+      enum "sonetOverheadChannel" {
+        value 185;
+        description
+          "SONET Overhead Channel";
+      }
+      enum "digitalWrapperOverheadChannel" {
+        value 186;
+        description
+          "Digital Wrapper";
+      }
+      enum "aal2" {
+        value 187;
+        description
+          "ATM adaptation layer 2";
+      }
+      enum "radioMAC" {
+        value 188;
+        description
+          "MAC layer over radio links";
+      }
+      enum "atmRadio" {
+        value 189;
+        description
+          "ATM over radio links";
+      }
+      enum "imt" {
+        value 190;
+        description
+          "Inter Machine Trunks";
+      }
+      enum "mvl" {
+        value 191;
+        description
+          "Multiple Virtual Lines DSL";
+      }
+      enum "reachDSL" {
+        value 192;
+        description
+          "Long Reach DSL";
+      }
+      enum "frDlciEndPt" {
+        value 193;
+        description
+          "Frame Relay DLCI End Point";
+      }
+      enum "atmVciEndPt" {
+        value 194;
+        description
+          "ATM VCI End Point";
+      }
+      enum "opticalChannel" {
+        value 195;
+        description
+          "Optical Channel";
+      }
+      enum "opticalTransport" {
+        value 196;
+        description
+          "Optical Transport";
+      }
+      enum "propAtm" {
+        value 197;
+        description
+          "Proprietary ATM";
+      }
+      enum "voiceOverCable" {
+        value 198;
+        description
+          "Voice Over Cable Interface";
+      }
+      enum "infiniband" {
+        value 199;
+        description
+          "Infiniband";
+      }
+      enum "teLink" {
+        value 200;
+        description
+          "TE Link";
+      }
+      enum "q2931" {
+        value 201;
+        description
+          "Q.2931";
+      }
+      enum "virtualTg" {
+        value 202;
+        description
+          "Virtual Trunk Group";
+      }
+      enum "sipTg" {
+        value 203;
+        description
+          "SIP Trunk Group";
+      }
+      enum "sipSig" {
+        value 204;
+        description
+          "SIP Signaling";
+      }
+      enum "docsCableUpstreamChannel" {
+        value 205;
+        description
+          "CATV Upstream Channel";
+      }
+      enum "econet" {
+        value 206;
+        description
+          "Acorn Econet";
+      }
+      enum "pon155" {
+        value 207;
+        description
+          "FSAN 155Mb Symetrical PON interface";
+      }
+      enum "pon622" {
+        value 208;
+        description
+          "FSAN622Mb Symetrical PON interface";
+      }
+      enum "bridge" {
+        value 209;
+        description
+          "Transparent bridge interface";
+      }
+      enum "linegroup" {
+        value 210;
+        description
+          "Interface common to multiple lines";
+      }
+      enum "voiceEMFGD" {
+        value 211;
+        description
+          "voice E&M Feature Group D";
+      }
+      enum "voiceFGDEANA" {
+        value 212;
+        description
+          "voice FGD Exchange Access North American";
+      }
+      enum "voiceDID" {
+        value 213;
+        description
+          "voice Direct Inward Dialing";
+      }
+      enum "mpegTransport" {
+        value 214;
+        description
+          "MPEG transport interface";
+      }
+      enum "sixToFour" {
+        value 215;
+        status deprecated;
+        description
+          "6to4 interface (DEPRECATED)";
+        reference
+          "RFC 4087 - IP Tunnel MIB";
+      }
+      enum "gtp" {
+        value 216;
+        description
+          "GTP (GPRS Tunneling Protocol)";
+      }
+      enum "pdnEtherLoop1" {
+        value 217;
+        description
+          "Paradyne EtherLoop 1";
+      }
+      enum "pdnEtherLoop2" {
+        value 218;
+        description
+          "Paradyne EtherLoop 2";
+      }
+      enum "opticalChannelGroup" {
+        value 219;
+        description
+          "Optical Channel Group";
+      }
+      enum "homepna" {
+        value 220;
+        description
+          "HomePNA ITU-T G.989";
+      }
+      enum "gfp" {
+        value 221;
+        description
+          "Generic Framing Procedure (GFP)";
+      }
+      enum "ciscoISLvlan" {
+        value 222;
+        description
+          "Layer 2 Virtual LAN using Cisco ISL";
+      }
+      enum "actelisMetaLOOP" {
+        value 223;
+        description
+          "Acteleis proprietary MetaLOOP High Speed Link";
+      }
+      enum "fcipLink" {
+        value 224;
+        description
+          "FCIP Link";
+      }
+      enum "rpr" {
+        value 225;
+        description
+          "Resilient Packet Ring Interface Type";
+      }
+      enum "qam" {
+        value 226;
+        description
+          "RF Qam Interface";
+      }
+      enum "lmp" {
+        value 227;
+        description
+          "Link Management Protocol";
+        reference
+          "RFC 4327 - Link Management Protocol (LMP) Management
+                      Information Base (MIB)";
+      }
+      enum "cblVectaStar" {
+        value 228;
+        description
+          "Cambridge Broadband Networks Limited VectaStar";
+      }
+      enum "docsCableMCmtsDownstream" {
+        value 229;
+        description
+          "CATV Modular CMTS Downstream Interface";
+      }
+      enum "adsl2" {
+        value 230;
+        status deprecated;
+        description
+          "Asymmetric Digital Subscriber Loop Version 2
+           (DEPRECATED/OBSOLETED - please use adsl2plus(238)
+           instead)";
+        reference
+          "RFC 4706 - Definitions of Managed Objects for Asymmetric
+                      Digital Subscriber Line 2 (ADSL2)";
+      }
+      enum "macSecControlledIF" {
+        value 231;
+        description
+          "MACSecControlled";
+      }
+      enum "macSecUncontrolledIF" {
+        value 232;
+        description
+          "MACSecUncontrolled";
+      }
+      enum "aviciOpticalEther" {
+        value 233;
+        description
+         "Avici Optical Ethernet Aggregate";
+      }
+      enum "atmbond" {
+        value 234;
+        description
+          "atmbond";
+      }
+      enum "voiceFGDOS" {
+        value 235;
+        description
+          "voice FGD Operator Services";
+      }
+      enum "mocaVersion1" {
+        value 236;
+        description
+          "MultiMedia over Coax Alliance (MoCA) Interface
+           as documented in information provided privately to IANA";
+      }
+      enum "ieee80216WMAN" {
+        value 237;
+        description
+          "IEEE 802.16 WMAN interface";
+      }
+      enum "adsl2plus" {
+        value 238;
+        description
+          "Asymmetric Digital Subscriber Loop Version 2,
+           Version 2 Plus and all variants";
+      }
+      enum "dvbRcsMacLayer" {
+        value 239;
+        description
+          "DVB-RCS MAC Layer";
+        reference
+          "RFC 5728 - The SatLabs Group DVB-RCS MIB";
+      }
+      enum "dvbTdm" {
+        value 240;
+        description
+          "DVB Satellite TDM";
+        reference
+          "RFC 5728 - The SatLabs Group DVB-RCS MIB";
+      }
+      enum "dvbRcsTdma" {
+        value 241;
+        description
+          "DVB-RCS TDMA";
+        reference
+          "RFC 5728 - The SatLabs Group DVB-RCS MIB";
+      }
+      enum "x86Laps" {
+        value 242;
+        description
+          "LAPS based on ITU-T X.86/Y.1323";
+      }
+      enum "wwanPP" {
+        value 243;
+        description
+          "3GPP WWAN";
+      }
+      enum "wwanPP2" {
+        value 244;
+        description
+          "3GPP2 WWAN";
+      }
+      enum "voiceEBS" {
+        value 245;
+        description
+          "voice P-phone EBS physical interface";
+      }
+      enum "ifPwType" {
+        value 246;
+        description
+          "Pseudowire interface type";
+        reference
+          "RFC 5601 - Pseudowire (PW) Management Information Base";
+      }
+      enum "ilan" {
+        value 247;
+        description
+          "Internal LAN on a bridge per IEEE 802.1ap";
+      }
+      enum "pip" {
+        value 248;
+        description
+          "Provider Instance Port on a bridge per IEEE 802.1ah PBB";
+      }
+      enum "aluELP" {
+        value 249;
+        description
+          "Alcatel-Lucent Ethernet Link Protection";
+      }
+      enum "gpon" {
+        value 250;
+        description
+          "Gigabit-capable passive optical networks (G-PON) as per
+           ITU-T G.948";
+      }
+      enum "vdsl2" {
+        value 251;
+        description
+          "Very high speed digital subscriber line Version 2
+           (as per ITU-T Recommendation G.993.2)";
+        reference
+          "RFC 5650 - Definitions of Managed Objects for Very High
+                      Speed Digital Subscriber Line 2 (VDSL2)";
+      }
+      enum "capwapDot11Profile" {
+        value 252;
+        description
+          "WLAN Profile Interface";
+        reference
+          "RFC 5834 - Control and Provisioning of Wireless Access
+                      Points (CAPWAP) Protocol Binding MIB for
+                      IEEE 802.11";
+      }
+      enum "capwapDot11Bss" {
+        value 253;
+        description
+          "WLAN BSS Interface";
+        reference
+          "RFC 5834 - Control and Provisioning of Wireless Access
+                      Points (CAPWAP) Protocol Binding MIB for
+                      IEEE 802.11";
+      }
+      enum "capwapWtpVirtualRadio" {
+        value 254;
+        description
+          "WTP Virtual Radio Interface";
+        reference
+          "RFC 5833 - Control and Provisioning of Wireless Access
+                      Points (CAPWAP) Protocol Base MIB";
+      }
+      enum "bits" {
+        value 255;
+        description
+          "bitsport";
+      }
+      enum "docsCableUpstreamRfPort" {
+        value 256;
+        description
+          "DOCSIS CATV Upstream RF Port";
+      }
+      enum "cableDownstreamRfPort" {
+        value 257;
+        description
+          "CATV downstream RF port";
+      }
+      enum "vmwareVirtualNic" {
+        value 258;
+        description
+          "VMware Virtual Network Interface";
+      }
+      enum "ieee802154" {
+        value 259;
+        description
+          "IEEE 802.15.4 WPAN interface";
+        reference
+          "IEEE 802.15.4-2006";
+      }
+      enum "otnOdu" {
+        value 260;
+        description
+          "OTN Optical Data Unit";
+      }
+      enum "otnOtu" {
+        value 261;
+        description
+          "OTN Optical channel Transport Unit";
+      }
+      enum "ifVfiType" {
+        value 262;
+        description
+          "VPLS Forwarding Instance Interface Type";
+      }
+      enum "g9981" {
+        value 263;
+        description
+          "G.998.1 bonded interface";
+      }
+      enum "g9982" {
+        value 264;
+        description
+          "G.998.2 bonded interface";
+      }
+      enum "g9983" {
+        value 265;
+        description
+          "G.998.3 bonded interface";
+      }
+      enum "aluEpon" {
+        value 266;
+        description
+          "Ethernet Passive Optical Networks (E-PON)";
+      }
+      enum "aluEponOnu" {
+        value 267;
+        description
+          "EPON Optical Network Unit";
+      }
+      enum "aluEponPhysicalUni" {
+        value 268;
+        description
+          "EPON physical User to Network interface";
+      }
+      enum "aluEponLogicalLink" {
+        value 269;
+        description
+          "The emulation of a point-to-point link over the EPON
+           layer";
+      }
+      enum "aluGponOnu" {
+        value 270;
+        description
+          "GPON Optical Network Unit";
+        reference
+          "ITU-T G.984.2";
+      }
+      enum "aluGponPhysicalUni" {
+        value 271;
+        description
+          "GPON physical User to Network interface";
+        reference
+          "ITU-T G.984.2";
+      }
+      enum "vmwareNicTeam" {
+        value 272;
+        description
+          "VMware NIC Team";
+      }
+    }
+    description
+      "This data type is used as the syntax of the 'type'
+       leaf in the 'interface' list in the YANG module
+       ietf-interface.
+
+       The definition of this typedef with the
+       addition of newly assigned values is published
+       periodically by the IANA, in either the Assigned
+       Numbers RFC, or some derivative of it specific to
+       Internet Network Management number assignments.  (The
+       latest arrangements can be obtained by contacting the
+       IANA.)
+
+       Requests for new values should be made to IANA via
+       email (iana&iana.org).";
+    reference
+      "ifType definitions registry.
+       <http://www.iana.org/assignments/smi-numbers>";
+  }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/ietf/iana-timezones@2012-07-09.yang b/yang/yang-parser-rfc7950/src/test/resources/ietf/iana-timezones@2012-07-09.yang
new file mode 100644 (file)
index 0000000..7fc1577
--- /dev/null
@@ -0,0 +1,1701 @@
+module iana-timezones {
+  namespace "urn:ietf:params:xml:ns:yang:iana-timezones";
+  prefix ianatz;
+
+  organization "IANA";
+  contact
+    "        Internet Assigned Numbers Authority
+
+     Postal: ICANN
+             4676 Admiralty Way, Suite 330
+             Marina del Rey, CA 90292
+
+     Tel:    +1 310 823 9358
+     E-Mail: iana&iana.org";
+  description
+    "This YANG module defines the iana-timezone typedef, which
+     contains YANG definitions for IANA-registered timezones.
+
+     This YANG module is maintained by IANA, and reflects the
+     IANA Time Zone Database.
+     (http://www.iana.org/time-zones)
+
+     The latest revision of this YANG module can be obtained from
+     the IANA web site.
+
+     Copyright (c) 2011 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD License
+     set forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC XXXX; see
+     the RFC itself for full legal notices.";
+
+  revision 2012-07-09 {
+    description
+      "Initial revision. Using IANA Time Zone Data v. 2012c
+       (Released 2012-03-27)";
+    reference "RFC XXXX: TITLE";
+  }
+  typedef iana-timezone {
+    description
+      "A timezone location as defined by the IANA timezone
+       database (http://www.iana.org/time-zones)";
+    type enumeration {
+      enum "Europe/Andorra" {
+        value 0;
+      }
+      enum "Asia/Dubai" {
+        value 1;
+      }
+      enum "Asia/Kabul" {
+        value 2;
+      }
+      enum "America/Antigua" {
+        value 3;
+      }
+      enum "America/Anguilla" {
+        value 4;
+      }
+      enum "Europe/Tirane" {
+        value 5;
+      }
+      enum "Asia/Yerevan" {
+        value 6;
+      }
+      enum "Africa/Luanda" {
+        value 7;
+      }
+      enum "Antarctica/McMurdo" {
+        value 8;
+        description
+          "McMurdo Station, Ross Island";
+      }
+      enum "Antarctica/South_Pole" {
+        value 9;
+        description
+          "Amundsen-Scott Station, South Pole";
+      }
+      enum "Antarctica/Rothera" {
+        value 10;
+        description
+          "Rothera Station, Adelaide Island";
+      }
+      enum "Antarctica/Palmer" {
+        value 11;
+        description
+          "Palmer Station, Anvers Island";
+      }
+      enum "Antarctica/Mawson" {
+        value 12;
+        description
+          "Mawson Station, Holme Bay";
+      }
+      enum "Antarctica/Davis" {
+        value 13;
+        description
+          "Davis Station, Vestfold Hills";
+      }
+      enum "Antarctica/Casey" {
+        value 14;
+        description
+          "Casey Station, Bailey Peninsula";
+      }
+      enum "Antarctica/Vostok" {
+        value 15;
+        description
+          "Vostok Station, Lake Vostok";
+      }
+      enum "Antarctica/DumontDUrville" {
+        value 16;
+        description
+          "Dumont-d'Urville Station, Terre Adelie";
+      }
+      enum "Antarctica/Syowa" {
+        value 17;
+        description
+          "Syowa Station, E Ongul I";
+      }
+      enum "Antarctica/Macquarie" {
+        value 18;
+        description
+          "Macquarie Island Station, Macquarie Island";
+      }
+      enum "America/Argentina/Buenos_Aires" {
+        value 19;
+        description
+          "Buenos Aires (BA, CF)";
+      }
+      enum "America/Argentina/Cordoba" {
+        value 20;
+        description
+          "most locations (CB, CC, CN, ER, FM, MN, SE, SF)";
+      }
+      enum "America/Argentina/Salta" {
+        value 21;
+        description
+          "(SA, LP, NQ, RN)";
+      }
+      enum "America/Argentina/Jujuy" {
+        value 22;
+        description
+          "Jujuy (JY)";
+      }
+      enum "America/Argentina/Tucuman" {
+        value 23;
+        description
+          "Tucuman (TM)";
+      }
+      enum "America/Argentina/Catamarca" {
+        value 24;
+        description
+          "Catamarca (CT), Chubut (CH)";
+      }
+      enum "America/Argentina/La_Rioja" {
+        value 25;
+        description
+          "La Rioja (LR)";
+      }
+      enum "America/Argentina/San_Juan" {
+        value 26;
+        description
+          "San Juan (SJ)";
+      }
+      enum "America/Argentina/Mendoza" {
+        value 27;
+        description
+          "Mendoza (MZ)";
+      }
+      enum "America/Argentina/San_Luis" {
+        value 28;
+        description
+          "San Luis (SL)";
+      }
+      enum "America/Argentina/Rio_Gallegos" {
+        value 29;
+        description
+          "Santa Cruz (SC)";
+      }
+      enum "America/Argentina/Ushuaia" {
+        value 30;
+        description
+          "Tierra del Fuego (TF)";
+      }
+      enum "Pacific/Pago_Pago" {
+        value 31;
+      }
+      enum "Europe/Vienna" {
+        value 32;
+      }
+      enum "Australia/Lord_Howe" {
+        value 33;
+        description
+          "Lord Howe Island";
+      }
+      enum "Australia/Hobart" {
+        value 34;
+        description
+          "Tasmania - most locations";
+      }
+      enum "Australia/Currie" {
+        value 35;
+        description
+          "Tasmania - King Island";
+      }
+      enum "Australia/Melbourne" {
+        value 36;
+        description
+          "Victoria";
+      }
+      enum "Australia/Sydney" {
+        value 37;
+        description
+          "New South Wales - most locations";
+      }
+      enum "Australia/Broken_Hill" {
+        value 38;
+        description
+          "New South Wales - Yancowinna";
+      }
+      enum "Australia/Brisbane" {
+        value 39;
+        description
+          "Queensland - most locations";
+      }
+      enum "Australia/Lindeman" {
+        value 40;
+        description
+          "Queensland - Holiday Islands";
+      }
+      enum "Australia/Adelaide" {
+        value 41;
+        description
+          "South Australia";
+      }
+      enum "Australia/Darwin" {
+        value 42;
+        description
+          "Northern Territory";
+      }
+      enum "Australia/Perth" {
+        value 43;
+        description
+          "Western Australia - most locations";
+      }
+      enum "Australia/Eucla" {
+        value 44;
+        description
+          "Western Australia - Eucla area";
+      }
+      enum "America/Aruba" {
+        value 45;
+      }
+      enum "Europe/Mariehamn" {
+        value 46;
+      }
+      enum "Asia/Baku" {
+        value 47;
+      }
+      enum "Europe/Sarajevo" {
+        value 48;
+      }
+      enum "America/Barbados" {
+        value 49;
+      }
+      enum "Asia/Dhaka" {
+        value 50;
+      }
+      enum "Europe/Brussels" {
+        value 51;
+      }
+      enum "Africa/Ouagadougou" {
+        value 52;
+      }
+      enum "Europe/Sofia" {
+        value 53;
+      }
+      enum "Asia/Bahrain" {
+        value 54;
+      }
+      enum "Africa/Bujumbura" {
+        value 55;
+      }
+      enum "Africa/Porto-Novo" {
+        value 56;
+      }
+      enum "America/St_Barthelemy" {
+        value 57;
+      }
+      enum "Atlantic/Bermuda" {
+        value 58;
+      }
+      enum "Asia/Brunei" {
+        value 59;
+      }
+      enum "America/La_Paz" {
+        value 60;
+      }
+      enum "America/Kralendijk" {
+        value 61;
+      }
+      enum "America/Noronha" {
+        value 62;
+        description
+          "Atlantic islands";
+      }
+      enum "America/Belem" {
+        value 63;
+        description
+          "Amapa, E Para";
+      }
+      enum "America/Fortaleza" {
+        value 64;
+        description
+          "NE Brazil (MA, PI, CE, RN, PB)";
+      }
+      enum "America/Recife" {
+        value 65;
+        description
+          "Pernambuco";
+      }
+      enum "America/Araguaina" {
+        value 66;
+        description
+          "Tocantins";
+      }
+      enum "America/Maceio" {
+        value 67;
+        description
+          "Alagoas, Sergipe";
+      }
+      enum "America/Bahia" {
+        value 68;
+        description
+          "Bahia";
+      }
+      enum "America/Sao_Paulo" {
+        value 69;
+        description
+          "S & SE Brazil (GO, DF, MG, ES, RJ, SP, PR, SC, RS)";
+      }
+      enum "America/Campo_Grande" {
+        value 70;
+        description
+          "Mato Grosso do Sul";
+      }
+      enum "America/Cuiaba" {
+        value 71;
+        description
+          "Mato Grosso";
+      }
+      enum "America/Santarem" {
+        value 72;
+        description
+          "W Para";
+      }
+      enum "America/Porto_Velho" {
+        value 73;
+        description
+          "Rondonia";
+      }
+      enum "America/Boa_Vista" {
+        value 74;
+        description
+          "Roraima";
+      }
+      enum "America/Manaus" {
+        value 75;
+        description
+          "E Amazonas";
+      }
+      enum "America/Eirunepe" {
+        value 76;
+        description
+          "W Amazonas";
+      }
+      enum "America/Rio_Branco" {
+        value 77;
+        description
+          "Acre";
+      }
+      enum "America/Nassau" {
+        value 78;
+      }
+      enum "Asia/Thimphu" {
+        value 79;
+      }
+      enum "Africa/Gaborone" {
+        value 80;
+      }
+      enum "Europe/Minsk" {
+        value 81;
+      }
+      enum "America/Belize" {
+        value 82;
+      }
+      enum "America/St_Johns" {
+        value 83;
+        description
+          "Newfoundland Time, including SE Labrador";
+      }
+      enum "America/Halifax" {
+        value 84;
+        description
+          "Atlantic Time - Nova Scotia (most places), PEI";
+      }
+      enum "America/Glace_Bay" {
+        value 85;
+        description
+          "Atlantic Time - Nova Scotia - places that did not observe
+           DST 1966-1971";
+      }
+      enum "America/Moncton" {
+        value 86;
+        description
+          "Atlantic Time - New Brunswick";
+      }
+      enum "America/Goose_Bay" {
+        value 87;
+        description
+          "Atlantic Time - Labrador - most locations";
+      }
+      enum "America/Blanc-Sablon" {
+        value 88;
+        description
+          "Atlantic Standard Time - Quebec - Lower North Shore";
+      }
+      enum "America/Montreal" {
+        value 89;
+        description
+          "Eastern Time - Quebec - most locations";
+      }
+      enum "America/Toronto" {
+        value 90;
+        description
+          "Eastern Time - Ontario - most locations";
+      }
+      enum "America/Nipigon" {
+        value 91;
+        description
+          "Eastern Time - Ontario & Quebec - places that did not
+           observe DST 1967-1973";
+      }
+      enum "America/Thunder_Bay" {
+        value 92;
+        description
+          "Eastern Time - Thunder Bay, Ontario";
+      }
+      enum "America/Iqaluit" {
+        value 93;
+        description
+          "Eastern Time - east Nunavut - most locations";
+      }
+      enum "America/Pangnirtung" {
+        value 94;
+        description
+          "Eastern Time - Pangnirtung, Nunavut";
+      }
+      enum "America/Resolute" {
+        value 95;
+        description
+          "Central Standard Time - Resolute, Nunavut";
+      }
+      enum "America/Atikokan" {
+        value 96;
+        description
+          "Eastern Standard Time - Atikokan, Ontario and Southampton I,
+           Nunavut";
+      }
+      enum "America/Rankin_Inlet" {
+        value 97;
+        description
+          "Central Time - central Nunavut";
+      }
+      enum "America/Winnipeg" {
+        value 98;
+        description
+          "Central Time - Manitoba & west Ontario";
+      }
+      enum "America/Rainy_River" {
+        value 99;
+        description
+          "Central Time - Rainy River & Fort Frances, Ontario";
+      }
+      enum "America/Regina" {
+        value 100;
+        description
+          "Central Standard Time - Saskatchewan - most locations";
+      }
+      enum "America/Swift_Current" {
+        value 101;
+        description
+          "Central Standard Time - Saskatchewan - midwest";
+      }
+      enum "America/Edmonton" {
+        value 102;
+        description
+          "Mountain Time - Alberta, east British Columbia & west
+           Saskatchewan";
+      }
+      enum "America/Cambridge_Bay" {
+        value 103;
+        description
+          "Mountain Time - west Nunavut";
+      }
+      enum "America/Yellowknife" {
+        value 104;
+        description
+          "Mountain Time - central Northwest Territories";
+      }
+      enum "America/Inuvik" {
+        value 105;
+        description
+          "Mountain Time - west Northwest Territories";
+      }
+      enum "America/Creston" {
+        value 106;
+        description
+          "Mountain Standard Time - Creston, British Columbia";
+      }
+      enum "America/Dawson_Creek" {
+        value 107;
+        description
+          "Mountain Standard Time - Dawson Creek & Fort Saint John,
+           British Columbia";
+      }
+      enum "America/Vancouver" {
+        value 108;
+        description
+          "Pacific Time - west British Columbia";
+      }
+      enum "America/Whitehorse" {
+        value 109;
+        description
+          "Pacific Time - south Yukon";
+      }
+      enum "America/Dawson" {
+        value 110;
+        description
+          "Pacific Time - north Yukon";
+      }
+      enum "Indian/Cocos" {
+        value 111;
+      }
+      enum "Africa/Kinshasa" {
+        value 112;
+        description
+          "west Dem. Rep. of Congo";
+      }
+      enum "Africa/Lubumbashi" {
+        value 113;
+        description
+          "east Dem. Rep. of Congo";
+      }
+      enum "Africa/Bangui" {
+        value 114;
+      }
+      enum "Africa/Brazzaville" {
+        value 115;
+      }
+      enum "Europe/Zurich" {
+        value 116;
+      }
+      enum "Africa/Abidjan" {
+        value 117;
+      }
+      enum "Pacific/Rarotonga" {
+        value 118;
+      }
+      enum "America/Santiago" {
+        value 119;
+        description
+          "most locations";
+      }
+      enum "Pacific/Easter" {
+        value 120;
+        description
+          "Easter Island & Sala y Gomez";
+      }
+      enum "Africa/Douala" {
+        value 121;
+      }
+      enum "Asia/Shanghai" {
+        value 122;
+        description
+          "east China - Beijing, Guangdong, Shanghai, etc.";
+      }
+      enum "Asia/Harbin" {
+        value 123;
+        description
+          "Heilongjiang (except Mohe), Jilin";
+      }
+      enum "Asia/Chongqing" {
+        value 124;
+        description
+          "central China - Sichuan, Yunnan, Guangxi, Shaanxi, Guizhou,
+           etc.";
+      }
+      enum "Asia/Urumqi" {
+        value 125;
+        description
+          "most of Tibet & Xinjiang";
+      }
+      enum "Asia/Kashgar" {
+        value 126;
+        description
+          "west Tibet & Xinjiang";
+      }
+      enum "America/Bogota" {
+        value 127;
+      }
+      enum "America/Costa_Rica" {
+        value 128;
+      }
+      enum "America/Havana" {
+        value 129;
+      }
+      enum "Atlantic/Cape_Verde" {
+        value 130;
+      }
+      enum "America/Curacao" {
+        value 131;
+      }
+      enum "Indian/Christmas" {
+        value 132;
+      }
+      enum "Asia/Nicosia" {
+        value 133;
+      }
+      enum "Europe/Prague" {
+        value 134;
+      }
+      enum "Europe/Berlin" {
+        value 135;
+      }
+      enum "Africa/Djibouti" {
+        value 136;
+      }
+      enum "Europe/Copenhagen" {
+        value 137;
+      }
+      enum "America/Dominica" {
+        value 138;
+      }
+      enum "America/Santo_Domingo" {
+        value 139;
+      }
+      enum "Africa/Algiers" {
+        value 140;
+      }
+      enum "America/Guayaquil" {
+        value 141;
+        description
+          "mainland";
+      }
+      enum "Pacific/Galapagos" {
+        value 142;
+        description
+          "Galapagos Islands";
+      }
+      enum "Europe/Tallinn" {
+        value 143;
+      }
+      enum "Africa/Cairo" {
+        value 144;
+      }
+      enum "Africa/El_Aaiun" {
+        value 145;
+      }
+      enum "Africa/Asmara" {
+        value 146;
+      }
+      enum "Europe/Madrid" {
+        value 147;
+        description
+          "mainland";
+      }
+      enum "Africa/Ceuta" {
+        value 148;
+        description
+          "Ceuta & Melilla";
+      }
+      enum "Atlantic/Canary" {
+        value 149;
+        description
+          "Canary Islands";
+      }
+      enum "Africa/Addis_Ababa" {
+        value 150;
+      }
+      enum "Europe/Helsinki" {
+        value 151;
+      }
+      enum "Pacific/Fiji" {
+        value 152;
+      }
+      enum "Atlantic/Stanley" {
+        value 153;
+      }
+      enum "Pacific/Chuuk" {
+        value 154;
+        description
+          "Chuuk (Truk) and Yap";
+      }
+      enum "Pacific/Pohnpei" {
+        value 155;
+        description
+          "Pohnpei (Ponape)";
+      }
+      enum "Pacific/Kosrae" {
+        value 156;
+        description
+          "Kosrae";
+      }
+      enum "Atlantic/Faroe" {
+        value 157;
+      }
+      enum "Europe/Paris" {
+        value 158;
+      }
+      enum "Africa/Libreville" {
+        value 159;
+      }
+      enum "Europe/London" {
+        value 160;
+      }
+      enum "America/Grenada" {
+        value 161;
+      }
+      enum "Asia/Tbilisi" {
+        value 162;
+      }
+      enum "America/Cayenne" {
+        value 163;
+      }
+      enum "Europe/Guernsey" {
+        value 164;
+      }
+      enum "Africa/Accra" {
+        value 165;
+      }
+      enum "Europe/Gibraltar" {
+        value 166;
+      }
+      enum "America/Godthab" {
+        value 167;
+        description
+          "most locations";
+      }
+      enum "America/Danmarkshavn" {
+        value 168;
+        description
+          "east coast, north of Scoresbysund";
+      }
+      enum "America/Scoresbysund" {
+        value 169;
+        description
+          "Scoresbysund / Ittoqqortoormiit";
+      }
+      enum "America/Thule" {
+        value 170;
+        description
+          "Thule / Pituffik";
+      }
+      enum "Africa/Banjul" {
+        value 171;
+      }
+      enum "Africa/Conakry" {
+        value 172;
+      }
+      enum "America/Guadeloupe" {
+        value 173;
+      }
+      enum "Africa/Malabo" {
+        value 174;
+      }
+      enum "Europe/Athens" {
+        value 175;
+      }
+      enum "Atlantic/South_Georgia" {
+        value 176;
+      }
+      enum "America/Guatemala" {
+        value 177;
+      }
+      enum "Pacific/Guam" {
+        value 178;
+      }
+      enum "Africa/Bissau" {
+        value 179;
+      }
+      enum "America/Guyana" {
+        value 180;
+      }
+      enum "Asia/Hong_Kong" {
+        value 181;
+      }
+      enum "America/Tegucigalpa" {
+        value 182;
+      }
+      enum "Europe/Zagreb" {
+        value 183;
+      }
+      enum "America/Port-au-Prince" {
+        value 184;
+      }
+      enum "Europe/Budapest" {
+        value 185;
+      }
+      enum "Asia/Jakarta" {
+        value 186;
+        description
+          "Java & Sumatra";
+      }
+      enum "Asia/Pontianak" {
+        value 187;
+        description
+          "west & central Borneo";
+      }
+      enum "Asia/Makassar" {
+        value 188;
+        description
+          "east & south Borneo, Sulawesi (Celebes), Bali, Nusa
+           Tengarra, west Timor";
+      }
+      enum "Asia/Jayapura" {
+        value 189;
+        description
+          "west New Guinea (Irian Jaya) & Malukus (Moluccas)";
+      }
+      enum "Europe/Dublin" {
+        value 190;
+      }
+      enum "Asia/Jerusalem" {
+        value 191;
+      }
+      enum "Europe/Isle_of_Man" {
+        value 192;
+      }
+      enum "Asia/Kolkata" {
+        value 193;
+      }
+      enum "Indian/Chagos" {
+        value 194;
+      }
+      enum "Asia/Baghdad" {
+        value 195;
+      }
+      enum "Asia/Tehran" {
+        value 196;
+      }
+      enum "Atlantic/Reykjavik" {
+        value 197;
+      }
+      enum "Europe/Rome" {
+        value 198;
+      }
+      enum "Europe/Jersey" {
+        value 199;
+      }
+      enum "America/Jamaica" {
+        value 200;
+      }
+      enum "Asia/Amman" {
+        value 201;
+      }
+      enum "Asia/Tokyo" {
+        value 202;
+      }
+      enum "Africa/Nairobi" {
+        value 203;
+      }
+      enum "Asia/Bishkek" {
+        value 204;
+      }
+      enum "Asia/Phnom_Penh" {
+        value 205;
+      }
+      enum "Pacific/Tarawa" {
+        value 206;
+        description
+          "Gilbert Islands";
+      }
+      enum "Pacific/Enderbury" {
+        value 207;
+        description
+          "Phoenix Islands";
+      }
+      enum "Pacific/Kiritimati" {
+        value 208;
+        description
+          "Line Islands";
+      }
+      enum "Indian/Comoro" {
+        value 209;
+      }
+      enum "America/St_Kitts" {
+        value 210;
+      }
+      enum "Asia/Pyongyang" {
+        value 211;
+      }
+      enum "Asia/Seoul" {
+        value 212;
+      }
+      enum "Asia/Kuwait" {
+        value 213;
+      }
+      enum "America/Cayman" {
+        value 214;
+      }
+      enum "Asia/Almaty" {
+        value 215;
+        description
+          "most locations";
+      }
+      enum "Asia/Qyzylorda" {
+        value 216;
+        description
+          "Qyzylorda (Kyzylorda, Kzyl-Orda)";
+      }
+      enum "Asia/Aqtobe" {
+        value 217;
+        description
+          "Aqtobe (Aktobe)";
+      }
+      enum "Asia/Aqtau" {
+        value 218;
+        description
+          "Atyrau (Atirau, Gur'yev), Mangghystau (Mankistau)";
+      }
+      enum "Asia/Oral" {
+        value 219;
+        description
+          "West Kazakhstan";
+      }
+      enum "Asia/Vientiane" {
+        value 220;
+      }
+      enum "Asia/Beirut" {
+        value 221;
+      }
+      enum "America/St_Lucia" {
+        value 222;
+      }
+      enum "Europe/Vaduz" {
+        value 223;
+      }
+      enum "Asia/Colombo" {
+        value 224;
+      }
+      enum "Africa/Monrovia" {
+        value 225;
+      }
+      enum "Africa/Maseru" {
+        value 226;
+      }
+      enum "Europe/Vilnius" {
+        value 227;
+      }
+      enum "Europe/Luxembourg" {
+        value 228;
+      }
+      enum "Europe/Riga" {
+        value 229;
+      }
+      enum "Africa/Tripoli" {
+        value 230;
+      }
+      enum "Africa/Casablanca" {
+        value 231;
+      }
+      enum "Europe/Monaco" {
+        value 232;
+      }
+      enum "Europe/Chisinau" {
+        value 233;
+      }
+      enum "Europe/Podgorica" {
+        value 234;
+      }
+      enum "America/Marigot" {
+        value 235;
+      }
+      enum "Indian/Antananarivo" {
+        value 236;
+      }
+      enum "Pacific/Majuro" {
+        value 237;
+        description
+          "most locations";
+      }
+      enum "Pacific/Kwajalein" {
+        value 238;
+        description
+          "Kwajalein";
+      }
+      enum "Europe/Skopje" {
+        value 239;
+      }
+      enum "Africa/Bamako" {
+        value 240;
+      }
+      enum "Asia/Rangoon" {
+        value 241;
+      }
+      enum "Asia/Ulaanbaatar" {
+        value 242;
+        description
+          "most locations";
+      }
+      enum "Asia/Hovd" {
+        value 243;
+        description
+          "Bayan-Olgiy, Govi-Altai, Hovd, Uvs, Zavkhan";
+      }
+      enum "Asia/Choibalsan" {
+        value 244;
+        description
+          "Dornod, Sukhbaatar";
+      }
+      enum "Asia/Macau" {
+        value 245;
+      }
+      enum "Pacific/Saipan" {
+        value 246;
+      }
+      enum "America/Martinique" {
+        value 247;
+      }
+      enum "Africa/Nouakchott" {
+        value 248;
+      }
+      enum "America/Montserrat" {
+        value 249;
+      }
+      enum "Europe/Malta" {
+        value 250;
+      }
+      enum "Indian/Mauritius" {
+        value 251;
+      }
+      enum "Indian/Maldives" {
+        value 252;
+      }
+      enum "Africa/Blantyre" {
+        value 253;
+      }
+      enum "America/Mexico_City" {
+        value 254;
+        description
+          "Central Time - most locations";
+      }
+      enum "America/Cancun" {
+        value 255;
+        description
+          "Central Time - Quintana Roo";
+      }
+      enum "America/Merida" {
+        value 256;
+        description
+          "Central Time - Campeche, Yucatan";
+      }
+      enum "America/Monterrey" {
+        value 257;
+        description
+          "Mexican Central Time - Coahuila, Durango, Nuevo Leon,
+           Tamaulipas away from US border";
+      }
+      enum "America/Matamoros" {
+        value 258;
+        description
+          "US Central Time - Coahuila, Durango, Nuevo Leon, Tamaulipas
+           near US border";
+      }
+      enum "America/Mazatlan" {
+        value 259;
+        description
+          "Mountain Time - S Baja, Nayarit, Sinaloa";
+      }
+      enum "America/Chihuahua" {
+        value 260;
+        description
+          "Mexican Mountain Time - Chihuahua away from US border";
+      }
+      enum "America/Ojinaga" {
+        value 261;
+        description
+          "US Mountain Time - Chihuahua near US border";
+      }
+      enum "America/Hermosillo" {
+        value 262;
+        description
+          "Mountain Standard Time - Sonora";
+      }
+      enum "America/Tijuana" {
+        value 263;
+        description
+          "US Pacific Time - Baja California near US border";
+      }
+      enum "America/Santa_Isabel" {
+        value 264;
+        description
+          "Mexican Pacific Time - Baja California away from US border";
+      }
+      enum "America/Bahia_Banderas" {
+        value 265;
+        description
+          "Mexican Central Time - Bahia de Banderas";
+      }
+      enum "Asia/Kuala_Lumpur" {
+        value 266;
+        description
+          "peninsular Malaysia";
+      }
+      enum "Asia/Kuching" {
+        value 267;
+        description
+          "Sabah & Sarawak";
+      }
+      enum "Africa/Maputo" {
+        value 268;
+      }
+      enum "Africa/Windhoek" {
+        value 269;
+      }
+      enum "Pacific/Noumea" {
+        value 270;
+      }
+      enum "Africa/Niamey" {
+        value 271;
+      }
+      enum "Pacific/Norfolk" {
+        value 272;
+      }
+      enum "Africa/Lagos" {
+        value 273;
+      }
+      enum "America/Managua" {
+        value 274;
+      }
+      enum "Europe/Amsterdam" {
+        value 275;
+      }
+      enum "Europe/Oslo" {
+        value 276;
+      }
+      enum "Asia/Kathmandu" {
+        value 277;
+      }
+      enum "Pacific/Nauru" {
+        value 278;
+      }
+      enum "Pacific/Niue" {
+        value 279;
+      }
+      enum "Pacific/Auckland" {
+        value 280;
+        description
+          "most locations";
+      }
+      enum "Pacific/Chatham" {
+        value 281;
+        description
+          "Chatham Islands";
+      }
+      enum "Asia/Muscat" {
+        value 282;
+      }
+      enum "America/Panama" {
+        value 283;
+      }
+      enum "America/Lima" {
+        value 284;
+      }
+      enum "Pacific/Tahiti" {
+        value 285;
+        description
+          "Society Islands";
+      }
+      enum "Pacific/Marquesas" {
+        value 286;
+        description
+          "Marquesas Islands";
+      }
+      enum "Pacific/Gambier" {
+        value 287;
+        description
+          "Gambier Islands";
+      }
+      enum "Pacific/Port_Moresby" {
+        value 288;
+      }
+      enum "Asia/Manila" {
+        value 289;
+      }
+      enum "Asia/Karachi" {
+        value 290;
+      }
+      enum "Europe/Warsaw" {
+        value 291;
+      }
+      enum "America/Miquelon" {
+        value 292;
+      }
+      enum "Pacific/Pitcairn" {
+        value 293;
+      }
+      enum "America/Puerto_Rico" {
+        value 294;
+      }
+      enum "Asia/Gaza" {
+        value 295;
+        description
+          "Gaza Strip";
+      }
+      enum "Asia/Hebron" {
+        value 296;
+        description
+          "West Bank";
+      }
+      enum "Europe/Lisbon" {
+        value 297;
+        description
+          "mainland";
+      }
+      enum "Atlantic/Madeira" {
+        value 298;
+        description
+          "Madeira Islands";
+      }
+      enum "Atlantic/Azores" {
+        value 299;
+        description
+          "Azores";
+      }
+      enum "Pacific/Palau" {
+        value 300;
+      }
+      enum "America/Asuncion" {
+        value 301;
+      }
+      enum "Asia/Qatar" {
+        value 302;
+      }
+      enum "Indian/Reunion" {
+        value 303;
+      }
+      enum "Europe/Bucharest" {
+        value 304;
+      }
+      enum "Europe/Belgrade" {
+        value 305;
+      }
+      enum "Europe/Kaliningrad" {
+        value 306;
+        description
+          "Moscow-01 - Kaliningrad";
+      }
+      enum "Europe/Moscow" {
+        value 307;
+        description
+          "Moscow+00 - west Russia";
+      }
+      enum "Europe/Volgograd" {
+        value 308;
+        description
+          "Moscow+00 - Caspian Sea";
+      }
+      enum "Europe/Samara" {
+        value 309;
+        description
+          "Moscow+00 - Samara, Udmurtia";
+      }
+      enum "Asia/Yekaterinburg" {
+        value 310;
+        description
+          "Moscow+02 - Urals";
+      }
+      enum "Asia/Omsk" {
+        value 311;
+        description
+          "Moscow+03 - west Siberia";
+      }
+      enum "Asia/Novosibirsk" {
+        value 312;
+        description
+          "Moscow+03 - Novosibirsk";
+      }
+      enum "Asia/Novokuznetsk" {
+        value 313;
+        description
+          "Moscow+03 - Novokuznetsk";
+      }
+      enum "Asia/Krasnoyarsk" {
+        value 314;
+        description
+          "Moscow+04 - Yenisei River";
+      }
+      enum "Asia/Irkutsk" {
+        value 315;
+        description
+          "Moscow+05 - Lake Baikal";
+      }
+      enum "Asia/Yakutsk" {
+        value 316;
+        description
+          "Moscow+06 - Lena River";
+      }
+      enum "Asia/Vladivostok" {
+        value 317;
+        description
+          "Moscow+07 - Amur River";
+      }
+      enum "Asia/Sakhalin" {
+        value 318;
+        description
+          "Moscow+07 - Sakhalin Island";
+      }
+      enum "Asia/Magadan" {
+        value 319;
+        description
+          "Moscow+08 - Magadan";
+      }
+      enum "Asia/Kamchatka" {
+        value 320;
+        description
+          "Moscow+08 - Kamchatka";
+      }
+      enum "Asia/Anadyr" {
+        value 321;
+        description
+          "Moscow+08 - Bering Sea";
+      }
+      enum "Africa/Kigali" {
+        value 322;
+      }
+      enum "Asia/Riyadh" {
+        value 323;
+      }
+      enum "Pacific/Guadalcanal" {
+        value 324;
+      }
+      enum "Indian/Mahe" {
+        value 325;
+      }
+      enum "Africa/Khartoum" {
+        value 326;
+      }
+      enum "Europe/Stockholm" {
+        value 327;
+      }
+      enum "Asia/Singapore" {
+        value 328;
+      }
+      enum "Atlantic/St_Helena" {
+        value 329;
+      }
+      enum "Europe/Ljubljana" {
+        value 330;
+      }
+      enum "Arctic/Longyearbyen" {
+        value 331;
+      }
+      enum "Europe/Bratislava" {
+        value 332;
+      }
+      enum "Africa/Freetown" {
+        value 333;
+      }
+      enum "Europe/San_Marino" {
+        value 334;
+      }
+      enum "Africa/Dakar" {
+        value 335;
+      }
+      enum "Africa/Mogadishu" {
+        value 336;
+      }
+      enum "America/Paramaribo" {
+        value 337;
+      }
+      enum "Africa/Juba" {
+        value 338;
+      }
+      enum "Africa/Sao_Tome" {
+        value 339;
+      }
+      enum "America/El_Salvador" {
+        value 340;
+      }
+      enum "America/Lower_Princes" {
+        value 341;
+      }
+      enum "Asia/Damascus" {
+        value 342;
+      }
+      enum "Africa/Mbabane" {
+        value 343;
+      }
+      enum "America/Grand_Turk" {
+        value 344;
+      }
+      enum "Africa/Ndjamena" {
+        value 345;
+      }
+      enum "Indian/Kerguelen" {
+        value 346;
+      }
+      enum "Africa/Lome" {
+        value 347;
+      }
+      enum "Asia/Bangkok" {
+        value 348;
+      }
+      enum "Asia/Dushanbe" {
+        value 349;
+      }
+      enum "Pacific/Fakaofo" {
+        value 350;
+      }
+      enum "Asia/Dili" {
+        value 351;
+      }
+      enum "Asia/Ashgabat" {
+        value 352;
+      }
+      enum "Africa/Tunis" {
+        value 353;
+      }
+      enum "Pacific/Tongatapu" {
+        value 354;
+      }
+      enum "Europe/Istanbul" {
+        value 355;
+      }
+      enum "America/Port_of_Spain" {
+        value 356;
+      }
+      enum "Pacific/Funafuti" {
+        value 357;
+      }
+      enum "Asia/Taipei" {
+        value 358;
+      }
+      enum "Africa/Dar_es_Salaam" {
+        value 359;
+      }
+      enum "Europe/Kiev" {
+        value 360;
+        description
+          "most locations";
+      }
+      enum "Europe/Uzhgorod" {
+        value 361;
+        description
+          "Ruthenia";
+      }
+      enum "Europe/Zaporozhye" {
+        value 362;
+        description
+          "Zaporozh'ye, E Lugansk / Zaporizhia, E Luhansk";
+      }
+      enum "Europe/Simferopol" {
+        value 363;
+        description
+          "central Crimea";
+      }
+      enum "Africa/Kampala" {
+        value 364;
+      }
+      enum "Pacific/Johnston" {
+        value 365;
+        description
+          "Johnston Atoll";
+      }
+      enum "Pacific/Midway" {
+        value 366;
+        description
+          "Midway Islands";
+      }
+      enum "Pacific/Wake" {
+        value 367;
+        description
+          "Wake Island";
+      }
+      enum "America/New_York" {
+        value 368;
+        description
+          "Eastern Time";
+      }
+      enum "America/Detroit" {
+        value 369;
+        description
+          "Eastern Time - Michigan - most locations";
+      }
+      enum "America/Kentucky/Louisville" {
+        value 370;
+        description
+          "Eastern Time - Kentucky - Louisville area";
+      }
+      enum "America/Kentucky/Monticello" {
+        value 371;
+        description
+          "Eastern Time - Kentucky - Wayne County";
+      }
+      enum "America/Indiana/Indianapolis" {
+        value 372;
+        description
+          "Eastern Time - Indiana - most locations";
+      }
+      enum "America/Indiana/Vincennes" {
+        value 373;
+        description
+          "Eastern Time - Indiana - Daviess, Dubois, Knox & Martin
+           Counties";
+      }
+      enum "America/Indiana/Winamac" {
+        value 374;
+        description
+          "Eastern Time - Indiana - Pulaski County";
+      }
+      enum "America/Indiana/Marengo" {
+        value 375;
+        description
+          "Eastern Time - Indiana - Crawford County";
+      }
+      enum "America/Indiana/Petersburg" {
+        value 376;
+        description
+          "Eastern Time - Indiana - Pike County";
+      }
+      enum "America/Indiana/Vevay" {
+        value 377;
+        description
+          "Eastern Time - Indiana - Switzerland County";
+      }
+      enum "America/Chicago" {
+        value 378;
+        description
+          "Central Time";
+      }
+      enum "America/Indiana/Tell_City" {
+        value 379;
+        description
+          "Central Time - Indiana - Perry County";
+      }
+      enum "America/Indiana/Knox" {
+        value 380;
+        description
+          "Central Time - Indiana - Starke County";
+      }
+      enum "America/Menominee" {
+        value 381;
+        description
+          "Central Time - Michigan - Dickinson, Gogebic, Iron &
+           Menominee Counties";
+      }
+      enum "America/North_Dakota/Center" {
+        value 382;
+        description
+          "Central Time - North Dakota - Oliver County";
+      }
+      enum "America/North_Dakota/New_Salem" {
+        value 383;
+        description
+          "Central Time - North Dakota - Morton County (except Mandan
+           area)";
+      }
+      enum "America/North_Dakota/Beulah" {
+        value 384;
+        description
+          "Central Time - North Dakota - Mercer County";
+      }
+      enum "America/Denver" {
+        value 385;
+        description
+          "Mountain Time";
+      }
+      enum "America/Boise" {
+        value 386;
+        description
+          "Mountain Time - south Idaho & east Oregon";
+      }
+      enum "America/Shiprock" {
+        value 387;
+        description
+          "Mountain Time - Navajo";
+      }
+      enum "America/Phoenix" {
+        value 388;
+        description
+          "Mountain Standard Time - Arizona";
+      }
+      enum "America/Los_Angeles" {
+        value 389;
+        description
+          "Pacific Time";
+      }
+      enum "America/Anchorage" {
+        value 390;
+        description
+          "Alaska Time";
+      }
+      enum "America/Juneau" {
+        value 391;
+        description
+          "Alaska Time - Alaska panhandle";
+      }
+      enum "America/Sitka" {
+        value 392;
+        description
+          "Alaska Time - southeast Alaska panhandle";
+      }
+      enum "America/Yakutat" {
+        value 393;
+        description
+          "Alaska Time - Alaska panhandle neck";
+      }
+      enum "America/Nome" {
+        value 394;
+        description
+          "Alaska Time - west Alaska";
+      }
+      enum "America/Adak" {
+        value 395;
+        description
+          "Aleutian Islands";
+      }
+      enum "America/Metlakatla" {
+        value 396;
+        description
+          "Metlakatla Time - Annette Island";
+      }
+      enum "Pacific/Honolulu" {
+        value 397;
+        description
+          "Hawaii";
+      }
+      enum "America/Montevideo" {
+        value 398;
+      }
+      enum "Asia/Samarkand" {
+        value 399;
+        description
+          "west Uzbekistan";
+      }
+      enum "Asia/Tashkent" {
+        value 400;
+        description
+          "east Uzbekistan";
+      }
+      enum "Europe/Vatican" {
+        value 401;
+      }
+      enum "America/St_Vincent" {
+        value 402;
+      }
+      enum "America/Caracas" {
+        value 403;
+      }
+      enum "America/Tortola" {
+        value 404;
+      }
+      enum "America/St_Thomas" {
+        value 405;
+      }
+      enum "Asia/Ho_Chi_Minh" {
+        value 406;
+      }
+      enum "Pacific/Efate" {
+        value 407;
+      }
+      enum "Pacific/Wallis" {
+        value 408;
+      }
+      enum "Pacific/Apia" {
+        value 409;
+      }
+      enum "Asia/Aden" {
+        value 410;
+      }
+      enum "Indian/Mayotte" {
+        value 411;
+      }
+      enum "Africa/Johannesburg" {
+        value 412;
+      }
+      enum "Africa/Lusaka" {
+        value 413;
+      }
+      enum "Africa/Harare" {
+        value 414;
+      }
+    }
+  }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/ietf/ietf-inet-types@2010-09-24.yang b/yang/yang-parser-rfc7950/src/test/resources/ietf/ietf-inet-types@2010-09-24.yang
new file mode 100644 (file)
index 0000000..de20feb
--- /dev/null
@@ -0,0 +1,418 @@
+ module ietf-inet-types {
+
+   namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
+   prefix "inet";
+
+   organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+   contact
+    "WG Web:   <http://tools.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+
+     WG Chair: David Partain
+               <mailto:david.partain@ericsson.com>
+
+     WG Chair: David Kessens
+               <mailto:david.kessens@nsn.com>
+
+     Editor:   Juergen Schoenwaelder
+               <mailto:j.schoenwaelder@jacobs-university.de>";
+
+   description
+    "This module contains a collection of generally useful derived
+     YANG data types for Internet addresses and related things.
+
+     Copyright (c) 2010 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or without
+     modification, is permitted pursuant to, and subject to the license
+     terms contained in, the Simplified BSD License set forth in Section
+     4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6021; see
+     the RFC itself for full legal notices.";
+
+   revision 2010-09-24 {
+     description
+      "Initial revision.";
+     reference
+      "RFC 6021: Common YANG Data Types";
+   }
+
+   /*** collection of protocol field related types ***/
+
+   typedef ip-version {
+     type enumeration {
+       enum unknown {
+         value "0";
+         description
+          "An unknown or unspecified version of the Internet protocol.";
+       }
+       enum ipv4 {
+         value "1";
+         description
+          "The IPv4 protocol as defined in RFC 791.";
+       }
+       enum ipv6 {
+         value "2";
+         description
+          "The IPv6 protocol as defined in RFC 2460.";
+       }
+     }
+     description
+      "This value represents the version of the IP protocol.
+
+       In the value set and its semantics, this type is equivalent
+       to the InetVersion textual convention of the SMIv2.";
+     reference
+      "RFC  791: Internet Protocol
+       RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+       RFC 4001: Textual Conventions for Internet Network Addresses";
+   }
+
+   typedef dscp {
+     type uint8 {
+       range "0..63";
+     }
+     description
+      "The dscp type represents a Differentiated Services Code-Point
+       that may be used for marking packets in a traffic stream.
+
+       In the value set and its semantics, this type is equivalent
+       to the Dscp textual convention of the SMIv2.";
+     reference
+      "RFC 3289: Management Information Base for the Differentiated
+                 Services Architecture
+       RFC 2474: Definition of the Differentiated Services Field
+                 (DS Field) in the IPv4 and IPv6 Headers
+       RFC 2780: IANA Allocation Guidelines For Values In
+                 the Internet Protocol and Related Headers";
+   }
+
+   typedef ipv6-flow-label {
+     type uint32 {
+       range "0..1048575";
+     }
+     description
+      "The flow-label type represents flow identifier or Flow Label
+       in an IPv6 packet header that may be used to discriminate
+       traffic flows.
+
+       In the value set and its semantics, this type is equivalent
+       to the IPv6FlowLabel textual convention of the SMIv2.";
+     reference
+      "RFC 3595: Textual Conventions for IPv6 Flow Label
+       RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
+   }
+
+   typedef port-number {
+     type uint16 {
+       range "0..65535";
+     }
+     description
+      "The port-number type represents a 16-bit port number of an
+       Internet transport layer protocol such as UDP, TCP, DCCP, or
+       SCTP.  Port numbers are assigned by IANA.  A current list of
+       all assignments is available from <http://www.iana.org/>.
+
+       Note that the port number value zero is reserved by IANA.  In
+       situations where the value zero does not make sense, it can
+       be excluded by subtyping the port-number type.
+
+       In the value set and its semantics, this type is equivalent
+       to the InetPortNumber textual convention of the SMIv2.";
+     reference
+      "RFC  768: User Datagram Protocol
+       RFC  793: Transmission Control Protocol
+       RFC 4960: Stream Control Transmission Protocol
+       RFC 4340: Datagram Congestion Control Protocol (DCCP)
+       RFC 4001: Textual Conventions for Internet Network Addresses";
+   }
+
+   /*** collection of autonomous system related types ***/
+
+   typedef as-number {
+     type uint32;
+     description
+      "The as-number type represents autonomous system numbers
+       which identify an Autonomous System (AS).  An AS is a set
+       of routers under a single technical administration, using
+       an interior gateway protocol and common metrics to route
+       packets within the AS, and using an exterior gateway
+       protocol to route packets to other ASs'.  IANA maintains
+       the AS number space and has delegated large parts to the
+       regional registries.
+
+       Autonomous system numbers were originally limited to 16
+       bits.  BGP extensions have enlarged the autonomous system
+       number space to 32 bits.  This type therefore uses an uint32
+       base type without a range restriction in order to support
+       a larger autonomous system number space.
+
+       In the value set and its semantics, this type is equivalent
+       to the InetAutonomousSystemNumber textual convention of
+       the SMIv2.";
+     reference
+      "RFC 1930: Guidelines for creation, selection, and registration
+                 of an Autonomous System (AS)
+       RFC 4271: A Border Gateway Protocol 4 (BGP-4)
+       RFC 4893: BGP Support for Four-octet AS Number Space
+       RFC 4001: Textual Conventions for Internet Network Addresses";
+   }
+
+   /*** collection of IP address and hostname related types ***/
+
+   typedef ip-address {
+     type union {
+       type inet:ipv4-address;
+       type inet:ipv6-address;
+     }
+     description
+      "The ip-address type represents an IP address and is IP
+       version neutral.  The format of the textual representations
+       implies the IP version.";
+   }
+
+   typedef ipv4-address {
+     type string {
+       pattern
+         '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+       +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+       + '(%[\p{N}\p{L}]+)?';
+     }
+     description
+       "The ipv4-address type represents an IPv4 address in
+        dotted-quad notation.  The IPv4 address may include a zone
+        index, separated by a % sign.
+
+        The zone index is used to disambiguate identical address
+        values.  For link-local addresses, the zone index will
+        typically be the interface index number or the name of an
+        interface.  If the zone index is not present, the default
+        zone of the device will be used.
+
+        The canonical format for the zone index is the numerical
+        format";
+   }
+
+   typedef ipv6-address {
+     type string {
+       pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+             + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+             + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+             + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+             + '(%[\p{N}\p{L}]+)?';
+       pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+             + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+             + '(%.+)?';
+     }
+     description
+      "The ipv6-address type represents an IPv6 address in full,
+       mixed, shortened, and shortened-mixed notation.  The IPv6
+       address may include a zone index, separated by a % sign.
+
+       The zone index is used to disambiguate identical address
+       values.  For link-local addresses, the zone index will
+       typically be the interface index number or the name of an
+       interface.  If the zone index is not present, the default
+       zone of the device will be used.
+
+       The canonical format of IPv6 addresses uses the compressed
+       format described in RFC 4291, Section 2.2, item 2 with the
+       following additional rules: the :: substitution must be
+       applied to the longest sequence of all-zero 16-bit chunks
+       in an IPv6 address.  If there is a tie, the first sequence
+       of all-zero 16-bit chunks is replaced by ::.  Single
+       all-zero 16-bit chunks are not compressed.  The canonical
+       format uses lowercase characters and leading zeros are
+       not allowed.  The canonical format for the zone index is
+       the numerical format as described in RFC 4007, Section
+       11.2.";
+     reference
+      "RFC 4291: IP Version 6 Addressing Architecture
+       RFC 4007: IPv6 Scoped Address Architecture
+       RFC 5952: A Recommendation for IPv6 Address Text Representation";
+   }
+
+   typedef ip-prefix {
+     type union {
+       type inet:ipv4-prefix;
+       type inet:ipv6-prefix;
+     }
+     description
+      "The ip-prefix type represents an IP prefix and is IP
+       version neutral.  The format of the textual representations
+       implies the IP version.";
+   }
+
+   typedef ipv4-prefix {
+     type string {
+       pattern
+          '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+        +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+        + '/(([0-9])|([1-2][0-9])|(3[0-2]))';
+     }
+     description
+      "The ipv4-prefix type represents an IPv4 address prefix.
+       The prefix length is given by the number following the
+       slash character and must be less than or equal to 32.
+
+       A prefix length value of n corresponds to an IP address
+       mask that has n contiguous 1-bits from the most
+       significant bit (MSB) and all other bits set to 0.
+
+       The canonical format of an IPv4 prefix has all bits of
+       the IPv4 address set to zero that are not part of the
+       IPv4 prefix.";
+   }
+
+   typedef ipv6-prefix {
+     type string {
+       pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+             + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+             + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+             + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+             + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
+       pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+             + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+             + '(/.+)';
+     }
+     description
+      "The ipv6-prefix type represents an IPv6 address prefix.
+       The prefix length is given by the number following the
+       slash character and must be less than or equal 128.
+
+       A prefix length value of n corresponds to an IP address
+       mask that has n contiguous 1-bits from the most
+       significant bit (MSB) and all other bits set to 0.
+
+       The IPv6 address should have all bits that do not belong
+       to the prefix set to zero.
+
+       The canonical format of an IPv6 prefix has all bits of
+       the IPv6 address set to zero that are not part of the
+       IPv6 prefix.  Furthermore, IPv6 address is represented
+       in the compressed format described in RFC 4291, Section
+       2.2, item 2 with the following additional rules: the ::
+       substitution must be applied to the longest sequence of
+       all-zero 16-bit chunks in an IPv6 address.  If there is
+       a tie, the first sequence of all-zero 16-bit chunks is
+       replaced by ::.  Single all-zero 16-bit chunks are not
+       compressed.  The canonical format uses lowercase
+       characters and leading zeros are not allowed.";
+     reference
+      "RFC 4291: IP Version 6 Addressing Architecture";
+   }
+
+   /*** collection of domain name and URI types ***/
+
+   typedef domain-name {
+     type string {
+       pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+            +  '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+            +  '|\.';
+       length "1..253";
+     }
+     description
+      "The domain-name type represents a DNS domain name.  The
+       name SHOULD be fully qualified whenever possible.
+
+       Internet domain names are only loosely specified.  Section
+       3.5 of RFC 1034 recommends a syntax (modified in Section
+       2.1 of RFC 1123).  The pattern above is intended to allow
+       for current practice in domain name use, and some possible
+       future expansion.  It is designed to hold various types of
+       domain names, including names used for A or AAAA records
+       (host names) and other records, such as SRV records.  Note
+       that Internet host names have a stricter syntax (described
+       in RFC 952) than the DNS recommendations in RFCs 1034 and
+       1123, and that systems that want to store host names in
+       schema nodes using the domain-name type are recommended to
+       adhere to this stricter standard to ensure interoperability.
+
+       The encoding of DNS names in the DNS protocol is limited
+       to 255 characters.  Since the encoding consists of labels
+       prefixed by a length bytes and there is a trailing NULL
+       byte, only 253 characters can appear in the textual dotted
+       notation.
+
+       The description clause of schema nodes using the domain-name
+       type MUST describe when and how these names are resolved to
+       IP addresses.  Note that the resolution of a domain-name value
+       may require to query multiple DNS records (e.g., A for IPv4
+       and AAAA for IPv6).  The order of the resolution process and
+       which DNS record takes precedence can either be defined
+       explicitely or it may depend on the configuration of the
+       resolver.
+
+       Domain-name values use the US-ASCII encoding.  Their canonical
+       format uses lowercase US-ASCII characters.  Internationalized
+       domain names MUST be encoded in punycode as described in RFC
+       3492";
+     reference
+      "RFC  952: DoD Internet Host Table Specification
+       RFC 1034: Domain Names - Concepts and Facilities
+       RFC 1123: Requirements for Internet Hosts -- Application
+                 and Support
+       RFC 2782: A DNS RR for specifying the location of services
+                 (DNS SRV)
+       RFC 3492: Punycode: A Bootstring encoding of Unicode for
+                 Internationalized Domain Names in Applications
+                 (IDNA)
+       RFC 5891: Internationalizing Domain Names in Applications
+                 (IDNA): Protocol";
+   }
+
+   typedef host {
+     type union {
+       type inet:ip-address;
+       type inet:domain-name;
+     }
+     description
+      "The host type represents either an IP address or a DNS
+       domain name.";
+   }
+
+   typedef uri {
+     type string;
+     description
+      "The uri type represents a Uniform Resource Identifier
+       (URI) as defined by STD 66.
+
+       Objects using the uri type MUST be in US-ASCII encoding,
+       and MUST be normalized as described by RFC 3986 Sections
+       6.2.1, 6.2.2.1, and 6.2.2.2.  All unnecessary
+       percent-encoding is removed, and all case-insensitive
+       characters are set to lowercase except for hexadecimal
+       digits, which are normalized to uppercase as described in
+       Section 6.2.2.1.
+
+       The purpose of this normalization is to help provide
+       unique URIs.  Note that this normalization is not
+       sufficient to provide uniqueness.  Two URIs that are
+       textually distinct after this normalization may still be
+       equivalent.
+
+       Objects using the uri type may restrict the schemes that
+       they permit.  For example, 'data:' and 'urn:' schemes
+       might not be appropriate.
+
+       A zero-length URI is not a valid URI.  This can be used to
+       express 'URI absent' where required.
+
+       In the value set and its semantics, this type is equivalent
+       to the Uri SMIv2 textual convention defined in RFC 5017.";
+     reference
+      "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
+       RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
+                 Group: Uniform Resource Identifiers (URIs), URLs,
+                 and Uniform Resource Names (URNs): Clarifications
+                 and Recommendations
+       RFC 5017: MIB Textual Conventions for Uniform Resource
+                 Identifiers (URIs)";
+   }
+
+ }
diff --git a/yang/yang-parser-rfc7950/src/test/resources/ietf/ietf-yang-types@2010-09-24.yang b/yang/yang-parser-rfc7950/src/test/resources/ietf/ietf-yang-types@2010-09-24.yang
new file mode 100644 (file)
index 0000000..51d9f8b
--- /dev/null
@@ -0,0 +1,396 @@
+ module ietf-yang-types {
+
+   namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+   prefix "yang";
+
+   organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+   contact
+    "WG Web:   <http://tools.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+
+     WG Chair: David Partain
+               <mailto:david.partain@ericsson.com>
+
+     WG Chair: David Kessens
+               <mailto:david.kessens@nsn.com>
+
+     Editor:   Juergen Schoenwaelder
+               <mailto:j.schoenwaelder@jacobs-university.de>";
+
+   description
+    "This module contains a collection of generally useful derived
+     YANG data types.
+
+     Copyright (c) 2010 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or without
+     modification, is permitted pursuant to, and subject to the license
+     terms contained in, the Simplified BSD License set forth in Section
+     4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6021; see
+     the RFC itself for full legal notices.";
+
+   revision 2010-09-24 {
+     description
+      "Initial revision.";
+     reference
+      "RFC 6021: Common YANG Data Types";
+   }
+
+   /*** collection of counter and gauge types ***/
+
+   typedef counter32 {
+     type uint32;
+     description
+      "The counter32 type represents a non-negative integer
+       that monotonically increases until it reaches a
+       maximum value of 2^32-1 (4294967295 decimal), when it
+       wraps around and starts increasing again from zero.
+
+       Counters have no defined 'initial' value, and thus, a
+       single value of a counter has (in general) no information
+       content.  Discontinuities in the monotonically increasing
+       value normally occur at re-initialization of the
+       management system, and at other times as specified in the
+       description of a schema node using this type.  If such
+       other times can occur, for example, the creation of
+       a schema node of type counter32 at times other than
+       re-initialization, then a corresponding schema node
+       should be defined, with an appropriate type, to indicate
+       the last discontinuity.
+
+       The counter32 type should not be used for configuration
+       schema nodes.  A default statement SHOULD NOT be used in
+       combination with the type counter32.
+
+       In the value set and its semantics, this type is equivalent
+       to the Counter32 type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef zero-based-counter32 {
+     type yang:counter32;
+     default "0";
+     description
+      "The zero-based-counter32 type represents a counter32
+       that has the defined 'initial' value zero.
+
+       A schema node of this type will be set to zero (0) on creation
+       and will thereafter increase monotonically until it reaches
+       a maximum value of 2^32-1 (4294967295 decimal), when it
+       wraps around and starts increasing again from zero.
+
+       Provided that an application discovers a new schema node
+       of this type within the minimum time to wrap, it can use the
+       'initial' value as a delta.  It is important for a management
+       station to be aware of this minimum time and the actual time
+       between polls, and to discard data if the actual time is too
+       long or there is no defined minimum time.
+
+       In the value set and its semantics, this type is equivalent
+       to the ZeroBasedCounter32 textual convention of the SMIv2.";
+     reference
+       "RFC 4502: Remote Network Monitoring Management Information
+                  Base Version 2";
+   }
+
+   typedef counter64 {
+     type uint64;
+     description
+      "The counter64 type represents a non-negative integer
+       that monotonically increases until it reaches a
+       maximum value of 2^64-1 (18446744073709551615 decimal),
+       when it wraps around and starts increasing again from zero.
+
+       Counters have no defined 'initial' value, and thus, a
+       single value of a counter has (in general) no information
+       content.  Discontinuities in the monotonically increasing
+       value normally occur at re-initialization of the
+       management system, and at other times as specified in the
+       description of a schema node using this type.  If such
+       other times can occur, for example, the creation of
+       a schema node of type counter64 at times other than
+       re-initialization, then a corresponding schema node
+       should be defined, with an appropriate type, to indicate
+       the last discontinuity.
+
+       The counter64 type should not be used for configuration
+       schema nodes.  A default statement SHOULD NOT be used in
+       combination with the type counter64.
+
+       In the value set and its semantics, this type is equivalent
+       to the Counter64 type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef zero-based-counter64 {
+     type yang:counter64;
+     default "0";
+     description
+      "The zero-based-counter64 type represents a counter64 that
+       has the defined 'initial' value zero.
+
+       A schema node of this type will be set to zero (0) on creation
+       and will thereafter increase monotonically until it reaches
+       a maximum value of 2^64-1 (18446744073709551615 decimal),
+       when it wraps around and starts increasing again from zero.
+
+       Provided that an application discovers a new schema node
+       of this type within the minimum time to wrap, it can use the
+       'initial' value as a delta.  It is important for a management
+       station to be aware of this minimum time and the actual time
+       between polls, and to discard data if the actual time is too
+       long or there is no defined minimum time.
+
+       In the value set and its semantics, this type is equivalent
+       to the ZeroBasedCounter64 textual convention of the SMIv2.";
+     reference
+      "RFC 2856: Textual Conventions for Additional High Capacity
+                 Data Types";
+   }
+
+   typedef gauge32 {
+     type uint32;
+     description
+      "The gauge32 type represents a non-negative integer, which
+       may increase or decrease, but shall never exceed a maximum
+       value, nor fall below a minimum value.  The maximum value
+       cannot be greater than 2^32-1 (4294967295 decimal), and
+       the minimum value cannot be smaller than 0.  The value of
+       a gauge32 has its maximum value whenever the information
+       being modeled is greater than or equal to its maximum
+       value, and has its minimum value whenever the information
+       being modeled is smaller than or equal to its minimum value.
+       If the information being modeled subsequently decreases
+       below (increases above) the maximum (minimum) value, the
+       gauge32 also decreases (increases).
+
+       In the value set and its semantics, this type is equivalent
+       to the Gauge32 type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef gauge64 {
+     type uint64;
+     description
+      "The gauge64 type represents a non-negative integer, which
+       may increase or decrease, but shall never exceed a maximum
+       value, nor fall below a minimum value.  The maximum value
+       cannot be greater than 2^64-1 (18446744073709551615), and
+       the minimum value cannot be smaller than 0.  The value of
+       a gauge64 has its maximum value whenever the information
+       being modeled is greater than or equal to its maximum
+       value, and has its minimum value whenever the information
+       being modeled is smaller than or equal to its minimum value.
+       If the information being modeled subsequently decreases
+       below (increases above) the maximum (minimum) value, the
+       gauge64 also decreases (increases).
+
+       In the value set and its semantics, this type is equivalent
+       to the CounterBasedGauge64 SMIv2 textual convention defined
+       in RFC 2856";
+     reference
+      "RFC 2856: Textual Conventions for Additional High Capacity
+                 Data Types";
+   }
+
+   /*** collection of identifier related types ***/
+
+   typedef object-identifier {
+     type string {
+       pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+             + '(\.(0|([1-9]\d*)))*';
+     }
+     description
+      "The object-identifier type represents administratively
+       assigned names in a registration-hierarchical-name tree.
+
+       Values of this type are denoted as a sequence of numerical
+       non-negative sub-identifier values.  Each sub-identifier
+       value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers
+       are separated by single dots and without any intermediate
+       whitespace.
+
+       The ASN.1 standard restricts the value space of the first
+       sub-identifier to 0, 1, or 2.  Furthermore, the value space
+       of the second sub-identifier is restricted to the range
+       0 to 39 if the first sub-identifier is 0 or 1.  Finally,
+       the ASN.1 standard requires that an object identifier
+       has always at least two sub-identifier.  The pattern
+       captures these restrictions.
+
+       Although the number of sub-identifiers is not limited,
+       module designers should realize that there may be
+       implementations that stick with the SMIv2 limit of 128
+       sub-identifiers.
+
+       This type is a superset of the SMIv2 OBJECT IDENTIFIER type
+       since it is not restricted to 128 sub-identifiers.  Hence,
+       this type SHOULD NOT be used to represent the SMIv2 OBJECT
+       IDENTIFIER type, the object-identifier-128 type SHOULD be
+       used instead.";
+     reference
+      "ISO9834-1: Information technology -- Open Systems
+       Interconnection -- Procedures for the operation of OSI
+       Registration Authorities: General procedures and top
+       arcs of the ASN.1 Object Identifier tree";
+   }
+
+
+
+
+   typedef object-identifier-128 {
+     type object-identifier {
+       pattern '\d*(\.\d*){1,127}';
+     }
+     description
+      "This type represents object-identifiers restricted to 128
+       sub-identifiers.
+
+       In the value set and its semantics, this type is equivalent
+       to the OBJECT IDENTIFIER type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   /*** collection of date and time related types ***/
+
+   typedef date-and-time {
+     type string {
+       pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+             + '(Z|[\+\-]\d{2}:\d{2})';
+     }
+     description
+      "The date-and-time type is a profile of the ISO 8601
+       standard for representation of dates and times using the
+       Gregorian calendar.  The profile is defined by the
+       date-time production in Section 5.6 of RFC 3339.
+
+       The date-and-time type is compatible with the dateTime XML
+       schema type with the following notable exceptions:
+
+       (a) The date-and-time type does not allow negative years.
+
+       (b) The date-and-time time-offset -00:00 indicates an unknown
+           time zone (see RFC 3339) while -00:00 and +00:00 and Z all
+           represent the same time zone in dateTime.
+
+       (c) The canonical format (see below) of data-and-time values
+           differs from the canonical format used by the dateTime XML
+           schema type, which requires all times to be in UTC using the
+           time-offset 'Z'.
+
+       This type is not equivalent to the DateAndTime textual
+       convention of the SMIv2 since RFC 3339 uses a different
+       separator between full-date and full-time and provides
+       higher resolution of time-secfrac.
+
+       The canonical format for date-and-time values with a known time
+       zone uses a numeric time zone offset that is calculated using
+       the device's configured known offset to UTC time.  A change of
+       the device's offset to UTC time will cause date-and-time values
+       to change accordingly.  Such changes might happen periodically
+       in case a server follows automatically daylight saving time
+       (DST) time zone offset changes.  The canonical format for
+       date-and-time values with an unknown time zone (usually referring
+       to the notion of local time) uses the time-offset -00:00.";
+     reference
+      "RFC 3339: Date and Time on the Internet: Timestamps
+       RFC 2579: Textual Conventions for SMIv2
+       XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
+   }
+
+   typedef timeticks {
+     type uint32;
+     description
+      "The timeticks type represents a non-negative integer that
+       represents the time, modulo 2^32 (4294967296 decimal), in
+       hundredths of a second between two epochs.  When a schema
+       node is defined that uses this type, the description of
+       the schema node identifies both of the reference epochs.
+
+       In the value set and its semantics, this type is equivalent
+       to the TimeTicks type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef timestamp {
+     type yang:timeticks;
+     description
+      "The timestamp type represents the value of an associated
+       timeticks schema node at which a specific occurrence happened.
+       The specific occurrence must be defined in the description
+       of any schema node defined using this type.  When the specific
+       occurrence occurred prior to the last time the associated
+       timeticks attribute was zero, then the timestamp value is
+       zero.  Note that this requires all timestamp values to be
+       reset to zero when the value of the associated timeticks
+       attribute reaches 497+ days and wraps around to zero.
+
+       The associated timeticks schema node must be specified
+       in the description of any schema node using this type.
+
+       In the value set and its semantics, this type is equivalent
+       to the TimeStamp textual convention of the SMIv2.";
+     reference
+      "RFC 2579: Textual Conventions for SMIv2";
+   }
+
+   /*** collection of generic address types ***/
+
+   typedef phys-address {
+     type string {
+       pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+     }
+     description
+      "Represents media- or physical-level addresses represented
+       as a sequence octets, each octet represented by two hexadecimal
+       numbers.  Octets are separated by colons.  The canonical
+       representation uses lowercase characters.
+
+       In the value set and its semantics, this type is equivalent
+       to the PhysAddress textual convention of the SMIv2.";
+     reference
+      "RFC 2579: Textual Conventions for SMIv2";
+   }
+
+   typedef mac-address {
+     type string {
+       pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+     }
+     description
+      "The mac-address type represents an IEEE 802 MAC address.
+       The canonical representation uses lowercase characters.
+
+       In the value set and its semantics, this type is equivalent
+       to the MacAddress textual convention of the SMIv2.";
+     reference
+      "IEEE 802: IEEE Standard for Local and Metropolitan Area
+                 Networks: Overview and Architecture
+       RFC 2579: Textual Conventions for SMIv2";
+   }
+
+   /*** collection of XML specific types ***/
+
+   typedef xpath1.0 {
+     type string;
+     description
+      "This type represents an XPATH 1.0 expression.
+
+       When a schema node is defined that uses this type, the
+       description of the schema node MUST specify the XPath
+       context in which the XPath expression is evaluated.";
+     reference
+      "XPATH: XML Path Language (XPath) Version 1.0";
+   }
+
+ }
diff --git a/yang/yang-parser-rfc7950/src/test/resources/ietf/network-topology@2013-07-12.yang b/yang/yang-parser-rfc7950/src/test/resources/ietf/network-topology@2013-07-12.yang
new file mode 100644 (file)
index 0000000..b87035c
--- /dev/null
@@ -0,0 +1,316 @@
+module network-topology  {
+    yang-version 1;
+    namespace "urn:TBD:params:xml:ns:yang:network-topology";
+    // replace with IANA namespace when assigned
+    prefix "nt";
+
+    import ietf-inet-types { prefix "inet"; }
+
+    organization "TBD";
+
+    contact "WILL-BE-DEFINED-LATER";
+/*
+    description 
+        "This module defines a model for the topology of a network.
+        Key design decisions are as follows:
+        A topology consists of a set of nodes and links.  
+        Links are point-to-point and unidirectional.  
+        Bidirectional connections need to be represented through
+        two separate links.  
+        Multipoint connections, broadcast domains etc can be represented 
+        through a hierarchy of nodes, then connecting nodes at 
+        upper layers of the hierarchy.";  
+*/
+    revision 2013-07-12 {
+        description
+            "Initial revision.";
+    }
+        
+    typedef topology-id {
+        type inet:uri;
+        description 
+            "An identifier for a topology.";
+    }
+
+    typedef node-id {
+        type inet:uri;
+        description
+            "An identifier for a node in a topology.  
+            The identifier may be opaque.  
+            The identifier SHOULD be chosen such that the same node in a 
+            real network topology will always be identified through the 
+            same identifier, even if the model is instantiated in separate 
+            datastores. An implementation MAY choose to capture semantics 
+            in the identifier, for example to indicate the type of node 
+            and/or the type of topology that the node is a part of.";  
+    }
+
+    typedef link-id {
+        type inet:uri;
+        description
+            "An identifier for a link in a topology.  
+            The identifier may be opaque.  
+            The identifier SHOULD be chosen such that the same link in a 
+            real network topology will always be identified through the 
+            same identifier, even if the model is instantiated in separate 
+            datastores. An implementation MAY choose to capture semantics 
+            in the identifier, for example to indicate the type of link 
+            and/or the type of topology that the link is a part of.";  
+    }
+
+    typedef tp-id {
+        type inet:uri;
+        description 
+            "An identifier for termination points on a node. 
+            The identifier may be opaque.  
+            The identifier SHOULD be chosen such that the same TP in a 
+            real network topology will always be identified through the 
+            same identifier, even if the model is instantiated in separate 
+            datastores. An implementation MAY choose to capture semantics 
+            in the identifier, for example to indicate the type of TP 
+            and/or the type of node and topology that the TP is a part of.";  
+    }
+
+    typedef tp-ref {
+        type leafref {
+            path "/network-topology/topology/node/termination-point/tp-id";
+        }
+        description 
+            "A type for an absolute reference to a termination point.
+            (This type should not be used for relative references.
+            In such a case, a relative path should be used instead.)";
+    }
+    typedef topology-ref {
+        type leafref {
+            path "/network-topology/topology/topology-id";
+        }
+        description 
+            "A type for an absolute reference a topology instance.";
+    }
+    
+    typedef node-ref {
+        type leafref {
+            path "/network-topology/topology/node/node-id";
+        }
+        description 
+            "A type for an absolute reference to a node instance.
+            (This type should not be used for relative references.
+            In such a case, a relative path should be used instead.)";
+    }
+
+    typedef link-ref {
+        type leafref {
+            path "/network-topology/topology/link/link-id";
+        }
+        description 
+            "A type for an absolute reference a link instance.
+            (This type should not be used for relative references.
+            In such a case, a relative path should be used instead.)";
+    }
+
+    grouping tp-attributes {
+        description 
+            "The data objects needed to define a termination point.
+            (This only includes a single leaf at this point, used
+            to identify the termination point.)  
+            Provided in a grouping so that in addition to the datastore,
+            the data can also be included in notifications.";
+        leaf tp-id {
+            type tp-id;
+        }
+        leaf-list tp-ref {
+            type tp-ref;
+            config false;
+            description 
+                "The leaf list identifies any termination points that the 
+                termination point is dependent on, or maps onto.  
+                Those termination points will themselves be contained 
+                in a supporting node.  
+                This dependency information can be inferred from 
+                the dependencies between links.  For this reason, 
+                this item is not separately configurable.  Hence no
+                corresponding constraint needs to be articulated.  
+                The corresponding information is simply provided by the
+                implementing system.";
+        }
+    } 
+    
+    grouping node-attributes {
+        description
+            "The data objects needed to define a node.
+            The objects are provided in a grouping so that in addition to
+            the datastore, the data can also be included in notifications
+            as needed.";
+        leaf node-id {
+            type node-id;
+            description 
+                "The identifier of a node in the topology.  
+                A node is specific to a topology to which it belongs.";   
+        }
+        list supporting-node {
+            description 
+                "This list defines vertical layering information for nodes. 
+                It allows to capture for any given node, which node (or nodes)
+                in the corresponding underlay topology it maps onto.  
+                A node can map to zero, one, or more nodes below it;
+                accordingly there can be zero, one, or more elements in the list.
+                If there are specific layering requirements, for example
+                specific to a particular type of topology that only allows
+                for certain layering relationships, the choice
+                below can be augmented with additional cases.
+                A list has been chosen rather than a leaf-list in order 
+                to provide room for augmentations, e.g. for 
+                statistics or priorization information associated with 
+                supporting nodes.";
+            key "node-ref";
+            leaf node-ref {
+                type node-ref;
+            }
+        }
+    }
+            
+    grouping link-attributes {
+        // This is a grouping, not defined inline with the link definition itself,
+        // so it can be included in a notification, if needed
+        leaf link-id {
+            type link-id;
+            description
+                "The identifier of a link in the topology.  
+                A link is specific to a topology to which it belongs.";   
+        }
+        container source { 
+            leaf source-node {
+                mandatory true;
+                type node-ref; 
+                description 
+                    "Source node identifier, must be in same topology.";
+            }
+            leaf source-tp {
+                type tp-ref; 
+                description 
+                    "Termination point within source node that terminates the link.";           
+            }
+        }
+        container destination { 
+            leaf dest-node {
+                mandatory true;
+                type node-ref; 
+                description 
+                    "Destination node identifier, must be in same topology.";
+            }
+            leaf dest-tp {
+                type tp-ref;
+                description 
+                    "Termination point within destination node that terminates the link.";
+            }
+        }
+        list supporting-link {
+            key "link-ref";
+            leaf link-ref {
+                type link-ref;
+            }
+        }
+    }
+    
+
+    container network-topology {
+        list topology {
+            description "
+                This is the model of an abstract topology.
+                A topology contins nodes and links.  
+                Each topology MUST be identified by
+                unique topology-id for reason that a network could contain many
+                topologies.
+            ";
+            key "topology-id";
+            leaf topology-id {
+                type topology-id; 
+                description "
+                    It is presumed that a datastore will contain many topologies. To
+                    distinguish between topologies it is vital to have UNIQUE
+                    topology identifiers.
+                ";
+            }
+            container topology-types {
+                description 
+                    "This container is used to identify the type, or types 
+                    (as a topology can support several types simultaneously), 
+                    of the topology.  
+                    Topology types are the subject of several integrity constraints 
+                    that an implementing server can validate in order to 
+                    maintain integrity of the datastore.  
+                    Topology types are indicated through separate data nodes; 
+                    the set of topology types is expected to increase over time.
+                    To add support for a new topology, an augmenting module
+                    needs to augment this container with a new empty optional 
+                    container to indicate the new topology type.  
+                    The use of a container allows to indicate a subcategorization
+                    of topology types.  
+                    The container SHALL NOT be augmented with any data nodes 
+                    that serve a purpose other than identifying a particular 
+                    topology type.  
+                ";
+            }   
+            list underlay-topology {
+                key "topology-ref";
+                leaf topology-ref {
+                    type topology-ref;
+                }  
+                // a list, not a leaf-list, to allow for potential augmentation 
+                // with properties specific to the underlay topology, 
+                // such as statistics, preferences, or cost.  
+                description 
+                    "Identifies the topology, or topologies, that this topology
+                    is dependent on.";
+            }
+            
+            list node {
+                description "The list of network nodes defined for the topology.";
+                key "node-id";
+                uses node-attributes;
+                must "boolean(../underlay-topology[*]/node[./supporting-nodes/node-ref])";
+                    // This constraint is meant to ensure that a referenced node is in fact 
+                    // a node in an underlay topology.  
+                list termination-point {
+                    description
+                        "A termination point can terminate a link.  
+                        Depending on the type of topology, a termination point could, 
+                        for example, refer to a port or an interface."; 
+                    key "tp-id";
+                    uses tp-attributes;
+                }
+            }
+            
+            list link {
+                description "
+                    A Network Link connects a by Local (Source) node and
+                    a Remote (Destination) Network Nodes via a set of the 
+                    nodes' termination points. 
+                    As it is possible to have several links between the same
+                    source and destination nodes, and as a link could potentially
+                    be re-homed between termination points, to ensure that we 
+                    would always know to distinguish between links, every link 
+                    is identified by a dedicated link identifier.  
+                    Note that a link models a point-to-point link, not a multipoint
+                    link.  
+                    Layering dependencies on links in underlay topologies are
+                    not represented as the layering information of nodes and of 
+                    termination points is sufficient.  
+                ";
+                key "link-id";
+                uses link-attributes;
+                must "boolean(../underlay-topology/link[./supporting-link])";
+                    // Constraint: any supporting link must be part of an underlay topology
+                must "boolean(../node[./source/source-node])";
+                    // Constraint: A link must have as source a node of the same topology
+                must "boolean(../node[./destination/dest-node])";
+                    // Constraint: A link must have as source a destination of the same topology
+                must "boolean(../node/termination-point[./source/source-tp])";
+                    // Constraint: The source termination point must be contained in the source node
+                must "boolean(../node/termination-point[./destination/dest-tp])";
+                    // Constraint: The destination termination point must be contained 
+                    // in the destination node
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/ietf/network-topology@2013-10-21.yang b/yang/yang-parser-rfc7950/src/test/resources/ietf/network-topology@2013-10-21.yang
new file mode 100644 (file)
index 0000000..c114334
--- /dev/null
@@ -0,0 +1,334 @@
+module network-topology  {
+    yang-version 1;
+    namespace "urn:TBD:params:xml:ns:yang:network-topology";
+    // replace with IANA namespace when assigned
+    prefix "nt";
+
+    import ietf-inet-types { prefix "inet"; revision-date 2010-09-24; }
+
+    organization "TBD";
+
+    contact "WILL-BE-DEFINED-LATER";
+
+    description
+        "This module defines a model for the topology of a network.
+        Key design decisions are as follows:
+        A topology consists of a set of nodes and links.
+        Links are point-to-point and unidirectional.
+        Bidirectional connections need to be represented through
+        two separate links.
+        Multipoint connections, broadcast domains etc can be represented
+        through a hierarchy of nodes, then connecting nodes at
+        upper layers of the hierarchy.";
+
+    revision 2013-10-21 {
+        description
+            "Initial revision.";
+    }
+
+    typedef topology-id {
+        type inet:uri;
+        description
+            "An identifier for a topology.";
+    }
+
+    typedef node-id {
+        type inet:uri;
+        description
+            "An identifier for a node in a topology.
+            The identifier may be opaque.
+            The identifier SHOULD be chosen such that the same node in a
+            real network topology will always be identified through the
+            same identifier, even if the model is instantiated in separate
+            datastores. An implementation MAY choose to capture semantics
+            in the identifier, for example to indicate the type of node
+            and/or the type of topology that the node is a part of.";
+    }
+
+
+    typedef link-id {
+        type inet:uri;
+        description
+            "An identifier for a link in a topology.
+            The identifier may be opaque.
+            The identifier SHOULD be chosen such that the same link in a
+            real network topology will always be identified through the
+            same identifier, even if the model is instantiated in separate
+            datastores. An implementation MAY choose to capture semantics
+            in the identifier, for example to indicate the type of link
+            and/or the type of topology that the link is a part of.";
+    }
+
+    typedef tp-id {
+        type inet:uri;
+        description
+            "An identifier for termination points on a node.
+            The identifier may be opaque.
+            The identifier SHOULD be chosen such that the same TP in a
+            real network topology will always be identified through the
+            same identifier, even if the model is instantiated in separate
+            datastores. An implementation MAY choose to capture semantics
+            in the identifier, for example to indicate the type of TP
+            and/or the type of node and topology that the TP is a part of.";
+    }
+
+    typedef tp-ref {
+        type leafref {
+            path "/network-topology/topology/node/termination-point/tp-id";
+        }
+        description
+            "A type for an absolute reference to a termination point.
+            (This type should not be used for relative references.
+            In such a case, a relative path should be used instead.)";
+    }
+    typedef topology-ref {
+        type leafref {
+            path "/network-topology/topology/topology-id";
+        }
+        description
+            "A type for an absolute reference a topology instance.";
+    }
+
+    typedef node-ref {
+        type leafref {
+            path "/network-topology/topology/node/node-id";
+        }
+        description
+
+            "A type for an absolute reference to a node instance.
+            (This type should not be used for relative references.
+            In such a case, a relative path should be used instead.)";
+    }
+
+    typedef link-ref {
+        type leafref {
+            path "/network-topology/topology/link/link-id";
+        }
+        description
+            "A type for an absolute reference a link instance.
+            (This type should not be used for relative references.
+            In such a case, a relative path should be used instead.)";
+    }
+
+    grouping tp-attributes {
+        description
+            "The data objects needed to define a termination point.
+            (This only includes a single leaf at this point, used
+            to identify the termination point.)
+            Provided in a grouping so that in addition to the datastore,
+            the data can also be included in notifications.";
+        leaf tp-id {
+            type tp-id;
+        }
+        leaf-list tp-ref {
+            type tp-ref;
+            config false;
+            description
+                "The leaf list identifies any termination points that the
+                termination point is dependent on, or maps onto.
+                Those termination points will themselves be contained
+                in a supporting node.
+                This dependency information can be inferred from
+                the dependencies between links.  For this reason,
+                this item is not separately configurable.  Hence no
+                corresponding constraint needs to be articulated.
+                The corresponding information is simply provided by the
+                implementing system.";
+        }
+    }
+
+    grouping node-attributes {
+        description
+            "The data objects needed to define a node.
+            The objects are provided in a grouping so that in addition to
+            the datastore, the data can also be included in notifications
+            as needed.";
+
+        leaf node-id {
+            type node-id;
+            description
+                "The identifier of a node in the topology.
+                A node is specific to a topology to which it belongs.";
+        }
+        list supporting-node {
+            description
+                "This list defines vertical layering information for nodes.
+                It allows to capture for any given node, which node (or nodes)
+                in the corresponding underlay topology it maps onto.
+                A node can map to zero, one, or more nodes below it;
+                accordingly there can be zero, one, or more elements in the list.
+                If there are specific layering requirements, for example
+                specific to a particular type of topology that only allows
+                for certain layering relationships, the choice
+                below can be augmented with additional cases.
+                A list has been chosen rather than a leaf-list in order
+                to provide room for augmentations, e.g. for
+                statistics or priorization information associated with
+                supporting nodes.";
+            key "node-ref";
+            leaf node-ref {
+                type node-ref;
+            }
+        }
+    }
+
+    grouping link-attributes {
+        // This is a grouping, not defined inline with the link definition itself,
+        // so it can be included in a notification, if needed
+        leaf link-id {
+            type link-id;
+            description
+                "The identifier of a link in the topology.
+                A link is specific to a topology to which it belongs.";
+        }
+        container source {
+            leaf source-node {
+                mandatory true;
+                type node-ref;
+                description
+                    "Source node identifier, must be in same topology.";
+            }
+            leaf source-tp {
+                type tp-ref;
+                description
+                    "Termination point within source node that terminates the link.";
+
+            }
+        }
+        container destination {
+            leaf dest-node {
+                mandatory true;
+                type node-ref;
+                description
+                    "Destination node identifier, must be in same topology.";
+            }
+            leaf dest-tp {
+                type tp-ref;
+                description
+                    "Termination point within destination node that terminates the link.";
+            }
+        }
+        list supporting-link {
+            key "link-ref";
+            leaf link-ref {
+                type link-ref;
+            }
+        }
+    }
+
+
+    container network-topology {
+        list topology {
+            description "
+                This is the model of an abstract topology.
+                A topology contains nodes and links.
+                Each topology MUST be identified by
+                unique topology-id for reason that a network could contain many
+                topologies.
+            ";
+            key "topology-id";
+            leaf topology-id {
+                type topology-id;
+                description "
+                    It is presumed that a datastore will contain many topologies. To
+                    distinguish between topologies it is vital to have UNIQUE
+                    topology identifiers.
+                ";
+            }
+            leaf server-provided {
+                type boolean;
+                config false;
+                description "
+                    Indicates whether the topology is configurable by clients,
+                    or whether it is provided by the server.  This leaf is
+
+                    populated by the server implementing the model.
+                    It is set to false for topologies that are created by a client;
+                    it is set to true otherwise.  If it is set to true, any
+                    attempt to edit the topology MUST be rejected.
+                ";
+            }
+            container topology-types {
+                description
+                    "This container is used to identify the type, or types
+                    (as a topology can support several types simultaneously),
+                    of the topology.
+                    Topology types are the subject of several integrity constraints
+                    that an implementing server can validate in order to
+                    maintain integrity of the datastore.
+                    Topology types are indicated through separate data nodes;
+                    the set of topology types is expected to increase over time.
+                    To add support for a new topology, an augmenting module
+                    needs to augment this container with a new empty optional
+                    container to indicate the new topology type.
+                    The use of a container allows to indicate a subcategorization
+                    of topology types.
+                    The container SHALL NOT be augmented with any data nodes
+                    that serve a purpose other than identifying a particular
+                    topology type.
+                ";
+            }
+            list underlay-topology {
+                key "topology-ref";
+                leaf topology-ref {
+                    type topology-ref;
+                }
+                // a list, not a leaf-list, to allow for potential augmentation
+                // with properties specific to the underlay topology,
+                // such as statistics, preferences, or cost.
+                description
+                    "Identifies the topology, or topologies, that this topology
+                    is dependent on.";
+            }
+
+            list node {
+                description "The list of network nodes defined for the topology.";
+                key "node-id";
+                uses node-attributes;
+                must "boolean(../underlay-topology[*]/node[./supporting-nodes/node-ref])";
+                    // This constraint is meant to ensure that a referenced node is in fact
+                    // a node in an underlay topology.
+                list termination-point {
+                    description
+
+                        "A termination point can terminate a link.
+                        Depending on the type of topology, a termination point could,
+                        for example, refer to a port or an interface.";
+                    key "tp-id";
+                    uses tp-attributes;
+                }
+            }
+
+            list link {
+                description "
+                    A Network Link connects a by Local (Source) node and
+                    a Remote (Destination) Network Nodes via a set of the
+                    nodes' termination points.
+                    As it is possible to have several links between the same
+                    source and destination nodes, and as a link could potentially
+                    be re-homed between termination points, to ensure that we
+                    would always know to distinguish between links, every link
+                    is identified by a dedicated link identifier.
+                    Note that a link models a point-to-point link, not a multipoint
+                    link.
+                    Layering dependencies on links in underlay topologies are
+                    not represented as the layering information of nodes and of
+                    termination points is sufficient.
+                ";
+                key "link-id";
+                uses link-attributes;
+                must "boolean(../underlay-topology/link[./supporting-link])";
+                    // Constraint: any supporting link must be part of an underlay topology
+                must "boolean(../node[./source/source-node])";
+                    // Constraint: A link must have as source a node of the same topology
+                must "boolean(../node[./destination/dest-node])";
+                    // Constraint: A link must have as source a destination of the same topology
+                must "boolean(../node/termination-point[./source/source-tp])";
+                    // Constraint: The source termination point must be contained in the source node
+                must "boolean(../node/termination-point[./destination/dest-tp])";
+                    // Constraint: The destination termination point must be contained
+                    // in the destination node
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/if-feature-resolution-test/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/if-feature-resolution-test/bar.yang
new file mode 100644 (file)
index 0000000..8f93e0b
--- /dev/null
@@ -0,0 +1,12 @@
+module bar {
+    namespace "bar-namespace";
+    prefix "bar-prefix";
+
+    feature imp-feature;
+
+    container bar-cont {
+        leaf bar-cont-leaf {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/if-feature-resolution-test/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/if-feature-resolution-test/foo.yang
new file mode 100644 (file)
index 0000000..a147247
--- /dev/null
@@ -0,0 +1,130 @@
+module foo {
+    namespace "foo-namespace";
+    prefix "foo-prefix";
+
+    import bar {
+        prefix br;
+    }
+
+    feature foo-feature;
+    feature test-feature-1;
+    feature test-feature-2;
+    feature test-feature-3;
+
+    container test-container-a {
+        if-feature foo-feature;
+
+        leaf test-leaf-a {
+            type string;
+        }
+    }
+
+    container test-container-b {
+        if-feature test-feature-1;
+
+        leaf test-leaf-b {
+            type string;
+        }
+    }
+
+    container test-container-c {
+        if-feature test-feature-1;
+        if-feature test-feature-2;
+        if-feature test-feature-3;
+
+        leaf test-leaf-c {
+            type string;
+        }
+    }
+
+    container test-container-d {
+        if-feature test-feature-1;
+        if-feature test-feature-2;
+        if-feature foo-feature;
+        if-feature test-feature-3;
+
+        leaf test-leaf-d {
+            type string;
+        }
+    }
+
+    container test-container-e {
+        container test-subcontainer-e {
+            leaf test-leaf-e {
+                if-feature foo-feature;
+                type string;
+            }
+        }
+    }
+
+    container test-container-f {
+        container test-subcontainer-f {
+            if-feature foo-feature;
+            container test-subsubcontainer-f {
+                leaf test-leaf-f {
+                    type string;
+                }
+            }
+        }
+    }
+
+    container test-container-g {
+        leaf test-leaf-g {
+            type string;
+        }
+    }
+
+    container test-container-h {
+        uses test-grouping {
+            if-feature "foo-feature";
+        }
+    }
+
+    container test-container-i {
+        uses test-grouping;
+    }
+
+    container test-container-j {
+        if-feature br:imp-feature;
+
+        leaf test-leaf-j {
+            type string;
+        }
+    }
+
+    container test-container-k {
+        uses test-grouping {
+            augment "test-grouping-subcontainer" {
+                leaf augmenting-test-grouping-leaf {
+                    if-feature test-feature-1;
+                    type string;
+                }
+
+                leaf augmenting-test-grouping-leaf-2 {
+                    if-feature foo-feature;
+                    type string;
+                }
+            }
+        }
+    }
+
+    augment "/test-container-g" {
+        if-feature foo-feature;
+        leaf augmenting-test-leaf-g {
+            type string;
+        }
+    }
+
+    augment "/test-container-g" {
+        anyxml augmenting-test-anyxml-g;
+    }
+
+    grouping test-grouping {
+        container test-grouping-subcontainer {
+            leaf test-grouping-leaf {
+                if-feature foo-feature;
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/if-feature-resolution-test/shared-schema-repository/foobar.yang b/yang/yang-parser-rfc7950/src/test/resources/if-feature-resolution-test/shared-schema-repository/foobar.yang
new file mode 100644 (file)
index 0000000..d839150
--- /dev/null
@@ -0,0 +1,26 @@
+module foobar {
+    namespace "foobar-namespace";
+    prefix "foobar-prefix";
+
+    container test-container-a {
+        if-feature test-feature-1;
+
+        leaf test-leaf-a {
+            type string;
+        }
+    }
+
+    container test-container-b {
+        if-feature foobar-feature;
+
+        leaf test-leaf-b {
+            type string;
+        }
+    }
+
+    container test-container-c {
+        leaf test-leaf-c {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-with-equal-revision-date.yang b/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-with-equal-revision-date.yang
new file mode 100644 (file)
index 0000000..39c9249
--- /dev/null
@@ -0,0 +1,9 @@
+module imported-module-with-equal-revision-date {
+    namespace "urn:opendaylight.org/imported-module-with-equal-revision-date";
+    prefix "imported-equal-date";
+
+    revision "2010-10-25" {
+        description "Initial revision";
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-with-no-revision-date.yang b/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-with-no-revision-date.yang
new file mode 100644 (file)
index 0000000..ebca482
--- /dev/null
@@ -0,0 +1,4 @@
+module imported-module-with-no-revision-date {
+    namespace "urn:opendaylight.org/imported-module-with-no-revision-date";
+    prefix "imported-no-date";
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-with-revision-date.yang b/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-with-revision-date.yang
new file mode 100644 (file)
index 0000000..691f4eb
--- /dev/null
@@ -0,0 +1,9 @@
+module imported-module-with-revision-date {
+    namespace "urn:opendaylight.org/imported-module-with-revision-date";
+    prefix "imported-with-date";
+
+    revision "2012-04-14" {
+        description "Initial revision";
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-with-unequal-revision-date.yang b/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-with-unequal-revision-date.yang
new file mode 100644 (file)
index 0000000..4d6455f
--- /dev/null
@@ -0,0 +1,9 @@
+module imported-module-with-unequal-revision-date {
+    namespace "urn:opendaylight.org/imported-module-with-unequal-revision-date";
+    prefix "imported-unequal-date";
+
+    revision "2012-10-26" {
+        description "Initial revision";
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-without-revision-date.yang b/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/imported-module-without-revision-date.yang
new file mode 100644 (file)
index 0000000..5d1166d
--- /dev/null
@@ -0,0 +1,5 @@
+module imported-module-without-revision-date {
+    namespace "urn:opendaylight.org/imported-module-without-revision-date";
+    prefix "imported-without-date";
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-with-equal-revision-date.yang b/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-with-equal-revision-date.yang
new file mode 100644 (file)
index 0000000..e0102d6
--- /dev/null
@@ -0,0 +1,10 @@
+module root-with-equal-revision-date {
+    namespace "urn:opendaylight.org/root-with-equal-revision-date";
+    prefix "root-equal-date";
+
+    import imported-module-with-equal-revision-date {
+        prefix "imported-equal-date";
+        revision-date 2010-10-25;
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-with-no-revision-date.yang b/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-with-no-revision-date.yang
new file mode 100644 (file)
index 0000000..5af7af5
--- /dev/null
@@ -0,0 +1,8 @@
+module root-with-no-revision-date {
+    namespace "urn:opendaylight.org/root-with-no-revision-date";
+    prefix "root-no-date";
+
+    import imported-module-with-no-revision-date {
+        prefix "imported-no-date";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-with-revision-date.yang b/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-with-revision-date.yang
new file mode 100644 (file)
index 0000000..51eff26
--- /dev/null
@@ -0,0 +1,9 @@
+module root-with-revision-date {
+    namespace "urn:opendaylight.org/root-with-revision-date";
+    prefix "root-with-date";
+
+    import imported-module-without-revision-date {
+        prefix "imported-without-date";
+        revision-date "2010-06-15";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-with-unequal-revision-date.yang b/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-with-unequal-revision-date.yang
new file mode 100644 (file)
index 0000000..0075310
--- /dev/null
@@ -0,0 +1,10 @@
+module root-with-unequal-revision-date {
+    namespace "urn:opendaylight.org/root-with-unequal-revision-date";
+    prefix "root-unequal-date";
+
+    import imported-module-with-unequal-revision-date {
+        prefix "imported-unequal-date";
+        revision-date 2012-10-25;
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-without-revision-date.yang b/yang/yang-parser-rfc7950/src/test/resources/import-revision-date-test/root-without-revision-date.yang
new file mode 100644 (file)
index 0000000..71ddaea
--- /dev/null
@@ -0,0 +1,9 @@
+module root-without-revision-date {
+    namespace "urn:opendaylight.org/root-without-revision-date";
+    prefix "root-without-date";
+
+    import imported-module-with-revision-date {
+        prefix "imported-with-date";
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/included-statements-test/child-module.yang b/yang/yang-parser-rfc7950/src/test/resources/included-statements-test/child-module.yang
new file mode 100644 (file)
index 0000000..e3c1ad8
--- /dev/null
@@ -0,0 +1,38 @@
+submodule child-module {
+
+    belongs-to "root-module" {
+        prefix "root";
+    }
+
+    typedef new-string-type {
+        type string {
+            pattern "[abc]{3}";
+        }
+    }
+
+    typedef new-int32-type {
+        type int32 {
+            range "1..4 | 10..20";
+         }
+    }
+
+    feature new-feature1 {
+        description "a new feature";
+    }
+
+    feature new-feature2 {
+        description "another new feature";
+    }
+
+    container parent-container {
+        container child-container {
+            leaf autumn-leaf {
+                type string;
+            }
+            leaf winter-snow {
+                type string;
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/included-statements-test/root-module.yang b/yang/yang-parser-rfc7950/src/test/resources/included-statements-test/root-module.yang
new file mode 100644 (file)
index 0000000..4ebf3c2
--- /dev/null
@@ -0,0 +1,14 @@
+module root-module {
+    namespace "urn:opendaylight.org/root-module";
+    prefix "rtm";
+
+    include child-module;
+
+    leaf new-leaf {
+        type new-string-type;
+    }
+
+    leaf old-leaf {
+        type new-int32-type;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/list-keys-test/correct-list-keys-test.yang b/yang/yang-parser-rfc7950/src/test/resources/list-keys-test/correct-list-keys-test.yang
new file mode 100644 (file)
index 0000000..707a04a
--- /dev/null
@@ -0,0 +1,79 @@
+module correct-list-keys-test {
+
+    namespace "correct-list-keys-test";
+    prefix "test";
+
+    list test1 {
+        key "test1_key";
+        leaf test1_key {
+            type string;
+        }
+    }
+
+    list test2 {
+        key "test2_key1 test2_key2 test2_key3";
+        leaf test2_key1 {
+            type string;
+        }
+        leaf test2_key2 {
+            type string;
+        }
+        leaf test2_key3 {
+            type string;
+        }
+    }
+
+    grouping grp_leaf {
+        leaf grp_leaf {
+            type string;
+        }
+    }
+
+    list test3 {
+        key "grp_leaf";
+        uses grp_leaf;
+    }
+
+    list test4 {
+        key "test4_key1 grp_leaf test4_key2";
+        uses grp_leaf;
+        leaf test4_key1 {
+            type string;
+        }
+        leaf test4_key2 {
+            type string;
+        }
+    }
+
+    list test5 {
+        key "aug_leaf";
+    }
+
+    augment /test5 {
+        leaf aug_leaf {
+            type string;
+        }
+    }
+
+    list test6 {
+        key "aug_leaf grp_leaf test6_key1";
+        uses grp_leaf;
+        leaf test6_key1 {
+            type string;
+        }
+    }
+
+    augment /test6 {
+        leaf aug_leaf {
+            type string;
+        }
+    }
+
+    list test7 {
+        key "grp_leaf";
+    }
+
+    augment /test7 {
+        uses grp_leaf;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/list-keys-test/incorrect-list-keys-test.yang b/yang/yang-parser-rfc7950/src/test/resources/list-keys-test/incorrect-list-keys-test.yang
new file mode 100644 (file)
index 0000000..afdf9a2
--- /dev/null
@@ -0,0 +1,17 @@
+module incorrect-list-keys-test {
+
+    namespace "incorrect-list-keys-test";
+    prefix "test";
+
+    list test1 {
+        key "test1_key1 test1_key2";
+        leaf test1_key1 {
+            type string;
+        }
+        list test1_key2 {
+            leaf test1_key2 {
+                type string;
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/list-keys-test/incorrect-list-keys-test2.yang b/yang/yang-parser-rfc7950/src/test/resources/list-keys-test/incorrect-list-keys-test2.yang
new file mode 100644 (file)
index 0000000..9fd843d
--- /dev/null
@@ -0,0 +1,12 @@
+module incorrect-list-keys-test2 {
+
+    namespace "incorrect-list-keys-test2";
+    prefix "test";
+
+    list test1 {
+        key "test1_key1 test1_key2";
+        leaf test1_key1 {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/list-keys-test/incorrect-list-keys-test3.yang b/yang/yang-parser-rfc7950/src/test/resources/list-keys-test/incorrect-list-keys-test3.yang
new file mode 100644 (file)
index 0000000..337ce36
--- /dev/null
@@ -0,0 +1,21 @@
+module incorrect-list-keys-test3 {
+
+    namespace "incorrect-list-keys-test3";
+    prefix "test";
+
+    grouping grp_leaf {
+        leaf grp_leaf {
+            type string;
+        }
+        list grp_list {
+            leaf grp_leaf {
+                type string;
+            }
+        }
+    }
+
+    list test1 {
+        key "grp_list";
+        uses grp_leaf;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/list-keys-test/incorrect-list-keys-test4.yang b/yang/yang-parser-rfc7950/src/test/resources/list-keys-test/incorrect-list-keys-test4.yang
new file mode 100644 (file)
index 0000000..b369295
--- /dev/null
@@ -0,0 +1,18 @@
+module incorrect-list-keys-test4 {
+
+    namespace "incorrect-list-keys-test4";
+    prefix "test";
+
+    grouping grp_leaf {
+        list grp_leaf {
+        }
+    }
+
+    list test1 {
+        key "grp_leaf";
+    }
+
+    augment /test1 {
+        uses grp_leaf;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/list-test/list-test.yang b/yang/yang-parser-rfc7950/src/test/resources/list-test/list-test.yang
new file mode 100644 (file)
index 0000000..82934a1
--- /dev/null
@@ -0,0 +1,38 @@
+module list-test {
+    namespace "urn:opendaylight.org/list-test";
+    prefix "lt";
+
+    list simple-list {
+        key "key1 key2";
+        ordered-by user;
+        min-elements 1;
+        max-elements 10;
+        config true;
+
+        leaf key1 {
+            type int32;
+            mandatory true;
+        }
+
+        leaf key2 {
+            type int16;
+            mandatory true;
+        }
+
+        leaf old-leaf {
+            type string;
+        }
+
+        leaf young-leaf {
+            type string;
+            default "default-value";
+        }
+
+        leaf-list list-of-leaves {
+            type string;
+            ordered-by user;
+            min-elements 2;
+            max-elements 20;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/mapped-build/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/mapped-build/bar.yang
new file mode 100644 (file)
index 0000000..f97ebda
--- /dev/null
@@ -0,0 +1,10 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    include sub-bar { revision-date 2015-03-01; }
+    
+    revision "2015-01-01" {
+        description "Initial version";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/mapped-build/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/mapped-build/foo.yang
new file mode 100644 (file)
index 0000000..0ce3675
--- /dev/null
@@ -0,0 +1,10 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    include sub-foo { revision-date 2015-02-01; }
+    
+    revision "2015-01-02" {
+        description "Initial version";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/mapped-build/sub-bar.yang b/yang/yang-parser-rfc7950/src/test/resources/mapped-build/sub-bar.yang
new file mode 100644 (file)
index 0000000..761d30f
--- /dev/null
@@ -0,0 +1,9 @@
+submodule sub-bar {
+    belongs-to bar {
+        prefix bar-m;
+    }
+    
+    revision "2015-03-01" {
+        description "Initial version";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/mapped-build/sub-foo.yang b/yang/yang-parser-rfc7950/src/test/resources/mapped-build/sub-foo.yang
new file mode 100644 (file)
index 0000000..cf0d31c
--- /dev/null
@@ -0,0 +1,9 @@
+submodule sub-foo {
+    belongs-to foo {
+        prefix foo-m;
+    }
+    
+    revision "2015-02-01" {
+        description "Initial version";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/model-new/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/model-new/bar.yang
new file mode 100644 (file)
index 0000000..b7085fc
--- /dev/null
@@ -0,0 +1,115 @@
+module bar {
+    yang-version 1;
+    namespace "urn:opendaylight.bar";
+    prefix "bar";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+    description "This model define custom type definitions";
+
+    revision "2013-07-03" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef int32-ext1 {
+        type int32 {
+            range "2..20";
+        }
+    }
+
+    typedef int32-ext2 {
+        type int32-ext1 {
+            range "3..9|11..max";
+        }
+        units "mile";
+        default "11";
+    }
+
+    typedef string-ext1 {
+        type string {
+            pattern "[a-k]*";
+            length "5..11";
+        }
+    }
+
+    typedef string-ext2 {
+        type string-ext1 {
+            length "6..10";
+        }
+    }
+
+    typedef string-ext3 {
+        type string-ext2 {
+            pattern "[b-u]*";
+        }
+    }
+
+    typedef string-ext4 {
+        type string-ext3 {
+            pattern "[e-z]*";
+        }
+    }
+
+    typedef invalid-string-pattern {
+        type string ;
+    }
+
+    typedef multiple-pattern-string {
+        type string {
+            pattern "[e-z]*";
+        }
+    }
+
+    typedef my-decimal-type {
+        type decimal64 {
+            fraction-digits 6;
+        }
+    }
+
+    typedef my-union {
+        type union {
+            type int16 {
+                range "1..100";
+            }
+            type int32;
+        }
+    }
+
+    typedef my-union-ext {
+        type my-union;
+    }
+
+    typedef nested-union2 {
+        type union {
+            type my-union-ext;
+            type string;
+        }
+    }
+
+    container interfaces {
+        grouping ifEntry {
+            container augment-holder;
+        }
+        list ifEntry {
+            key "ifIndex";
+
+            leaf ifIndex {
+                type uint32;
+                units minutes;
+            }
+
+            leaf ifMtu {
+                type int32;
+            }
+
+            min-elements 1;
+            max-elements 11;
+        }
+    }
+
+    extension opendaylight {
+        argument "name" {
+            yin-element "true";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/model-new/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/model-new/baz.yang
new file mode 100644 (file)
index 0000000..b54db27
--- /dev/null
@@ -0,0 +1,192 @@
+module baz {
+    yang-version 1;
+    namespace "urn:opendaylight.baz";
+    prefix "baz";
+
+    import bar { prefix "br"; revision-date 2013-07-03; }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef union1 {
+        type union2;
+    }
+
+    typedef union2 {
+        type union {
+            type int32;
+            type br:nested-union2;
+        }
+    }
+
+    augment "/br:interfaces/br:ifEntry" {
+        when "if:ifType='ds0'";
+        container augment-holder {
+            description "Description for augment holder";
+        }
+    }
+
+    augment "/br:interfaces/br:ifEntry" {
+        when "if:ifType='ds2'";
+        container augment-holder2 {
+            description "Description for augment holder";
+        }
+    }
+
+    container network {
+        baz:c-define point {
+        }
+
+        description "network-description";
+        reference "network-reference";
+        status obsolete;
+        config true;
+        presence "some presence text";
+    }
+
+    feature local-storage {
+        description
+                "This feature means the device supports local storage (memory,
+                flash or disk) that can be used to store syslog messages.";
+    }
+
+    feature candidate;
+    feature startup;
+
+    extension c-define {
+        description "Takes as argument a name string. Makes the code generator use
+                the given name in the #define.";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+    notification event {
+        leaf event-class {
+            type string;
+        }
+        anyxml reporting-entity;
+        leaf severity {
+            type string;
+        }
+    }
+
+    rpc get-config {
+        description "Retrieve all or part of a specified configuration.";
+        reference "RFC 6241, Section 7.1";
+
+        input {
+            container source {
+                description
+                                "Particular configuration to retrieve.";
+
+                choice config-source {
+                    mandatory true;
+                    description
+                                        "The configuration to retrieve.";
+                    case a {
+                        leaf candidate {
+                            if-feature candidate;
+                            type empty;
+                            description
+                                                        "The candidate configuration is the config source.";
+                        }
+                    }
+                    case b {
+                        leaf running {
+                            type empty;
+                            description
+                                                        "The running configuration is the config source.";
+                        }
+                    }
+                    case c {
+                        leaf startup {
+                            if-feature startup;
+                            type empty;
+                            description
+                                                        "The startup configuration is the config source. This is optional-to-implement
+                                                        on the server because not all servers will support filtering for
+                                                        this datastore.";
+                        }
+                    }
+                }
+            }
+
+            anyxml filter {
+                description "Subtree or XPath filter to use.";
+                baz:c-define element-attributes;
+            }
+        }
+
+        output {
+            anyxml data {
+                description
+                                "Copy of the source datastore subset that matched the filter criteria
+                                (if any). An empty data container indicates that the request did
+                                not produce any results.";
+            }
+        }
+    }
+
+    grouping target {
+        anyxml data {
+            config true;
+            description "Copy of the source datastore subset.";
+            mandatory false;
+            must "test-condition-text";
+            reference "test-no-reference";
+            status "obsolete";
+            when "test-when-text";
+        }
+        choice how {
+            description "test choice description";
+            default interval;
+            case interval {
+                leaf interval {
+                    type uint16;
+                    default 30;
+                    units minutes;
+                }
+            }
+            case daily {
+                leaf daily {
+                    type empty;
+                }
+                leaf time-of-day {
+                    type string;
+                    units 24-hour-clock;
+                    default 1am;
+                }
+            }
+        }
+        leaf address {
+            type string;
+            description "Target IP address";
+            mandatory true;
+        }
+        container port {
+            description "Target port container";
+        }
+        list addresses {
+            key "id";
+            leaf id {
+                type int8;
+            }
+        }
+        grouping target-inner {
+            description "target-inner default description";
+            leaf inner-grouping-id {
+                type int8;
+            }
+        }
+        typedef group-type {
+            type br:my-decimal-type;
+        }
+
+        br:opendaylight awesome;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/model-new/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/model-new/foo.yang
new file mode 100644 (file)
index 0000000..4fda3b2
--- /dev/null
@@ -0,0 +1,224 @@
+module foo {
+    yang-version 1;
+    namespace "urn:opendaylight.foo";
+    prefix "foo";
+
+    import bar { prefix "br"; revision-date 2013-07-03; }
+
+    import baz { prefix "bz"; revision-date 2013-02-27; }
+
+    include subfoo {
+        revision-date "2013-02-27";
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef identifier {
+        type int32;
+    }
+
+    leaf int32-leaf {
+        type br:int32-ext2 {
+            range "12..max";
+        }
+    }
+
+    leaf string-leaf {
+        type br:string-ext4;
+    }
+
+    leaf invalid-pattern-string-leaf {
+        type br:invalid-string-pattern;
+    }
+
+    leaf invalid-direct-string-pattern-def-leaf {
+        type string ;
+    }
+
+    leaf multiple-pattern-string-leaf {
+        type br:multiple-pattern-string;
+    }
+
+    leaf multiple-pattern-direct-string-def-leaf {
+        type string {
+            pattern "[e-z]*";
+            pattern "[a-d]*";
+        }
+    }
+
+    leaf length-leaf {
+        type br:string-ext2 {
+            length "7..max";
+        }
+    }
+
+    leaf decimal-leaf {
+        type br:my-decimal-type {
+            fraction-digits 4;
+        }
+    }
+
+    leaf decimal-leaf2 {
+        type br:my-decimal-type;
+    }
+
+    container ext {
+        bz:c-define "MY_INTERFACES";
+    }
+
+    leaf union-leaf {
+        type br:my-union-ext;
+    }
+
+    deviation /br:interfaces/br:ifEntry {
+        deviate add {
+            config "true";
+        }
+        reference "system/user ref";
+    }
+
+    leaf custom-union-leaf {
+        type bz:union1;
+    }
+
+    container transfer {
+        choice how {
+            default interval;
+            container input {
+            }
+            list output {
+                leaf id {
+                    type string;
+                }
+            }
+            case interval {
+                leaf interval {
+                    type uint16;
+                    default 30;
+                    units minutes;
+                }
+            }
+            case daily {
+                leaf daily {
+                    type empty;
+                }
+                leaf time-of-day {
+                    type string;
+                    units 24-hour-clock;
+                    default 1am;
+                }
+            }
+            case manual {
+                leaf manual {
+                    type empty;
+                }
+            }
+        }
+    }
+
+    anyxml datas {
+        description
+                "Copy of the source typesstore subset that matched the filter
+                criteria (if any). An empty types container indicates that the
+                request did not produce any results.";
+        status obsolete;
+    }
+
+    augment "/br:interfaces/br:ifEntry/bz:augment-holder" {
+        when "if:ifType='ds0'";
+        leaf ds0ChannelNumber {
+            type string;
+        }
+        leaf interface-id {
+            type leafref {
+                path "/br:interfaces/br:ifEntry/br:ifIndex";
+            }
+        }
+        leaf my-type {
+            type br:int32-ext2;
+        }
+        container schemas {
+        }
+        choice odl {
+            leaf id {
+                type int8;
+            }
+            case node1 {
+                description "node1";
+            }
+            case node2 {
+                description "node2";
+            }
+            container node3 {
+                description "node3";
+            }
+        }
+    }
+
+    container mycont {
+        container innercont {
+            typedef mytype {
+                type string;
+            }
+            leaf myleaf {
+                type mytype;
+            }
+        }
+    }
+
+    uses bz:target {
+        augment "how/interval" {
+            description "inner augment";
+            leaf name {
+                type string;
+            }
+        }
+    }
+
+    container peer {
+        container destination {
+            uses bz:target {
+                refine address {
+                    default "1.2.3.4";
+                    description "IP address of target node";
+                    reference "address reference added by refine";
+                    config false;
+                    mandatory false;
+                    must "ifType != 'ethernet' or " +
+                                        "(ifType = 'ethernet' and ifMTU = 1500)" {
+                        error-message "An ethernet MTU must be 1500";
+                    }
+                    foo:mountpoint "mnt-extension";
+                }
+                refine port {
+                    description "description of port defined by refine";
+                    reference "port reference added by refine";
+                    config false;
+                    presence "presence is required";
+                }
+                refine "addresses" {
+                    description "description of addresses defined by refine";
+                    reference "addresses reference added by refine";
+                    config false;
+                    min-elements 2;
+                    max-elements unbounded;
+                }
+                refine addresses/id {
+                    description "id of address";
+                }
+            }
+        }
+    }
+
+    extension mountpoint {
+        description "enter point";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/model-new/subfoo.yang b/yang/yang-parser-rfc7950/src/test/resources/model-new/subfoo.yang
new file mode 100644 (file)
index 0000000..412f9c1
--- /dev/null
@@ -0,0 +1,70 @@
+submodule subfoo {
+    yang-version 1;
+
+    belongs-to foo {
+        prefix f;
+    }
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    import baz {
+        prefix "bz";
+        revision-date 2013-02-27;
+    }
+
+    revision "2013-02-27" {
+    }
+
+    leaf id {
+        type br:int32-ext2 {
+            range "12..max";
+        }
+    }
+
+    leaf foo-id {
+        type f:identifier;
+    }
+
+    container sub-ext {
+        bz:c-define "MY_INTERFACES";
+    }
+
+    container sub-transfer {
+        choice how {
+            default interval;
+            container input {
+            }
+            list output {
+                leaf id {
+                    type string;
+                }
+            }
+            case manual {
+                leaf manual {
+                    type empty;
+                }
+            }
+        }
+    }
+
+    anyxml sub-datas {
+        status obsolete;
+    }
+
+    augment "/br:interfaces/br:ifEntry/bz:augment-holder" {
+        when "if:ifType='ds0'";
+        leaf subleaf {
+            type string;
+        }
+    }
+
+    extension sub-mountpoint {
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/model/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/model/bar.yang
new file mode 100644 (file)
index 0000000..3501ff4
--- /dev/null
@@ -0,0 +1,119 @@
+module bar {
+    yang-version 1;
+    namespace "urn:opendaylight.bar";
+    prefix "bar";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+    description "This model define custom type definitions";
+
+    revision "2013-07-03" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef int32-ext1 {
+        type int32 {
+            range "2..20";
+        }
+    }
+
+    typedef int32-ext2 {
+        type int32-ext1 {
+            range "3..9|11..max";
+        }
+        units "mile";
+        default "11";
+    }
+
+    typedef string-ext1 {
+        type string {
+            pattern "[a-k]*";
+            length "5..11";
+        }
+    }
+
+    typedef string-ext2 {
+        type string-ext1 {
+            length "6..10";
+        }
+    }
+
+    typedef string-ext3 {
+        type string-ext2 {
+            pattern "[b-u]*";
+        }
+    }
+
+    typedef string-ext4 {
+        type string-ext3 {
+            pattern "[e-z]*";
+        }
+    }
+
+    typedef invalid-string-pattern {
+        type string {
+            pattern "[[A-1*-%22!^^}";
+        }
+    }
+
+    typedef multiple-pattern-string {
+        type string {
+            pattern "[[A-1*-%22!^^}";
+            pattern "[e-z]*";
+        }
+    }
+
+    typedef my-decimal-type {
+        type decimal64 {
+            fraction-digits 6;
+        }
+    }
+
+    typedef my-union {
+        type union {
+            type int16 {
+                range "1..100";
+            }
+            type int32;
+        }
+    }
+
+    typedef my-union-ext {
+        type my-union;
+    }
+
+    typedef nested-union2 {
+        type union {
+            type my-union-ext;
+            type string;
+        }
+    }
+
+    container interfaces {
+        grouping ifEntry {
+            container augment-holder;
+        }
+        list ifEntry {
+            key "ifIndex";
+
+            leaf ifIndex {
+                type uint32;
+                units minutes;
+            }
+
+            leaf ifMtu {
+                type int32;
+            }
+
+            min-elements 1;
+            max-elements 11;
+        }
+    }
+
+    extension opendaylight {
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/model/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/model/baz.yang
new file mode 100644 (file)
index 0000000..c50ce61
--- /dev/null
@@ -0,0 +1,197 @@
+module baz {
+    yang-version 1;
+    namespace "urn:opendaylight.baz";
+    prefix "baz";
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef union1 {
+        type union2;
+    }
+
+    typedef union2 {
+        type union {
+            type int32;
+            type br:nested-union2;
+        }
+    }
+
+    augment "/br:interfaces/br:ifEntry" {
+        when "if:ifType='ds0'";
+        container augment-holder {
+            description "Description for augment holder";
+        }
+    }
+
+    augment "/br:interfaces/br:ifEntry" {
+        when "if:ifType='ds2'";
+        container augment-holder2 {
+            description "Description for augment holder";
+        }
+    }
+
+    container network {
+        baz:c-define point {
+        }
+
+        description "network-description";
+        reference "network-reference";
+        status obsolete;
+        config true;
+        presence "some presence text";
+    }
+
+    feature local-storage {
+        description
+            "This feature means the device supports local
+             storage (memory, flash or disk) that can be used to
+             store syslog messages.";
+    }
+
+    feature candidate;
+    feature startup;
+
+    extension c-define {
+        description "Takes as argument a name string. Makes the code generator use the given name in the #define.";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+    notification event {
+        leaf event-class {
+            type string;
+        }
+        anyxml reporting-entity;
+        leaf severity {
+            type string;
+        }
+    }
+
+    rpc get-config {
+        description "Retrieve all or part of a specified configuration.";
+        reference "RFC 6241, Section 7.1";
+
+        input {
+            container source {
+                description
+                  "Particular configuration to retrieve.";
+
+                choice config-source {
+                    mandatory true;
+                    description
+                      "The configuration to retrieve.";
+                    case a {
+                        leaf candidate {
+                            if-feature candidate;
+                            type empty;
+                            description
+                              "The candidate configuration is the config source.";
+                        }
+                    }
+                    case b {
+                        leaf running {
+                            type empty;
+                            description
+                              "The running configuration is the config source.";
+                        }
+                    }
+                    case c {
+                        leaf startup {
+                            if-feature startup;
+                            type empty;
+                            description
+                              "The startup configuration is the config source.
+                               This is optional-to-implement on the server because
+                               not all servers will support filtering for this
+                               datastore.";
+                        }
+                    }
+                }
+            }
+
+            anyxml filter {
+                description "Subtree or XPath filter to use.";
+                baz:c-define element-attributes;
+            }
+        }
+
+        output {
+            anyxml data {
+                description
+                  "Copy of the source datastore subset that matched
+                   the filter criteria (if any).  An empty data container
+                   indicates that the request did not produce any results.";
+            }
+        }
+    }
+
+    grouping target {
+        anyxml data {
+            config true;
+            description "Copy of the source datastore subset.";
+            mandatory false;
+            must "test-condition-text";
+            reference "test-no-reference";
+            status "obsolete";
+            when "test-when-text";
+        }
+        choice how {
+            description "test choice description";
+            default interval;
+            case interval {
+                leaf interval {
+                    type uint16;
+                    default 30;
+                    units minutes;
+                }
+            }
+            case daily {
+                leaf daily {
+                    type empty;
+                }
+                leaf time-of-day {
+                    type string;
+                    units 24-hour-clock;
+                    default 1am;
+                }
+            }
+        }
+        leaf address {
+            type string;
+            description "Target IP address";
+            mandatory true;
+        }
+        container port {
+            description "Target port container";
+        }
+        list addresses {
+            key "id";
+            leaf id {
+                type int8;
+            }
+        }
+        grouping target-inner {
+            description "target-inner default description";
+            leaf inner-grouping-id {
+                type int8;
+            }
+        }
+        typedef group-type {
+            type br:my-decimal-type;
+        }
+        
+        br:opendaylight awesome;
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/model/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/model/foo.yang
new file mode 100644 (file)
index 0000000..a26eea3
--- /dev/null
@@ -0,0 +1,234 @@
+module foo {
+    yang-version 1;
+    namespace "urn:opendaylight.foo";
+    prefix "foo";
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    import baz {
+        prefix "bz";
+        revision-date 2013-02-27;
+    }
+
+    include subfoo {
+        revision-date "2013-02-27";
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef identifier {
+        type int32;
+    }
+
+    leaf int32-leaf {
+        type br:int32-ext2 {
+            range "12..max";
+        }
+    }
+
+    leaf string-leaf {
+        type br:string-ext4;
+    }
+
+    leaf invalid-pattern-string-leaf {
+        type br:invalid-string-pattern;
+    }
+
+    leaf invalid-direct-string-pattern-def-leaf {
+        type string {
+            pattern "[[A-1*-%22!^^}";
+        }
+    }
+
+    leaf multiple-pattern-string-leaf {
+        type br:multiple-pattern-string;
+    }
+
+    leaf multiple-pattern-direct-string-def-leaf {
+        type string {
+            pattern "[e-z]*";
+            pattern "[[A-1*-%22!^^}";
+            pattern "[a-d]*";
+        }
+    }
+
+    leaf length-leaf {
+        type br:string-ext2 {
+            length "7..max";
+        }
+    }
+
+    leaf decimal-leaf {
+        type br:my-decimal-type {
+            fraction-digits 6;
+        }
+    }
+
+    leaf decimal-leaf2 {
+        type br:my-decimal-type;
+    }
+
+    container ext {
+        bz:c-define "MY_INTERFACES";
+    }
+
+    leaf union-leaf {
+        type br:my-union-ext;
+    }
+
+    deviation /br:interfaces/br:ifEntry {
+        deviate add {
+            config "true";
+        }
+        reference "system/user ref";
+    }
+
+    leaf custom-union-leaf {
+        type bz:union1;
+    }
+
+    container transfer {
+        choice how {
+            default interval;
+            container input {
+            }
+            list output {
+                leaf id {
+                    type string;
+                }
+            }
+            case interval {
+                leaf interval {
+                    type uint16;
+                    default 30;
+                    units minutes;
+                }
+            }
+            case daily {
+                leaf daily {
+                    type empty;
+                }
+                leaf time-of-day {
+                    type string;
+                    units 24-hour-clock;
+                    default 1am;
+                }
+            }
+            case manual {
+                leaf manual {
+                    type empty;
+                }
+            }
+        }
+    }
+
+    anyxml datas {
+        description
+          "Copy of the source typesstore subset that matched
+           the filter criteria (if any).  An empty types container
+           indicates that the request did not produce any results.";
+        status obsolete;
+    }
+
+    augment "/br:interfaces/br:ifEntry/bz:augment-holder" {
+        when "if:ifType='ds0'";
+        leaf ds0ChannelNumber {
+            type string;
+        }
+        leaf interface-id {
+            type leafref {
+                path "/br:interfaces/br:ifEntry/br:ifIndex";
+            }
+        }
+        leaf my-type {
+            type br:int32-ext2;
+        }
+        container schemas {
+        }
+        choice odl {
+            leaf id {
+                type int8;
+            }
+            case node1 {
+                description "node1";
+            }
+            case node2 {
+                description "node2";
+            }
+            container node3 {
+                description "node3";
+            }
+        }
+    }
+
+    container mycont {
+        container innercont {
+            typedef mytype {
+                type string;
+            }
+            leaf myleaf {
+                type mytype;
+            }
+        }
+    }
+
+    uses bz:target {
+        augment "how/interval" {
+            description "inner augment";
+            leaf name {
+                type string;
+            }
+        }
+    }
+
+    container peer {
+        container destination {
+            uses bz:target {
+                refine address {
+                    default "1.2.3.4";
+                    description "IP address of target node";
+                    reference "address reference added by refine";
+                    config false;
+                    mandatory false;
+                    must "ifType != 'ethernet' or " +
+                            "(ifType = 'ethernet' and ifMTU = 1500)" {
+                        error-message "An ethernet MTU must be 1500";
+                    }
+                    foo:mountpoint "mnt-extension";
+                }
+                refine port {
+                    description "description of port defined by refine";
+                    reference "port reference added by refine";
+                    config false;
+                    presence "presence is required";
+                }
+                refine "addresses" {
+                    description "description of addresses defined by refine";
+                    reference "addresses reference added by refine";
+                    config false;
+                    min-elements 2;
+                    max-elements unbounded;
+                }
+                refine addresses/id {
+                    description "id of address";
+                }
+            }
+        }
+    }
+
+    extension mountpoint {
+        description "enter point";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/model/subfoo.yang b/yang/yang-parser-rfc7950/src/test/resources/model/subfoo.yang
new file mode 100644 (file)
index 0000000..5338ec3
--- /dev/null
@@ -0,0 +1,70 @@
+submodule subfoo {
+    yang-version 1;
+
+    belongs-to foo {
+        prefix f;
+    }
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    import baz {
+        prefix "bz";
+        revision-date 2013-02-27;
+    }
+
+    revision "2013-02-27" {
+    }
+
+    leaf id {
+        type br:int32-ext2 {
+            range "12..max";
+        }
+    }
+
+    leaf foo-id {
+        type f:identifier;
+    }
+
+    container sub-ext {
+        bz:c-define "MY_INTERFACES";
+    }
+
+    container sub-transfer {
+        choice how {
+            default input;
+            container input {
+            }
+            list output {
+                leaf id {
+                    type string;
+                }
+            }
+            case manual {
+                leaf manual {
+                    type empty;
+                }
+            }
+        }
+    }
+
+    anyxml sub-datas {
+        status obsolete;
+    }
+
+    augment "/br:interfaces/br:ifEntry/bz:augment-holder" {
+        when "if:ifType='ds0'";
+        leaf subleaf {
+            type string;
+        }
+    }
+
+    extension sub-mountpoint {
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/module-source/simple-module.yang b/yang/yang-parser-rfc7950/src/test/resources/module-source/simple-module.yang
new file mode 100644 (file)
index 0000000..6aea830
--- /dev/null
@@ -0,0 +1,4 @@
+module simple-module {
+    namespace "test";
+    prefix test;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/must-when-stmt-test/must-test.yang b/yang/yang-parser-rfc7950/src/test/resources/must-when-stmt-test/must-test.yang
new file mode 100644 (file)
index 0000000..dd5dc7c
--- /dev/null
@@ -0,0 +1,28 @@
+module must-test {
+    namespace "urn:opendaylight.org/must-test";
+    prefix "mt";
+
+    container interface {
+         presence "some text";
+
+         leaf ifType {
+             type enumeration {
+                 enum ethernet;
+                 enum atm;
+             }
+         }
+         leaf ifMTU {
+             type uint32;
+         }
+         must "ifType != 'ethernet' or " +
+              "(ifType = 'ethernet' and ifMTU = 1500)" {
+             error-app-tag "An ethernet error";
+             error-message "An ethernet MTU must be 1500";
+         }
+         must "ifType != 'atm' or " +
+              "(ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)" {
+             error-app-tag "An atm error";
+             error-message "An atm MTU must be 64 .. 17966";
+         }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/must-when-stmt-test/when-test.yang b/yang/yang-parser-rfc7950/src/test/resources/must-when-stmt-test/when-test.yang
new file mode 100644 (file)
index 0000000..63a0e06
--- /dev/null
@@ -0,0 +1,13 @@
+module when-test {
+    namespace "urn:opendaylight.org/when-test";
+    prefix "wt";
+
+    container test-container {
+        when "conditional-leaf = 'autumn-leaf'";
+        leaf conditional-leaf {
+            type string;
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/augment0.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/augment0.yang
new file mode 100644 (file)
index 0000000..27b0f3f
--- /dev/null
@@ -0,0 +1,27 @@
+module augment0 {
+    yang-version 1;
+    namespace "urn:simple.augment0.demo";
+    prefix "a0";
+
+    revision "2014-06-02";
+
+    container foo {
+        description "foo container";
+        container bar {
+            leaf id {
+                type int8;
+            }
+            typedef int-ext {
+                type int8 {
+                    range "5..10";
+                }
+            }
+            choice choice-ext {
+                leaf delta {
+                    type int8;
+                }
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/augment1.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/augment1.yang
new file mode 100644 (file)
index 0000000..8ccafd5
--- /dev/null
@@ -0,0 +1,25 @@
+module augment1 {
+    yang-version 1;
+    namespace "urn:simple.augment1.demo";
+    prefix "a1";
+
+
+    import augment0 {
+        prefix "a0";
+    }
+
+    revision "2014-06-02";
+
+    augment "/a0:foo/a0:bar" {
+        leaf id {
+            type string;
+        }
+    }
+
+    augment "/a0:foo/a0:bar" {
+        leaf id {
+            type string;
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/augment2.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/augment2.yang
new file mode 100644 (file)
index 0000000..632a6c5
--- /dev/null
@@ -0,0 +1,20 @@
+module augment2 {
+    yang-version 1;
+    namespace "urn:simple.augment2.demo";
+    prefix "a2";
+
+    import augment0 {
+        prefix "a0";
+    }
+
+    revision "2014-06-02";
+
+    augment "/a0:foo/a0:bar/a0:choice-ext" {
+        anyxml delta;
+    }
+
+    augment "/a0:foo/a0:bar/a0:choice-ext" {
+        anyxml delta;
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/container-leaf.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/container-leaf.yang
new file mode 100644 (file)
index 0000000..9679385
--- /dev/null
@@ -0,0 +1,14 @@
+module container-leaf {
+    yang-version 1;
+    namespace "urn:simple.container.demo";
+    prefix "t1";
+
+    container foo {
+        description "foo container";
+    }
+
+    leaf foo {
+        type int8;
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/container-list.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/container-list.yang
new file mode 100644 (file)
index 0000000..08d90af
--- /dev/null
@@ -0,0 +1,14 @@
+module container-list {
+    yang-version 1;
+    namespace "urn:simple.container.demo";
+    prefix "t1";
+
+    container foo {
+        description "foo container";
+    }
+
+    list foo {
+        description "foo list";
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/container.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/container.yang
new file mode 100644 (file)
index 0000000..8e347a1
--- /dev/null
@@ -0,0 +1,14 @@
+module container {
+    yang-version 1;
+    namespace "urn:simple.container.demo";
+    prefix "t1";
+
+    container foo {
+        description "foo 1";
+    }
+
+    container foo {
+        description "foo 2";
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/identity.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/identity.yang
new file mode 100644 (file)
index 0000000..f81e6e5
--- /dev/null
@@ -0,0 +1,14 @@
+module identity {
+    yang-version 1;
+    namespace "urn:simple.container.demo";
+    prefix "t1";
+
+    identity id1;
+
+    identity id2;
+
+    identity id1 {
+        base id2;
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/typedef.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/duplicity/typedef.yang
new file mode 100644 (file)
index 0000000..b4ec590
--- /dev/null
@@ -0,0 +1,14 @@
+module typedef {
+    yang-version 1;
+    namespace "urn:simple.container.demo";
+    prefix "t1";
+
+    typedef int-ext {
+        type int32;
+    }
+
+    typedef int-ext {
+        type int16;
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/invalid-list-key-def.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/invalid-list-key-def.yang
new file mode 100644 (file)
index 0000000..6fb0a66
--- /dev/null
@@ -0,0 +1,14 @@
+module invalid-list-key-def {
+    yang-version 1;
+    namespace "invalid:list:key:def";
+    prefix "p";
+
+    list application-map {
+        key "rib-id";
+
+        leaf bgp-id {
+            type string;
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile0.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile0.yang
new file mode 100644 (file)
index 0000000..6e6377e
--- /dev/null
@@ -0,0 +1,9 @@
+module test0 {
+    yang-version 1;
+    namespace "urn:simple.container.demo";
+    prefix "t0";
+
+    container interfaces {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile1.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile1.yang
new file mode 100644 (file)
index 0000000..fdc7e71
--- /dev/null
@@ -0,0 +1,11 @@
+module test1 {
+    yang-version 1;
+    namespace "urn:simple.container.demo";
+    prefix "t1";
+
+    import some-module {
+        prefix "data";
+        revision-date 2013-02-27;
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile2.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile2.yang
new file mode 100644 (file)
index 0000000..ea5a11f
--- /dev/null
@@ -0,0 +1,29 @@
+module test2 {
+    yang-version 1;
+    namespace "urn:simple.types.data.demo";
+    prefix "t2";
+    
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+    
+    description "This is types-data test description";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+    
+    container c1 {
+        typedef int-ext {
+            type int32 {
+                range "10..20";
+            }
+        }
+    }
+    
+    container top {
+        leaf id {
+            type int-ext;
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile3.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile3.yang
new file mode 100644 (file)
index 0000000..8d5302a
--- /dev/null
@@ -0,0 +1,19 @@
+module test3 {
+    yang-version 1;
+    namespace "urn:simple.container.demo.test3";
+    prefix "t1";
+
+    import test0 {
+        prefix "data";
+    }
+    
+    augment "/data:unknown" {
+        when "if:ifType='ds0'";
+        leaf interface-id {
+            type leafref {
+                path "/data:interfaces/data:interface/data:name";
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile4.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile4.yang
new file mode 100644 (file)
index 0000000..c327de3
--- /dev/null
@@ -0,0 +1,26 @@
+module test4 {
+    yang-version 1;
+    namespace "urn:simple.container.demo";
+    prefix "t1";
+
+    grouping tree {
+        leaf-list node {
+            type string;
+        }
+        container holder {
+        
+        }
+    }
+
+    container schema {
+        uses tree {
+            refine node {
+                presence "true";
+            }
+            refine holder {
+                default "one";
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile5.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile5.yang
new file mode 100644 (file)
index 0000000..0d8de38
--- /dev/null
@@ -0,0 +1,19 @@
+module test5 {
+    yang-version 1;
+    namespace "urn:simple.container.demo";
+    prefix "t1";
+
+    typedef my-custom-string {
+        type string {
+            pattern "[a-k]*";
+            length "5..11";
+        }
+    }
+
+    typedef my-string-type {
+        type my-custom-string {
+            length "4..10";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile6.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile6.yang
new file mode 100644 (file)
index 0000000..31a4c1e
--- /dev/null
@@ -0,0 +1,18 @@
+module test5 {
+    yang-version 1;
+    namespace "urn:simple.container.demo";
+    prefix "t1";
+
+    typedef my-custom-int {
+        type int32 {
+            range "5..11 | 15..20";
+        }
+    }
+
+    typedef my-int-type {
+        type my-custom-int {
+            range "min..max";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile7.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile7.yang
new file mode 100644 (file)
index 0000000..17dddeb
--- /dev/null
@@ -0,0 +1,26 @@
+module testfile7 {
+    yang-version 1;
+    namespace "urn:simple.test7.demo";
+    prefix "t7";
+
+    import testfile8 {
+        prefix "t8";
+        revision-date 2013-08-03;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-08-03" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    augment "/t8:interfaces/t8:ifEntry" {
+        when "if:leafType='ds1'";
+        leaf linkleaf {
+            mandatory true;
+            type binary;
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile8.yang b/yang/yang-parser-rfc7950/src/test/resources/negative-scenario/testfile8.yang
new file mode 100644 (file)
index 0000000..baf3557
--- /dev/null
@@ -0,0 +1,27 @@
+module testfile8 {
+    yang-version 1;
+    namespace "urn:simple.test8.demo";
+    prefix "t8";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+    description "This is types-data test description";
+
+    revision "2013-08-03" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    container interfaces {
+        grouping ifEntry {
+            container augment-holder;
+        }
+        list ifEntry {
+            key "ifIndex";
+            leaf ifIndex {
+                type uint32;
+                units minutes;
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/no-revision/imported.yang b/yang/yang-parser-rfc7950/src/test/resources/no-revision/imported.yang
new file mode 100644 (file)
index 0000000..d36c996
--- /dev/null
@@ -0,0 +1,10 @@
+module imported {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test1";
+    prefix "imp";
+
+    //organization "opendaylight";
+    //contact "WILL-BE-DEFINED-LATER";
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/no-revision/imported@2012-12-12.yang b/yang/yang-parser-rfc7950/src/test/resources/no-revision/imported@2012-12-12.yang
new file mode 100644 (file)
index 0000000..68004c8
--- /dev/null
@@ -0,0 +1,12 @@
+module imported {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test1";
+    prefix "imp";
+
+    revision 2012-12-12 {}
+
+    //organization "opendaylight";
+    //contact "WILL-BE-DEFINED-LATER";
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/no-revision/module-without-revision.yang b/yang/yang-parser-rfc7950/src/test/resources/no-revision/module-without-revision.yang
new file mode 100644 (file)
index 0000000..d4046d2
--- /dev/null
@@ -0,0 +1,27 @@
+module module-without-revision {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test1";
+    prefix "mwr";
+
+    import ietf-inet-types {
+        prefix "inet";
+        revision-date 2010-09-24;
+    }
+
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+
+    typedef dscp-ext {
+        type inet:dscp {
+            range "min..max";
+        }
+    }
+
+    leaf id {
+        type inet:port-number {
+            range "0..65535";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/no-revision/top@2012-10-10.yang b/yang/yang-parser-rfc7950/src/test/resources/no-revision/top@2012-10-10.yang
new file mode 100644 (file)
index 0000000..2e200bd
--- /dev/null
@@ -0,0 +1,16 @@
+module top {
+
+    yang-version 1;
+    namespace "urn:simple.demo.test1";
+    prefix "top";
+
+    import imported {
+        prefix "imp";
+    }
+
+    revision 2012-10-10 {}
+
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-2/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-2/bar.yang
new file mode 100644 (file)
index 0000000..64c9a19
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29;}
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-2/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-2/foo.yang
new file mode 100644 (file)
index 0000000..22a7e15
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29;}
+    import bar { prefix bar; revision-date 2016-01-31;}
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-2/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-2/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-3/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-3/foo.yang
new file mode 100644 (file)
index 0000000..3945e54
--- /dev/null
@@ -0,0 +1,15 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-3/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-3/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-1/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-1/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..2a435be
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-1/bar@2016-01-31.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-1/bar@2016-01-31.yang
new file mode 100644 (file)
index 0000000..2e84793
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "1.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-1/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-1/foo.yang
new file mode 100644 (file)
index 0000000..1a1c25a
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; oc-ext:openconfig-version "0.1.2";}
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-1/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-1/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-1/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-1/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..e1c85ad
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "1.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-1/bar@2016-01-31.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-1/bar@2016-01-31.yang
new file mode 100644 (file)
index 0000000..4f4f695
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "1.1.3";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-1/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-1/foo.yang
new file mode 100644 (file)
index 0000000..1a1c25a
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; oc-ext:openconfig-version "0.1.2";}
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-1/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-1/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-2/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-2/bar.yang
new file mode 100644 (file)
index 0000000..2e84793
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "1.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-2/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-2/foo.yang
new file mode 100644 (file)
index 0000000..2b37c24
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; revision-date 2016-01-31; oc-ext:openconfig-version "0.1.2";}
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-2/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic-import-invalid-2/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic/bar.yang
new file mode 100644 (file)
index 0000000..b973ab3
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic/foo.yang
new file mode 100644 (file)
index 0000000..2b37c24
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; revision-date 2016-01-31; oc-ext:openconfig-version "0.1.2";}
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/basic/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..5ccae0c
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "2.5.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/bar@2016-01-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/bar@2016-01-02.yang
new file mode 100644 (file)
index 0000000..4cc416c
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "3.5.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/bar@2016-01-03.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/bar@2016-01-03.yang
new file mode 100644 (file)
index 0000000..7ba0f33
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-03" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "4.5.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/foo.yang
new file mode 100644 (file)
index 0000000..4edb32e
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; oc-ext:openconfig-version "5.5.5"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-major/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..eca43da
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.2.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/bar@2016-01-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/bar@2016-01-02.yang
new file mode 100644 (file)
index 0000000..dc86015
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.3.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/bar@2016-01-03.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/bar@2016-01-03.yang
new file mode 100644 (file)
index 0000000..5a3afb6
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-03" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.4.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/foo.yang
new file mode 100644 (file)
index 0000000..4edb32e
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; oc-ext:openconfig-version "5.5.5"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-minor/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..68b2f66
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.5.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/bar@2016-01-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/bar@2016-01-02.yang
new file mode 100644 (file)
index 0000000..b780b3d
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.5.3";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/bar@2016-01-03.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/bar@2016-01-03.yang
new file mode 100644 (file)
index 0000000..391975a
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-03" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.5.4";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/foo.yang
new file mode 100644 (file)
index 0000000..4edb32e
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; oc-ext:openconfig-version "5.5.5"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-invalid-patch/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..38172e4
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "4.5.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/bar@2016-01-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/bar@2016-01-02.yang
new file mode 100644 (file)
index 0000000..2221251
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.5.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/bar@2016-01-03.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/bar@2016-01-03.yang
new file mode 100644 (file)
index 0000000..2aa155d
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-03" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "6.5.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/foo.yang
new file mode 100644 (file)
index 0000000..4edb32e
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; oc-ext:openconfig-version "5.5.5"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-major/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..5f6dfc8
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.4.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/bar@2016-01-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/bar@2016-01-02.yang
new file mode 100644 (file)
index 0000000..2221251
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.5.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/bar@2016-01-03.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/bar@2016-01-03.yang
new file mode 100644 (file)
index 0000000..0092918
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-03" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.6.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/foo.yang
new file mode 100644 (file)
index 0000000..4edb32e
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; oc-ext:openconfig-version "5.5.5"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-minor/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..6f0b2ae
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.5.4";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/bar@2016-01-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/bar@2016-01-02.yang
new file mode 100644 (file)
index 0000000..2221251
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.5.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/bar@2016-01-03.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/bar@2016-01-03.yang
new file mode 100644 (file)
index 0000000..2b35449
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-03" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.5.6";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/foo.yang
new file mode 100644 (file)
index 0000000..4edb32e
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; oc-ext:openconfig-version "5.5.5"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/border-case/border-case-valid-patch/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/bar@2016-01-03.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/bar@2016-01-03.yang
new file mode 100644 (file)
index 0000000..138dbad
--- /dev/null
@@ -0,0 +1,24 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "1.2.012"; }
+    import foobar { prefix foobar; oc-ext:openconfig-version "2.25.3"; }
+
+    revision "2016-01-03" {
+        description "Imported version";
+    }
+    oc-ext:openconfig-version "1.2.6";
+
+    container root {
+        container test-container {
+            uses foobar:test-grouping;
+        }
+    }
+
+    leaf-list should-present {
+        type string;
+        description "List of strings";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/bar@2016-01-04.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/bar@2016-01-04.yang
new file mode 100644 (file)
index 0000000..10d176b
--- /dev/null
@@ -0,0 +1,24 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "1.2.210"; }
+    import foobar { prefix foobar; oc-ext:openconfig-version "2.25.3"; }
+
+    revision "2016-01-04" {
+        description "Not-imported version";
+    }
+    oc-ext:openconfig-version "2.200.200";
+
+    container root {
+        container test-container {
+            uses foobar:test-grouping;
+        }
+    }
+
+    leaf-list should-not-be-present {
+        type uint32;
+        description "List of integers";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/foo.yang
new file mode 100644 (file)
index 0000000..9caffd9
--- /dev/null
@@ -0,0 +1,13 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "1.0.0"; }
+    import bar { prefix bar; oc-ext:openconfig-version "1.2.5"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "1.50.02";
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/foobar@2016-01-31.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/foobar@2016-01-31.yang
new file mode 100644 (file)
index 0000000..1730825
--- /dev/null
@@ -0,0 +1,23 @@
+module foobar {
+    namespace "foobar";
+    prefix foobar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "1.2.95"; }
+
+    revision "2016-01-31" {
+        description "Not-imported version";
+    }
+    oc-ext:openconfig-version "2.25.2";
+
+    grouping test-grouping {
+        leaf name {
+            type string;
+            description "Name";
+        }
+        leaf oldnumber {
+            type uint8;
+            description "Old number";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/foobar@2016-02-28.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/foobar@2016-02-28.yang
new file mode 100644 (file)
index 0000000..5e647e9
--- /dev/null
@@ -0,0 +1,23 @@
+module foobar {
+    namespace "foobar";
+    prefix foobar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "1.3.95"; }
+
+    revision "2016-02-28" {
+        description "Imported version";
+    }
+    oc-ext:openconfig-version "2.26.465";
+
+    grouping test-grouping {
+        leaf name {
+            type string;
+            description "Name";
+        }
+        leaf number {
+            type uint32;
+            description "Number";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-1/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..6cff937
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "1.3.95";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/bar@2016-01-03.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/bar@2016-01-03.yang
new file mode 100644 (file)
index 0000000..fe922ff
--- /dev/null
@@ -0,0 +1,23 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "2.0.40"; }
+    import foobar { prefix foobar; oc-ext:openconfig-version "7.12.54"; }
+
+    revision "2016-01-03" {
+        description "Imported version";
+    }
+    oc-ext:openconfig-version "4.9.8";
+
+    augment "/foobar:root/foobar:test-container" {
+        leaf should-present-leaf-1 {
+            type string;
+        }
+        leaf should-present-leaf-2 {
+            type uint8;
+            default 0;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/bar@2016-01-04.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/bar@2016-01-04.yang
new file mode 100644 (file)
index 0000000..c232860
--- /dev/null
@@ -0,0 +1,23 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "2.0.50"; }
+    import foobar { prefix foobar; oc-ext:openconfig-version "7.12.54"; }
+
+    revision "2016-01-04" {
+        description "Not-imported version";
+    }
+    oc-ext:openconfig-version "3.0.5";
+
+    augment "/foobar:root/foobar:test-container" {
+        leaf should-not-be-present-leaf-1 {
+            type string;
+        }
+        leaf should-not-be-present-leaf-2 {
+            type uint8;
+            default 0;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/foo.yang
new file mode 100644 (file)
index 0000000..5f53491
--- /dev/null
@@ -0,0 +1,13 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "2.5.40"; }
+    import bar { prefix bar; oc-ext:openconfig-version "4.1.1"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "2.32.2";
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/foobar@2016-01-31.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/foobar@2016-01-31.yang
new file mode 100644 (file)
index 0000000..7c7ac1a
--- /dev/null
@@ -0,0 +1,20 @@
+module foobar {
+    namespace "foobar";
+    prefix foobar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "2.1.950"; }
+
+    revision "2016-01-31" {
+        description "Not-imported version";
+    }
+    oc-ext:openconfig-version "8.0.0";
+
+    container root {
+        leaf included-not-correct-mark {
+            type empty;
+        }
+        container test-container {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/foobar@2016-02-27.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/foobar@2016-02-27.yang
new file mode 100644 (file)
index 0000000..930c116
--- /dev/null
@@ -0,0 +1,20 @@
+module foobar {
+    namespace "foobar";
+    prefix foobar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "2.5.50"; }
+
+    revision "2016-02-27" {
+        description "Not-imported version";
+    }
+    oc-ext:openconfig-version "7.13.0";
+
+    container root {
+        leaf included-not-correct-mark {
+            type empty;
+        }
+        container test-container {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/foobar@2016-02-28.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/foobar@2016-02-28.yang
new file mode 100644 (file)
index 0000000..676834c
--- /dev/null
@@ -0,0 +1,20 @@
+module foobar {
+    namespace "foobar";
+    prefix foobar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "2.5.50"; }
+
+    revision "2016-02-28" {
+        description "Imported version";
+    }
+    oc-ext:openconfig-version "7.13.99";
+
+    container root {
+        leaf included-correct-mark {
+            type empty;
+        }
+        container test-container {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/complex/complex-2/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..9b021b6
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "2.5.50";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-invalid/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-invalid/bar.yang
new file mode 100644 (file)
index 0000000..049c662
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "1.0.0";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-invalid/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-invalid/foo.yang
new file mode 100644 (file)
index 0000000..2985d68
--- /dev/null
@@ -0,0 +1,15 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; revision-date 2016-01-31; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-invalid/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-invalid/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-valid/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-valid/bar.yang
new file mode 100644 (file)
index 0000000..62e0506
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.99.99";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-valid/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-valid/foo.yang
new file mode 100644 (file)
index 0000000..3db5613
--- /dev/null
@@ -0,0 +1,15 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; revision-date 2016-01-31; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-valid/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/default-major-valid/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/defaults/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/defaults/bar.yang
new file mode 100644 (file)
index 0000000..7dedebe
--- /dev/null
@@ -0,0 +1,12 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/defaults/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/defaults/defaults/foo.yang
new file mode 100644 (file)
index 0000000..005e06a
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import bar { prefix bar; revision-date 2016-01-31; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision-2/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision-2/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..2a435be
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision-2/bar@2016-01-31.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision-2/bar@2016-01-31.yang
new file mode 100644 (file)
index 0000000..2e84793
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "1.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision-2/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision-2/foo.yang
new file mode 100644 (file)
index 0000000..2b37c24
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; revision-date 2016-01-31; oc-ext:openconfig-version "0.1.2";}
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision-2/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision-2/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..2a435be
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision/foo.yang
new file mode 100644 (file)
index 0000000..2b37c24
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; revision-date 2016-01-31; oc-ext:openconfig-version "0.1.2";}
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/ignoring-revision/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-deprecated-1/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-deprecated-1/foo.yang
new file mode 100644 (file)
index 0000000..7d043d9
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "1.0.0"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-deprecated-1/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-deprecated-1/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..4601af9
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "2.0.0";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-deprecated-2/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-deprecated-2/foo.yang
new file mode 100644 (file)
index 0000000..6a217a9
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.9.9"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-deprecated-2/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-deprecated-2/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..a310b5c
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "1.0.0";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-notsufficient-1/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-notsufficient-1/foo.yang
new file mode 100644 (file)
index 0000000..12837d6
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "2.0.0"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-notsufficient-1/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-notsufficient-1/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..a310b5c
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "1.0.0";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-notsufficient-2/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-notsufficient-2/foo.yang
new file mode 100644 (file)
index 0000000..987eea3
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "2.0.5"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-notsufficient-2/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-invalid-notsufficient-2/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..4601af9
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "2.0.0";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-valid/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-valid/foo.yang
new file mode 100644 (file)
index 0000000..7d043d9
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "1.0.0"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-valid/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/import/import-valid/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..a310b5c
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "1.0.0";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..ff09241
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-02.yang
new file mode 100644 (file)
index 0000000..4280db0
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-03.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-03.yang
new file mode 100644 (file)
index 0000000..c2ff5a9
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-03" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.3";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-04.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-04.yang
new file mode 100644 (file)
index 0000000..afa6b30
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-04" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.10.4";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-31.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-01-31.yang
new file mode 100644 (file)
index 0000000..2e84793
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "1.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-02-28.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/bar@2016-02-28.yang
new file mode 100644 (file)
index 0000000..c6968db
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-28" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "1.2.28";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/foo.yang
new file mode 100644 (file)
index 0000000..1a1c25a
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; oc-ext:openconfig-version "0.1.2";}
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple-modules/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..f701a5b
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "2.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/bar@2016-01-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/bar@2016-01-02.yang
new file mode 100644 (file)
index 0000000..c65cd27
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "2.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/bar@2016-02-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/bar@2016-02-01.yang
new file mode 100644 (file)
index 0000000..2371e1a
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "4.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/bar@2016-02-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/bar@2016-02-02.yang
new file mode 100644 (file)
index 0000000..03cdd7e
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.0.0";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/foo.yang
new file mode 100644 (file)
index 0000000..243aa35
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; revision-date 2016-01-31; oc-ext:openconfig-version "1.0.0"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-deprecated/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..ae8c8d5
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "2.0.0";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/bar@2016-01-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/bar@2016-01-02.yang
new file mode 100644 (file)
index 0000000..7aff7c5
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "2.1.0";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/bar@2016-02-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/bar@2016-02-01.yang
new file mode 100644 (file)
index 0000000..5f124db
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "2.5.0";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/bar@2016-02-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/bar@2016-02-02.yang
new file mode 100644 (file)
index 0000000..7fd4d00
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "2.5.4";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/foo.yang
new file mode 100644 (file)
index 0000000..f5c591b
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; oc-ext:openconfig-version "2.5.5"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-invalid-nosufficient/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..5679d89
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.0.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/bar@2016-01-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/bar@2016-01-02.yang
new file mode 100644 (file)
index 0000000..0f94048
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.0";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/bar@2016-02-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/bar@2016-02-01.yang
new file mode 100644 (file)
index 0000000..6a2560a
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.9.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/bar@2016-02-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/bar@2016-02-02.yang
new file mode 100644 (file)
index 0000000..d343e2a
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "1.0.0";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/foo.yang
new file mode 100644 (file)
index 0000000..14783cc
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-defaults/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/bar@2016-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/bar@2016-01-01.yang
new file mode 100644 (file)
index 0000000..720eee2
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "4.4.0";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/bar@2016-01-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/bar@2016-01-02.yang
new file mode 100644 (file)
index 0000000..a6116f3
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.5.0";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/bar@2016-02-01.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/bar@2016-02-01.yang
new file mode 100644 (file)
index 0000000..26049d4
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.5.5";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/bar@2016-02-02.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/bar@2016-02-02.yang
new file mode 100644 (file)
index 0000000..0bf5091
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-02" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "5.5.6";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/foo.yang
new file mode 100644 (file)
index 0000000..4edb32e
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; oc-ext:openconfig-version "5.5.5"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/multiple/multiple-valid-specified/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/bar@2016-04-04.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/bar@2016-04-04.yang
new file mode 100644 (file)
index 0000000..5e70700
--- /dev/null
@@ -0,0 +1,18 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+    
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision 2016-04-04 {
+        description "test";
+    }
+    
+    oc-ext:openconfig-version "2016.4.4";
+
+    grouping grp {
+        container con20160404 {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/bar@2016-04-05.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/bar@2016-04-05.yang
new file mode 100644 (file)
index 0000000..97faa03
--- /dev/null
@@ -0,0 +1,20 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+    
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision 2016-04-05 {
+        description "test";
+    }
+    
+    oc-ext:openconfig-version "2016.4.5";
+
+    grouping grp {
+        container con20160404 {
+        }
+        container con20160405 {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/bar@2016-04-06.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/bar@2016-04-06.yang
new file mode 100644 (file)
index 0000000..d2bd0f3
--- /dev/null
@@ -0,0 +1,22 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    
+    revision 2016-04-06 {
+        description "test";
+    }
+    
+    oc-ext:openconfig-version "2016.4.6";
+
+    grouping grp {
+        container con20160404 {
+        }
+        container con20160405 {
+        }
+        container con20160406 {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/bar@2017-04-06.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/bar@2017-04-06.yang
new file mode 100644 (file)
index 0000000..bff38fc
--- /dev/null
@@ -0,0 +1,24 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    
+    revision 2017-04-06 {
+        description "test";
+    }
+    
+    oc-ext:openconfig-version "2017.4.6";
+
+    grouping grp {
+        container con20160404 {
+        }
+        container con20160405 {
+        }
+        container con20160406 {
+        }
+        container con20170406 {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/foo.yang
new file mode 100644 (file)
index 0000000..c723cc5
--- /dev/null
@@ -0,0 +1,18 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; revision-date 2017-04-06; oc-ext:openconfig-version "2016.4.5"; }
+
+    revision 2016-01-01 {
+        description "test";
+    }
+    
+    oc-ext:openconfig-version "2016.1.1";
+
+    container foo-root {
+        uses bar:grp;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/node-test/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-head/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-head/bar.yang
new file mode 100644 (file)
index 0000000..9e6429c
--- /dev/null
@@ -0,0 +1,16 @@
+module bar {
+    oc-ext:openconfig-version "0.1.2";
+
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-head/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-head/foo.yang
new file mode 100644 (file)
index 0000000..41b5fdf
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    oc-ext:openconfig-version "0.1.1";
+    import bar { prefix bar; revision-date 2016-01-31; oc-ext:openconfig-version "0.1.2";}
+
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-head/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-head/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..43c5f3a
--- /dev/null
@@ -0,0 +1,83 @@
+module openconfig-extensions {
+    oc-ext:openconfig-version "0.0.1";
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-middle/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-middle/bar.yang
new file mode 100644 (file)
index 0000000..b973ab3
--- /dev/null
@@ -0,0 +1,15 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.2";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-middle/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-middle/foo.yang
new file mode 100644 (file)
index 0000000..2b37c24
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+    import bar { prefix bar; revision-date 2016-01-31; oc-ext:openconfig-version "0.1.2";}
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "0.1.1";
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-middle/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-middle/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..23e90e3
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-tail/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-tail/bar.yang
new file mode 100644 (file)
index 0000000..65b82d4
--- /dev/null
@@ -0,0 +1,16 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-01-31" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+
+    oc-ext:openconfig-version "0.1.2";
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-tail/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-tail/foo.yang
new file mode 100644 (file)
index 0000000..005ef0b
--- /dev/null
@@ -0,0 +1,17 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; revision-date 2017-01-29; oc-ext:openconfig-version "0.0.1"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+
+    container root {
+    }
+
+    oc-ext:openconfig-version "0.1.1";
+    import bar { prefix bar; revision-date 2016-01-31; oc-ext:openconfig-version "0.1.2";}
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-tail/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/position/position-tail/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..40d9fb9
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+
+    oc-ext:openconfig-version "0.0.1";
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import-invalid/bar@2016-01-01.yin b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import-invalid/bar@2016-01-01.yin
new file mode 100644 (file)
index 0000000..60c968d
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:bar="bar"
+        xmlns:oc-ext="http://openconfig.net/yang/openconfig-ext" name="bar">
+    <yang-version value="1"></yang-version>
+    <namespace uri="bar"></namespace>
+    <prefix value="bar"></prefix>
+    <import module="openconfig-extensions">
+        <prefix value="oc-ext"></prefix>
+        <revision-date date="2016-02-02"></revision-date>
+        <oc-ext:openconfig-version semver="0.0.1"></oc-ext:openconfig-version>
+    </import>
+    <organization>
+        <text></text>
+    </organization>
+    <contact>
+        <text></text>
+    </contact>
+    <revision date="2016-01-01"></revision>
+    <oc-ext:openconfig-version semver="1.1.2"></oc-ext:openconfig-version>
+    <container name="root">
+        <presence value="false"></presence>
+        <config value="true"></config>
+        <status value="current"></status>
+    </container>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import-invalid/bar@2016-01-31.yin b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import-invalid/bar@2016-01-31.yin
new file mode 100644 (file)
index 0000000..31f989b
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:bar="bar"
+        xmlns:oc-ext="http://openconfig.net/yang/openconfig-ext" name="bar">
+    <yang-version value="1"></yang-version>
+    <namespace uri="bar"></namespace>
+    <prefix value="bar"></prefix>
+    <import module="openconfig-extensions">
+        <prefix value="oc-ext"></prefix>
+        <revision-date date="2016-02-02"></revision-date>
+        <oc-ext:openconfig-version semver="0.0.1"></oc-ext:openconfig-version>
+    </import>
+    <organization>
+        <text></text>
+    </organization>
+    <contact>
+        <text></text>
+    </contact>
+    <revision date="2016-01-31"></revision>
+    <oc-ext:openconfig-version semver="1.1.3"></oc-ext:openconfig-version>
+    <container name="root">
+        <presence value="false"></presence>
+        <config value="true"></config>
+        <status value="current"></status>
+    </container>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import-invalid/foo@2016-02-01.yin b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import-invalid/foo@2016-02-01.yin
new file mode 100644 (file)
index 0000000..3183859
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:foo="foo" xmlns:bar="bar"
+        xmlns:oc-ext="http://openconfig.net/yang/openconfig-ext" name="foo">
+    <yang-version value="1"></yang-version>
+    <namespace uri="foo"></namespace>
+    <prefix value="foo"></prefix>
+    <import module="bar">
+        <prefix value="bar"></prefix>
+        <revision-date date="2016-01-31"></revision-date>
+        <oc-ext:openconfig-version semver="0.1.2"></oc-ext:openconfig-version>
+    </import>
+    <import module="openconfig-extensions">
+        <prefix value="oc-ext"></prefix>
+        <revision-date date="2016-02-02"></revision-date>
+        <oc-ext:openconfig-version semver="0.0.1"></oc-ext:openconfig-version>
+    </import>
+    <organization>
+        <text></text>
+    </organization>
+    <contact>
+        <text></text>
+    </contact>
+    <revision date="2016-02-01"></revision>
+    <oc-ext:openconfig-version semver="0.1.1"></oc-ext:openconfig-version>
+    <container name="root">
+        <presence value="false"></presence>
+        <config value="true"></config>
+        <status value="current"></status>
+    </container>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import-invalid/openconfig-extensions@2017-01-29.yin b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import-invalid/openconfig-extensions@2017-01-29.yin
new file mode 100644 (file)
index 0000000..e93a5a7
--- /dev/null
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:oc-ext="http://openconfig.net/yang/openconfig-ext"
+        name="openconfig-extensions">
+    <yang-version value="1"></yang-version>
+    <namespace uri="http://openconfig.net/yang/openconfig-ext"></namespace>
+    <prefix value="oc-ext"></prefix>
+    <organization>
+        <text>OpenConfig working group</text>
+    </organization>
+    <contact>
+        <text>OpenConfig working group www.openconfig.net</text>
+    </contact>
+    <description>
+        <text>
+            This module provides extensions to the YANG language to allow
+            OpenConfig specific functionality and meta-data to be defined.
+        </text>
+    </description>
+    <revision date="2017-01-29">
+        <description>
+            <text>Added extension for annotating encrypted values.</text>
+        </description>
+        <reference>
+            <text>TBD</text>
+        </reference>
+    </revision>
+    <revision date="2015-10-09">
+        <description>
+            <text>Initial OpenConfig public release</text>
+        </description>
+        <reference>
+            <text>TBD</text>
+        </reference>
+    </revision>
+    <revision date="2015-10-05">
+        <description>
+            <text>Initial revision</text>
+        </description>
+        <reference>
+            <text>TBD</text>
+        </reference>
+    </revision>
+    <oc-ext:openconfig-version semver="0.0.1"></oc-ext:openconfig-version>
+    <extension name="openconfig-version">
+        <argument name="semver">
+            <yin-element value="false"></yin-element>
+        </argument>
+        <status value="current"></status>
+        <description>
+            <text>The OpenConfig version number for the module. This is
+                expressed as a semantic version number of the form:
+                x.y.z
+                where:
+                * x corresponds to the major version,
+                * y corresponds to a minor version,
+                * z corresponds to a patch version.
+                This version corresponds to the model file within which it is
+                defined, and does not cover the whole set of OpenConfig models.
+                Where several modules are used to build up a single block of
+                functionality, the same module version is specified across each
+                file that makes up the module.
+
+                A major version number of 0 indicates that this model is still
+                in development (whether within OpenConfig or with industry
+                partners), and is potentially subject to change.
+
+                Following a release of major version 1, all modules will
+                increment major revision number where backwards incompatible
+                changes to the model are made.
+
+                The minor version is changed when features are added to the
+                model that do not impact current clients use of the model.
+
+                The patch-level version is incremented when non-feature changes
+                (such as bugfixes or clarifications to human-readable
+                descriptions that do not impact model functionality) are made
+                that maintain backwards compatibility.
+
+                The version number is stored in the module meta-data.
+            </text>
+        </description>
+    </extension>
+    <extension name="openconfig-encrypted-value">
+        <description>
+            <text>
+                This extension provides an annotation on schema nodes to
+                indicate that the corresponding value should be stored and
+                reported in encrypted form.
+                Clients reading the configuration or applied configuration
+                for the node should expect to receive only the encrypted value.
+                This annotation may be used on nodes such as secure passwords
+                in which the device never reports a cleartext value, even
+                if the input is provided as cleartext.
+            </text>
+        </description>
+    </extension>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import/bar@2016-01-01.yin b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import/bar@2016-01-01.yin
new file mode 100644 (file)
index 0000000..a3e08f1
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:bar="bar"
+        xmlns:oc-ext="http://openconfig.net/yang/openconfig-ext" name="bar">
+    <yang-version value="1"></yang-version>
+    <namespace uri="bar"></namespace>
+    <prefix value="bar"></prefix>
+    <import module="openconfig-extensions">
+        <prefix value="oc-ext"></prefix>
+        <revision-date date="2016-02-02"></revision-date>
+        <oc-ext:openconfig-version semver="0.0.1"></oc-ext:openconfig-version>
+    </import>
+    <organization>
+        <text></text>
+    </organization>
+    <contact>
+        <text></text>
+    </contact>
+    <revision date="2016-01-01"></revision>
+    <oc-ext:openconfig-version semver="0.1.2"></oc-ext:openconfig-version>
+    <container name="root">
+        <presence value="false"></presence>
+        <config value="true"></config>
+        <status value="current"></status>
+    </container>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import/bar@2016-01-31.yin b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import/bar@2016-01-31.yin
new file mode 100644 (file)
index 0000000..959624b
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:bar="bar"
+        xmlns:oc-ext="http://openconfig.net/yang/openconfig-ext" name="bar">
+    <yang-version value="1"></yang-version>
+    <namespace uri="bar"></namespace>
+    <prefix value="bar"></prefix>
+    <import module="openconfig-extensions">
+        <prefix value="oc-ext"></prefix>
+        <revision-date date="2016-02-02"></revision-date>
+        <oc-ext:openconfig-version semver="0.0.1"></oc-ext:openconfig-version>
+    </import>
+    <organization>
+        <text></text>
+    </organization>
+    <contact>
+        <text></text>
+    </contact>
+    <revision date="2016-01-31"></revision>
+    <oc-ext:openconfig-version semver="1.1.2"></oc-ext:openconfig-version>
+    <container name="root">
+        <presence value="false"></presence>
+        <config value="true"></config>
+        <status value="current"></status>
+    </container>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import/foo@2016-02-01.yin b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import/foo@2016-02-01.yin
new file mode 100644 (file)
index 0000000..3183859
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:foo="foo" xmlns:bar="bar"
+        xmlns:oc-ext="http://openconfig.net/yang/openconfig-ext" name="foo">
+    <yang-version value="1"></yang-version>
+    <namespace uri="foo"></namespace>
+    <prefix value="foo"></prefix>
+    <import module="bar">
+        <prefix value="bar"></prefix>
+        <revision-date date="2016-01-31"></revision-date>
+        <oc-ext:openconfig-version semver="0.1.2"></oc-ext:openconfig-version>
+    </import>
+    <import module="openconfig-extensions">
+        <prefix value="oc-ext"></prefix>
+        <revision-date date="2016-02-02"></revision-date>
+        <oc-ext:openconfig-version semver="0.0.1"></oc-ext:openconfig-version>
+    </import>
+    <organization>
+        <text></text>
+    </organization>
+    <contact>
+        <text></text>
+    </contact>
+    <revision date="2016-02-01"></revision>
+    <oc-ext:openconfig-version semver="0.1.1"></oc-ext:openconfig-version>
+    <container name="root">
+        <presence value="false"></presence>
+        <config value="true"></config>
+        <status value="current"></status>
+    </container>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import/openconfig-extensions@2016-01-29.yin b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic-import/openconfig-extensions@2016-01-29.yin
new file mode 100644 (file)
index 0000000..e93a5a7
--- /dev/null
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:oc-ext="http://openconfig.net/yang/openconfig-ext"
+        name="openconfig-extensions">
+    <yang-version value="1"></yang-version>
+    <namespace uri="http://openconfig.net/yang/openconfig-ext"></namespace>
+    <prefix value="oc-ext"></prefix>
+    <organization>
+        <text>OpenConfig working group</text>
+    </organization>
+    <contact>
+        <text>OpenConfig working group www.openconfig.net</text>
+    </contact>
+    <description>
+        <text>
+            This module provides extensions to the YANG language to allow
+            OpenConfig specific functionality and meta-data to be defined.
+        </text>
+    </description>
+    <revision date="2017-01-29">
+        <description>
+            <text>Added extension for annotating encrypted values.</text>
+        </description>
+        <reference>
+            <text>TBD</text>
+        </reference>
+    </revision>
+    <revision date="2015-10-09">
+        <description>
+            <text>Initial OpenConfig public release</text>
+        </description>
+        <reference>
+            <text>TBD</text>
+        </reference>
+    </revision>
+    <revision date="2015-10-05">
+        <description>
+            <text>Initial revision</text>
+        </description>
+        <reference>
+            <text>TBD</text>
+        </reference>
+    </revision>
+    <oc-ext:openconfig-version semver="0.0.1"></oc-ext:openconfig-version>
+    <extension name="openconfig-version">
+        <argument name="semver">
+            <yin-element value="false"></yin-element>
+        </argument>
+        <status value="current"></status>
+        <description>
+            <text>The OpenConfig version number for the module. This is
+                expressed as a semantic version number of the form:
+                x.y.z
+                where:
+                * x corresponds to the major version,
+                * y corresponds to a minor version,
+                * z corresponds to a patch version.
+                This version corresponds to the model file within which it is
+                defined, and does not cover the whole set of OpenConfig models.
+                Where several modules are used to build up a single block of
+                functionality, the same module version is specified across each
+                file that makes up the module.
+
+                A major version number of 0 indicates that this model is still
+                in development (whether within OpenConfig or with industry
+                partners), and is potentially subject to change.
+
+                Following a release of major version 1, all modules will
+                increment major revision number where backwards incompatible
+                changes to the model are made.
+
+                The minor version is changed when features are added to the
+                model that do not impact current clients use of the model.
+
+                The patch-level version is incremented when non-feature changes
+                (such as bugfixes or clarifications to human-readable
+                descriptions that do not impact model functionality) are made
+                that maintain backwards compatibility.
+
+                The version number is stored in the module meta-data.
+            </text>
+        </description>
+    </extension>
+    <extension name="openconfig-encrypted-value">
+        <description>
+            <text>
+                This extension provides an annotation on schema nodes to
+                indicate that the corresponding value should be stored and
+                reported in encrypted form.
+                Clients reading the configuration or applied configuration
+                for the node should expect to receive only the encrypted value.
+                This annotation may be used on nodes such as secure passwords
+                in which the device never reports a cleartext value, even
+                if the input is provided as cleartext.
+            </text>
+        </description>
+    </extension>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic/bar@2016-01-31.yin b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic/bar@2016-01-31.yin
new file mode 100644 (file)
index 0000000..a3a644f
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:bar="bar"
+        xmlns:oc-ext="http://openconfig.net/yang/openconfig-ext" name="bar">
+    <yang-version value="1"></yang-version>
+    <namespace uri="bar"></namespace>
+    <prefix value="bar"></prefix>
+    <import module="openconfig-extensions">
+        <prefix value="oc-ext"></prefix>
+        <revision-date date="2017-01-29"></revision-date>
+        <oc-ext:openconfig-version semver="0.0.1"></oc-ext:openconfig-version>
+    </import>
+    <organization>
+        <text></text>
+    </organization>
+    <contact>
+        <text></text>
+    </contact>
+    <revision date="2016-01-31"></revision>
+    <oc-ext:openconfig-version semver="0.1.2"></oc-ext:openconfig-version>
+    <container name="root">
+        <presence value="false"></presence>
+        <config value="true"></config>
+        <status value="current"></status>
+    </container>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic/foo@2016-02-01.yin b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic/foo@2016-02-01.yin
new file mode 100644 (file)
index 0000000..663286d
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:foo="foo" xmlns:bar="bar"
+        xmlns:oc-ext="http://openconfig.net/yang/openconfig-ext" name="foo">
+    <yang-version value="1"></yang-version>
+    <namespace uri="foo"></namespace>
+    <prefix value="foo"></prefix>
+    <import module="bar">
+        <prefix value="bar"></prefix>
+        <revision-date date="2016-01-31"></revision-date>
+        <oc-ext:openconfig-version semver="0.1.2"></oc-ext:openconfig-version>
+    </import>
+    <import module="openconfig-extensions">
+        <prefix value="oc-ext"></prefix>
+        <revision-date date="2017-01-29"></revision-date>
+        <oc-ext:openconfig-version semver="0.0.1"></oc-ext:openconfig-version>
+    </import>
+    <organization>
+        <text></text>
+    </organization>
+    <contact>
+        <text></text>
+    </contact>
+    <revision date="2016-02-01"></revision>
+    <oc-ext:openconfig-version semver="0.1.1"></oc-ext:openconfig-version>
+    <container name="root">
+        <presence value="false"></presence>
+        <config value="true"></config>
+        <status value="current"></status>
+    </container>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic/openconfig-extensions@2017-01-29.yin b/yang/yang-parser-rfc7950/src/test/resources/openconfig-version/yin-input/basic/openconfig-extensions@2017-01-29.yin
new file mode 100644 (file)
index 0000000..e93a5a7
--- /dev/null
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" xmlns:oc-ext="http://openconfig.net/yang/openconfig-ext"
+        name="openconfig-extensions">
+    <yang-version value="1"></yang-version>
+    <namespace uri="http://openconfig.net/yang/openconfig-ext"></namespace>
+    <prefix value="oc-ext"></prefix>
+    <organization>
+        <text>OpenConfig working group</text>
+    </organization>
+    <contact>
+        <text>OpenConfig working group www.openconfig.net</text>
+    </contact>
+    <description>
+        <text>
+            This module provides extensions to the YANG language to allow
+            OpenConfig specific functionality and meta-data to be defined.
+        </text>
+    </description>
+    <revision date="2017-01-29">
+        <description>
+            <text>Added extension for annotating encrypted values.</text>
+        </description>
+        <reference>
+            <text>TBD</text>
+        </reference>
+    </revision>
+    <revision date="2015-10-09">
+        <description>
+            <text>Initial OpenConfig public release</text>
+        </description>
+        <reference>
+            <text>TBD</text>
+        </reference>
+    </revision>
+    <revision date="2015-10-05">
+        <description>
+            <text>Initial revision</text>
+        </description>
+        <reference>
+            <text>TBD</text>
+        </reference>
+    </revision>
+    <oc-ext:openconfig-version semver="0.0.1"></oc-ext:openconfig-version>
+    <extension name="openconfig-version">
+        <argument name="semver">
+            <yin-element value="false"></yin-element>
+        </argument>
+        <status value="current"></status>
+        <description>
+            <text>The OpenConfig version number for the module. This is
+                expressed as a semantic version number of the form:
+                x.y.z
+                where:
+                * x corresponds to the major version,
+                * y corresponds to a minor version,
+                * z corresponds to a patch version.
+                This version corresponds to the model file within which it is
+                defined, and does not cover the whole set of OpenConfig models.
+                Where several modules are used to build up a single block of
+                functionality, the same module version is specified across each
+                file that makes up the module.
+
+                A major version number of 0 indicates that this model is still
+                in development (whether within OpenConfig or with industry
+                partners), and is potentially subject to change.
+
+                Following a release of major version 1, all modules will
+                increment major revision number where backwards incompatible
+                changes to the model are made.
+
+                The minor version is changed when features are added to the
+                model that do not impact current clients use of the model.
+
+                The patch-level version is incremented when non-feature changes
+                (such as bugfixes or clarifications to human-readable
+                descriptions that do not impact model functionality) are made
+                that maintain backwards compatibility.
+
+                The version number is stored in the module meta-data.
+            </text>
+        </description>
+    </extension>
+    <extension name="openconfig-encrypted-value">
+        <description>
+            <text>
+                This extension provides an annotation on schema nodes to
+                indicate that the corresponding value should be stored and
+                reported in encrypted form.
+                Clients reading the configuration or applied configuration
+                for the node should expect to receive only the encrypted value.
+                This annotation may be used on nodes such as secure passwords
+                in which the device never reports a cleartext value, even
+                if the input is provided as cleartext.
+            </text>
+        </description>
+    </extension>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/ordering/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/ordering/foo.yang
new file mode 100644 (file)
index 0000000..6854e33
--- /dev/null
@@ -0,0 +1,49 @@
+module foo {
+    yang-version 1;
+    namespace "urn:opendaylight.foo";
+    prefix "foo";
+
+    revision "2015-07-08" {
+    }
+
+    container x {
+        leaf x15 {
+            type string;
+        }
+
+        list x10 {}
+
+        container x5 {}
+
+        leaf-list x1 {
+            type string;
+        }
+
+        uses a;
+
+        container x2 {}
+
+        uses b;
+
+        container x3 {}
+    }
+
+
+    grouping a {
+        anyxml a5;
+        anyxml a1;
+    }
+
+    grouping b {
+        anyxml b5;
+        anyxml b1;
+    }
+
+    augment /x  {
+        leaf ax15 {
+            type string;
+        }
+        container ax5 {}
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m2@2013-09-30.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m2@2013-09-30.yang
new file mode 100644 (file)
index 0000000..d541875
--- /dev/null
@@ -0,0 +1,17 @@
+module m2 {
+
+    yang-version 1;
+    namespace "urn:test.m2";
+    prefix "m2";
+
+    import m4 {
+        prefix "m4";
+    }
+    import m5 {
+        prefix "m5";
+    }
+
+    revision 2013-09-30 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m2@2013-10-01.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m2@2013-10-01.yang
new file mode 100644 (file)
index 0000000..1eb8f26
--- /dev/null
@@ -0,0 +1,17 @@
+module m2 {
+
+    yang-version 1;
+    namespace "urn:test.m2";
+    prefix "m2";
+
+    import m4 {
+        prefix "m4";
+    }
+    import m5 {
+        prefix "m5";
+    }
+
+     revision 2013-10-01 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m3.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m3.yang
new file mode 100644 (file)
index 0000000..ffa687a
--- /dev/null
@@ -0,0 +1,17 @@
+module m3 {
+
+    yang-version 1;
+    namespace "urn:test.m3";
+    prefix "m3";
+
+    import m4 {
+        prefix "m4";
+    }
+    import m6 {
+        prefix "m6";
+    }
+
+     revision 2013-09-30 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m4.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m4.yang
new file mode 100644 (file)
index 0000000..f4ccdc6
--- /dev/null
@@ -0,0 +1,10 @@
+module m4 {
+
+    yang-version 1;
+    namespace "urn:test.m4";
+    prefix "m4";
+
+     revision 2013-09-30 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m5.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m5.yang
new file mode 100644 (file)
index 0000000..88fc73d
--- /dev/null
@@ -0,0 +1,10 @@
+module m5 {
+
+    yang-version 1;
+    namespace "urn:test.m5";
+    prefix "m5";
+
+     revision 2012-04-16 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m6.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/m6.yang
new file mode 100644 (file)
index 0000000..70dd8d6
--- /dev/null
@@ -0,0 +1,10 @@
+module m6 {
+
+    yang-version 1;
+    namespace "urn:test.m6";
+    prefix "m6";
+
+     revision 2012-04-16 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/unused.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/dependencies/unused.yang
new file mode 100644 (file)
index 0000000..778043a
--- /dev/null
@@ -0,0 +1,17 @@
+module unused {
+
+    yang-version 1;
+    namespace "urn:test.unused";
+    prefix "u";
+
+    import m2 {
+        prefix "m2";
+    }
+    import m3 {
+        prefix "m3";
+    }
+
+     revision 2012-04-16 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m1.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m1.yang
new file mode 100644 (file)
index 0000000..1a92628
--- /dev/null
@@ -0,0 +1,18 @@
+module m1 {
+
+    yang-version 1;
+    namespace "urn:test.m1";
+    prefix "m1";
+
+    import m2 {
+        prefix "m2";
+        revision-date 2013-09-30;
+    }
+    import m3 {
+        prefix "m3";
+    }
+
+    revision 2013-10-16 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m2@2013-09-30.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m2@2013-09-30.yang
new file mode 100644 (file)
index 0000000..d541875
--- /dev/null
@@ -0,0 +1,17 @@
+module m2 {
+
+    yang-version 1;
+    namespace "urn:test.m2";
+    prefix "m2";
+
+    import m4 {
+        prefix "m4";
+    }
+    import m5 {
+        prefix "m5";
+    }
+
+    revision 2013-09-30 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m2@2013-10-01.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m2@2013-10-01.yang
new file mode 100644 (file)
index 0000000..1eb8f26
--- /dev/null
@@ -0,0 +1,17 @@
+module m2 {
+
+    yang-version 1;
+    namespace "urn:test.m2";
+    prefix "m2";
+
+    import m4 {
+        prefix "m4";
+    }
+    import m5 {
+        prefix "m5";
+    }
+
+     revision 2013-10-01 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m3.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m3.yang
new file mode 100644 (file)
index 0000000..ffa687a
--- /dev/null
@@ -0,0 +1,17 @@
+module m3 {
+
+    yang-version 1;
+    namespace "urn:test.m3";
+    prefix "m3";
+
+    import m4 {
+        prefix "m4";
+    }
+    import m6 {
+        prefix "m6";
+    }
+
+     revision 2013-09-30 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m4.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m4.yang
new file mode 100644 (file)
index 0000000..f4ccdc6
--- /dev/null
@@ -0,0 +1,10 @@
+module m4 {
+
+    yang-version 1;
+    namespace "urn:test.m4";
+    prefix "m4";
+
+     revision 2013-09-30 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m5.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m5.yang
new file mode 100644 (file)
index 0000000..88fc73d
--- /dev/null
@@ -0,0 +1,10 @@
+module m5 {
+
+    yang-version 1;
+    namespace "urn:test.m5";
+    prefix "m5";
+
+     revision 2012-04-16 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m6.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/m6.yang
new file mode 100644 (file)
index 0000000..70dd8d6
--- /dev/null
@@ -0,0 +1,10 @@
+module m6 {
+
+    yang-version 1;
+    namespace "urn:test.m6";
+    prefix "m6";
+
+     revision 2012-04-16 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/parse-methods/unused.yang b/yang/yang-parser-rfc7950/src/test/resources/parse-methods/unused.yang
new file mode 100644 (file)
index 0000000..778043a
--- /dev/null
@@ -0,0 +1,17 @@
+module unused {
+
+    yang-version 1;
+    namespace "urn:test.unused";
+    prefix "u";
+
+    import m2 {
+        prefix "m2";
+    }
+    import m3 {
+        prefix "m3";
+    }
+
+     revision 2012-04-16 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/plugin-test/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/plugin-test/foo.yang
new file mode 100644 (file)
index 0000000..f9fd922
--- /dev/null
@@ -0,0 +1,15 @@
+module foo {
+    namespace "urn:opendaylight:yang:extension:third-party";
+    prefix foo;
+    yang-version 1;
+
+    revision 2016-06-09;
+
+    container root {
+        foo:third-party-extension "plugin test";
+    }
+
+    extension third-party-extension {
+        argument argument-name;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/revisions/equal-rev.yang b/yang/yang-parser-rfc7950/src/test/resources/revisions/equal-rev.yang
new file mode 100644 (file)
index 0000000..4b4a752
--- /dev/null
@@ -0,0 +1,7 @@
+submodule equal-rev {
+    belongs-to "equal-root" {
+        prefix equal-root;
+    }
+
+    revision "2001-05-19";
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/revisions/equal-root.yang b/yang/yang-parser-rfc7950/src/test/resources/revisions/equal-root.yang
new file mode 100644 (file)
index 0000000..6ce1a06
--- /dev/null
@@ -0,0 +1,8 @@
+module equal-root {
+    namespace equal-root;
+    prefix equal-root;
+
+    include equal-rev {
+        revision-date "2001-05-19";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/revisions/mod-only-rev.yang b/yang/yang-parser-rfc7950/src/test/resources/revisions/mod-only-rev.yang
new file mode 100644 (file)
index 0000000..37e996c
--- /dev/null
@@ -0,0 +1,5 @@
+submodule mod-only-rev {
+    belongs-to "mod-only-root" {
+        prefix mod-only-root;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/revisions/mod-only-root.yang b/yang/yang-parser-rfc7950/src/test/resources/revisions/mod-only-root.yang
new file mode 100644 (file)
index 0000000..ff71d51
--- /dev/null
@@ -0,0 +1,8 @@
+module mod-only-root {
+    namespace mod-only-root;
+    prefix mod-only-root;
+
+    include mod-only-rev {
+        revision-date "2001-05-19";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/revisions/nowhere-rev.yang b/yang/yang-parser-rfc7950/src/test/resources/revisions/nowhere-rev.yang
new file mode 100644 (file)
index 0000000..d6525bf
--- /dev/null
@@ -0,0 +1,5 @@
+submodule nowhere-rev {
+    belongs-to "nowhere-root" {
+        prefix nowhere-root;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/revisions/nowhere-root.yang b/yang/yang-parser-rfc7950/src/test/resources/revisions/nowhere-root.yang
new file mode 100644 (file)
index 0000000..fb5460f
--- /dev/null
@@ -0,0 +1,6 @@
+module nowhere-root {
+    namespace nowhere-root;
+    prefix nowhere-root;
+
+    include nowhere-rev;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/revisions/submod-only-rev.yang b/yang/yang-parser-rfc7950/src/test/resources/revisions/submod-only-rev.yang
new file mode 100644 (file)
index 0000000..3c92a91
--- /dev/null
@@ -0,0 +1,7 @@
+submodule submod-only-rev {
+    belongs-to "submod-only-root" {
+        prefix submod-only-root;
+    }
+
+    revision "2001-05-19";
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/revisions/submod-only-root.yang b/yang/yang-parser-rfc7950/src/test/resources/revisions/submod-only-root.yang
new file mode 100644 (file)
index 0000000..3743a23
--- /dev/null
@@ -0,0 +1,6 @@
+module submod-only-root {
+    namespace submod-only-root;
+    prefix submod-only-root;
+
+    include submod-only-rev;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/revisions/unequal-rev.yang b/yang/yang-parser-rfc7950/src/test/resources/revisions/unequal-rev.yang
new file mode 100644 (file)
index 0000000..04a322c
--- /dev/null
@@ -0,0 +1,7 @@
+submodule unequal-rev {
+    belongs-to "unequal-root" {
+        prefix unequal-root;
+    }
+
+    revision "2001-05-20";
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/revisions/unequal-root.yang b/yang/yang-parser-rfc7950/src/test/resources/revisions/unequal-root.yang
new file mode 100644 (file)
index 0000000..0965750
--- /dev/null
@@ -0,0 +1,8 @@
+module unequal-root {
+    namespace unequal-root;
+    prefix unequal-root;
+
+    include unequal-rev {
+        revision-date "2001-05-19";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/bar-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/bar-invalid.yang
new file mode 100644 (file)
index 0000000..666bbaf
--- /dev/null
@@ -0,0 +1,23 @@
+module bar-invalid {
+    namespace bar-namespace;
+    prefix bar-prefix;
+    yang-version 1.1;
+
+    revision 2016-12-13;
+
+    list my-unkeyed-list {
+        container my-container {
+            list my-keyed-list {
+                key key-leaf;
+
+                leaf key-leaf {
+                    type string;
+                }
+
+                action my-action {
+
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/baz-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/baz-invalid.yang
new file mode 100644 (file)
index 0000000..2c37b3f
--- /dev/null
@@ -0,0 +1,26 @@
+module baz-invalid {
+    namespace baz-namespace;
+    prefix baz-prefix;
+    yang-version 1.1;
+
+    revision 2016-12-13;
+
+    container my-container {
+        choice my-choice {
+            case first {
+                leaf first-leaf {
+                    type string;
+                }
+            }
+            case second {
+                uses grouping-with-action;
+            }
+        }
+    }
+
+    grouping grouping-with-action {
+        action action-in-grouping {
+
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/foo-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/foo-invalid.yang
new file mode 100644 (file)
index 0000000..3ada723
--- /dev/null
@@ -0,0 +1,27 @@
+module foo-invalid {
+    namespace foo-namespace;
+    prefix foo-prefix;
+    yang-version 1.1;
+
+    revision 2016-12-13;
+
+    rpc my-rpc {
+        input {
+            container in {
+            }
+        }
+        output {
+            container out {
+            }
+        }
+    }
+
+    grouping grouping-with-action {
+        action action-in-grouping {
+        }
+    }
+
+    augment "/my-rpc/input/in" {
+        uses grouping-with-action;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/foo.yang
new file mode 100644 (file)
index 0000000..e7b22a6
--- /dev/null
@@ -0,0 +1,37 @@
+module foo {
+    namespace foo-namespace;
+    prefix foo-prefix;
+    yang-version 1.1;
+
+    revision 2016-12-13;
+
+    container root {
+    }
+
+    container top {
+        action top-action {
+        }
+    }
+
+    list top-list {
+        key key-leaf;
+
+        leaf key-leaf {
+            type string;
+        }
+
+        action top-list-action {
+        }
+    }
+
+    grouping grp {
+        action grp-action {
+        }
+    }
+
+    augment "/root" {
+        uses grp;
+        action aug-action {
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/foo10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/foo10.yang
new file mode 100644 (file)
index 0000000..83b2fe4
--- /dev/null
@@ -0,0 +1,26 @@
+module foo10 {
+    namespace foo10-namespace;
+    prefix foo10-prefix;
+    yang-version 1;
+
+    revision 2012-12-13;
+
+    container root {
+    }
+
+    list top-list {
+        action top-list-action {
+        }
+    }
+
+    grouping grp {
+        action grp-action {
+        }
+    }
+
+    augment "/root" {
+        uses grp;
+        action aug-action {
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/foobar-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/action-stmt/foobar-invalid.yang
new file mode 100644 (file)
index 0000000..9de6018
--- /dev/null
@@ -0,0 +1,23 @@
+module foobar-invalid {
+    namespace foobar-namespace;
+    prefix foobar-prefix;
+    yang-version 1.1;
+
+    revision 2016-12-13;
+
+    uses grouping-with-action;
+
+    grouping grouping-with-action {
+        action my-action {
+            input {
+                container in {
+                }
+            }
+            output {
+                container out {
+                }
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/anydata-10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/anydata-10.yang
new file mode 100644 (file)
index 0000000..7bbeac5
--- /dev/null
@@ -0,0 +1,11 @@
+module anydata-10 {
+    namespace "anydata-10";
+    prefix a;
+    yang-version 1;
+
+    anydata my-data {
+    }
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/anydata-11.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/anydata-11.yang
new file mode 100644 (file)
index 0000000..7201150
--- /dev/null
@@ -0,0 +1,13 @@
+module anydata-11 {
+    namespace "anydata-11";
+    prefix a;
+    yang-version 1.1;
+
+    anydata my-data {
+    }
+
+    container root {
+        notification root-notification {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/invalid-10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/invalid-10.yang
new file mode 100644 (file)
index 0000000..b0cc012
--- /dev/null
@@ -0,0 +1,9 @@
+module invalid-10 {
+    namespace "invalid-10";
+    prefix v;
+
+    container root {
+        notification root-notification {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/invalid-11.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/invalid-11.yang
new file mode 100644 (file)
index 0000000..8ccc3ad
--- /dev/null
@@ -0,0 +1,14 @@
+module invalid-11 {
+    namespace "invalid-11";
+    prefix v;
+    yang-version 1.1;
+
+    container root {
+        rpc root-rpc {
+            input {
+            }
+            output {
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/unsupported-version.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/unsupported-version.yang
new file mode 100644 (file)
index 0000000..1785f93
--- /dev/null
@@ -0,0 +1,8 @@
+module unsupported-version {
+    namespace "version-test";
+    prefix un;
+    yang-version 2.3;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/valid-10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/valid-10.yang
new file mode 100644 (file)
index 0000000..8cdabef
--- /dev/null
@@ -0,0 +1,7 @@
+module valid-10 {
+    namespace "valid-10";
+    prefix v;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/valid-11.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/basic-test/valid-11.yang
new file mode 100644 (file)
index 0000000..9fa1962
--- /dev/null
@@ -0,0 +1,10 @@
+module valid-11 {
+    namespace "valid-11";
+    prefix v;
+    yang-version 1.1;
+
+    container root {
+        notification root-notification {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6868/invalid10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6868/invalid10.yang
new file mode 100644 (file)
index 0000000..5d78fb6
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    container my-container-1 {
+        if-feature "(not foo) or (bar and baz)";
+    }
+
+    container my-container-2 {
+        if-feature "not foo or bar and baz";
+    }
+
+    container my-container-3 {
+        if-feature "(not foo or bar) and baz)";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6868/yang11/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6868/yang11/foo.yang
new file mode 100644 (file)
index 0000000..0e4b686
--- /dev/null
@@ -0,0 +1,39 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    import imported { prefix imp; revision-date 2017-01-09; }
+
+    feature foo;
+    feature bar;
+    feature baz;
+
+    container my-container-1 {
+        if-feature "(not foo) or (bar and baz)";
+    }
+
+    container my-container-2 {
+        if-feature "not foo or bar and baz";
+    }
+
+    container my-container-3 {
+        if-feature "((not foo or bar) and baz)";
+    }
+
+    container foo {
+        if-feature "foo";
+    }
+
+    container not-foo {
+        if-feature "not foo";
+    }
+
+    container imp-bar {
+        if-feature "imp:bar";
+    }
+
+    container imp-bar-2 {
+        if-feature "imp:bar and not bar";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6868/yang11/imported.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6868/yang11/imported.yang
new file mode 100644 (file)
index 0000000..8c7dfe8
--- /dev/null
@@ -0,0 +1,9 @@
+module imported {
+    namespace "imp";
+    prefix imp;
+    yang-version 1;
+
+    revision "2017-01-09";
+
+    feature bar;
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6869/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6869/foo.yang
new file mode 100644 (file)
index 0000000..d4f0f63
--- /dev/null
@@ -0,0 +1,55 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    identity my-identity {
+        if-feature identity-feature;
+    }
+
+    feature identity-feature;
+    feature mandatory-leaf;
+    feature ssh;
+    feature tls;
+    feature two;
+    feature three;
+
+    container root {
+        uses grp {
+            refine grp-leaf {
+                if-feature mandatory-leaf;
+                mandatory true;
+            }
+        }
+    }
+
+    grouping grp {
+        leaf grp-leaf {
+            type empty;
+        }
+    }
+
+    typedef my-enum {
+        type enumeration {
+            enum tcp;
+            enum ssh {
+                if-feature ssh;
+            }
+            enum tls {
+                if-feature tls;
+            }
+        }
+    }
+
+    typedef my-bits {
+        type bits {
+            bit one;
+            bit two {
+                if-feature two;
+            }
+            bit three {
+                if-feature three;
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6869/invalid10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6869/invalid10.yang
new file mode 100644 (file)
index 0000000..026e9e3
--- /dev/null
@@ -0,0 +1,8 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    identity foo {
+        if-feature foo-identity;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6870/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6870/foo.yang
new file mode 100644 (file)
index 0000000..02752a0
--- /dev/null
@@ -0,0 +1,22 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    container root {
+        leaf my-leaf {
+            type string {
+                pattern '[xX][mM][lL].*' {
+                    modifier invert-match;
+                }
+            }
+        }
+        leaf my-leaf-2 {
+            type string {
+                pattern '[xX][mM][lL].*' {
+                    description "no modifier";
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6870/invalid10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6870/invalid10.yang
new file mode 100644 (file)
index 0000000..ce59371
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    container root {
+        leaf my-leaf {
+            type string {
+                pattern '[xX][mM][lL].*' {
+                    modifier invert-match;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6870/invalid11.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6870/invalid11.yang
new file mode 100644 (file)
index 0000000..60b3672
--- /dev/null
@@ -0,0 +1,15 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    container root {
+        leaf my-leaf {
+            type string {
+                pattern '[xX][mM][lL].*' {
+                    modifier Invert-match;
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6871/bar10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6871/bar10.yang
new file mode 100644 (file)
index 0000000..a1ab5af
--- /dev/null
@@ -0,0 +1,14 @@
+module bar10 {
+    namespace bar10-namespace;
+    prefix bar10-prefix;
+    yang-version 1;
+
+    revision 2016-12-14;
+
+    rpc my-rpc {
+        input {
+            must "bar = foo";
+            must "c != d";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6871/baz10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6871/baz10.yang
new file mode 100644 (file)
index 0000000..25e1640
--- /dev/null
@@ -0,0 +1,14 @@
+module baz10 {
+    namespace baz10-namespace;
+    prefix baz10-prefix;
+    yang-version 1;
+
+    revision 2016-12-14;
+
+    rpc my-rpc {
+        output {
+            must "baz != bar";
+            must "x = y";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6871/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6871/foo.yang
new file mode 100644 (file)
index 0000000..fd48d5f
--- /dev/null
@@ -0,0 +1,24 @@
+module foo {
+    namespace foo-namespace;
+    prefix foo-prefix;
+    yang-version 1.1;
+
+    revision 2016-12-14;
+
+    notification my-notification {
+        must "foo != bar";
+        must "a = b";
+    }
+
+    rpc my-rpc {
+        input {
+            must "bar = foo";
+            must "c != d";
+        }
+
+        output {
+            must "baz != bar";
+            must "x = y";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6871/foo10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6871/foo10.yang
new file mode 100644 (file)
index 0000000..cfbd276
--- /dev/null
@@ -0,0 +1,12 @@
+module foo10 {
+    namespace foo10-namespace;
+    prefix foo10-prefix;
+    yang-version 1;
+
+    revision 2016-12-14;
+
+    notification my-notification {
+        must "foo != bar";
+        must "a = b";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-0/bar@1999-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-0/bar@1999-01-01.yang
new file mode 100644 (file)
index 0000000..d254cad
--- /dev/null
@@ -0,0 +1,11 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 1999-01-01;
+
+    grouping grp {
+        container my-container-1 {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-0/bar@2017-02-06.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-0/bar@2017-02-06.yang
new file mode 100644 (file)
index 0000000..247854e
--- /dev/null
@@ -0,0 +1,11 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-02-06;
+
+    grouping grp {
+        container my-container-2 {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-0/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-0/foo.yang
new file mode 100644 (file)
index 0000000..555b765
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    import bar { prefix bar-1; revision-date 1999-01-01; }
+    import bar { prefix bar-2; revision-date 2017-02-06; }
+
+    container root {
+        uses bar-1:grp;
+        uses bar-2:grp;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-1/bar@1970-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-1/bar@1970-01-01.yang
new file mode 100644 (file)
index 0000000..ad77e4c
--- /dev/null
@@ -0,0 +1,9 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 1970-01-01;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-1/bar@1999-01-01.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-1/bar@1999-01-01.yang
new file mode 100644 (file)
index 0000000..03681fa
--- /dev/null
@@ -0,0 +1,14 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 1999-01-01;
+
+    container root {
+    }
+
+    grouping grp {
+        container my-container-1 {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-1/bar@2017-02-06.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-1/bar@2017-02-06.yang
new file mode 100644 (file)
index 0000000..32661d0
--- /dev/null
@@ -0,0 +1,14 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-02-06;
+
+    container root {
+    }
+
+    grouping grp {
+        container my-container-2 {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-1/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6875/yang1-1/foo.yang
new file mode 100644 (file)
index 0000000..8bb4299
--- /dev/null
@@ -0,0 +1,22 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    import bar { prefix bar-1; revision-date 1999-01-01; }
+    import bar { prefix bar-2; revision-date 2017-02-06; }
+    import bar { prefix bar-3; revision-date 1970-01-01; }
+
+    grouping grp {
+        uses bar-1:grp;
+        uses bar-2:grp;
+    }
+
+    container root {
+        uses grp;
+    }
+
+    augment "/bar-3:root" {
+        uses foo:grp;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6876/yang10/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6876/yang10/bar.yang
new file mode 100644 (file)
index 0000000..79f4de1
--- /dev/null
@@ -0,0 +1,12 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+
+    revision 2017-01-11;
+
+    container augment-target {
+        leaf my-leaf {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6876/yang10/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6876/yang10/foo.yang
new file mode 100644 (file)
index 0000000..d8e2773
--- /dev/null
@@ -0,0 +1,14 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    import bar { prefix bar; revision-date 2017-01-11; }
+
+    augment "/bar:augment-target" {
+        when "bar:my-leaf='x'";
+        leaf mandatory-leaf {
+            mandatory true;
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6876/yang11/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6876/yang11/bar.yang
new file mode 100644 (file)
index 0000000..e68821c
--- /dev/null
@@ -0,0 +1,13 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1.1;
+
+    revision 2017-01-11;
+
+    container augment-target {
+        leaf my-leaf {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6876/yang11/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6876/yang11/foo.yang
new file mode 100644 (file)
index 0000000..28e0716
--- /dev/null
@@ -0,0 +1,15 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    import bar { prefix bar; revision-date 2017-01-11; }
+
+    augment "/bar:augment-target" {
+        when "bar:my-leaf='x'";
+        leaf mandatory-leaf {
+            mandatory true;
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6878/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6878/foo.yang
new file mode 100644 (file)
index 0000000..5a6d3c9
--- /dev/null
@@ -0,0 +1,93 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    revision 2016-02-24;
+
+    identity interface-type;
+
+    identity ethernet {
+        base interface-type;
+    }
+
+    identity fast-ethernet {
+        base ethernet;
+    }
+
+    identity gigabit-ethernet {
+        base ethernet;
+    }
+
+    list interface {
+        key "name";
+        leaf name {
+            type string;
+        }
+
+        leaf type {
+            type identityref {
+                base interface-type;
+            }
+        }
+
+        leaf enabled {
+            type boolean;
+        }
+    }
+
+    leaf outgoing-interface {
+        type leafref {
+            path "/interface/name";
+        }
+
+        must 'count(/interface[re-match(name, "eth0\.\d+")]) = 3';
+    }
+
+    container mgmt-interface {
+        leaf name {
+            type leafref {
+                path "/interface/name";
+            }
+        }
+        leaf type {
+            type leafref {
+                path "/interface[name=current()/../name]/type";
+            }
+            must 'derived-from-or-self(deref(.), "foo:ethernet")';
+        }
+    }
+
+    container my-cont {
+        leaf enum-value {
+            type enumeration {
+                enum a {
+                    value 1;
+                }
+                enum b {
+                    value 2;
+                }
+            }
+        }
+
+        must "enum-value(current()/enum-value) = 1";
+
+        leaf bit-is-set {
+            type bits {
+                bit x;
+                bit y;
+                bit z;
+            }
+        }
+
+        leaf bits-leaf {
+            type bits {
+                bit a;
+                bit b;
+                bit c;
+            }
+        }
+
+        must 'bit-is-set(current()/bit-is-set, "z") and bit-is-set(current()/bits-leaf, "c")';
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6878/foo10-invalid-2.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6878/foo10-invalid-2.yang
new file mode 100644 (file)
index 0000000..08f9d41
--- /dev/null
@@ -0,0 +1,35 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    revision 2017-02-10;
+
+    list interface {
+        key "name type";
+        leaf name {
+            type string;
+        }
+        leaf type {
+            type string;
+        }
+        leaf enabled {
+            type boolean;
+        }
+    }
+
+    container mgmt-interface {
+        leaf name {
+            type leafref {
+                path "/interface/name";
+            }
+        }
+        leaf type {
+            type leafref {
+                path "/interface[name=current()/../name]/type";
+            }
+            must "deref(.)/../enabled = 'true'" {
+                error-message "The management interface cannot be disabled.";
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6878/foo10-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6878/foo10-invalid.yang
new file mode 100644 (file)
index 0000000..25ecbdf
--- /dev/null
@@ -0,0 +1,25 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    revision 2017-02-10;
+
+    list interface {
+        key "name";
+        leaf name {
+            type string;
+        }
+
+        leaf enabled {
+            type boolean;
+        }
+    }
+
+    leaf outgoing-interface {
+        type leafref {
+            path "/interface/name";
+        }
+
+        must "count(/interface[re-match(name, 'eth0\.\d+')]) = 3";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6880/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6880/foo.yang
new file mode 100644 (file)
index 0000000..cbfae88
--- /dev/null
@@ -0,0 +1,11 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    leaf-list my-leaf-list {
+        type string;
+        default "my-default-value-1";
+        default "my-default-value-2";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6880/invalid10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6880/invalid10.yang
new file mode 100644 (file)
index 0000000..e52780d
--- /dev/null
@@ -0,0 +1,10 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    leaf-list my-leaf-list {
+        type string;
+        default "my-default-value-1";
+        default "my-default-value-2";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6883/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6883/foo.yang
new file mode 100644 (file)
index 0000000..5d626a5
--- /dev/null
@@ -0,0 +1,51 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    include sub-foo;
+
+    anydata top {
+        description "top anydata";
+        status deprecated;
+        mandatory true;
+        must "1!=0";
+    }
+
+    container root {
+        anydata root-anydata;
+    }
+
+    list my-list {
+        anydata list-anydata;
+    }
+
+    rpc my-rpc {
+        input {
+            anydata input-anydata;
+        }
+        output {
+            anydata output-anydata;
+        }
+    }
+
+    notification my-notification {
+        anydata notification-anydata;
+    }
+
+    choice my-choice {
+        case one {
+            anydata case-anydata;
+        }
+        anydata case-shorthand-anydata;
+    }
+
+    grouping grp {
+        anydata grp-anydata;
+    }
+
+    augment "/root" {
+        uses grp;
+        anydata aug-anydata;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6883/sub-foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6883/sub-foo.yang
new file mode 100644 (file)
index 0000000..00aaebc
--- /dev/null
@@ -0,0 +1,14 @@
+submodule sub-foo {
+    yang-version 1.1;
+
+    belongs-to foo {
+        prefix foo;
+    }
+
+    anydata sub-data {
+        description "submodule anydata";
+        status current;
+        mandatory true;
+        must "1!=0";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/circular-includes/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/circular-includes/foo.yang
new file mode 100644 (file)
index 0000000..7b576f9
--- /dev/null
@@ -0,0 +1,12 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+
+    include sub-foo {
+        revision-date 2017-02-09;
+    }
+    include sub-foo-2;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/circular-includes/sub-foo-2.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/circular-includes/sub-foo-2.yang
new file mode 100644 (file)
index 0000000..3c61477
--- /dev/null
@@ -0,0 +1,18 @@
+submodule sub-foo-2 {
+    belongs-to foo {
+        prefix foo;
+    }
+
+    include sub-foo {
+        revision-date 2017-02-09;
+    }
+
+    grouping sub-foo-2-grp {
+        container sub-foo-2-con {
+        }
+    }
+
+    container sub-root-2 {
+        uses sub-foo-grp;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/circular-includes/sub-foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/circular-includes/sub-foo.yang
new file mode 100644 (file)
index 0000000..b7546d1
--- /dev/null
@@ -0,0 +1,18 @@
+submodule sub-foo {
+    belongs-to foo {
+        prefix foo;
+    }
+
+    include sub-foo-2;
+
+    revision 2017-02-09;
+
+    grouping sub-foo-grp {
+        container sub-foo-con {
+        }
+    }
+
+    container sub-root {
+        uses sub-foo-2-grp;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/yang1-1/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/yang1-1/foo.yang
new file mode 100644 (file)
index 0000000..0aefd03
--- /dev/null
@@ -0,0 +1,13 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    include sub-foo {
+        revision-date 2017-02-09;
+    }
+    include sub-foo-2;
+
+    container root {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/yang1-1/sub-foo-2.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/yang1-1/sub-foo-2.yang
new file mode 100644 (file)
index 0000000..7fe0e84
--- /dev/null
@@ -0,0 +1,15 @@
+submodule sub-foo-2 {
+    yang-version 1.1;
+
+    belongs-to foo {
+        prefix foo;
+    }
+
+    grouping sub-foo-2-grp {
+        container sub-foo-2-con {
+        }
+    }
+
+    container sub-root-2 {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/yang1-1/sub-foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6884/yang1-1/sub-foo.yang
new file mode 100644 (file)
index 0000000..d5cb861
--- /dev/null
@@ -0,0 +1,13 @@
+submodule sub-foo {
+    yang-version 1.1;
+
+    belongs-to foo {
+        prefix foo;
+    }
+
+    revision 2017-02-09;
+
+    container sub-root {
+        uses sub-foo-2-grp;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang10/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang10/foo.yang
new file mode 100644 (file)
index 0000000..663e209
--- /dev/null
@@ -0,0 +1,6 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    description illegalchars"test1;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang10/foo2.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang10/foo2.yang
new file mode 100644 (file)
index 0000000..55e3b0b
--- /dev/null
@@ -0,0 +1,6 @@
+module foo2 {
+    namespace foo2;
+    prefix foo2;
+
+    description illegalchars'test2;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang10/foo3.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang10/foo3.yang
new file mode 100644 (file)
index 0000000..a987711
--- /dev/null
@@ -0,0 +1,6 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    description "i\\\\l\nl\te\"\galcharstest1";
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang11/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang11/foo.yang
new file mode 100644 (file)
index 0000000..75e1c9e
--- /dev/null
@@ -0,0 +1,7 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    description illegalchars"test1;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang11/foo2.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang11/foo2.yang
new file mode 100644 (file)
index 0000000..f1856fa
--- /dev/null
@@ -0,0 +1,7 @@
+module foo2 {
+    namespace foo2;
+    prefix foo2;
+    yang-version 1.1;
+
+    description illegalchars'test2;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang11/foo3.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6886/yang11/foo3.yang
new file mode 100644 (file)
index 0000000..27ef195
--- /dev/null
@@ -0,0 +1,7 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    description "i\\\\l\nl\te\"\galcharstest1";
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar-invalid-2.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar-invalid-2.yang
new file mode 100644 (file)
index 0000000..a7e1085
--- /dev/null
@@ -0,0 +1,46 @@
+module bar {
+    namespace bar;
+    prefix bar;
+    yang-version 1.1;
+
+    revision 2017-02-02;
+
+    typedef my-base-bits-type {
+        type bits {
+            bit bit-a {
+                position 1;
+            }
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+        }
+    }
+
+    typedef my-derived-bits-type {
+        type my-base-bits-type {
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+            bit bit-x {
+                position 10;
+            }
+        }
+    }
+
+    leaf my-bits-leaf {
+        type my-derived-bits-type {
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar-invalid-3.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar-invalid-3.yang
new file mode 100644 (file)
index 0000000..d803144
--- /dev/null
@@ -0,0 +1,43 @@
+module bar {
+    namespace bar;
+    prefix bar;
+    yang-version 1.1;
+
+    revision 2017-02-02;
+
+    typedef my-base-bits-type {
+        type bits {
+            bit bit-a {
+                position 1;
+            }
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+        }
+    }
+
+    typedef my-derived-bits-type {
+        type my-base-bits-type {
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+        }
+    }
+
+    leaf my-bits-leaf {
+        type my-derived-bits-type {
+            bit bit-b;
+            bit bit-c {
+                position 5;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar-invalid-4.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar-invalid-4.yang
new file mode 100644 (file)
index 0000000..6013c3a
--- /dev/null
@@ -0,0 +1,43 @@
+module bar {
+    namespace bar;
+    prefix bar;
+    yang-version 1.1;
+
+    revision 2017-02-02;
+
+    typedef my-base-bits-type {
+        type bits {
+            bit bit-a {
+                position 1;
+            }
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+        }
+    }
+
+    typedef my-derived-bits-type {
+        type my-base-bits-type {
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 6;
+            }
+        }
+    }
+
+    leaf my-bits-leaf {
+        type my-derived-bits-type {
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar-invalid.yang
new file mode 100644 (file)
index 0000000..548835c
--- /dev/null
@@ -0,0 +1,44 @@
+module bar {
+    namespace bar;
+    prefix bar;
+    yang-version 1.1;
+
+    revision 2017-02-02;
+
+    typedef my-base-bits-type {
+        type bits {
+            bit bit-a {
+                position 1;
+            }
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+        }
+    }
+
+    typedef my-derived-bits-type {
+        type my-base-bits-type {
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+        }
+    }
+
+    leaf my-bits-leaf {
+        type my-derived-bits-type {
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-w;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar.yang
new file mode 100644 (file)
index 0000000..5eef206
--- /dev/null
@@ -0,0 +1,49 @@
+module bar {
+    namespace bar;
+    prefix bar;
+    yang-version 1.1;
+
+    revision 2017-02-02;
+
+    typedef my-base-bits-type {
+        type bits {
+            bit bit-a {
+                position 1;
+            }
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+        }
+    }
+
+    typedef my-derived-bits-type {
+        type my-base-bits-type {
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+        }
+    }
+
+    leaf my-bits-leaf {
+        type my-derived-bits-type {
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+        }
+    }
+
+    leaf my-bits-leaf-2 {
+        type my-derived-bits-type {
+            // inherits bits from the base type
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar10-invalid-2.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar10-invalid-2.yang
new file mode 100644 (file)
index 0000000..41f771b
--- /dev/null
@@ -0,0 +1,30 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-02-02;
+
+    typedef my-base-bits-type {
+        type bits {
+            bit bit-a {
+                position 1;
+            }
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+        }
+    }
+
+    leaf my-bits-leaf {
+        type my-base-bits-type {
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar10-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar10-invalid.yang
new file mode 100644 (file)
index 0000000..4bf7f77
--- /dev/null
@@ -0,0 +1,33 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-02-02;
+
+    typedef my-base-bits-type {
+        type bits {
+            bit bit-a {
+                position 1;
+            }
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+        }
+    }
+
+    typedef my-derived-bits-type {
+        type my-base-bits-type {
+            bit bit-b;
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar10-valid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/bar10-valid.yang
new file mode 100644 (file)
index 0000000..dd87930
--- /dev/null
@@ -0,0 +1,31 @@
+module bar {
+    namespace bar;
+    prefix bar;
+
+    revision 2017-02-21;
+
+    extension ext;
+
+    typedef my-base-bits-type {
+        type bits {
+            bit bit-a {
+                position 1;
+            }
+            bit bit-b {
+                position 2;
+            }
+            bit bit-c {
+                position 3;
+            }
+            bit bit-d {
+                position 4;
+            }
+        }
+    }
+
+    leaf my-bits-leaf {
+        type my-base-bits-type {
+            bar:ext;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo-invalid-2.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo-invalid-2.yang
new file mode 100644 (file)
index 0000000..482688f
--- /dev/null
@@ -0,0 +1,50 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    revision 2017-02-02;
+
+    typedef my-base-enumeration-type {
+        type enumeration {
+            enum white {
+                value 1;
+            }
+            enum yellow {
+                value 2;
+            }
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+        }
+    }
+
+    typedef my-derived-enumeration-type {
+        type my-base-enumeration-type {
+            enum yellow {
+                value 2;
+            }
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+            enum magenta {
+                value 10;
+            }
+        }
+    }
+
+    leaf my-enumeration-leaf {
+        type my-derived-enumeration-type {
+            enum yellow;
+            enum red {
+                value 3;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo-invalid-3.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo-invalid-3.yang
new file mode 100644 (file)
index 0000000..0a61ae5
--- /dev/null
@@ -0,0 +1,47 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    revision 2017-02-02;
+
+    typedef my-base-enumeration-type {
+        type enumeration {
+            enum white {
+                value 1;
+            }
+            enum yellow {
+                value 2;
+            }
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+        }
+    }
+
+    typedef my-derived-enumeration-type {
+        type my-base-enumeration-type {
+            enum yellow {
+                value 2;
+            }
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+        }
+    }
+
+    leaf my-enumeration-leaf {
+        type my-derived-enumeration-type {
+            enum yellow;
+            enum red {
+                value 5;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo-invalid-4.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo-invalid-4.yang
new file mode 100644 (file)
index 0000000..06ed492
--- /dev/null
@@ -0,0 +1,47 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    revision 2017-02-02;
+
+    typedef my-base-enumeration-type {
+        type enumeration {
+            enum white {
+                value 1;
+            }
+            enum yellow {
+                value 2;
+            }
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+        }
+    }
+
+    typedef my-derived-enumeration-type {
+        type my-base-enumeration-type {
+            enum yellow {
+                value 2;
+            }
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 6;
+            }
+        }
+    }
+
+    leaf my-enumeration-leaf {
+        type my-derived-enumeration-type {
+            enum yellow;
+            enum red {
+                value 3;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo-invalid.yang
new file mode 100644 (file)
index 0000000..3b3be05
--- /dev/null
@@ -0,0 +1,48 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    revision 2017-02-02;
+
+    typedef my-base-enumeration-type {
+        type enumeration {
+            enum white {
+                value 1;
+            }
+            enum yellow {
+                value 2;
+            }
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+        }
+    }
+
+    typedef my-derived-enumeration-type {
+        type my-base-enumeration-type {
+            enum yellow {
+                value 2;
+            }
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+        }
+    }
+
+    leaf my-enumeration-leaf {
+        type my-derived-enumeration-type {
+            enum yellow;
+            enum red {
+                value 3;
+            }
+            enum purple;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo.yang
new file mode 100644 (file)
index 0000000..50ff568
--- /dev/null
@@ -0,0 +1,49 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    revision 2017-01-26;
+
+    typedef my-base-enumeration-type {
+        type enumeration {
+            enum white {
+                value 1;
+            }
+            enum yellow;
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+        }
+    }
+
+    typedef my-derived-enumeration-type {
+        type my-base-enumeration-type {
+            enum yellow;
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+        }
+    }
+
+    leaf my-enumeration-leaf {
+        type my-derived-enumeration-type {
+            enum yellow;
+            enum red {
+                value 3;
+            }
+        }
+    }
+
+    leaf my-enumeration-leaf-2 {
+        type my-derived-enumeration-type {
+            // inherits enums from the base type
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo10-invalid-2.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo10-invalid-2.yang
new file mode 100644 (file)
index 0000000..cfa900e
--- /dev/null
@@ -0,0 +1,30 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    revision 2017-02-02;
+
+    typedef my-base-enumeration-type {
+        type enumeration {
+            enum white {
+                value 1;
+            }
+            enum yellow;
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+        }
+    }
+
+    leaf my-enumeration-leaf {
+        type my-base-enumeration-type {
+            enum yellow;
+            enum red {
+                value 3;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo10-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo10-invalid.yang
new file mode 100644 (file)
index 0000000..36b245f
--- /dev/null
@@ -0,0 +1,33 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    revision 2017-02-02;
+
+    typedef my-base-enumeration-type {
+        type enumeration {
+            enum white {
+                value 1;
+            }
+            enum yellow;
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+        }
+    }
+
+    typedef my-derived-enumeration-type {
+        type my-base-enumeration-type {
+            enum yellow;
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo10-valid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6887/foo10-valid.yang
new file mode 100644 (file)
index 0000000..ed37030
--- /dev/null
@@ -0,0 +1,31 @@
+module foo {
+    namespace foo;
+    prefix foo;
+
+    revision 2017-02-21;
+
+    extension ext;
+
+    typedef my-base-enumeration-type {
+        type enumeration {
+            enum white {
+                value 1;
+            }
+            enum yellow {
+                value 2;
+            }
+            enum red {
+                value 3;
+            }
+            enum black {
+                value 4;
+            }
+        }
+    }
+
+    leaf my-enumeration-leaf {
+        type my-base-enumeration-type {
+            foo:ext;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/foo.yang
new file mode 100644 (file)
index 0000000..e1e311b
--- /dev/null
@@ -0,0 +1,45 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    feature f;
+
+    typedef enum-typedef {
+        type enumeration {
+            enum one;
+            enum two {
+                if-feature f;
+            }
+            enum three;
+        }
+    }
+
+    typedef enum-typedef-2 {
+        type enum-typedef;
+    }
+
+    leaf enum-leaf {
+        type enum-typedef-2;
+        default three;
+    }
+
+    typedef bits-typedef {
+        type bits {
+            bit one;
+            bit two {
+                if-feature f;
+            }
+            bit three;
+        }
+    }
+
+    typedef bits-typedef-2 {
+        type bits-typedef;
+        default three;
+    }
+
+    leaf bit-leaf {
+        type bits-typedef-2;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-10-bit.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-10-bit.yang
new file mode 100644 (file)
index 0000000..550d05b
--- /dev/null
@@ -0,0 +1,25 @@
+module invalid-foo-10-bit {
+    namespace "foo";
+    prefix foo;
+
+    feature f;
+
+    typedef bits-typedef {
+        type bits {
+            bit one;
+            bit two {
+                if-feature f;
+            }
+            bit three;
+        }
+    }
+
+    typedef bits-typedef-2 {
+        type bits-typedef;
+        default three;
+    }
+
+    leaf bit-leaf {
+        type bits-typedef-2;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-10-enum.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-10-enum.yang
new file mode 100644 (file)
index 0000000..c38eddc
--- /dev/null
@@ -0,0 +1,25 @@
+module invalid-foo-10-enum {
+    namespace "foo";
+    prefix foo;
+
+    feature f;
+
+    typedef enum-typedef {
+        type enumeration {
+            enum one;
+            enum two {
+                if-feature f;
+            }
+            enum three;
+        }
+    }
+
+    typedef enum-typedef-2 {
+        type enum-typedef;
+    }
+
+    leaf enum-leaf {
+        type enum-typedef-2;
+        default three;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-bit.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-bit.yang
new file mode 100644 (file)
index 0000000..9fa9c33
--- /dev/null
@@ -0,0 +1,26 @@
+module invalid-foo-bit {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    feature f;
+
+    typedef bits-typedef {
+        type bits {
+            bit one;
+            bit two {
+                if-feature f;
+            }
+            bit three;
+        }
+    }
+
+    typedef bits-typedef-2 {
+        type bits-typedef;
+        default two;
+    }
+
+    leaf bit-leaf {
+        type bits-typedef-2;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-enum-2.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-enum-2.yang
new file mode 100644 (file)
index 0000000..5da6eeb
--- /dev/null
@@ -0,0 +1,18 @@
+module invalid-foo-enum-2 {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    feature f;
+
+    leaf enum-leaf {
+        type enumeration {
+            enum one;
+            enum two {
+                if-feature f;
+            }
+            enum three;
+        }
+        default two;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-enum-3.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-enum-3.yang
new file mode 100644 (file)
index 0000000..b19e7fc
--- /dev/null
@@ -0,0 +1,28 @@
+module invalid-foo-enum-3 {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    feature f;
+
+    typedef enum-typedef {
+        type enumeration {
+            enum one;
+            enum two {
+                if-feature f;
+            }
+            enum three;
+        }
+        description "enum-typedef desc";
+    }
+
+    typedef enum-typedef-2 {
+        type enum-typedef;
+        description "enum-typedef-2 desc";
+    }
+
+    leaf enum-leaf {
+        type enum-typedef-2;
+        default two;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-enum.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-enum.yang
new file mode 100644 (file)
index 0000000..c5e71e3
--- /dev/null
@@ -0,0 +1,26 @@
+module invalid-foo-enum {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    feature f;
+
+    typedef enum-typedef {
+        type enumeration {
+            enum one;
+            enum two {
+                if-feature f;
+            }
+            enum three;
+        }
+    }
+
+    typedef enum-typedef-2 {
+        type enum-typedef;
+    }
+
+    leaf enum-leaf {
+        type enum-typedef-2;
+        default two;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-union.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug6901/invalid-foo-union.yang
new file mode 100644 (file)
index 0000000..4817219
--- /dev/null
@@ -0,0 +1,29 @@
+module invalid-foo-union {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    feature f;
+
+    typedef enum-typedef {
+        type enumeration {
+            enum one;
+            enum two {
+                if-feature f;
+            }
+            enum three;
+        }
+    }
+
+    typedef union-typedef {
+        type union {
+            type enum-typedef;
+            type int16;
+        }
+    }
+
+    leaf union-leaf {
+        type union-typedef;
+        default two;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug9241/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/bug9241/foo.yang
new file mode 100644 (file)
index 0000000..eba36f6
--- /dev/null
@@ -0,0 +1,25 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    revision 2017-10-13;
+
+    container action-cont {
+        action action-in-cont {
+
+        }
+    }
+
+    augment "/action-cont/action-in-cont/input" {
+        leaf augmented-leaf-in-input {
+            type string;
+        }
+    }
+
+    augment "/action-cont/action-in-cont/output" {
+        leaf augmented-leaf-in-output {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/identity-stmt/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/identity-stmt/foo.yang
new file mode 100644 (file)
index 0000000..25b37b3
--- /dev/null
@@ -0,0 +1,19 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prfx;
+    yang-version 1.1;
+
+    revision 2016-12-21;
+
+    identity base-id-a;
+
+    identity base-id-b;
+
+    identity base-id-c;
+
+    identity derived-id {
+        base base-id-a;
+        base base-id-b;
+        base base-id-c;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/identity-stmt/foo10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/identity-stmt/foo10.yang
new file mode 100644 (file)
index 0000000..5199cc0
--- /dev/null
@@ -0,0 +1,19 @@
+module foo10 {
+    namespace foo10-ns;
+    prefix foo10-prfx;
+    yang-version 1;
+
+    revision 2016-12-21;
+
+    identity base-id-a;
+
+    identity base-id-b;
+
+    identity base-id-c;
+
+    identity derived-id {
+        base base-id-a;
+        base base-id-b;
+        base base-id-c;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/identityref-stmt/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/identityref-stmt/foo.yang
new file mode 100644 (file)
index 0000000..7529705
--- /dev/null
@@ -0,0 +1,19 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prfx;
+    yang-version 1.1;
+
+    revision 2017-01-11;
+
+    identity id-a;
+    identity id-b;
+    identity id-c;
+
+    leaf idref-leaf {
+        type identityref {
+            base id-a;
+            base id-b;
+            base id-c;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/identityref-stmt/foo10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/identityref-stmt/foo10.yang
new file mode 100644 (file)
index 0000000..40a4424
--- /dev/null
@@ -0,0 +1,19 @@
+module foo10 {
+    namespace foo10-ns;
+    prefix foo10-prfx;
+    yang-version 1;
+
+    revision 2017-01-11;
+
+    identity id-a;
+    identity id-b;
+    identity id-c;
+
+    leaf idref-leaf {
+        type identityref {
+            base id-a;
+            base id-b;
+            base id-c;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/invalid-import-10/import-test-imported.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/invalid-import-10/import-test-imported.yang
new file mode 100644 (file)
index 0000000..4acd826
--- /dev/null
@@ -0,0 +1,10 @@
+module import-test-imported {
+    yang-version 1.1;
+    namespace "urn:opendaylight:imported-module";
+    prefix "import-test-imported";
+
+    revision 2016-12-12 {
+        description "Initial revision";
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/invalid-import-10/import-test-root.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/invalid-import-10/import-test-root.yang
new file mode 100644 (file)
index 0000000..732bcbe
--- /dev/null
@@ -0,0 +1,15 @@
+module import-test-root {
+    namespace "urn:opendaylight:Import-test-root";
+    prefix "import-test-root";
+
+    revision 2016-12-12 {
+        description "Initial revision";
+    }
+
+    import import-test-imported {
+        prefix "imported-module";
+        revision-date "2016-12-12";
+        description "Yang 1.1: Allow description and reference in include and import.";
+        reference "https://tools.ietf.org/html/rfc7950 section-7.1.5/6";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/invalid-include-10/child-module.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/invalid-include-10/child-module.yang
new file mode 100644 (file)
index 0000000..da7d968
--- /dev/null
@@ -0,0 +1,15 @@
+submodule child-module {
+    belongs-to "root-module" {
+        prefix "root";
+    }
+
+    typedef new-string-type {
+        type string {
+            pattern "[abc]{3}";
+        }
+    }
+
+    feature new-feature {
+        description "A new feature";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/invalid-include-10/root-module.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/invalid-include-10/root-module.yang
new file mode 100644 (file)
index 0000000..9e76581
--- /dev/null
@@ -0,0 +1,13 @@
+module root-module {
+    namespace "urn:opendaylight.org/root-module";
+    prefix "rtm";
+
+    include child-module {
+        description "Test for Yang 1.1 description substatement support";
+        reference "RFC 7950: The YANG 1.1 Data Modeling Language";
+    }
+
+    leaf new-leaf {
+        type new-string-type;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/valid-11/child-module-1.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/valid-11/child-module-1.yang
new file mode 100644 (file)
index 0000000..781062f
--- /dev/null
@@ -0,0 +1,13 @@
+submodule child-module-1 {
+    yang-version 1.1;
+    belongs-to "root-module" {
+        prefix "root";
+    }
+
+    container new-container {
+        description "New container definition";
+        leaf new-leaf {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/valid-11/child-module.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/valid-11/child-module.yang
new file mode 100644 (file)
index 0000000..296dde8
--- /dev/null
@@ -0,0 +1,16 @@
+submodule child-module {
+    yang-version 1.1;
+    belongs-to "root-module" {
+        prefix "root";
+    }
+
+    typedef new-string-type {
+        type string {
+            pattern "[abc]{3}";
+        }
+    }
+
+    feature new-feature {
+        description "A new feature";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/valid-11/imported-module.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/valid-11/imported-module.yang
new file mode 100644 (file)
index 0000000..afd7044
--- /dev/null
@@ -0,0 +1,10 @@
+module imported-module {
+    yang-version 1.1;
+    namespace "urn:opendaylight:imported-module";
+    prefix "imported-module";
+
+    revision 2016-12-12 {
+        description "Initial revision";
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/valid-11/root-module.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/include-import-stmt-test/valid-11/root-module.yang
new file mode 100644 (file)
index 0000000..5232966
--- /dev/null
@@ -0,0 +1,23 @@
+module root-module {
+    yang-version 1.1;
+    namespace "urn:opendaylight.org:root-module";
+    prefix "rtm";
+
+    include child-module {
+        description "Yang 1.1: Allow description and reference in include and import.";
+        reference "https://tools.ietf.org/html/rfc7950 section-7.1.5/6";
+    }
+
+    include child-module-1;
+
+    import imported-module {
+        prefix "imported-module";
+        revision-date "2016-12-12";
+        description "Yang 1.1: Allow description and reference in include and import.";
+        reference "https://tools.ietf.org/html/rfc7950 section-7.1.5/6";
+    }
+
+    leaf new-leaf {
+        type new-string-type;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/leafref-stmt/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/leafref-stmt/foo.yang
new file mode 100644 (file)
index 0000000..ba39f23
--- /dev/null
@@ -0,0 +1,43 @@
+module foo {
+    namespace foo-ns;
+    prefix foo-prfx;
+    yang-version 1.1;
+
+    revision 2016-12-20;
+
+    leaf leafref-a {
+        type leafref {
+            path "../leaf-a";
+            require-instance true;
+        }
+    }
+
+    leaf leaf-a {
+        type string;
+    }
+
+    leaf leafref-b {
+        type leafref-typedef;
+    }
+
+    leaf leaf-b {
+        type string;
+    }
+
+    typedef leafref-typedef {
+        type leafref {
+            path "../leaf-b";
+            require-instance true;
+        }
+    }
+
+    leaf leafref-c {
+        type leafref {
+            path "../leaf-c";
+        }
+    }
+
+    leaf leaf-c {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/leafref-stmt/foo10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/leafref-stmt/foo10.yang
new file mode 100644 (file)
index 0000000..f6b574c
--- /dev/null
@@ -0,0 +1,18 @@
+module foo10 {
+    namespace foo10-ns;
+    prefix foo10-prfx;
+    yang-version 1;
+
+    revision 2016-12-20;
+
+    leaf leafref-a {
+        type leafref {
+            path "../leaf-a";
+            require-instance false;
+        }
+    }
+
+    leaf leaf-a {
+        type string;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/correct-list-keys-test.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/correct-list-keys-test.yang
new file mode 100644 (file)
index 0000000..1e93349
--- /dev/null
@@ -0,0 +1,48 @@
+module correct-list-keys-test {
+    yang-version 1;
+    namespace "correct-list-keys-test";
+    prefix "test";
+
+    feature feature-b;
+    feature feature-d;
+
+    list valid-list-a {
+        leaf a1 {
+            type string;
+        }
+        leaf a2 {
+            when "../a1 = 'key-a1'";
+            type int32;
+        }
+        key "a1 test:a2";
+    }
+
+    list valid-list-b {
+        key "b";
+        leaf b {
+            if-feature feature-b;
+            type string;
+        }
+    }
+
+    grouping d {
+        leaf d1 {
+            type int32;
+            if-feature feature-d;
+        }
+        leaf d2 {
+            when "../d1 = '2'";
+            type string;
+        }
+    }
+
+    list valid-list-d1 {
+        key d1;
+        uses d;
+    }
+
+    list valid-list-d2 {
+        key test:d2;
+        uses d;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test.yang
new file mode 100644 (file)
index 0000000..5ed9974
--- /dev/null
@@ -0,0 +1,20 @@
+module incorrect-list-keys-test {
+    yang-version 1.1;
+    namespace "incorrect-list-keys-test";
+    prefix "test";
+
+    revision 2017-02-06 {
+        description "Bug 6855: Test for disallowing WHEN on list key";
+    }
+
+    list invalid-list-a {
+        leaf a1 {
+            type string;
+        }
+        leaf a2 {
+            when "../a1 = 'key-a1'";
+            type int32;
+        }
+        key "a1 a2";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test1.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test1.yang
new file mode 100644 (file)
index 0000000..db2bf27
--- /dev/null
@@ -0,0 +1,17 @@
+module incorrect-list-keys-test1 {
+    yang-version 1.1;
+    namespace "incorrect-list-keys-test1";
+    prefix "test";
+
+    revision 2017-02-06 {
+        description "Bug 6855: Test disallowing IF-FEATURE on list key";
+    }
+
+    list invalid-list-b {
+        key "b";
+        leaf b {
+            if-feature feature-b;
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test2.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test2.yang
new file mode 100644 (file)
index 0000000..62360bc
--- /dev/null
@@ -0,0 +1,25 @@
+module incorrect-list-keys-test2 {
+    yang-version 1.1;
+    namespace "incorrect-list-keys-test2";
+    prefix "test";
+
+    revision 2017-02-06 {
+        description "Bug 6855: Test for grouping stmt";
+    }
+
+    grouping a {
+        leaf a1 {
+            type int32;
+            if-feature feature-a;
+        }
+        leaf a2 {
+            when "../a1 = '2'";
+            type string;
+        }
+    }
+
+    list invalid-list-a1 {
+        key a1;
+        uses test:a;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test3.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test3.yang
new file mode 100644 (file)
index 0000000..44f7362
--- /dev/null
@@ -0,0 +1,25 @@
+module incorrect-list-keys-test3 {
+    yang-version 1.1;
+    namespace "incorrect-list-keys-test3";
+    prefix "test";
+
+    revision 2017-02-06 {
+        description "Bug 6855: Test for grouping stmt";
+    }
+
+    grouping a {
+        leaf a1 {
+            type int32;
+            if-feature feature-a;
+        }
+        leaf a2 {
+            when "../a1 = '2'";
+            type string;
+        }
+    }
+
+    list invalid-list-a2 {
+        uses test:a;
+        key a2;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test4.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test4.yang
new file mode 100644 (file)
index 0000000..f2ac59c
--- /dev/null
@@ -0,0 +1,32 @@
+module incorrect-list-keys-test4 {
+    yang-version 1.1;
+    namespace "incorrect-list-keys-test4";
+    prefix "test";
+
+    revision 2017-02-06 {
+        description "Bug 6855: Test for grouping stmt";
+    }
+
+    grouping a {
+        leaf a1 {
+            type int32;
+            if-feature feature-a;
+        }
+        leaf a2 {
+            when "../a1 = '2'";
+            type string;
+        }
+    }
+
+    grouping b {
+        leaf b1 {
+            type string;
+        }
+        uses test:a;
+    }
+
+    list invalid-list-b1 {
+        key a1;
+        uses b;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test5.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/list-keys-test/incorrect-list-keys-test5.yang
new file mode 100644 (file)
index 0000000..d6c9aac
--- /dev/null
@@ -0,0 +1,24 @@
+module incorrect-list-keys-test5 {
+    yang-version 1.1;
+    namespace "incorrect-list-keys-test5";
+    prefix "test";
+
+    revision 2017-02-06 {
+        description "Bug 6855: Test for uses stmt with refine substatement";
+    }
+
+    grouping a {
+        leaf a1 {
+            type int32;
+        }
+    }
+
+    list invalid-list-a1 {
+        key a1;
+        uses test:a {
+            refine a1 {
+                if-feature feature-a;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/bar.yang
new file mode 100644 (file)
index 0000000..aa8643a
--- /dev/null
@@ -0,0 +1,119 @@
+module bar {
+    yang-version 1.1;
+    namespace "urn:opendaylight.bar";
+    prefix "bar";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+    description "This model define custom type definitions";
+
+    revision "2013-07-03" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef int32-ext1 {
+        type int32 {
+            range "2..20";
+        }
+    }
+
+    typedef int32-ext2 {
+        type int32-ext1 {
+            range "3..9|11..max";
+        }
+        units "mile";
+        default "11";
+    }
+
+    typedef string-ext1 {
+        type string {
+            pattern "[a-k]*";
+            length "5..11";
+        }
+    }
+
+    typedef string-ext2 {
+        type string-ext1 {
+            length "6..10";
+        }
+    }
+
+    typedef string-ext3 {
+        type string-ext2 {
+            pattern "[b-u]*";
+        }
+    }
+
+    typedef string-ext4 {
+        type string-ext3 {
+            pattern "[e-z]*";
+        }
+    }
+
+    typedef invalid-string-pattern {
+        type string {
+            pattern "[[A-1*-%22!^^}";
+        }
+    }
+
+    typedef multiple-pattern-string {
+        type string {
+            pattern "[[A-1*-%22!^^}";
+            pattern "[e-z]*";
+        }
+    }
+
+    typedef my-decimal-type {
+        type decimal64 {
+            fraction-digits 6;
+        }
+    }
+
+    typedef my-union {
+        type union {
+            type int16 {
+                range "1..100";
+            }
+            type int32;
+        }
+    }
+
+    typedef my-union-ext {
+        type my-union;
+    }
+
+    typedef nested-union2 {
+        type union {
+            type my-union-ext;
+            type string;
+        }
+    }
+
+    container interfaces {
+        grouping ifEntry {
+            container augment-holder;
+        }
+        list ifEntry {
+            key "ifIndex";
+
+            leaf ifIndex {
+                type uint32;
+                units minutes;
+            }
+
+            leaf ifMtu {
+                type int32;
+            }
+
+            min-elements 1;
+            max-elements 11;
+        }
+    }
+
+    extension opendaylight {
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/baz.yang
new file mode 100644 (file)
index 0000000..de092fa
--- /dev/null
@@ -0,0 +1,197 @@
+module baz {
+    yang-version 1.1;
+    namespace "urn:opendaylight.baz";
+    prefix "baz";
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef union1 {
+        type union2;
+    }
+
+    typedef union2 {
+        type union {
+            type int32;
+            type br:nested-union2;
+        }
+    }
+
+    augment "/br:interfaces/br:ifEntry" {
+        when "if:ifType='ds0'";
+        container augment-holder {
+            description "Description for augment holder";
+        }
+    }
+
+    augment "/br:interfaces/br:ifEntry" {
+        when "if:ifType='ds2'";
+        container augment-holder2 {
+            description "Description for augment holder";
+        }
+    }
+
+    container network {
+        baz:c-define point {
+        }
+
+        description "network-description";
+        reference "network-reference";
+        status obsolete;
+        config true;
+        presence "some presence text";
+    }
+
+    feature local-storage {
+        description
+            "This feature means the device supports local
+             storage (memory, flash or disk) that can be used to
+             store syslog messages.";
+    }
+
+    feature candidate;
+    feature startup;
+
+    extension c-define {
+        description "Takes as argument a name string. Makes the code generator use the given name in the #define.";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+    notification event {
+        leaf event-class {
+            type string;
+        }
+        anyxml reporting-entity;
+        leaf severity {
+            type string;
+        }
+    }
+
+    rpc get-config {
+        description "Retrieve all or part of a specified configuration.";
+        reference "RFC 6241, Section 7.1";
+
+        input {
+            container source {
+                description
+                  "Particular configuration to retrieve.";
+
+                choice config-source {
+                    mandatory true;
+                    description
+                      "The configuration to retrieve.";
+                    case a {
+                        leaf candidate {
+                            if-feature candidate;
+                            type empty;
+                            description
+                              "The candidate configuration is the config source.";
+                        }
+                    }
+                    case b {
+                        leaf running {
+                            type empty;
+                            description
+                              "The running configuration is the config source.";
+                        }
+                    }
+                    case c {
+                        leaf startup {
+                            if-feature startup;
+                            type empty;
+                            description
+                              "The startup configuration is the config source.
+                               This is optional-to-implement on the server because
+                               not all servers will support filtering for this
+                               datastore.";
+                        }
+                    }
+                }
+            }
+
+            anyxml filter {
+                description "Subtree or XPath filter to use.";
+                baz:c-define element-attributes;
+            }
+        }
+
+        output {
+            anyxml data {
+                description
+                  "Copy of the source datastore subset that matched
+                   the filter criteria (if any).  An empty data container
+                   indicates that the request did not produce any results.";
+            }
+        }
+    }
+
+    grouping target {
+        anyxml data {
+            config true;
+            description "Copy of the source datastore subset.";
+            mandatory false;
+            must "test-condition-text";
+            reference "test-no-reference";
+            status "obsolete";
+            when "test-when-text";
+        }
+        choice how {
+            description "test choice description";
+            default interval;
+            case interval {
+                leaf interval {
+                    type uint16;
+                    default 30;
+                    units minutes;
+                }
+            }
+            case daily {
+                leaf daily {
+                    type empty;
+                }
+                leaf time-of-day {
+                    type string;
+                    units 24-hour-clock;
+                    default 1am;
+                }
+            }
+        }
+        leaf address {
+            type string;
+            description "Target IP address";
+            mandatory true;
+        }
+        container port {
+            description "Target port container";
+        }
+        list addresses {
+            key "id";
+            leaf id {
+                type int8;
+            }
+        }
+        grouping target-inner {
+            description "target-inner default description";
+            leaf inner-grouping-id {
+                type int8;
+            }
+        }
+        typedef group-type {
+            type br:my-decimal-type;
+        }
+        
+        br:opendaylight awesome;
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/foo.yang
new file mode 100644 (file)
index 0000000..4228623
--- /dev/null
@@ -0,0 +1,234 @@
+module foo {
+    yang-version 1.1;
+    namespace "urn:opendaylight.foo";
+    prefix "foo";
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    import baz {
+        prefix "bz";
+        revision-date 2013-02-27;
+    }
+
+    include subfoo {
+        revision-date "2013-02-27";
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef identifier {
+        type int32;
+    }
+
+    leaf int32-leaf {
+        type br:int32-ext2 {
+            range "12..max";
+        }
+    }
+
+    leaf string-leaf {
+        type br:string-ext4;
+    }
+
+    leaf invalid-pattern-string-leaf {
+        type br:invalid-string-pattern;
+    }
+
+    leaf invalid-direct-string-pattern-def-leaf {
+        type string {
+            pattern "[[A-1*-%22!^^}";
+        }
+    }
+
+    leaf multiple-pattern-string-leaf {
+        type br:multiple-pattern-string;
+    }
+
+    leaf multiple-pattern-direct-string-def-leaf {
+        type string {
+            pattern "[e-z]*";
+            pattern "[[A-1*-%22!^^}";
+            pattern "[a-d]*";
+        }
+    }
+
+    leaf length-leaf {
+        type br:string-ext2 {
+            length "7..max";
+        }
+    }
+
+    leaf decimal-leaf {
+        type br:my-decimal-type {
+            fraction-digits 6;
+        }
+    }
+
+    leaf decimal-leaf2 {
+        type br:my-decimal-type;
+    }
+
+    container ext {
+        bz:c-define "MY_INTERFACES";
+    }
+
+    leaf union-leaf {
+        type br:my-union-ext;
+    }
+
+    deviation /br:interfaces/br:ifEntry {
+        deviate add {
+            config "true";
+        }
+        reference "system/user ref";
+    }
+
+    leaf custom-union-leaf {
+        type bz:union1;
+    }
+
+    container transfer {
+        choice how {
+            default interval;
+            container input {
+            }
+            list output {
+                leaf id {
+                    type string;
+                }
+            }
+            case interval {
+                leaf interval {
+                    type uint16;
+                    default 30;
+                    units minutes;
+                }
+            }
+            case daily {
+                leaf daily {
+                    type empty;
+                }
+                leaf time-of-day {
+                    type string;
+                    units 24-hour-clock;
+                    default 1am;
+                }
+            }
+            case manual {
+                leaf manual {
+                    type empty;
+                }
+            }
+        }
+    }
+
+    anyxml datas {
+        description
+          "Copy of the source typesstore subset that matched
+           the filter criteria (if any).  An empty types container
+           indicates that the request did not produce any results.";
+        status obsolete;
+    }
+
+    augment "/br:interfaces/br:ifEntry/bz:augment-holder" {
+        when "if:ifType='ds0'";
+        leaf ds0ChannelNumber {
+            type string;
+        }
+        leaf interface-id {
+            type leafref {
+                path "/br:interfaces/br:ifEntry/br:ifIndex";
+            }
+        }
+        leaf my-type {
+            type br:int32-ext2;
+        }
+        container schemas {
+        }
+        choice odl {
+            leaf id {
+                type int8;
+            }
+            case node1 {
+                description "node1";
+            }
+            case node2 {
+                description "node2";
+            }
+            container node3 {
+                description "node3";
+            }
+        }
+    }
+
+    container mycont {
+        container innercont {
+            typedef mytype {
+                type string;
+            }
+            leaf myleaf {
+                type mytype;
+            }
+        }
+    }
+
+    uses bz:target {
+        augment "how/interval" {
+            description "inner augment";
+            leaf name {
+                type string;
+            }
+        }
+    }
+
+    container peer {
+        container destination {
+            uses bz:target {
+                refine address {
+                    default "1.2.3.4";
+                    description "IP address of target node";
+                    reference "address reference added by refine";
+                    config false;
+                    mandatory false;
+                    must "ifType != 'ethernet' or " +
+                            "(ifType = 'ethernet' and ifMTU = 1500)" {
+                        error-message "An ethernet MTU must be 1500";
+                    }
+                    foo:mountpoint "mnt-extension";
+                }
+                refine port {
+                    description "description of port defined by refine";
+                    reference "port reference added by refine";
+                    config false;
+                    presence "presence is required";
+                }
+                refine "addresses" {
+                    description "description of addresses defined by refine";
+                    reference "addresses reference added by refine";
+                    config false;
+                    min-elements 2;
+                    max-elements unbounded;
+                }
+                refine addresses/id {
+                    description "id of address";
+                }
+            }
+        }
+    }
+
+    extension mountpoint {
+        description "enter point";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/subfoo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/model/subfoo.yang
new file mode 100644 (file)
index 0000000..e591f80
--- /dev/null
@@ -0,0 +1,70 @@
+submodule subfoo {
+    yang-version 1.1;
+
+    belongs-to foo {
+        prefix f;
+    }
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    import baz {
+        prefix "bz";
+        revision-date 2013-02-27;
+    }
+
+    revision "2013-02-27" {
+    }
+
+    leaf id {
+        type br:int32-ext2 {
+            range "12..max";
+        }
+    }
+
+    leaf foo-id {
+        type f:identifier;
+    }
+
+    container sub-ext {
+        bz:c-define "MY_INTERFACES";
+    }
+
+    container sub-transfer {
+        choice how {
+            default input;
+            container input {
+            }
+            list output {
+                leaf id {
+                    type string;
+                }
+            }
+            case manual {
+                leaf manual {
+                    type empty;
+                }
+            }
+        }
+    }
+
+    anyxml sub-datas {
+        status obsolete;
+    }
+
+    augment "/br:interfaces/br:ifEntry/bz:augment-holder" {
+        when "if:ifType='ds0'";
+        leaf subleaf {
+            type string;
+        }
+    }
+
+    extension sub-mountpoint {
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/bar-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/bar-invalid.yang
new file mode 100644 (file)
index 0000000..658a03d
--- /dev/null
@@ -0,0 +1,23 @@
+module bar-invalid {
+    namespace bar-namespace;
+    prefix bar-prefix;
+    yang-version 1.1;
+
+    revision 2016-12-08;
+
+    list my-unkeyed-list {
+        container my-container {
+            list my-keyed-list {
+                key key-leaf;
+
+                leaf key-leaf {
+                    type string;
+                }
+
+                notification my-notification {
+
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/baz-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/baz-invalid.yang
new file mode 100644 (file)
index 0000000..660f93c
--- /dev/null
@@ -0,0 +1,26 @@
+module baz-invalid {
+    namespace baz-namespace;
+    prefix baz-prefix;
+    yang-version 1.1;
+
+    revision 2016-12-08;
+
+    container my-container {
+        choice my-choice {
+            case first {
+                leaf first-leaf {
+                    type string;
+                }
+            }
+            case second {
+                uses grouping-with-notification;
+            }
+        }
+    }
+
+    grouping grouping-with-notification {
+        notification notification-in-grouping {
+
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/foo-invalid.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/foo-invalid.yang
new file mode 100644 (file)
index 0000000..4f8a08a
--- /dev/null
@@ -0,0 +1,25 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    rpc my-rpc {
+        input {
+            container i {
+            }
+        }
+        output {
+            container o {
+            }
+        }
+    }
+
+    grouping grp {
+        notification grp-notification {
+        }
+    }
+
+    augment "/my-rpc/input/i" {
+        uses grp;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/foo.yang
new file mode 100644 (file)
index 0000000..89e93b3
--- /dev/null
@@ -0,0 +1,35 @@
+module foo {
+    namespace foo;
+    prefix foo;
+    yang-version 1.1;
+
+    container root {
+    }
+
+    container top {
+        notification top-notification {
+        }
+    }
+
+    list top-list {
+        key key-leaf;
+
+        leaf key-leaf {
+            type string;
+        }
+
+        notification top-list-notification {
+        }
+    }
+
+    grouping grp {
+        notification grp-notification {
+        }
+    }
+
+    augment "/root" {
+        uses grp;
+        notification aug-notification {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/foo10.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/notifications-in-data-nodes/foo10.yang
new file mode 100644 (file)
index 0000000..0de3901
--- /dev/null
@@ -0,0 +1,24 @@
+module foo10 {
+    namespace foo10;
+    prefix foo10;
+    yang-version 1;
+
+    container root {
+    }
+
+    list top-list {
+        notification top-list-notification {
+        }
+    }
+
+    grouping grp {
+        notification grp-notification {
+        }
+    }
+
+    augment "/root" {
+        uses grp;
+        notification aug-notification {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/bar@2016-01-03.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/bar@2016-01-03.yang
new file mode 100644 (file)
index 0000000..3f495b8
--- /dev/null
@@ -0,0 +1,24 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1.1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "1.2.012"; }
+    import foobar { prefix foobar; oc-ext:openconfig-version "2.25.3"; }
+
+    revision "2016-01-03" {
+        description "Imported version";
+    }
+    oc-ext:openconfig-version "1.2.6";
+
+    container root {
+        container test-container {
+            uses foobar:test-grouping;
+        }
+    }
+
+    leaf-list should-present {
+        type string;
+        description "List of strings";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/bar@2016-01-04.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/bar@2016-01-04.yang
new file mode 100644 (file)
index 0000000..7490e8d
--- /dev/null
@@ -0,0 +1,24 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1.1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "1.2.210"; }
+    import foobar { prefix foobar; oc-ext:openconfig-version "2.25.3"; }
+
+    revision "2016-01-04" {
+        description "Not-imported version";
+    }
+    oc-ext:openconfig-version "2.200.200";
+
+    container root {
+        container test-container {
+            uses foobar:test-grouping;
+        }
+    }
+
+    leaf-list should-not-be-present {
+        type uint32;
+        description "List of integers";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/foo.yang
new file mode 100644 (file)
index 0000000..087c05f
--- /dev/null
@@ -0,0 +1,13 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "1.0.0"; }
+    import bar { prefix bar; oc-ext:openconfig-version "1.2.5"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "1.50.02";
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/foobar@2016-01-31.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/foobar@2016-01-31.yang
new file mode 100644 (file)
index 0000000..89863b2
--- /dev/null
@@ -0,0 +1,23 @@
+module foobar {
+    namespace "foobar";
+    prefix foobar;
+    yang-version 1.1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "1.2.95"; }
+
+    revision "2016-01-31" {
+        description "Not-imported version";
+    }
+    oc-ext:openconfig-version "2.25.2";
+
+    grouping test-grouping {
+        leaf name {
+            type string;
+            description "Name";
+        }
+        leaf oldnumber {
+            type uint8;
+            description "Old number";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/foobar@2016-02-28.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/foobar@2016-02-28.yang
new file mode 100644 (file)
index 0000000..132e305
--- /dev/null
@@ -0,0 +1,23 @@
+module foobar {
+    namespace "foobar";
+    prefix foobar;
+    yang-version 1.1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "1.3.95"; }
+
+    revision "2016-02-28" {
+        description "Imported version";
+    }
+    oc-ext:openconfig-version "2.26.465";
+
+    grouping test-grouping {
+        leaf name {
+            type string;
+            description "Name";
+        }
+        leaf number {
+            type uint32;
+            description "Number";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-1/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..ff84c41
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1.1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "1.3.95";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/bar@2016-01-03.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/bar@2016-01-03.yang
new file mode 100644 (file)
index 0000000..fe922ff
--- /dev/null
@@ -0,0 +1,23 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "2.0.40"; }
+    import foobar { prefix foobar; oc-ext:openconfig-version "7.12.54"; }
+
+    revision "2016-01-03" {
+        description "Imported version";
+    }
+    oc-ext:openconfig-version "4.9.8";
+
+    augment "/foobar:root/foobar:test-container" {
+        leaf should-present-leaf-1 {
+            type string;
+        }
+        leaf should-present-leaf-2 {
+            type uint8;
+            default 0;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/bar@2016-01-04.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/bar@2016-01-04.yang
new file mode 100644 (file)
index 0000000..858bb04
--- /dev/null
@@ -0,0 +1,23 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1.1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "2.0.50"; }
+    import foobar { prefix foobar; oc-ext:openconfig-version "7.12.54"; }
+
+    revision "2016-01-04" {
+        description "Not-imported version";
+    }
+    oc-ext:openconfig-version "3.0.5";
+
+    augment "/foobar:root/foobar:test-container" {
+        leaf should-not-be-present-leaf-1 {
+            type string;
+        }
+        leaf should-not-be-present-leaf-2 {
+            type uint8;
+            default 0;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/foo.yang
new file mode 100644 (file)
index 0000000..734f554
--- /dev/null
@@ -0,0 +1,13 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1.1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "2.5.40"; }
+    import bar { prefix bar; oc-ext:openconfig-version "4.1.1"; }
+
+    revision "2016-02-01" {
+        description "Initial version";
+    }
+    oc-ext:openconfig-version "2.32.2";
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/foobar@2016-01-31.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/foobar@2016-01-31.yang
new file mode 100644 (file)
index 0000000..7c7ac1a
--- /dev/null
@@ -0,0 +1,20 @@
+module foobar {
+    namespace "foobar";
+    prefix foobar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "2.1.950"; }
+
+    revision "2016-01-31" {
+        description "Not-imported version";
+    }
+    oc-ext:openconfig-version "8.0.0";
+
+    container root {
+        leaf included-not-correct-mark {
+            type empty;
+        }
+        container test-container {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/foobar@2016-02-27.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/foobar@2016-02-27.yang
new file mode 100644 (file)
index 0000000..930c116
--- /dev/null
@@ -0,0 +1,20 @@
+module foobar {
+    namespace "foobar";
+    prefix foobar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "2.5.50"; }
+
+    revision "2016-02-27" {
+        description "Not-imported version";
+    }
+    oc-ext:openconfig-version "7.13.0";
+
+    container root {
+        leaf included-not-correct-mark {
+            type empty;
+        }
+        container test-container {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/foobar@2016-02-28.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/foobar@2016-02-28.yang
new file mode 100644 (file)
index 0000000..676834c
--- /dev/null
@@ -0,0 +1,20 @@
+module foobar {
+    namespace "foobar";
+    prefix foobar;
+    yang-version 1;
+
+    import openconfig-extensions { prefix oc-ext; oc-ext:openconfig-version "2.5.50"; }
+
+    revision "2016-02-28" {
+        description "Imported version";
+    }
+    oc-ext:openconfig-version "7.13.99";
+
+    container root {
+        leaf included-correct-mark {
+            type empty;
+        }
+        container test-container {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/openconfig-extensions.yang b/yang/yang-parser-rfc7950/src/test/resources/rfc7950/openconfig-version/complex-2/openconfig-extensions.yang
new file mode 100644 (file)
index 0000000..9b021b6
--- /dev/null
@@ -0,0 +1,84 @@
+module openconfig-extensions {
+    yang-version "1";
+    namespace "http://openconfig.net/yang/openconfig-ext";
+    prefix "oc-ext";
+
+    organization "OpenConfig working group";
+
+    contact
+        "OpenConfig working group
+        www.openconfig.net";
+
+    description
+        "This module provides extensions to the YANG language to allow
+        OpenConfig specific functionality and meta-data to be defined.";
+
+    revision "2017-01-29" {
+        description
+            "Added extension for annotating encrypted values.";
+        reference "TBD";
+    }
+
+    revision "2015-10-09" {
+        description
+            "Initial OpenConfig public release";
+        reference "TBD";
+    }
+
+    revision "2015-10-05" {
+        description
+            "Initial revision";
+        reference "TBD";
+    }
+
+    oc-ext:openconfig-version "2.5.50";
+
+    extension openconfig-version {
+        argument "semver" {
+            yin-element false;
+        }
+        description
+            "The OpenConfig version number for the module. This is
+            expressed as a semantic version number of the form:
+              x.y.z
+            where:
+              * x corresponds to the major version,
+              * y corresponds to a minor version,
+              * z corresponds to a patch version.
+            This version corresponds to the model file within which it is
+            defined, and does not cover the whole set of OpenConfig models.
+            Where several modules are used to build up a single block of
+            functionality, the same module version is specified across each
+            file that makes up the module.
+
+            A major version number of 0 indicates that this model is still
+            in development (whether within OpenConfig or with industry
+            partners), and is potentially subject to change.
+
+            Following a release of major version 1, all modules will
+            increment major revision number where backwards incompatible
+            changes to the model are made.
+
+            The minor version is changed when features are added to the
+            model that do not impact current clients use of the model.
+
+            The patch-level version is incremented when non-feature changes
+            (such as bugfixes or clarifications to human-readable
+            descriptions that do not impact model functionality) are made
+            that maintain backwards compatibility.
+
+            The version number is stored in the module meta-data.";
+    }
+
+    extension openconfig-encrypted-value {
+        description
+            "This extension provides an annotation on schema nodes to
+            indicate that the corresponding value should be stored and
+            reported in encrypted form.
+            Clients reading the configuration or applied configuration
+            for the node should expect to receive only the encrypted value.
+            This annotation may be used on nodes such as secure passwords
+            in which the device never reports a cleartext value, even
+            if the input is provided as cleartext.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rpc-stmt-test/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/rpc-stmt-test/bar.yang
new file mode 100644 (file)
index 0000000..4a8c7b8
--- /dev/null
@@ -0,0 +1,30 @@
+module bar {
+    namespace bar-namespace;
+    prefix bar-prefix;
+
+    revision 2016-11-25;
+
+    rpc bar-rpc {
+
+    }
+
+    augment "/bar-rpc/input" {
+        leaf bar-leaf-a {
+            type string;
+        }
+
+        leaf bar-leaf-b {
+            type string;
+        }
+    }
+
+    augment "/bar-rpc/output" {
+        leaf bar-leaf-c {
+            type string;
+        }
+
+        leaf bar-leaf-d {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/rpc-stmt-test/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/rpc-stmt-test/foo.yang
new file mode 100644 (file)
index 0000000..7ebd025
--- /dev/null
@@ -0,0 +1,34 @@
+module foo {
+    namespace foo-namespace;
+    prefix foo-prefix;
+
+    revision 2016-09-23;
+
+    rpc foo-rpc-1 {
+        input {
+            leaf foo-leaf-1 {
+                type string;
+            }
+        }
+
+        output {
+            leaf foo-leaf-2 {
+                type string;
+            }
+        }
+    }
+
+    rpc foo-rpc-2 {
+        input {
+            leaf foo-leaf-3 {
+                type string;
+            }
+        }
+
+        output {
+            leaf foo-leaf-4 {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/config.yang b/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/config.yang
new file mode 100644 (file)
index 0000000..c0afa1b
--- /dev/null
@@ -0,0 +1,179 @@
+module config {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:config";
+    prefix "config";
+
+    description
+        "This module contains the base YANG definitions for NS-OS configuration
+        subsystem. The system modeled revolves around two major concepts:
+        modules and services.";
+
+    revision "2013-04-05" {
+        description
+                "Reworked to give modules their own space.";
+    }
+
+    revision "2013-04-03" {
+        description
+                "Initial revision.";
+    }
+
+    extension java-class {
+        description
+                "YANG language extension carrying the fully-qualified name of
+                a Java class. Code generation tools use the provided reference
+                to tie a specific construct to its Java representation.";
+
+        argument "name";
+    }
+
+    extension required-identity {
+        description
+                "YANG language extension which indicates that a particular leafref,
+                which points to a identityref, should additionally require the
+                target node is actually set to a descendant to of a particular
+                identity. This is a workaround to two YANG deficiencies: 1) not
+                being able to leafref instances of identityref 2) not being able
+                to refine an identityref This extension takes one argument, name,
+                which MUST be the name of an identity. Furthermore, that identity
+                MUST be based, directly or indirectly, on the identity, which
+                is referenced by the leaf reference, which is annotated with this
+                extension.";
+
+        argument "name";
+    }
+
+    extension inner-state-bean {
+        description
+                "YANG language extension which indicates that a particular list
+                located under module's state should be treated as a list of child
+                state beans instead of just an ordinary list attribute";
+    }
+
+    extension provided-service {
+        description
+                "YANG language extension which indicates that a particular module
+                provides certain service. This extension can be placed on identities
+                that are based on module-type. Zero or more services can be provided.
+                This extension takes one argument - name - which MUST be the name
+                of an identity. Furthermore, this identity MUST be based on service-type.";
+
+        argument "name";
+    }
+
+    extension java-name-prefix {
+        description
+                "YANG language extension carrying java simple class name prefix
+                that will be taken into account when generating java code from
+                identities that are based on module-type.";
+        argument "java-prefix";
+    }
+
+    identity module-type {
+        description
+                "Module identity base type. All module identities must be derived
+                from this type. A module type uniquely defines a single atomic
+                component, such as an application. Each such component is assumed
+                to have its unique, stable and versioned configuration structure.";
+    }
+
+    identity service-type {
+        description
+                "Service identity base type. All service identities must be derived
+                from this type. A service type uniquely defines a single atomic
+                API contract, such as a Java interface, a set of C function declarations,
+                or similar. If the service type has a corresponding Java interface,
+                the name of that interface should be attached to the derived identity
+                MUST include a java-class keyword, whose name argument points
+                to that interface.";
+    }
+
+    typedef service-type-ref {
+        description
+                "Internal type of references to service type identity.";
+
+        type identityref {
+            base service-type;
+        }
+    }
+
+    grouping service-ref {
+        description
+                "Type of references to a particular service instance. This type
+                can be used when defining module configuration to refer to a particular
+                service instance. Containers using this grouping should not define
+                anything else. The run-time implementation is expected to inject
+                a reference to the service as the value of the container.";
+
+        leaf type {
+            description
+                        "Type of the service being referenced. Users of this grouping
+                        should refine this leaf with required-identity pointing to the
+                        actual service-type which is actually required.";
+
+            mandatory true;
+            type leafref {
+                path "/config:services/config:service/config:type";
+            }
+        }
+
+        leaf name {
+            mandatory true;
+            type leafref {
+                path "/config:services/config:service[config:type=current()/../type]/config:instance/config:name";
+            }
+        }
+    }
+
+    container modules {
+        description
+                "Top level container encapsulating configuration of all modules.";
+
+        list module {
+            key "type name";
+            leaf name {
+                description "Unique module instance name";
+                type string;
+                mandatory true;
+            }
+
+            leaf type {
+                type identityref {
+                    base module-type;
+                }
+                mandatory true;
+            }
+
+            choice configuration {
+                mandatory true;
+                config true;
+            }
+
+            choice state {
+                config false;
+            }
+        }
+    }
+
+    container services {
+        list service {
+            key "type";
+            leaf type {
+                type service-type-ref;
+            }
+            list instance {
+                key "name";
+                leaf name {
+                    type string;
+                }
+
+                leaf provider {
+                    mandatory true;
+                    type leafref {
+                        path "/modules/module/name";
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/ietf-yang-types.yang b/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/ietf-yang-types.yang
new file mode 100644 (file)
index 0000000..51d9f8b
--- /dev/null
@@ -0,0 +1,396 @@
+ module ietf-yang-types {
+
+   namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+   prefix "yang";
+
+   organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+   contact
+    "WG Web:   <http://tools.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+
+     WG Chair: David Partain
+               <mailto:david.partain@ericsson.com>
+
+     WG Chair: David Kessens
+               <mailto:david.kessens@nsn.com>
+
+     Editor:   Juergen Schoenwaelder
+               <mailto:j.schoenwaelder@jacobs-university.de>";
+
+   description
+    "This module contains a collection of generally useful derived
+     YANG data types.
+
+     Copyright (c) 2010 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or without
+     modification, is permitted pursuant to, and subject to the license
+     terms contained in, the Simplified BSD License set forth in Section
+     4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6021; see
+     the RFC itself for full legal notices.";
+
+   revision 2010-09-24 {
+     description
+      "Initial revision.";
+     reference
+      "RFC 6021: Common YANG Data Types";
+   }
+
+   /*** collection of counter and gauge types ***/
+
+   typedef counter32 {
+     type uint32;
+     description
+      "The counter32 type represents a non-negative integer
+       that monotonically increases until it reaches a
+       maximum value of 2^32-1 (4294967295 decimal), when it
+       wraps around and starts increasing again from zero.
+
+       Counters have no defined 'initial' value, and thus, a
+       single value of a counter has (in general) no information
+       content.  Discontinuities in the monotonically increasing
+       value normally occur at re-initialization of the
+       management system, and at other times as specified in the
+       description of a schema node using this type.  If such
+       other times can occur, for example, the creation of
+       a schema node of type counter32 at times other than
+       re-initialization, then a corresponding schema node
+       should be defined, with an appropriate type, to indicate
+       the last discontinuity.
+
+       The counter32 type should not be used for configuration
+       schema nodes.  A default statement SHOULD NOT be used in
+       combination with the type counter32.
+
+       In the value set and its semantics, this type is equivalent
+       to the Counter32 type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef zero-based-counter32 {
+     type yang:counter32;
+     default "0";
+     description
+      "The zero-based-counter32 type represents a counter32
+       that has the defined 'initial' value zero.
+
+       A schema node of this type will be set to zero (0) on creation
+       and will thereafter increase monotonically until it reaches
+       a maximum value of 2^32-1 (4294967295 decimal), when it
+       wraps around and starts increasing again from zero.
+
+       Provided that an application discovers a new schema node
+       of this type within the minimum time to wrap, it can use the
+       'initial' value as a delta.  It is important for a management
+       station to be aware of this minimum time and the actual time
+       between polls, and to discard data if the actual time is too
+       long or there is no defined minimum time.
+
+       In the value set and its semantics, this type is equivalent
+       to the ZeroBasedCounter32 textual convention of the SMIv2.";
+     reference
+       "RFC 4502: Remote Network Monitoring Management Information
+                  Base Version 2";
+   }
+
+   typedef counter64 {
+     type uint64;
+     description
+      "The counter64 type represents a non-negative integer
+       that monotonically increases until it reaches a
+       maximum value of 2^64-1 (18446744073709551615 decimal),
+       when it wraps around and starts increasing again from zero.
+
+       Counters have no defined 'initial' value, and thus, a
+       single value of a counter has (in general) no information
+       content.  Discontinuities in the monotonically increasing
+       value normally occur at re-initialization of the
+       management system, and at other times as specified in the
+       description of a schema node using this type.  If such
+       other times can occur, for example, the creation of
+       a schema node of type counter64 at times other than
+       re-initialization, then a corresponding schema node
+       should be defined, with an appropriate type, to indicate
+       the last discontinuity.
+
+       The counter64 type should not be used for configuration
+       schema nodes.  A default statement SHOULD NOT be used in
+       combination with the type counter64.
+
+       In the value set and its semantics, this type is equivalent
+       to the Counter64 type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef zero-based-counter64 {
+     type yang:counter64;
+     default "0";
+     description
+      "The zero-based-counter64 type represents a counter64 that
+       has the defined 'initial' value zero.
+
+       A schema node of this type will be set to zero (0) on creation
+       and will thereafter increase monotonically until it reaches
+       a maximum value of 2^64-1 (18446744073709551615 decimal),
+       when it wraps around and starts increasing again from zero.
+
+       Provided that an application discovers a new schema node
+       of this type within the minimum time to wrap, it can use the
+       'initial' value as a delta.  It is important for a management
+       station to be aware of this minimum time and the actual time
+       between polls, and to discard data if the actual time is too
+       long or there is no defined minimum time.
+
+       In the value set and its semantics, this type is equivalent
+       to the ZeroBasedCounter64 textual convention of the SMIv2.";
+     reference
+      "RFC 2856: Textual Conventions for Additional High Capacity
+                 Data Types";
+   }
+
+   typedef gauge32 {
+     type uint32;
+     description
+      "The gauge32 type represents a non-negative integer, which
+       may increase or decrease, but shall never exceed a maximum
+       value, nor fall below a minimum value.  The maximum value
+       cannot be greater than 2^32-1 (4294967295 decimal), and
+       the minimum value cannot be smaller than 0.  The value of
+       a gauge32 has its maximum value whenever the information
+       being modeled is greater than or equal to its maximum
+       value, and has its minimum value whenever the information
+       being modeled is smaller than or equal to its minimum value.
+       If the information being modeled subsequently decreases
+       below (increases above) the maximum (minimum) value, the
+       gauge32 also decreases (increases).
+
+       In the value set and its semantics, this type is equivalent
+       to the Gauge32 type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef gauge64 {
+     type uint64;
+     description
+      "The gauge64 type represents a non-negative integer, which
+       may increase or decrease, but shall never exceed a maximum
+       value, nor fall below a minimum value.  The maximum value
+       cannot be greater than 2^64-1 (18446744073709551615), and
+       the minimum value cannot be smaller than 0.  The value of
+       a gauge64 has its maximum value whenever the information
+       being modeled is greater than or equal to its maximum
+       value, and has its minimum value whenever the information
+       being modeled is smaller than or equal to its minimum value.
+       If the information being modeled subsequently decreases
+       below (increases above) the maximum (minimum) value, the
+       gauge64 also decreases (increases).
+
+       In the value set and its semantics, this type is equivalent
+       to the CounterBasedGauge64 SMIv2 textual convention defined
+       in RFC 2856";
+     reference
+      "RFC 2856: Textual Conventions for Additional High Capacity
+                 Data Types";
+   }
+
+   /*** collection of identifier related types ***/
+
+   typedef object-identifier {
+     type string {
+       pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+             + '(\.(0|([1-9]\d*)))*';
+     }
+     description
+      "The object-identifier type represents administratively
+       assigned names in a registration-hierarchical-name tree.
+
+       Values of this type are denoted as a sequence of numerical
+       non-negative sub-identifier values.  Each sub-identifier
+       value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers
+       are separated by single dots and without any intermediate
+       whitespace.
+
+       The ASN.1 standard restricts the value space of the first
+       sub-identifier to 0, 1, or 2.  Furthermore, the value space
+       of the second sub-identifier is restricted to the range
+       0 to 39 if the first sub-identifier is 0 or 1.  Finally,
+       the ASN.1 standard requires that an object identifier
+       has always at least two sub-identifier.  The pattern
+       captures these restrictions.
+
+       Although the number of sub-identifiers is not limited,
+       module designers should realize that there may be
+       implementations that stick with the SMIv2 limit of 128
+       sub-identifiers.
+
+       This type is a superset of the SMIv2 OBJECT IDENTIFIER type
+       since it is not restricted to 128 sub-identifiers.  Hence,
+       this type SHOULD NOT be used to represent the SMIv2 OBJECT
+       IDENTIFIER type, the object-identifier-128 type SHOULD be
+       used instead.";
+     reference
+      "ISO9834-1: Information technology -- Open Systems
+       Interconnection -- Procedures for the operation of OSI
+       Registration Authorities: General procedures and top
+       arcs of the ASN.1 Object Identifier tree";
+   }
+
+
+
+
+   typedef object-identifier-128 {
+     type object-identifier {
+       pattern '\d*(\.\d*){1,127}';
+     }
+     description
+      "This type represents object-identifiers restricted to 128
+       sub-identifiers.
+
+       In the value set and its semantics, this type is equivalent
+       to the OBJECT IDENTIFIER type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   /*** collection of date and time related types ***/
+
+   typedef date-and-time {
+     type string {
+       pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+             + '(Z|[\+\-]\d{2}:\d{2})';
+     }
+     description
+      "The date-and-time type is a profile of the ISO 8601
+       standard for representation of dates and times using the
+       Gregorian calendar.  The profile is defined by the
+       date-time production in Section 5.6 of RFC 3339.
+
+       The date-and-time type is compatible with the dateTime XML
+       schema type with the following notable exceptions:
+
+       (a) The date-and-time type does not allow negative years.
+
+       (b) The date-and-time time-offset -00:00 indicates an unknown
+           time zone (see RFC 3339) while -00:00 and +00:00 and Z all
+           represent the same time zone in dateTime.
+
+       (c) The canonical format (see below) of data-and-time values
+           differs from the canonical format used by the dateTime XML
+           schema type, which requires all times to be in UTC using the
+           time-offset 'Z'.
+
+       This type is not equivalent to the DateAndTime textual
+       convention of the SMIv2 since RFC 3339 uses a different
+       separator between full-date and full-time and provides
+       higher resolution of time-secfrac.
+
+       The canonical format for date-and-time values with a known time
+       zone uses a numeric time zone offset that is calculated using
+       the device's configured known offset to UTC time.  A change of
+       the device's offset to UTC time will cause date-and-time values
+       to change accordingly.  Such changes might happen periodically
+       in case a server follows automatically daylight saving time
+       (DST) time zone offset changes.  The canonical format for
+       date-and-time values with an unknown time zone (usually referring
+       to the notion of local time) uses the time-offset -00:00.";
+     reference
+      "RFC 3339: Date and Time on the Internet: Timestamps
+       RFC 2579: Textual Conventions for SMIv2
+       XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
+   }
+
+   typedef timeticks {
+     type uint32;
+     description
+      "The timeticks type represents a non-negative integer that
+       represents the time, modulo 2^32 (4294967296 decimal), in
+       hundredths of a second between two epochs.  When a schema
+       node is defined that uses this type, the description of
+       the schema node identifies both of the reference epochs.
+
+       In the value set and its semantics, this type is equivalent
+       to the TimeTicks type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef timestamp {
+     type yang:timeticks;
+     description
+      "The timestamp type represents the value of an associated
+       timeticks schema node at which a specific occurrence happened.
+       The specific occurrence must be defined in the description
+       of any schema node defined using this type.  When the specific
+       occurrence occurred prior to the last time the associated
+       timeticks attribute was zero, then the timestamp value is
+       zero.  Note that this requires all timestamp values to be
+       reset to zero when the value of the associated timeticks
+       attribute reaches 497+ days and wraps around to zero.
+
+       The associated timeticks schema node must be specified
+       in the description of any schema node using this type.
+
+       In the value set and its semantics, this type is equivalent
+       to the TimeStamp textual convention of the SMIv2.";
+     reference
+      "RFC 2579: Textual Conventions for SMIv2";
+   }
+
+   /*** collection of generic address types ***/
+
+   typedef phys-address {
+     type string {
+       pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+     }
+     description
+      "Represents media- or physical-level addresses represented
+       as a sequence octets, each octet represented by two hexadecimal
+       numbers.  Octets are separated by colons.  The canonical
+       representation uses lowercase characters.
+
+       In the value set and its semantics, this type is equivalent
+       to the PhysAddress textual convention of the SMIv2.";
+     reference
+      "RFC 2579: Textual Conventions for SMIv2";
+   }
+
+   typedef mac-address {
+     type string {
+       pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+     }
+     description
+      "The mac-address type represents an IEEE 802 MAC address.
+       The canonical representation uses lowercase characters.
+
+       In the value set and its semantics, this type is equivalent
+       to the MacAddress textual convention of the SMIv2.";
+     reference
+      "IEEE 802: IEEE Standard for Local and Metropolitan Area
+                 Networks: Overview and Architecture
+       RFC 2579: Textual Conventions for SMIv2";
+   }
+
+   /*** collection of XML specific types ***/
+
+   typedef xpath1.0 {
+     type string;
+     description
+      "This type represents an XPATH 1.0 expression.
+
+       When a schema node is defined that uses this type, the
+       description of the schema node MUST specify the XPath
+       context in which the XPath expression is evaluated.";
+     reference
+      "XPATH: XML Path Language (XPath) Version 1.0";
+   }
+
+ }
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-config-dom-datastore.yang b/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-config-dom-datastore.yang
new file mode 100644 (file)
index 0000000..470d02d
--- /dev/null
@@ -0,0 +1,21 @@
+module opendaylight-config-dom-datastore {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:config-dom-store";
+    prefix "config-dom-store-spi";
+
+    import config { prefix config; revision-date 2013-04-05; }
+
+    description
+        "DOM Service Provider Interface definition for MD-SAL config store";
+
+    revision "2014-06-17" {
+        description
+                "Initial revision";
+    }
+
+    identity config-dom-datastore {
+        base "config:service-type";
+        config:java-class "org.opendaylight.controller.sal.core.spi.data.DOMStore";
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-dom-broker-impl.yang b/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-dom-broker-impl.yang
new file mode 100644 (file)
index 0000000..944a63d
--- /dev/null
@@ -0,0 +1,218 @@
+module opendaylight-sal-dom-broker-impl {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl";
+    prefix "broker";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import ietf-yang-types { prefix yang; }
+    import opendaylight-md-sal-dom { prefix sal; }
+    import opendaylight-md-sal-common { prefix common; }
+    import opendaylight-config-dom-datastore { prefix config-dom-store-spi; }
+    import opendaylight-operational-dom-datastore { prefix operational-dom-store-spi; }
+    import rpc-context { prefix rpcx; revision-date 2013-06-17; }
+
+    description
+        "Service definition for Binding Aware MD-SAL. Note: The dom-inmemory-data-broker
+        utilizes configurable config-dom-datastore and operation-dom-datastore.
+        If configuration is not done for this stores then it defaults
+        to InMemoryDOMDataStore";
+
+    revision "2013-10-28" {
+        description
+                "Initial revision";
+    }
+
+    identity dom-broker-impl {
+        base config:module-type;
+        config:provided-service sal:dom-broker-osgi-registry;
+        config:java-name-prefix DomBrokerImpl;
+    }
+
+    identity dom-inmemory-data-broker {
+        base config:module-type;
+        config:provided-service sal:dom-async-data-broker;
+    }
+
+    identity schema-service-singleton {
+        base config:module-type;
+        config:provided-service sal:schema-service;
+        config:java-name-prefix SchemaServiceImplSingleton;
+    }
+
+    typedef max-queue-depth {
+        type uint32 {
+            range 1..1073741824;
+        }
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case dom-broker-impl {
+            when "/config:modules/config:module/config:type = 'dom-broker-impl'";
+
+            container async-data-broker {
+                uses config:service-ref {
+                    refine type {
+                        mandatory false;
+                        config:required-identity sal:dom-async-data-broker;
+                    }
+                }
+            }
+
+            container root-schema-service {
+                uses config:service-ref {
+                    refine type {
+                        mandatory false;
+                        config:required-identity sal:schema-service;
+                    }
+                }
+            }
+
+            leaf notification-queue-depth {
+                description "Maximum number of elements in the notification queue, must be
+                                power-of-two.";
+                type max-queue-depth;
+                default 65536;
+            }
+            leaf notification-queue-spin {
+                description "Number of milliseconds notification queue should spin for new
+                                requests before parking.";
+                type uint16;
+                units milliseconds;
+                default 1;
+            }
+            leaf notification-queue-park {
+                description "Number of milliseconds notification queue should park for new
+                                requests before blocking.";
+                type uint16;
+                units milliseconds;
+                default 30;
+            }
+        }
+    }
+
+    grouping dom-broker-config {
+        container schema-service {
+            uses config:service-ref {
+                refine type {
+                    mandatory false;
+                    config:required-identity sal:schema-service;
+                }
+            }
+        }
+
+        container config-data-store {
+            uses config:service-ref {
+                refine type {
+                    mandatory false;
+                    config:required-identity config-dom-store-spi:config-dom-datastore;
+                }
+            }
+        }
+
+        container operational-data-store {
+            uses config:service-ref {
+                refine type {
+                    mandatory false;
+                    config:required-identity operational-dom-store-spi:operational-dom-datastore;
+                }
+            }
+        }
+
+        leaf max-data-broker-future-callback-queue-size {
+            default 1000;
+            type uint16;
+            description "The maximum queue size for the data broker's commit future callback
+                        executor.";
+        }
+
+        leaf max-data-broker-future-callback-pool-size {
+            default 20;
+            type uint16;
+            description "The maximum thread pool size for the data broker's commit future
+                        callback executor.";
+        }
+
+        leaf max-data-broker-commit-queue-size {
+            default 5000;
+            type uint16;
+            description "The maximum queue size for the data broker's commit executor.";
+        }
+    }
+
+    grouping dom-broker-operational {
+        leaf total-commits {
+            type uint64;
+        }
+
+        leaf average-commit {
+            type uint64;
+            units ns;
+        }
+
+        leaf longest-commit-duration {
+            type uint64;
+            units ns;
+        }
+
+        leaf longest-commit-timestamp {
+            type yang:date-and-time;
+        }
+
+        leaf shortest-commit-duration {
+            type uint64;
+            units ns;
+        }
+
+        leaf shortest-commit-timestamp {
+            type yang:date-and-time;
+        }
+
+        rpcx:rpc-context-instance dom-broker-rpc-ctx;
+    }
+
+    identity dom-broker-rpc-ctx;
+
+    rpc reset-statistics {
+        description
+                "JMX call to clear the toasts-made counter.";
+
+        input {
+            uses rpcx:rpc-context-ref {
+                refine context-instance {
+                    rpcx:rpc-context-instance dom-broker-rpc-ctx;
+                }
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case dom-inmemory-data-broker {
+            when "/config:modules/config:module/config:type = 'dom-inmemory-data-broker'";
+
+            uses dom-broker-config;
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case dom-inmemory-data-broker {
+            when "/config:modules/config:module/config:type = 'dom-inmemory-data-broker'";
+
+            uses dom-broker-operational;
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case schema-service-singleton {
+            when "/config:modules/config:module/config:type = 'schema-service-singleton'";
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case dom-broker-impl {
+            when "/config:modules/config:module/config:type = 'dom-broker-impl'";
+            container data {
+                uses common:data-state;
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-md-sal-common.yang b/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-md-sal-common.yang
new file mode 100644 (file)
index 0000000..bdfdb50
--- /dev/null
@@ -0,0 +1,70 @@
+module opendaylight-md-sal-common {\r
+    yang-version 1;\r
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:common";\r
+    prefix "md-sal-common";\r
+\r
+    description\r
+        "Common definition for MD-SAL.";\r
+\r
+    revision "2013-10-28" {\r
+        description\r
+                "Initial revision";\r
+    }\r
+\r
+    grouping rpc-routing-table {\r
+\r
+        leaf routing-context {\r
+            type string;\r
+        }\r
+        list routes {\r
+            leaf path {\r
+                type string;\r
+            }\r
+            leaf destination {\r
+                type string;\r
+            }\r
+        }\r
+    }\r
+\r
+    grouping rpc-router {\r
+        leaf module {\r
+            type string;\r
+        }\r
+        container routing-tables {\r
+            list routing-table {\r
+                uses rpc-routing-table;\r
+            }\r
+        }\r
+    }\r
+\r
+    grouping rpc-state {\r
+        list rpc-router {\r
+            uses rpc-router;\r
+        }\r
+    }\r
+\r
+    grouping notification-state {\r
+        container notifications {\r
+            leaf published {\r
+                type uint32;\r
+            }\r
+        }\r
+    }\r
+\r
+    grouping data-state {\r
+        container transactions {\r
+            leaf created {\r
+                type uint32;\r
+            }\r
+            leaf submitted {\r
+                type uint32;\r
+            }\r
+            leaf successful {\r
+                type uint32;\r
+            }\r
+            leaf failed {\r
+                type uint32;\r
+            }\r
+        }\r
+    }\r
+}\r
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-md-sal-dom.yang b/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-md-sal-dom.yang
new file mode 100644 (file)
index 0000000..1960c1f
--- /dev/null
@@ -0,0 +1,30 @@
+module opendaylight-md-sal-dom {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom";
+    prefix "md-sal-dom";
+
+    import config { prefix config; revision-date 2013-04-05; }
+
+    description
+        "Service definition for Binding Aware MD-SAL.";
+
+    revision "2013-10-28" {
+        description
+                "Initial revision";
+    }
+
+    identity dom-broker-osgi-registry {
+        base "config:service-type";
+        config:java-class "org.opendaylight.controller.sal.core.api.Broker";
+    }
+
+    identity dom-async-data-broker {
+        base "config:service-type";
+        config:java-class "org.opendaylight.controller.md.sal.dom.api.DOMDataBroker";
+    }
+
+    identity schema-service {
+        base "config:service-type";
+        config:java-class "org.opendaylight.controller.sal.core.api.model.SchemaService";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-operational-dom-datastore.yang b/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-operational-dom-datastore.yang
new file mode 100644 (file)
index 0000000..90197ea
--- /dev/null
@@ -0,0 +1,22 @@
+module opendaylight-operational-dom-datastore {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:operational-dom-store";
+    prefix "operational-dom-store-spi";
+
+    import config { prefix config; revision-date 2013-04-05; }
+
+    description
+        "DOM Service Provider Interface definition for MD-SAL operational
+        store";
+
+    revision "2014-06-17" {
+        description
+                "Initial revision";
+    }
+
+    identity operational-dom-datastore {
+        base "config:service-type";
+        config:java-class "org.opendaylight.controller.sal.core.spi.data.DOMStore";
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-pingpong-broker.yang b/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/opendaylight-pingpong-broker.yang
new file mode 100644 (file)
index 0000000..0ed1bbd
--- /dev/null
@@ -0,0 +1,39 @@
+module opendaylight-pingpong-broker {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:pingpong";
+    prefix "pingpong";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import opendaylight-md-sal-dom {prefix sal;}
+    import opendaylight-md-sal-common {prefix common;}
+    import opendaylight-config-dom-datastore {prefix config-dom-store-spi;}
+    import opendaylight-operational-dom-datastore {prefix operational-dom-store-spi;}
+
+    description
+        "Service definition for Ping-Pong DOM broker";
+
+    revision "2014-11-07" {
+        description
+            "Initial revision";
+    }
+
+    identity pingpong-data-broker {
+        base config:module-type;
+        config:provided-service sal:dom-async-data-broker;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case pingpong-data-broker {
+            when "/config:modules/config:module/config:type = 'pingpong-data-broker'";
+
+            container data-broker {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity sal:dom-async-data-broker;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/rpc-context.yang b/yang/yang-parser-rfc7950/src/test/resources/sal-broker-impl/rpc-context.yang
new file mode 100644 (file)
index 0000000..5c8b113
--- /dev/null
@@ -0,0 +1,32 @@
+module rpc-context {
+    yang-version 1;
+    namespace "urn:ietf:params:xml:ns:yang:rpc-context";
+    prefix "rpcx";
+
+    organization "TBD";
+
+    contact "TBD";
+
+    description "";
+
+    revision 2013-06-17 {
+        description "Initial mock";
+    }
+
+
+    grouping rpc-context-ref {
+        description "A reference to RPC context.";
+        leaf context-instance {
+            type instance-identifier;
+            description "Pointer to the context. ";
+        }
+    }
+
+    extension "rpc-context-instance" {
+        description
+            "Marks enclosing (parent) schema node as suitable RPC context.
+             The argument is identity which is used to identify RPC context
+             type.";
+        argument "context-type";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/schema-context-util-test/imported-module.yang b/yang/yang-parser-rfc7950/src/test/resources/schema-context-util-test/imported-module.yang
new file mode 100644 (file)
index 0000000..bba0989
--- /dev/null
@@ -0,0 +1,16 @@
+module imported-module {
+    yang-version 1;
+    namespace "uri:imported-module";
+    prefix imp;
+
+    revision 2014-10-07 {
+        description
+                "Imported yang model";
+    }
+
+    container my-imported-container {
+        leaf my-imported-leaf {
+            type string;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/schema-context-util-test/my-module.yang b/yang/yang-parser-rfc7950/src/test/resources/schema-context-util-test/my-module.yang
new file mode 100644 (file)
index 0000000..1bb1d21
--- /dev/null
@@ -0,0 +1,135 @@
+module my-module {
+    yang-version 1;
+    namespace "uri:my-module";
+    prefix my;
+
+    import imported-module { prefix imp; revision-date 2014-10-07; }
+
+    revision 2014-10-07 {
+        description
+                "My yang model";
+    }
+
+    grouping my-grouping {
+        container my-container-in-grouping {
+            leaf my-leaf-in-grouping {
+                type int16;
+            }
+        }
+        leaf my-leaf-in-gouping2 {
+            type string;
+        }
+        grouping my-grouping-in-grouping {
+            leaf my-leaf-in-grouping-in-grouping {
+                type string;
+            }
+        }
+    }
+
+    container my-container {
+        leaf my-leaf-in-container {
+            type int32;
+        }
+        uses my-grouping;
+        list my-list {
+            leaf my-leaf-in-list {
+                type string;
+            }
+            leaf-list my-leaf-list-in-list {
+                type int16;
+            }
+            grouping my-grouping-in-list {
+                leaf my-leaf-in-grouping-in-list {
+                    type string;
+                }
+            }
+        }
+        grouping my-grouping-in-container {
+            leaf my-leaf-in-grouping-in-container {
+                type string;
+            }
+        }
+    }
+
+    rpc my-rpc {
+        input {
+            leaf my-input-leaf {
+                type string;
+            }
+            grouping my-grouping-in-input {
+                leaf my-leaf-in-grouping-in-input {
+                    type string;
+                }
+            }
+        }
+
+        output {
+            leaf my-output-leaf {
+                type int16;
+            }
+            grouping my-grouping-in-output {
+                leaf my-leaf-in-grouping-in-output {
+                    type string;
+                }
+            }
+        }
+
+        grouping my-grouping-in-rpc {
+            leaf my-leaf-in-grouping-in-rpc {
+                type string;
+            }
+        }
+    }
+
+    notification my-notification {
+        leaf my-notification-leaf {
+            type string;
+        }
+        grouping my-grouping-in-notification {
+            leaf my-leaf-in-grouping-in-notification {
+                type string;
+            }
+        }
+    }
+
+    choice my-choice {
+        case one {
+            leaf my-choice-leaf-one {
+                type string;
+            }
+            container my-container-in-case {
+                grouping my-grouping-in-case {
+                    leaf my-leaf-in-grouping-in-case {
+                        type string;
+                    }
+                }
+            }
+        }
+        case two {
+            leaf my-choice-leaf-two {
+                type int16;
+            }
+        }
+    }
+
+    grouping my-name {
+        choice my-choice {
+            case case-one {
+                container one {
+                    //empty
+                }
+            }
+            case case-two {
+                container two {
+                    //empty
+                }
+            }
+        }
+    }
+
+    rpc my-name {
+        input {
+            uses my-name;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/imported.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/imported.yang
new file mode 100644 (file)
index 0000000..3fa44c4
--- /dev/null
@@ -0,0 +1,19 @@
+module imported {
+       namespace imp;
+       prefix imp;
+
+       container aug1 {
+        container aug11 {
+            container aug111 {
+
+            }
+        }
+    }
+
+    grouping grp {
+        container aug2 {
+            container aug22 {
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-abs-no-imp.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-abs-no-imp.yang
new file mode 100644 (file)
index 0000000..77802f4
--- /dev/null
@@ -0,0 +1,9 @@
+module root-invalid-abs-no-imp {
+       namespace root;
+       prefix root;
+
+       augment "imp:aug1/imp:aug11/imp:aug111" {
+               container add {
+               }
+       }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-abs.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-abs.yang
new file mode 100644 (file)
index 0000000..ea0ed83
--- /dev/null
@@ -0,0 +1,9 @@
+module root-invalid-abs {
+       namespace root;
+       prefix root;
+
+       augment "//aug1/aug11/aug111" {
+               container add {
+               }
+       }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-empty.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-empty.yang
new file mode 100644 (file)
index 0000000..6d46632
--- /dev/null
@@ -0,0 +1,9 @@
+module root-invalid-empty {
+       namespace root;
+       prefix root;
+
+       augment "" {
+               container add {
+               }
+       }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-rel1.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-rel1.yang
new file mode 100644 (file)
index 0000000..a35f83f
--- /dev/null
@@ -0,0 +1,9 @@
+module root-invalid-rel1 {
+       namespace root;
+       prefix root;
+
+       augment "./aug1/aug11" {
+               container add {
+               }
+       }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-rel2.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-rel2.yang
new file mode 100644 (file)
index 0000000..2c8d32a
--- /dev/null
@@ -0,0 +1,9 @@
+module root-invalid-rel2 {
+       namespace root;
+       prefix root;
+
+       augment "../aug1/aug11" {
+               container add {
+               }
+       }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-xpath.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-invalid-xpath.yang
new file mode 100644 (file)
index 0000000..2a8e82c
--- /dev/null
@@ -0,0 +1,9 @@
+module root-invalid-xpath {
+       namespace root;
+       prefix root;
+
+       augment "/aug1/-" {
+               container add {
+               }
+       }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-valid-aug-args.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augment-arg-parsing/root-valid-aug-args.yang
new file mode 100644 (file)
index 0000000..36ea846
--- /dev/null
@@ -0,0 +1,41 @@
+module root-valid-aug-args {
+       namespace root;
+       prefix root;
+
+       import imported {
+               prefix "imp";
+       }
+
+       augment "/aug1/aug11/aug111" {
+               container add {
+               }
+       }
+
+       container aug1 {
+               container aug11 {
+                       container aug111 {
+
+            }
+               }
+       }
+
+    augment "/imp:aug1/imp:aug11/imp:aug111" {
+        container add {
+        }
+    }
+
+       grouping grp {
+               container aug2 {
+                       container aug22 {
+                       }
+               }
+       }
+
+       uses grp {
+
+               augment "aug2/aug22" {
+                       container add {
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augmented.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/augmented.yang
new file mode 100644 (file)
index 0000000..7d43280
--- /dev/null
@@ -0,0 +1,11 @@
+module augmented {
+       prefix augmented_pref;
+       namespace augmented_ns;
+
+       container cont1 {
+               container cont2 {
+                       container cont-target {
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/bug2649/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/bug2649/foo.yang
new file mode 100644 (file)
index 0000000..65cb108
--- /dev/null
@@ -0,0 +1,6 @@
+module foo {
+    prefix foo;
+    namespace "namespace-foo";
+
+    import import-module { prefix imp; revision-date 1970-01-01; }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/bug2649/import-module.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/bug2649/import-module.yang
new file mode 100644 (file)
index 0000000..f5408aa
--- /dev/null
@@ -0,0 +1,8 @@
+module import-module {
+    prefix imp;
+    namespace "import-module";
+    
+    revision 1970-01-01 {
+        description "Initial revision.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/effective-module/imported.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/effective-module/imported.yang
new file mode 100644 (file)
index 0000000..2cd5514
--- /dev/null
@@ -0,0 +1,8 @@
+module imported {
+    namespace imported;
+    prefix imported;
+
+    revision "2000-01-01";
+
+    container cont {}
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/effective-module/root.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/effective-module/root.yang
new file mode 100644 (file)
index 0000000..63a76d2
--- /dev/null
@@ -0,0 +1,56 @@
+module root {
+    namespace root-ns;
+    prefix root-pref;
+
+    yang-version 1;
+    
+    import imported { prefix imp-pref; revision-date "2000-01-01"; }
+    include submod;
+    
+    organization "cisco";
+    contact "cisco email";
+
+    container cont-helper {
+        choice cont-choice {
+            case c1 {
+            }
+            case c2 {
+            }
+        }
+    }
+
+    container cont {
+        must "../cont-helper" {
+            error-message "this is an error if helper not present";
+        }
+    }
+
+    augment "/cont" {
+        container cont-aug {
+        }
+    }
+
+    notification notif1 {
+    }
+
+    rpc rpc1 {
+    }
+
+    deviation /imp-pref:cont {
+        deviate add {
+        }
+        reference "deviate reference";
+    }
+
+    identity identity1 {
+    }
+
+    feature feature1 {
+        description "feature1 description";
+        reference "feature1 reference";
+        status "current";
+    }
+
+    extension ext1 {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/effective-module/submod.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/effective-module/submod.yang
new file mode 100644 (file)
index 0000000..be060f8
--- /dev/null
@@ -0,0 +1,6 @@
+submodule submod {
+       belongs-to "root" {
+               prefix root;
+       }
+       revision "2000-01-01";
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/ext-typedef.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/ext-typedef.yang
new file mode 100644 (file)
index 0000000..02ea794
--- /dev/null
@@ -0,0 +1,26 @@
+module ext-typedef {
+    yang-version 1;
+    namespace "urn:simple.extension.typedefs";
+    prefix "ext";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    description "Model for testing and resolving of extension typedefs.";
+
+    revision "2015-03-30" {
+        reference "Initial revision.";
+    }
+
+    extension simple-ext-w-arg {
+        argument "ext-arg";
+        description "Extension with single argument definition.";
+    }
+
+    leaf myleaf {
+        type union {
+            type string;
+            type int32;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/ext-use.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/ext-use.yang
new file mode 100644 (file)
index 0000000..f2b6b65
--- /dev/null
@@ -0,0 +1,43 @@
+module ext-use {
+    yang-version 1;
+    namespace "urn:simple.extension.use";
+    prefix "ext-use";
+
+    import ext-typedef {
+        prefix "ext";
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    description "Model for testing of used extensions across model.";
+
+    revision "2015-03-30" {
+        reference "Initial revision.";
+    }
+
+    leaf value {
+      ext:simple-ext-w-arg "key:value";
+      type uint32;
+      description "value";
+    }
+
+    grouping inner-ext {
+        ext:simple-ext-w-arg "value:test";
+
+        leaf inner-value {
+          ext:simple-ext-w-arg "key:inner-value";
+          type uint32;
+          description "value";
+        }
+    }
+
+    container cont-ext {
+        ext:simple-ext-w-arg "value:test";
+
+        leaf inner-value {
+            ext:simple-ext-w-arg "value:test";
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/foobar.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/foobar.yang
new file mode 100644 (file)
index 0000000..6870055
--- /dev/null
@@ -0,0 +1,49 @@
+module foobar {
+    yang-version 1;
+    namespace "urn:opendaylight.foobar";
+    prefix "foobar";
+
+    revision "2015-07-08" {
+    }
+
+    container x {
+        leaf x15 {
+            type string;
+        }
+
+        list x10 {}
+
+        container x5 {}
+
+        leaf-list x1 {
+            type string;
+        }
+
+        uses a;
+
+        container x2 {}
+
+        uses b;
+
+        container x3 {}
+    }
+
+
+    grouping a {
+        anyxml a5;
+        anyxml a1;
+    }
+
+    grouping b {
+        anyxml b5;
+        anyxml b1;
+    }
+
+    augment /x  {
+        leaf ax15 {
+            type string;
+        }
+        container ax5 {}
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/identityreftest.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/identityreftest.yang
new file mode 100644 (file)
index 0000000..a39946c
--- /dev/null
@@ -0,0 +1,14 @@
+module identity-ref-test {
+    yang-version 1;
+    namespace "urn:irt";
+    prefix "irt";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2015-06-03" {
+        reference "Initial revision.";
+    }
+
+    identity my-type;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/cycle-yang.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/cycle-yang.yang
new file mode 100644 (file)
index 0000000..894362f
--- /dev/null
@@ -0,0 +1,8 @@
+module cycle-yang {
+    namespace cycle-yang;
+    prefix cya;
+
+    import cycle-yin {
+        prefix cyi;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/cycle-yin.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/cycle-yin.yang
new file mode 100644 (file)
index 0000000..6624dfd
--- /dev/null
@@ -0,0 +1,8 @@
+module cycle-yin {
+    namespace cycle-yin;
+    prefix cyi;
+
+    import cycle-yang {
+        prefix cya;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/egocentric.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/egocentric.yang
new file mode 100644 (file)
index 0000000..e6f3a9c
--- /dev/null
@@ -0,0 +1,8 @@
+module egocentric {
+    namespace egocentric;
+    prefix ego;
+
+    import egocentric {
+        prefix ego;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/human.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/human.yang
new file mode 100644 (file)
index 0000000..955dfa7
--- /dev/null
@@ -0,0 +1,8 @@
+module human {
+    namespace human;
+    prefix hum;
+
+    import mammal {
+        prefix mammal;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/mammal.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/mammal.yang
new file mode 100644 (file)
index 0000000..1489fa0
--- /dev/null
@@ -0,0 +1,8 @@
+module mammal {
+    namespace mammal;
+    prefix mam;
+
+    import nature {
+        prefix nature;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/nature.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/import-arg-parsing/nature.yang
new file mode 100644 (file)
index 0000000..92c3470
--- /dev/null
@@ -0,0 +1,4 @@
+module nature {
+    namespace nature;
+    prefix nat;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/importedtest.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/importedtest.yang
new file mode 100644 (file)
index 0000000..9aacac9
--- /dev/null
@@ -0,0 +1,15 @@
+module importedtest {
+    yang-version 1;
+    namespace "urn:imptst:yang";
+    prefix "imptst";
+
+    description "This is a to be imported test module";
+
+    revision 2015-03-20;
+
+    container myimportedcontainer {
+        leaf myimportedleaf {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/error-module.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/error-module.yang
new file mode 100644 (file)
index 0000000..067bf20
--- /dev/null
@@ -0,0 +1,6 @@
+module error-module {
+    prefix "errm";
+    namespace "errm";
+
+    include foo;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/error-submodule.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/error-submodule.yang
new file mode 100644 (file)
index 0000000..181434a
--- /dev/null
@@ -0,0 +1,7 @@
+submodule error-submodule {
+    belongs-to "root-module" {
+        prefix "rm";
+    }
+
+    include foo;
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/missing-parent.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/missing-parent.yang
new file mode 100644 (file)
index 0000000..d9345d8
--- /dev/null
@@ -0,0 +1,5 @@
+submodule missing-parent {
+    belongs-to foo {
+        prefix "f";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/root-module.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/root-module.yang
new file mode 100644 (file)
index 0000000..46089f3
--- /dev/null
@@ -0,0 +1,6 @@
+module root-module {
+    prefix rm;
+    namespace rm;
+
+    include "submodule-1";
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/submodule-1.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/submodule-1.yang
new file mode 100644 (file)
index 0000000..db7886f
--- /dev/null
@@ -0,0 +1,6 @@
+submodule submodule-1 {
+    belongs-to "root-module" {
+        prefix "rm";
+    }
+    include "submodule-2";
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/submodule-2.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/include-arg-parsing/submodule-2.yang
new file mode 100644 (file)
index 0000000..7b6e8ce
--- /dev/null
@@ -0,0 +1,5 @@
+submodule submodule-2 {
+    belongs-to "root-module" {
+        prefix "rm";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/key-arg-parsing/key-comp-duplicate.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/key-arg-parsing/key-comp-duplicate.yang
new file mode 100644 (file)
index 0000000..e56e1f7
--- /dev/null
@@ -0,0 +1,14 @@
+module key-comp-duplicate {
+       namespace root;
+       prefix root;
+
+       list comp {
+               key "key1 key2 key2";
+               container key1 {
+        }
+               container key2 {
+        }
+               container key3 {
+        }
+       }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/key-arg-parsing/key-simple-and-comp.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/key-arg-parsing/key-simple-and-comp.yang
new file mode 100644 (file)
index 0000000..86ce815
--- /dev/null
@@ -0,0 +1,20 @@
+module key-simple-and-comp {
+       namespace root;
+       prefix root;
+
+       list simple {
+               key "key1";
+               container key1 {
+               }
+       }
+
+       list comp {
+               key "key1 key2 key3";
+               container key1 {
+        }
+               container key2 {
+        }
+               container key3 {
+        }
+       }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/bar.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/bar.yang
new file mode 100644 (file)
index 0000000..d5cecfe
--- /dev/null
@@ -0,0 +1,119 @@
+module bar {
+    yang-version 1;
+    namespace "urn:opendaylight.bar";
+    prefix "bar";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+    description "This model define custom type definitions";
+
+    revision "2013-07-03" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef int32-ext1 {
+        type int32 {
+            range "2..20";
+        }
+    }
+
+    typedef int32-ext2 {
+        type int32-ext1 {
+            range "3..9|11..max";
+        }
+        units "mile";
+        default "11";
+    }
+
+    typedef string-ext1 {
+        type string {
+            pattern "[a-k]*";
+            length "5..11";
+        }
+    }
+
+    typedef string-ext2 {
+        type string-ext1 {
+            length "6..10";
+        }
+    }
+
+    typedef string-ext3 {
+        type string-ext2 {
+            pattern "[b-u]*";
+        }
+    }
+
+    typedef string-ext4 {
+        type string-ext3 {
+            pattern "[e-z]*";
+        }
+    }
+
+    typedef invalid-string-pattern {
+        type string {
+            //pattern "[[A-1*-%22!^^}";
+        }
+    }
+
+    typedef multiple-pattern-string {
+        type string {
+            //pattern "[[A-1*-%22!^^}";
+            pattern "[e-z]*";
+        }
+    }
+
+    typedef my-decimal-type {
+        type decimal64 {
+            fraction-digits 6;
+        }
+    }
+
+    typedef my-union {
+        type union {
+            type int16 {
+                range "1..100";
+            }
+            type int32;
+        }
+    }
+
+    typedef my-union-ext {
+        type my-union;
+    }
+
+    typedef nested-union2 {
+        type union {
+            type my-union-ext;
+            type string;
+        }
+    }
+
+    container interfaces {
+        grouping ifEntry {
+            container augment-holder;
+        }
+        list ifEntry {
+            key "ifIndex";
+
+            leaf ifIndex {
+                type uint32;
+                units minutes;
+            }
+
+            leaf ifMtu {
+                type int32;
+            }
+
+            min-elements 1;
+            max-elements 11;
+        }
+    }
+
+    extension opendaylight {
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/baz.yang
new file mode 100644 (file)
index 0000000..c50ce61
--- /dev/null
@@ -0,0 +1,197 @@
+module baz {
+    yang-version 1;
+    namespace "urn:opendaylight.baz";
+    prefix "baz";
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef union1 {
+        type union2;
+    }
+
+    typedef union2 {
+        type union {
+            type int32;
+            type br:nested-union2;
+        }
+    }
+
+    augment "/br:interfaces/br:ifEntry" {
+        when "if:ifType='ds0'";
+        container augment-holder {
+            description "Description for augment holder";
+        }
+    }
+
+    augment "/br:interfaces/br:ifEntry" {
+        when "if:ifType='ds2'";
+        container augment-holder2 {
+            description "Description for augment holder";
+        }
+    }
+
+    container network {
+        baz:c-define point {
+        }
+
+        description "network-description";
+        reference "network-reference";
+        status obsolete;
+        config true;
+        presence "some presence text";
+    }
+
+    feature local-storage {
+        description
+            "This feature means the device supports local
+             storage (memory, flash or disk) that can be used to
+             store syslog messages.";
+    }
+
+    feature candidate;
+    feature startup;
+
+    extension c-define {
+        description "Takes as argument a name string. Makes the code generator use the given name in the #define.";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+    notification event {
+        leaf event-class {
+            type string;
+        }
+        anyxml reporting-entity;
+        leaf severity {
+            type string;
+        }
+    }
+
+    rpc get-config {
+        description "Retrieve all or part of a specified configuration.";
+        reference "RFC 6241, Section 7.1";
+
+        input {
+            container source {
+                description
+                  "Particular configuration to retrieve.";
+
+                choice config-source {
+                    mandatory true;
+                    description
+                      "The configuration to retrieve.";
+                    case a {
+                        leaf candidate {
+                            if-feature candidate;
+                            type empty;
+                            description
+                              "The candidate configuration is the config source.";
+                        }
+                    }
+                    case b {
+                        leaf running {
+                            type empty;
+                            description
+                              "The running configuration is the config source.";
+                        }
+                    }
+                    case c {
+                        leaf startup {
+                            if-feature startup;
+                            type empty;
+                            description
+                              "The startup configuration is the config source.
+                               This is optional-to-implement on the server because
+                               not all servers will support filtering for this
+                               datastore.";
+                        }
+                    }
+                }
+            }
+
+            anyxml filter {
+                description "Subtree or XPath filter to use.";
+                baz:c-define element-attributes;
+            }
+        }
+
+        output {
+            anyxml data {
+                description
+                  "Copy of the source datastore subset that matched
+                   the filter criteria (if any).  An empty data container
+                   indicates that the request did not produce any results.";
+            }
+        }
+    }
+
+    grouping target {
+        anyxml data {
+            config true;
+            description "Copy of the source datastore subset.";
+            mandatory false;
+            must "test-condition-text";
+            reference "test-no-reference";
+            status "obsolete";
+            when "test-when-text";
+        }
+        choice how {
+            description "test choice description";
+            default interval;
+            case interval {
+                leaf interval {
+                    type uint16;
+                    default 30;
+                    units minutes;
+                }
+            }
+            case daily {
+                leaf daily {
+                    type empty;
+                }
+                leaf time-of-day {
+                    type string;
+                    units 24-hour-clock;
+                    default 1am;
+                }
+            }
+        }
+        leaf address {
+            type string;
+            description "Target IP address";
+            mandatory true;
+        }
+        container port {
+            description "Target port container";
+        }
+        list addresses {
+            key "id";
+            leaf id {
+                type int8;
+            }
+        }
+        grouping target-inner {
+            description "target-inner default description";
+            leaf inner-grouping-id {
+                type int8;
+            }
+        }
+        typedef group-type {
+            type br:my-decimal-type;
+        }
+        
+        br:opendaylight awesome;
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/foo.yang
new file mode 100644 (file)
index 0000000..4869c64
--- /dev/null
@@ -0,0 +1,203 @@
+module foo {
+    yang-version 1;
+    namespace "urn:opendaylight.foo";
+    prefix "foo";
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    import baz {
+        prefix "bz";
+        revision-date 2013-02-27;
+    }
+
+    include subfoo {
+        revision-date "2013-02-27";
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2013-02-27" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    typedef identifier {
+        type int32;
+    }
+
+    leaf int32-leaf {
+        type br:int32-ext2 {
+            range "12..max";
+        }
+    }
+
+    leaf string-leaf {
+        type br:string-ext4;
+    }
+
+    leaf invalid-pattern-string-leaf {
+        type br:invalid-string-pattern;
+    }
+
+    leaf invalid-direct-string-pattern-def-leaf {
+        type string {
+            //pattern "[[A-1*-%22!^^}";
+        }
+    }
+
+    leaf multiple-pattern-string-leaf {
+        type br:multiple-pattern-string;
+    }
+
+    leaf multiple-pattern-direct-string-def-leaf {
+        type string {
+            pattern "[e-z]*";
+            //pattern "[[A-1*-%22!^^}";
+            pattern "[a-d]*";
+        }
+    }
+
+    leaf length-leaf {
+        type br:string-ext2 {
+            length "7..max";
+        }
+    }
+
+    leaf decimal-leaf {
+        type br:my-decimal-type {
+            fraction-digits 6;
+        }
+    }
+
+    leaf decimal-leaf2 {
+        type br:my-decimal-type;
+    }
+
+    container ext {
+        bz:c-define "MY_INTERFACES";
+    }
+
+    leaf union-leaf {
+        type br:my-union-ext;
+    }
+
+    deviation /br:interfaces/br:ifEntry {
+        deviate add {
+            config "true";
+        }
+        reference "system/user ref";
+    }
+
+    leaf custom-union-leaf {
+        type bz:union1;
+    }
+
+    container transfer {
+        choice how {
+            default interval;
+            container input {
+            }
+            list output {
+                leaf id {
+                    type string;
+                }
+            }
+            case interval {
+                leaf interval {
+                    type uint16;
+                    default 30;
+                    units minutes;
+                }
+            }
+            case daily {
+                leaf daily {
+                    type empty;
+                }
+                leaf time-of-day {
+                    type string;
+                    units 24-hour-clock;
+                    default 1am;
+                }
+            }
+            case manual {
+                leaf manual {
+                    type empty;
+                }
+            }
+        }
+    }
+
+    anyxml datas {
+        description
+          "Copy of the source typesstore subset that matched
+           the filter criteria (if any).  An empty types container
+           indicates that the request did not produce any results.";
+        status obsolete;
+    }
+
+    container mycont {
+        container innercont {
+            typedef mytype {
+                type string;
+            }
+            leaf myleaf {
+                type mytype;
+            }
+        }
+    }
+
+    uses bz:target {
+        augment "how/interval" {
+            description "inner augment";
+            leaf name {
+                type string;
+            }
+        }
+    }
+
+    container peer {
+        container destination {
+            uses bz:target {
+                refine address {
+                    default "1.2.3.4";
+                    description "IP address of target node";
+                    reference "address reference added by refine";
+                    config false;
+                    mandatory false;
+                    must "ifType != 'ethernet' or " +
+                            "(ifType = 'ethernet' and ifMTU = 1500)" {
+                        error-message "An ethernet MTU must be 1500";
+                    }
+                    foo:mountpoint "mnt-extension";
+                }
+                refine port {
+                    description "description of port defined by refine";
+                    reference "port reference added by refine";
+                    config false;
+                    presence "presence is required";
+                }
+                refine "addresses" {
+                    description "description of addresses defined by refine";
+                    reference "addresses reference added by refine";
+                    config false;
+                    min-elements 2;
+                    max-elements unbounded;
+                }
+                refine addresses/id {
+                    description "id of address";
+                }
+            }
+        }
+    }
+
+    extension mountpoint {
+        description "enter point";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/subfoo.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/model/subfoo.yang
new file mode 100644 (file)
index 0000000..9c003fe
--- /dev/null
@@ -0,0 +1,65 @@
+submodule subfoo {
+    yang-version 1;
+
+    belongs-to foo {
+        prefix f;
+    }
+
+    import bar {
+        prefix "br";
+        revision-date 2013-07-03;
+    }
+
+    import baz {
+        prefix "bz";
+        revision-date 2013-02-27;
+    }
+
+    revision "2013-02-27" {
+    }
+
+    leaf id {
+        type br:int32-ext2 {
+            range "12..max";
+        }
+    }
+
+    leaf foo-id {
+        type f:identifier;
+    }
+
+    container sub-ext {
+        bz:c-define "MY_INTERFACES";
+    }
+
+    container sub-transfer {
+        choice how {
+            default input;
+            container input {
+            }
+            list output {
+                leaf id {
+                    type string;
+                }
+            }
+            case manual {
+                leaf manual {
+                    type empty;
+                }
+            }
+        }
+    }
+
+    anyxml sub-datas {
+        status obsolete;
+    }
+
+    extension sub-mountpoint {
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+    f:mountpoint "aaa";
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-inet-types.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-inet-types.yang
new file mode 100644 (file)
index 0000000..de20feb
--- /dev/null
@@ -0,0 +1,418 @@
+ module ietf-inet-types {
+
+   namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
+   prefix "inet";
+
+   organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+   contact
+    "WG Web:   <http://tools.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+
+     WG Chair: David Partain
+               <mailto:david.partain@ericsson.com>
+
+     WG Chair: David Kessens
+               <mailto:david.kessens@nsn.com>
+
+     Editor:   Juergen Schoenwaelder
+               <mailto:j.schoenwaelder@jacobs-university.de>";
+
+   description
+    "This module contains a collection of generally useful derived
+     YANG data types for Internet addresses and related things.
+
+     Copyright (c) 2010 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or without
+     modification, is permitted pursuant to, and subject to the license
+     terms contained in, the Simplified BSD License set forth in Section
+     4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6021; see
+     the RFC itself for full legal notices.";
+
+   revision 2010-09-24 {
+     description
+      "Initial revision.";
+     reference
+      "RFC 6021: Common YANG Data Types";
+   }
+
+   /*** collection of protocol field related types ***/
+
+   typedef ip-version {
+     type enumeration {
+       enum unknown {
+         value "0";
+         description
+          "An unknown or unspecified version of the Internet protocol.";
+       }
+       enum ipv4 {
+         value "1";
+         description
+          "The IPv4 protocol as defined in RFC 791.";
+       }
+       enum ipv6 {
+         value "2";
+         description
+          "The IPv6 protocol as defined in RFC 2460.";
+       }
+     }
+     description
+      "This value represents the version of the IP protocol.
+
+       In the value set and its semantics, this type is equivalent
+       to the InetVersion textual convention of the SMIv2.";
+     reference
+      "RFC  791: Internet Protocol
+       RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+       RFC 4001: Textual Conventions for Internet Network Addresses";
+   }
+
+   typedef dscp {
+     type uint8 {
+       range "0..63";
+     }
+     description
+      "The dscp type represents a Differentiated Services Code-Point
+       that may be used for marking packets in a traffic stream.
+
+       In the value set and its semantics, this type is equivalent
+       to the Dscp textual convention of the SMIv2.";
+     reference
+      "RFC 3289: Management Information Base for the Differentiated
+                 Services Architecture
+       RFC 2474: Definition of the Differentiated Services Field
+                 (DS Field) in the IPv4 and IPv6 Headers
+       RFC 2780: IANA Allocation Guidelines For Values In
+                 the Internet Protocol and Related Headers";
+   }
+
+   typedef ipv6-flow-label {
+     type uint32 {
+       range "0..1048575";
+     }
+     description
+      "The flow-label type represents flow identifier or Flow Label
+       in an IPv6 packet header that may be used to discriminate
+       traffic flows.
+
+       In the value set and its semantics, this type is equivalent
+       to the IPv6FlowLabel textual convention of the SMIv2.";
+     reference
+      "RFC 3595: Textual Conventions for IPv6 Flow Label
+       RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
+   }
+
+   typedef port-number {
+     type uint16 {
+       range "0..65535";
+     }
+     description
+      "The port-number type represents a 16-bit port number of an
+       Internet transport layer protocol such as UDP, TCP, DCCP, or
+       SCTP.  Port numbers are assigned by IANA.  A current list of
+       all assignments is available from <http://www.iana.org/>.
+
+       Note that the port number value zero is reserved by IANA.  In
+       situations where the value zero does not make sense, it can
+       be excluded by subtyping the port-number type.
+
+       In the value set and its semantics, this type is equivalent
+       to the InetPortNumber textual convention of the SMIv2.";
+     reference
+      "RFC  768: User Datagram Protocol
+       RFC  793: Transmission Control Protocol
+       RFC 4960: Stream Control Transmission Protocol
+       RFC 4340: Datagram Congestion Control Protocol (DCCP)
+       RFC 4001: Textual Conventions for Internet Network Addresses";
+   }
+
+   /*** collection of autonomous system related types ***/
+
+   typedef as-number {
+     type uint32;
+     description
+      "The as-number type represents autonomous system numbers
+       which identify an Autonomous System (AS).  An AS is a set
+       of routers under a single technical administration, using
+       an interior gateway protocol and common metrics to route
+       packets within the AS, and using an exterior gateway
+       protocol to route packets to other ASs'.  IANA maintains
+       the AS number space and has delegated large parts to the
+       regional registries.
+
+       Autonomous system numbers were originally limited to 16
+       bits.  BGP extensions have enlarged the autonomous system
+       number space to 32 bits.  This type therefore uses an uint32
+       base type without a range restriction in order to support
+       a larger autonomous system number space.
+
+       In the value set and its semantics, this type is equivalent
+       to the InetAutonomousSystemNumber textual convention of
+       the SMIv2.";
+     reference
+      "RFC 1930: Guidelines for creation, selection, and registration
+                 of an Autonomous System (AS)
+       RFC 4271: A Border Gateway Protocol 4 (BGP-4)
+       RFC 4893: BGP Support for Four-octet AS Number Space
+       RFC 4001: Textual Conventions for Internet Network Addresses";
+   }
+
+   /*** collection of IP address and hostname related types ***/
+
+   typedef ip-address {
+     type union {
+       type inet:ipv4-address;
+       type inet:ipv6-address;
+     }
+     description
+      "The ip-address type represents an IP address and is IP
+       version neutral.  The format of the textual representations
+       implies the IP version.";
+   }
+
+   typedef ipv4-address {
+     type string {
+       pattern
+         '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+       +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+       + '(%[\p{N}\p{L}]+)?';
+     }
+     description
+       "The ipv4-address type represents an IPv4 address in
+        dotted-quad notation.  The IPv4 address may include a zone
+        index, separated by a % sign.
+
+        The zone index is used to disambiguate identical address
+        values.  For link-local addresses, the zone index will
+        typically be the interface index number or the name of an
+        interface.  If the zone index is not present, the default
+        zone of the device will be used.
+
+        The canonical format for the zone index is the numerical
+        format";
+   }
+
+   typedef ipv6-address {
+     type string {
+       pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+             + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+             + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+             + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+             + '(%[\p{N}\p{L}]+)?';
+       pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+             + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+             + '(%.+)?';
+     }
+     description
+      "The ipv6-address type represents an IPv6 address in full,
+       mixed, shortened, and shortened-mixed notation.  The IPv6
+       address may include a zone index, separated by a % sign.
+
+       The zone index is used to disambiguate identical address
+       values.  For link-local addresses, the zone index will
+       typically be the interface index number or the name of an
+       interface.  If the zone index is not present, the default
+       zone of the device will be used.
+
+       The canonical format of IPv6 addresses uses the compressed
+       format described in RFC 4291, Section 2.2, item 2 with the
+       following additional rules: the :: substitution must be
+       applied to the longest sequence of all-zero 16-bit chunks
+       in an IPv6 address.  If there is a tie, the first sequence
+       of all-zero 16-bit chunks is replaced by ::.  Single
+       all-zero 16-bit chunks are not compressed.  The canonical
+       format uses lowercase characters and leading zeros are
+       not allowed.  The canonical format for the zone index is
+       the numerical format as described in RFC 4007, Section
+       11.2.";
+     reference
+      "RFC 4291: IP Version 6 Addressing Architecture
+       RFC 4007: IPv6 Scoped Address Architecture
+       RFC 5952: A Recommendation for IPv6 Address Text Representation";
+   }
+
+   typedef ip-prefix {
+     type union {
+       type inet:ipv4-prefix;
+       type inet:ipv6-prefix;
+     }
+     description
+      "The ip-prefix type represents an IP prefix and is IP
+       version neutral.  The format of the textual representations
+       implies the IP version.";
+   }
+
+   typedef ipv4-prefix {
+     type string {
+       pattern
+          '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+        +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+        + '/(([0-9])|([1-2][0-9])|(3[0-2]))';
+     }
+     description
+      "The ipv4-prefix type represents an IPv4 address prefix.
+       The prefix length is given by the number following the
+       slash character and must be less than or equal to 32.
+
+       A prefix length value of n corresponds to an IP address
+       mask that has n contiguous 1-bits from the most
+       significant bit (MSB) and all other bits set to 0.
+
+       The canonical format of an IPv4 prefix has all bits of
+       the IPv4 address set to zero that are not part of the
+       IPv4 prefix.";
+   }
+
+   typedef ipv6-prefix {
+     type string {
+       pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+             + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+             + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+             + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+             + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
+       pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+             + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+             + '(/.+)';
+     }
+     description
+      "The ipv6-prefix type represents an IPv6 address prefix.
+       The prefix length is given by the number following the
+       slash character and must be less than or equal 128.
+
+       A prefix length value of n corresponds to an IP address
+       mask that has n contiguous 1-bits from the most
+       significant bit (MSB) and all other bits set to 0.
+
+       The IPv6 address should have all bits that do not belong
+       to the prefix set to zero.
+
+       The canonical format of an IPv6 prefix has all bits of
+       the IPv6 address set to zero that are not part of the
+       IPv6 prefix.  Furthermore, IPv6 address is represented
+       in the compressed format described in RFC 4291, Section
+       2.2, item 2 with the following additional rules: the ::
+       substitution must be applied to the longest sequence of
+       all-zero 16-bit chunks in an IPv6 address.  If there is
+       a tie, the first sequence of all-zero 16-bit chunks is
+       replaced by ::.  Single all-zero 16-bit chunks are not
+       compressed.  The canonical format uses lowercase
+       characters and leading zeros are not allowed.";
+     reference
+      "RFC 4291: IP Version 6 Addressing Architecture";
+   }
+
+   /*** collection of domain name and URI types ***/
+
+   typedef domain-name {
+     type string {
+       pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+            +  '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+            +  '|\.';
+       length "1..253";
+     }
+     description
+      "The domain-name type represents a DNS domain name.  The
+       name SHOULD be fully qualified whenever possible.
+
+       Internet domain names are only loosely specified.  Section
+       3.5 of RFC 1034 recommends a syntax (modified in Section
+       2.1 of RFC 1123).  The pattern above is intended to allow
+       for current practice in domain name use, and some possible
+       future expansion.  It is designed to hold various types of
+       domain names, including names used for A or AAAA records
+       (host names) and other records, such as SRV records.  Note
+       that Internet host names have a stricter syntax (described
+       in RFC 952) than the DNS recommendations in RFCs 1034 and
+       1123, and that systems that want to store host names in
+       schema nodes using the domain-name type are recommended to
+       adhere to this stricter standard to ensure interoperability.
+
+       The encoding of DNS names in the DNS protocol is limited
+       to 255 characters.  Since the encoding consists of labels
+       prefixed by a length bytes and there is a trailing NULL
+       byte, only 253 characters can appear in the textual dotted
+       notation.
+
+       The description clause of schema nodes using the domain-name
+       type MUST describe when and how these names are resolved to
+       IP addresses.  Note that the resolution of a domain-name value
+       may require to query multiple DNS records (e.g., A for IPv4
+       and AAAA for IPv6).  The order of the resolution process and
+       which DNS record takes precedence can either be defined
+       explicitely or it may depend on the configuration of the
+       resolver.
+
+       Domain-name values use the US-ASCII encoding.  Their canonical
+       format uses lowercase US-ASCII characters.  Internationalized
+       domain names MUST be encoded in punycode as described in RFC
+       3492";
+     reference
+      "RFC  952: DoD Internet Host Table Specification
+       RFC 1034: Domain Names - Concepts and Facilities
+       RFC 1123: Requirements for Internet Hosts -- Application
+                 and Support
+       RFC 2782: A DNS RR for specifying the location of services
+                 (DNS SRV)
+       RFC 3492: Punycode: A Bootstring encoding of Unicode for
+                 Internationalized Domain Names in Applications
+                 (IDNA)
+       RFC 5891: Internationalizing Domain Names in Applications
+                 (IDNA): Protocol";
+   }
+
+   typedef host {
+     type union {
+       type inet:ip-address;
+       type inet:domain-name;
+     }
+     description
+      "The host type represents either an IP address or a DNS
+       domain name.";
+   }
+
+   typedef uri {
+     type string;
+     description
+      "The uri type represents a Uniform Resource Identifier
+       (URI) as defined by STD 66.
+
+       Objects using the uri type MUST be in US-ASCII encoding,
+       and MUST be normalized as described by RFC 3986 Sections
+       6.2.1, 6.2.2.1, and 6.2.2.2.  All unnecessary
+       percent-encoding is removed, and all case-insensitive
+       characters are set to lowercase except for hexadecimal
+       digits, which are normalized to uppercase as described in
+       Section 6.2.2.1.
+
+       The purpose of this normalization is to help provide
+       unique URIs.  Note that this normalization is not
+       sufficient to provide uniqueness.  Two URIs that are
+       textually distinct after this normalization may still be
+       equivalent.
+
+       Objects using the uri type may restrict the schemes that
+       they permit.  For example, 'data:' and 'urn:' schemes
+       might not be appropriate.
+
+       A zero-length URI is not a valid URI.  This can be used to
+       express 'URI absent' where required.
+
+       In the value set and its semantics, this type is equivalent
+       to the Uri SMIv2 textual convention defined in RFC 5017.";
+     reference
+      "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
+       RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
+                 Group: Uniform Resource Identifiers (URIs), URLs,
+                 and Uniform Resource Names (URNs): Clarifications
+                 and Recommendations
+       RFC 5017: MIB Textual Conventions for Uniform Resource
+                 Identifiers (URIs)";
+   }
+
+ }
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-interfaces.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-interfaces.yang
new file mode 100644 (file)
index 0000000..481a5d2
--- /dev/null
@@ -0,0 +1,469 @@
+module ietf-interfaces {
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces";
+  prefix if;
+
+  import ietf-yang-types {
+    prefix yang;
+    revision-date 2010-09-24;
+  }
+
+  organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+    "WG Web:   <http://tools.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+
+     WG Chair: David Kessens
+               <mailto:david.kessens@nsn.com>
+
+     WG Chair: Juergen Schoenwaelder
+               <mailto:j.schoenwaelder@jacobs-university.de>
+
+     Editor:   Martin Bjorklund
+               <mailto:mbj@tail-f.com>";
+
+  description
+    "This module contains a collection of YANG definitions for
+     managing network interfaces.
+
+     Copyright (c) 2012 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD License
+     set forth in Section 4.c of the IETF Trust's Legal Provisions
+     Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC XXXX; see
+     the RFC itself for full legal notices.";
+
+  // RFC Ed.: replace XXXX with actual RFC number and remove this
+  // note.
+
+  // RFC Ed.: update the date below with the date of RFC publication
+  // and remove this note.
+  revision 2012-11-15 {
+    description
+      "Initial revision.";
+    reference
+      "RFC XXXX: A YANG Data Model for Interface Management";
+  }
+
+  /* Typedefs */
+
+  typedef interface-ref {
+    type leafref {
+      path "/if:interfaces/if:interface/if:name";
+    }
+    description
+      "This type is used by data models that need to reference
+       interfaces.";
+  }
+
+  /* Features */
+
+  feature arbitrary-names {
+    description
+      "This feature indicates that the server allows interfaces to
+       be named arbitrarily.";
+  }
+
+  feature if-mib {
+    description
+      "This feature indicates that the server implements IF-MIB.";
+    reference
+      "RFC 2863: The Interfaces Group MIB";
+  }
+
+  /* Data nodes */
+
+  container interfaces {
+    description
+      "Interface parameters.";
+
+    list interface {
+      key "name";
+      unique "type location";
+
+      description
+        "The list of interfaces on the device.";
+
+      leaf name {
+        type string;
+        description
+          "The name of the interface.
+
+           A device MAY restrict the allowed values for this leaf,
+           possibly depending on the type and location.
+
+           If the device allows arbitrarily named interfaces, the
+           feature 'arbitrary-names' is advertised.
+
+           This leaf MAY be mapped to ifName by an implementation.
+           Such an implementation MAY restrict the allowed values for
+           this leaf so that it matches the restrictions of ifName.
+           If a NETCONF server that implements this restriction is
+           sent a value that doesn't match the restriction, it MUST
+           reply with an rpc-error with the error-tag
+           'invalid-value'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifName";
+      }
+
+      leaf description {
+        type string;
+        description
+          "A textual description of the interface.
+
+           This leaf MAY be mapped to ifAlias by an implementation.
+           Such an implementation MAY restrict the allowed values for
+           this leaf so that it matches the restrictions of ifAlias.
+           If a NETCONF server that implements this restriction is
+           sent a value that doesn't match the restriction, it MUST
+           reply with an rpc-error with the error-tag
+           'invalid-value'.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifAlias";
+      }
+
+      leaf location {
+        type string;
+        description
+          "The device-specific location of the interface of a
+           particular type.  The format of the location string
+           depends on the interface type and the device.
+
+           If the interface's type represents a physical interface,
+           this leaf MUST be set.
+
+           When an interface entry is created, a server MAY
+           initialize the location leaf with a valid value, e.g., if
+           it is possible to derive the location from the name of
+           the interface.";
+      }
+
+      leaf enabled {
+        type boolean;
+        default "true";
+        description
+          "The desired state of the interface.
+
+           This leaf contains the configured, desired state of the
+           interface.  Systems that implement the IF-MIB use the
+           value of this leaf to set IF-MIB.ifAdminStatus to 'up' or
+           'down' after an ifEntry has been initialized, as described
+           in RFC 2863.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+      }
+
+      leaf last-change {
+        type yang:date-and-time;
+        config false;
+        description
+          "The time the interface entered its current operational
+           state.  If the current state was entered prior to the
+           last re-initialization of the local network management
+           subsystem, then this node is not present.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifLastChange";
+      }
+
+      leaf if-index {
+        if-feature if-mib;
+        type int32 {
+          range "1..2147483647";
+        }
+        config false;
+        description
+          "The ifIndex value for the ifEntry represented by this
+           interface.
+
+           Media-specific modules must specify how the type is
+           mapped to entries in the ifTable.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifIndex";
+      }
+
+      leaf phys-address {
+        type yang:phys-address;
+        config false;
+        description
+          "The interface's address at its protocol sub-layer.  For
+          example, for an 802.x interface, this object normally
+          contains a MAC address.  The interface's media-specific
+          modules must define the bit and byte ordering and the
+          format of the value of this object.  For interfaces that do
+          not have such an address (e.g., a serial line), this node
+          is not present.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifPhysAddress";
+      }
+
+      leaf-list higher-layer-if {
+        type interface-ref;
+        config false;
+        description
+          "A list of references to interfaces layered on top of this
+           interface.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifStackTable";
+      }
+
+      leaf-list lower-layer-if {
+        type interface-ref;
+        config false;
+        description
+          "A list of references to interfaces layered underneath this
+           interface.";
+        reference
+          "RFC 2863: The Interfaces Group MIB - ifStackTable";
+      }
+
+      leaf speed {
+        type yang:gauge64;
+        units "bits / second";
+        config false;
+        description
+            "An estimate of the interface's current bandwidth in bits
+             per second.  For interfaces which do not vary in
+             bandwidth or for those where no accurate estimation can
+             be made, this node should contain the nominal bandwidth.
+             For interfaces that has no concept of bandwidth, this
+             node is not present.";
+        reference
+          "RFC 2863: The Interfaces Group MIB -
+                     ifSpeed, ifHighSpeed";
+      }
+
+      container statistics {
+        config false;
+        description
+          "A collection of interface-related statistics objects.";
+
+        leaf discontinuity-time {
+          type yang:date-and-time;
+          description
+            "The time on the most recent occasion at which any one or
+             more of this interface's counters suffered a
+             discontinuity.  If no such discontinuities have occurred
+             since the last re-initialization of the local management
+             subsystem, then this node contains the time the local
+             management subsystem re-initialized itself.";
+        }
+
+        leaf in-octets {
+          type yang:counter64;
+          description
+            "The total number of octets received on the interface,
+             including framing characters.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifHCInOctets";
+        }
+        leaf in-unicast-pkts {
+          type yang:counter64;
+          description
+            "The number of packets, delivered by this sub-layer to a
+             higher (sub-)layer, which were not addressed to a
+             multicast or broadcast address at this sub-layer.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts";
+        }
+        leaf in-broadcast-pkts {
+          type yang:counter64;
+          description
+            "The number of packets, delivered by this sub-layer to a
+             higher (sub-)layer, which were addressed to a broadcast
+             address at this sub-layer.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB -
+                       ifHCInBroadcastPkts";
+        }
+        leaf in-multicast-pkts {
+          type yang:counter64;
+          description
+            "The number of packets, delivered by this sub-layer to a
+             higher (sub-)layer, which were addressed to a multicast
+             address at this sub-layer.  For a MAC layer protocol,
+             this includes both Group and Functional addresses.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB -
+                       ifHCInMulticastPkts";
+        }
+        leaf in-discards {
+          type yang:counter32;
+          description
+            "The number of inbound packets which were chosen to be
+             discarded even though no errors had been detected to
+             prevent their being deliverable to a higher-layer
+             protocol.  One possible reason for discarding such a
+             packet could be to free up buffer space.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifInDiscards";
+        }
+        leaf in-errors {
+          type yang:counter32;
+          description
+            "For packet-oriented interfaces, the number of inbound
+             packets that contained errors preventing them from being
+             deliverable to a higher-layer protocol.  For character-
+             oriented or fixed-length interfaces, the number of
+             inbound transmission units that contained errors
+             preventing them from being deliverable to a higher-layer
+             protocol.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifInErrors";
+        }
+        leaf in-unknown-protos {
+          type yang:counter32;
+          description
+            "For packet-oriented interfaces, the number of packets
+             received via the interface which were discarded because
+             of an unknown or unsupported protocol.  For
+             character-oriented or fixed-length interfaces that
+             support protocol multiplexing the number of transmission
+             units received via the interface which were discarded
+             because of an unknown or unsupported protocol.  For any
+             interface that does not support protocol multiplexing,
+             this counter is not present.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos";
+        }
+
+        leaf out-octets {
+          type yang:counter64;
+          description
+            "The total number of octets transmitted out of the
+             interface, including framing characters.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifHCOutOctets";
+        }
+        leaf out-unicast-pkts {
+          type yang:counter64;
+          description
+            "The total number of packets that higher-level protocols
+             requested be transmitted, and which were not addressed
+             to a multicast or broadcast address at this sub-layer,
+             including those that were discarded or not sent.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts";
+        }
+        leaf out-broadcast-pkts {
+          type yang:counter64;
+          description
+            "The total number of packets that higher-level protocols
+             requested be transmitted, and which were addressed to a
+             broadcast address at this sub-layer, including those
+             that were discarded or not sent.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB -
+                       ifHCOutBroadcastPkts";
+        }
+        leaf out-multicast-pkts {
+          type yang:counter64;
+          description
+            "The total number of packets that higher-level protocols
+             requested be transmitted, and which were addressed to a
+             multicast address at this sub-layer, including those
+             that were discarded or not sent.  For a MAC layer
+             protocol, this includes both Group and Functional
+             addresses.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB -
+                       ifHCOutMulticastPkts";
+        }
+        leaf out-discards {
+          type yang:counter32;
+          description
+            "The number of outbound packets which were chosen to be
+             discarded even though no errors had been detected to
+             prevent their being transmitted.  One possible reason
+             for discarding such a packet could be to free up buffer
+             space.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifOutDiscards";
+        }
+        leaf out-errors {
+          type yang:counter32;
+          description
+            "For packet-oriented interfaces, the number of outbound
+             packets that could not be transmitted because of errors.
+             For character-oriented or fixed-length interfaces, the
+             number of outbound transmission units that could not be
+             transmitted because of errors.
+
+             Discontinuities in the value of this counter can occur
+             at re-initialization of the management system, and at
+             other times as indicated by the value of
+             'discontinuity-time'.";
+          reference
+            "RFC 2863: The Interfaces Group MIB - ifOutErrors";
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-netconf-monitoring.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-netconf-monitoring.yang
new file mode 100644 (file)
index 0000000..b4ad89b
--- /dev/null
@@ -0,0 +1,593 @@
+module ietf-netconf-monitoring {
+
+    yang-version 1;
+
+    namespace
+      "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
+
+    prefix ncm;
+
+    import ietf-yang-types {
+      prefix yang;
+    }
+    import ietf-inet-types {
+      prefix inet;
+    }
+
+    organization
+      "IETF NETCONF (Network Configuration) Working Group";
+
+    contact
+      "WG Web:   <http://tools.ietf.org/wg/netconf/>
+     WG List:  <mailto:netconf@ietf.org>
+
+     WG Chair: Mehmet Ersue
+               <mailto:mehmet.ersue@nsn.com>
+
+     WG Chair: Bert Wijnen
+               <mailto:bertietf@bwijnen.net>
+
+     Editor:   Mark Scott
+               <mailto:mark.scott@ericsson.com>
+
+     Editor:   Martin Bjorklund
+               <mailto:mbj@tail-f.com>";
+
+    description
+      "NETCONF Monitoring Module.
+     All elements in this module are read-only.
+
+     Copyright (c) 2010 IETF Trust and the persons identified as
+     authors of the code. All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD
+     License set forth in Section 4.c of the IETF Trust's
+     Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6022; see
+     the RFC itself for full legal notices.";
+
+    revision "2010-10-04" {
+      description "Initial revision.";
+      reference
+        "RFC 6022: YANG Module for NETCONF Monitoring";
+
+    }
+
+
+    typedef netconf-datastore-type {
+      type enumeration {
+        enum "running" {
+          value 0;
+        }
+        enum "candidate" {
+          value 1;
+        }
+        enum "startup" {
+          value 2;
+        }
+      }
+      description
+        "Enumeration of possible NETCONF datastore types.";
+      reference
+        "RFC 4741: NETCONF Configuration Protocol";
+
+    }
+
+    identity transport {
+      description
+        "Base identity for NETCONF transport types.";
+    }
+
+    identity netconf-ssh {
+      base transport;
+      description
+        "NETCONF over Secure Shell (SSH).";
+      reference
+        "RFC 4742: Using the NETCONF Configuration Protocol
+              over Secure SHell (SSH)";
+
+    }
+
+    identity netconf-soap-over-beep {
+      base transport;
+      description
+        "NETCONF over Simple Object Access Protocol (SOAP) over
+       Blocks Extensible Exchange Protocol (BEEP).";
+      reference
+        "RFC 4743: Using NETCONF over the Simple Object
+              Access Protocol (SOAP)";
+
+    }
+
+    identity netconf-soap-over-https {
+      base transport;
+      description
+        "NETCONF over Simple Object Access Protocol (SOAP)
+      over Hypertext Transfer Protocol Secure (HTTPS).";
+      reference
+        "RFC 4743: Using NETCONF over the Simple Object
+              Access Protocol (SOAP)";
+
+    }
+
+    identity netconf-beep {
+      base transport;
+      description
+        "NETCONF over Blocks Extensible Exchange Protocol (BEEP).";
+      reference
+        "RFC 4744: Using the NETCONF Protocol over the
+              Blocks Extensible Exchange Protocol (BEEP)";
+
+    }
+
+    identity netconf-tls {
+      base transport;
+      description
+        "NETCONF over Transport Layer Security (TLS).";
+      reference
+        "RFC 5539: NETCONF over Transport Layer Security (TLS)";
+
+    }
+
+    identity schema-format {
+      description
+        "Base identity for data model schema languages.";
+    }
+
+    identity xsd {
+      base schema-format;
+      description
+        "W3C XML Schema Definition.";
+      reference
+        "W3C REC REC-xmlschema-1-20041028:
+          XML Schema Part 1: Structures";
+
+    }
+
+    identity yang {
+      base schema-format;
+      description
+        "The YANG data modeling language for NETCONF.";
+      reference
+        "RFC 6020:  YANG - A Data Modeling Language for the
+               Network Configuration Protocol (NETCONF)";
+
+    }
+
+    identity yin {
+      base schema-format;
+      description "The YIN syntax for YANG.";
+      reference
+        "RFC 6020:  YANG - A Data Modeling Language for the
+               Network Configuration Protocol (NETCONF)";
+
+    }
+
+    identity rng {
+      base schema-format;
+      description
+        "Regular Language for XML Next Generation (RELAX NG).";
+      reference
+        "ISO/IEC 19757-2:2008: RELAX NG";
+
+    }
+
+    identity rnc {
+      base schema-format;
+      description "Relax NG Compact Syntax";
+      reference
+        "ISO/IEC 19757-2:2008: RELAX NG";
+
+    }
+
+    grouping common-counters {
+      description
+        "Counters that exist both per session, and also globally,
+       accumulated from all sessions.";
+      leaf in-rpcs {
+        type yang:zero-based-counter32;
+        description
+          "Number of correct <rpc> messages received.";
+      }
+
+      leaf in-bad-rpcs {
+        type yang:zero-based-counter32;
+        description
+          "Number of messages received when an <rpc> message was expected,
+         that were not correct <rpc> messages.  This includes XML parse
+         errors and errors on the rpc layer.";
+      }
+
+      leaf out-rpc-errors {
+        type yang:zero-based-counter32;
+        description
+          "Number of <rpc-reply> messages sent that contained an
+         <rpc-error> element.";
+      }
+
+      leaf out-notifications {
+        type yang:zero-based-counter32;
+        description
+          "Number of <notification> messages sent.";
+      }
+    }  // grouping common-counters
+
+    container netconf-state {
+      config false;
+      description
+        "The netconf-state container is the root of the monitoring
+       data model.";
+      container capabilities {
+        description
+          "Contains the list of NETCONF capabilities supported by the
+         server.";
+        leaf-list capability {
+          type inet:uri;
+          description
+            "List of NETCONF capabilities supported by the server.";
+        }
+      }  // container capabilities
+
+      container datastores {
+        description
+          "Contains the list of NETCONF configuration datastores.";
+        list datastore {
+          key "name";
+          description
+            "List of NETCONF configuration datastores supported by
+           the NETCONF server and related information.";
+          leaf name {
+            type netconf-datastore-type;
+            description
+              "Name of the datastore associated with this list entry.";
+          }
+
+          container locks {
+            presence
+              "This container is present only if the datastore
+             is locked.";
+            description
+              "The NETCONF <lock> and <partial-lock> operations allow
+             a client to lock specific resources in a datastore.  The
+             NETCONF server will prevent changes to the locked
+             resources by all sessions except the one that acquired
+             the lock(s).
+
+             Monitoring information is provided for each datastore
+             entry including details such as the session that acquired
+             the lock, the type of lock (global or partial) and the
+             list of locked resources.  Multiple locks per datastore
+             are supported.";
+            grouping lock-info {
+              description
+                "Lock related parameters, common to both global and
+               partial locks.";
+              leaf locked-by-session {
+                type uint32;
+                mandatory true;
+                description
+                  "The session ID of the session that has locked
+                 this resource.  Both a global lock and a partial
+                 lock MUST contain the NETCONF session-id.
+
+                 If the lock is held by a session that is not managed
+                 by the NETCONF server (e.g., a CLI session), a session
+                 id of 0 (zero) is reported.";
+                reference
+                  "RFC 4741: NETCONF Configuration Protocol";
+
+              }
+
+              leaf locked-time {
+                type yang:date-and-time;
+                mandatory true;
+                description
+                  "The date and time of when the resource was
+                 locked.";
+              }
+            }  // grouping lock-info
+            choice lock-type {
+              description
+                "Indicates if a global lock or a set of partial locks
+               are set.";
+              container global-lock {
+                description
+                  "Present if the global lock is set.";
+                uses lock-info;
+              }  // container global-lock
+              list partial-lock {
+                key "lock-id";
+                description
+                  "List of partial locks.";
+                reference
+                  "RFC 5717: Partial Lock Remote Procedure Call (RPC) for
+                      NETCONF";
+
+                leaf lock-id {
+                  type uint32;
+                  description
+                    "This is the lock id returned in the <partial-lock>
+                   response.";
+                }
+
+                uses lock-info;
+
+                leaf-list select {
+                  type yang:xpath1.0;
+                  min-elements 1;
+                  description
+                    "The xpath expression that was used to request
+                   the lock.  The select expression indicates the
+                   original intended scope of the lock.";
+                }
+
+                leaf-list locked-node {
+                  type instance-identifier;
+                  description
+                    "The list of instance-identifiers (i.e., the
+                   locked nodes).
+
+                   The scope of the partial lock is defined by the list
+                   of locked nodes.";
+                }
+              }  // list partial-lock
+            }  // choice lock-type
+          }  // container locks
+        }  // list datastore
+      }  // container datastores
+
+      container schemas {
+        description
+          "Contains the list of data model schemas supported by the
+         server.";
+        list schema {
+          key "identifier version format";
+          description
+            "List of data model schemas supported by the server.";
+          leaf identifier {
+            type string;
+            description
+              "Identifier to uniquely reference the schema.  The
+             identifier is used in the <get-schema> operation and may
+             be used for other purposes such as file retrieval.
+
+             For modeling languages that support or require a data
+             model name (e.g., YANG module name) the identifier MUST
+             match that name.  For YANG data models, the identifier is
+             the name of the module or submodule.  In other cases, an
+             identifier such as a filename MAY be used instead.";
+          }
+
+          leaf version {
+            type string;
+            description
+              "Version of the schema supported.  Multiple versions MAY be
+             supported simultaneously by a NETCONF server.  Each
+             version MUST be reported individually in the schema list,
+             i.e., with same identifier, possibly different location,
+             but different version.
+
+             For YANG data models, version is the value of the most
+             recent YANG 'revision' statement in the module or
+             submodule, or the empty string if no 'revision' statement
+             is present.";
+          }
+
+          leaf format {
+            type identityref {
+              base schema-format;
+            }
+            description
+              "The data modeling language the schema is written
+             in (currently xsd, yang, yin, rng, or rnc).
+             For YANG data models, 'yang' format MUST be supported and
+             'yin' format MAY also be provided.";
+          }
+
+          leaf namespace {
+            type inet:uri;
+            mandatory true;
+            description
+              "The XML namespace defined by the data model.
+
+             For YANG data models, this is the module's namespace.
+             If the list entry describes a submodule, this field
+             contains the namespace of the module to which the
+             submodule belongs.";
+          }
+
+          leaf-list location {
+            type union {
+              type enumeration {
+                enum "NETCONF" {
+                  value 0;
+                }
+              }
+              type inet:uri;
+            }
+            description
+              "One or more locations from which the schema can be
+             retrieved.  This list SHOULD contain at least one
+             entry per schema.
+
+             A schema entry may be located on a remote file system
+             (e.g., reference to file system for ftp retrieval) or
+             retrieved directly from a server supporting the
+             <get-schema> operation (denoted by the value 'NETCONF').";
+          }
+        }  // list schema
+      }  // container schemas
+
+      container sessions {
+        description
+          "The sessions container includes session-specific data for
+         NETCONF management sessions.  The session list MUST include
+         all currently active NETCONF sessions.";
+        list session {
+          key "session-id";
+          description
+            "All NETCONF sessions managed by the NETCONF server
+           MUST be reported in this list.";
+          leaf session-id {
+            type uint32 {
+              range "1..max";
+            }
+            description
+              "Unique identifier for the session.  This value is the
+             NETCONF session identifier, as defined in RFC 4741.";
+            reference
+              "RFC 4741: NETCONF Configuration Protocol";
+
+          }
+
+          leaf transport {
+            type identityref {
+              base transport;
+            }
+            mandatory true;
+            description
+              "Identifies the transport for each session, e.g.,
+            'netconf-ssh', 'netconf-soap', etc.";
+          }
+
+          leaf username {
+            type string;
+            mandatory true;
+            description
+              "The username is the client identity that was authenticated
+            by the NETCONF transport protocol.  The algorithm used to
+            derive the username is NETCONF transport protocol specific
+            and in addition specific to the authentication mechanism
+            used by the NETCONF transport protocol.";
+          }
+
+          leaf source-host {
+            type inet:host;
+            description
+              "Host identifier of the NETCONF client.  The value
+             returned is implementation specific (e.g., hostname,
+             IPv4 address, IPv6 address)";
+          }
+
+          leaf login-time {
+            type yang:date-and-time;
+            mandatory true;
+            description
+              "Time at the server at which the session was established.";
+          }
+
+          uses common-counters {
+            description
+              "Per-session counters.  Zero based with following reset
+             behaviour:
+               - at start of a session
+               - when max value is reached";
+          }
+        }  // list session
+      }  // container sessions
+
+      container statistics {
+        description
+          "Statistical data pertaining to the NETCONF server.";
+        leaf netconf-start-time {
+          type yang:date-and-time;
+          description
+            "Date and time at which the management subsystem was
+           started.";
+        }
+
+        leaf in-bad-hellos {
+          type yang:zero-based-counter32;
+          description
+            "Number of sessions silently dropped because an
+          invalid <hello> message was received.  This includes <hello>
+          messages with a 'session-id' attribute, bad namespace, and
+          bad capability declarations.";
+        }
+
+        leaf in-sessions {
+          type yang:zero-based-counter32;
+          description
+            "Number of sessions started.  This counter is incremented
+           when a <hello> message with a <session-id> is sent.
+
+          'in-sessions' - 'in-bad-hellos' =
+              'number of correctly started netconf sessions'";
+        }
+
+        leaf dropped-sessions {
+          type yang:zero-based-counter32;
+          description
+            "Number of sessions that were abnormally terminated, e.g.,
+           due to idle timeout or transport close.  This counter is not
+           incremented when a session is properly closed by a
+           <close-session> operation, or killed by a <kill-session>
+           operation.";
+        }
+
+        uses common-counters {
+          description
+            "Global counters, accumulated from all sessions.
+           Zero based with following reset behaviour:
+             - re-initialization of NETCONF server
+             - when max value is reached";
+        }
+      }  // container statistics
+    }  // container netconf-state
+
+    rpc get-schema {
+      description
+        "This operation is used to retrieve a schema from the
+       NETCONF server.
+
+       Positive Response:
+         The NETCONF server returns the requested schema.
+
+       Negative Response:
+         If requested schema does not exist, the <error-tag> is
+         'invalid-value'.
+
+         If more than one schema matches the requested parameters, the
+         <error-tag> is 'operation-failed', and <error-app-tag> is
+         'data-not-unique'.";
+      input {
+        leaf identifier {
+          type string;
+          mandatory true;
+          description
+            "Identifier for the schema list entry.";
+        }
+
+        leaf version {
+          type string;
+          description
+            "Version of the schema requested.  If this parameter is not
+           present, and more than one version of the schema exists on
+           the server, a 'data-not-unique' error is returned, as
+           described above.";
+        }
+
+        leaf format {
+          type identityref {
+            base schema-format;
+          }
+          description
+            "The data modeling language of the schema.  If this
+            parameter is not present, and more than one formats of
+            the schema exists on the server, a 'data-not-unique' error
+            is returned, as described above.";
+        }
+      }
+
+      output {
+        anyxml data {
+          description
+            "Contains the schema content.";
+        }
+      }
+    }  // rpc get-schema
+} // module
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-yang-types.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-yang-types.yang
new file mode 100644 (file)
index 0000000..51d9f8b
--- /dev/null
@@ -0,0 +1,396 @@
+ module ietf-yang-types {
+
+   namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+   prefix "yang";
+
+   organization
+    "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+   contact
+    "WG Web:   <http://tools.ietf.org/wg/netmod/>
+     WG List:  <mailto:netmod@ietf.org>
+
+     WG Chair: David Partain
+               <mailto:david.partain@ericsson.com>
+
+     WG Chair: David Kessens
+               <mailto:david.kessens@nsn.com>
+
+     Editor:   Juergen Schoenwaelder
+               <mailto:j.schoenwaelder@jacobs-university.de>";
+
+   description
+    "This module contains a collection of generally useful derived
+     YANG data types.
+
+     Copyright (c) 2010 IETF Trust and the persons identified as
+     authors of the code.  All rights reserved.
+
+     Redistribution and use in source and binary forms, with or without
+     modification, is permitted pursuant to, and subject to the license
+     terms contained in, the Simplified BSD License set forth in Section
+     4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6021; see
+     the RFC itself for full legal notices.";
+
+   revision 2010-09-24 {
+     description
+      "Initial revision.";
+     reference
+      "RFC 6021: Common YANG Data Types";
+   }
+
+   /*** collection of counter and gauge types ***/
+
+   typedef counter32 {
+     type uint32;
+     description
+      "The counter32 type represents a non-negative integer
+       that monotonically increases until it reaches a
+       maximum value of 2^32-1 (4294967295 decimal), when it
+       wraps around and starts increasing again from zero.
+
+       Counters have no defined 'initial' value, and thus, a
+       single value of a counter has (in general) no information
+       content.  Discontinuities in the monotonically increasing
+       value normally occur at re-initialization of the
+       management system, and at other times as specified in the
+       description of a schema node using this type.  If such
+       other times can occur, for example, the creation of
+       a schema node of type counter32 at times other than
+       re-initialization, then a corresponding schema node
+       should be defined, with an appropriate type, to indicate
+       the last discontinuity.
+
+       The counter32 type should not be used for configuration
+       schema nodes.  A default statement SHOULD NOT be used in
+       combination with the type counter32.
+
+       In the value set and its semantics, this type is equivalent
+       to the Counter32 type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef zero-based-counter32 {
+     type yang:counter32;
+     default "0";
+     description
+      "The zero-based-counter32 type represents a counter32
+       that has the defined 'initial' value zero.
+
+       A schema node of this type will be set to zero (0) on creation
+       and will thereafter increase monotonically until it reaches
+       a maximum value of 2^32-1 (4294967295 decimal), when it
+       wraps around and starts increasing again from zero.
+
+       Provided that an application discovers a new schema node
+       of this type within the minimum time to wrap, it can use the
+       'initial' value as a delta.  It is important for a management
+       station to be aware of this minimum time and the actual time
+       between polls, and to discard data if the actual time is too
+       long or there is no defined minimum time.
+
+       In the value set and its semantics, this type is equivalent
+       to the ZeroBasedCounter32 textual convention of the SMIv2.";
+     reference
+       "RFC 4502: Remote Network Monitoring Management Information
+                  Base Version 2";
+   }
+
+   typedef counter64 {
+     type uint64;
+     description
+      "The counter64 type represents a non-negative integer
+       that monotonically increases until it reaches a
+       maximum value of 2^64-1 (18446744073709551615 decimal),
+       when it wraps around and starts increasing again from zero.
+
+       Counters have no defined 'initial' value, and thus, a
+       single value of a counter has (in general) no information
+       content.  Discontinuities in the monotonically increasing
+       value normally occur at re-initialization of the
+       management system, and at other times as specified in the
+       description of a schema node using this type.  If such
+       other times can occur, for example, the creation of
+       a schema node of type counter64 at times other than
+       re-initialization, then a corresponding schema node
+       should be defined, with an appropriate type, to indicate
+       the last discontinuity.
+
+       The counter64 type should not be used for configuration
+       schema nodes.  A default statement SHOULD NOT be used in
+       combination with the type counter64.
+
+       In the value set and its semantics, this type is equivalent
+       to the Counter64 type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef zero-based-counter64 {
+     type yang:counter64;
+     default "0";
+     description
+      "The zero-based-counter64 type represents a counter64 that
+       has the defined 'initial' value zero.
+
+       A schema node of this type will be set to zero (0) on creation
+       and will thereafter increase monotonically until it reaches
+       a maximum value of 2^64-1 (18446744073709551615 decimal),
+       when it wraps around and starts increasing again from zero.
+
+       Provided that an application discovers a new schema node
+       of this type within the minimum time to wrap, it can use the
+       'initial' value as a delta.  It is important for a management
+       station to be aware of this minimum time and the actual time
+       between polls, and to discard data if the actual time is too
+       long or there is no defined minimum time.
+
+       In the value set and its semantics, this type is equivalent
+       to the ZeroBasedCounter64 textual convention of the SMIv2.";
+     reference
+      "RFC 2856: Textual Conventions for Additional High Capacity
+                 Data Types";
+   }
+
+   typedef gauge32 {
+     type uint32;
+     description
+      "The gauge32 type represents a non-negative integer, which
+       may increase or decrease, but shall never exceed a maximum
+       value, nor fall below a minimum value.  The maximum value
+       cannot be greater than 2^32-1 (4294967295 decimal), and
+       the minimum value cannot be smaller than 0.  The value of
+       a gauge32 has its maximum value whenever the information
+       being modeled is greater than or equal to its maximum
+       value, and has its minimum value whenever the information
+       being modeled is smaller than or equal to its minimum value.
+       If the information being modeled subsequently decreases
+       below (increases above) the maximum (minimum) value, the
+       gauge32 also decreases (increases).
+
+       In the value set and its semantics, this type is equivalent
+       to the Gauge32 type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef gauge64 {
+     type uint64;
+     description
+      "The gauge64 type represents a non-negative integer, which
+       may increase or decrease, but shall never exceed a maximum
+       value, nor fall below a minimum value.  The maximum value
+       cannot be greater than 2^64-1 (18446744073709551615), and
+       the minimum value cannot be smaller than 0.  The value of
+       a gauge64 has its maximum value whenever the information
+       being modeled is greater than or equal to its maximum
+       value, and has its minimum value whenever the information
+       being modeled is smaller than or equal to its minimum value.
+       If the information being modeled subsequently decreases
+       below (increases above) the maximum (minimum) value, the
+       gauge64 also decreases (increases).
+
+       In the value set and its semantics, this type is equivalent
+       to the CounterBasedGauge64 SMIv2 textual convention defined
+       in RFC 2856";
+     reference
+      "RFC 2856: Textual Conventions for Additional High Capacity
+                 Data Types";
+   }
+
+   /*** collection of identifier related types ***/
+
+   typedef object-identifier {
+     type string {
+       pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+             + '(\.(0|([1-9]\d*)))*';
+     }
+     description
+      "The object-identifier type represents administratively
+       assigned names in a registration-hierarchical-name tree.
+
+       Values of this type are denoted as a sequence of numerical
+       non-negative sub-identifier values.  Each sub-identifier
+       value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers
+       are separated by single dots and without any intermediate
+       whitespace.
+
+       The ASN.1 standard restricts the value space of the first
+       sub-identifier to 0, 1, or 2.  Furthermore, the value space
+       of the second sub-identifier is restricted to the range
+       0 to 39 if the first sub-identifier is 0 or 1.  Finally,
+       the ASN.1 standard requires that an object identifier
+       has always at least two sub-identifier.  The pattern
+       captures these restrictions.
+
+       Although the number of sub-identifiers is not limited,
+       module designers should realize that there may be
+       implementations that stick with the SMIv2 limit of 128
+       sub-identifiers.
+
+       This type is a superset of the SMIv2 OBJECT IDENTIFIER type
+       since it is not restricted to 128 sub-identifiers.  Hence,
+       this type SHOULD NOT be used to represent the SMIv2 OBJECT
+       IDENTIFIER type, the object-identifier-128 type SHOULD be
+       used instead.";
+     reference
+      "ISO9834-1: Information technology -- Open Systems
+       Interconnection -- Procedures for the operation of OSI
+       Registration Authorities: General procedures and top
+       arcs of the ASN.1 Object Identifier tree";
+   }
+
+
+
+
+   typedef object-identifier-128 {
+     type object-identifier {
+       pattern '\d*(\.\d*){1,127}';
+     }
+     description
+      "This type represents object-identifiers restricted to 128
+       sub-identifiers.
+
+       In the value set and its semantics, this type is equivalent
+       to the OBJECT IDENTIFIER type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   /*** collection of date and time related types ***/
+
+   typedef date-and-time {
+     type string {
+       pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+             + '(Z|[\+\-]\d{2}:\d{2})';
+     }
+     description
+      "The date-and-time type is a profile of the ISO 8601
+       standard for representation of dates and times using the
+       Gregorian calendar.  The profile is defined by the
+       date-time production in Section 5.6 of RFC 3339.
+
+       The date-and-time type is compatible with the dateTime XML
+       schema type with the following notable exceptions:
+
+       (a) The date-and-time type does not allow negative years.
+
+       (b) The date-and-time time-offset -00:00 indicates an unknown
+           time zone (see RFC 3339) while -00:00 and +00:00 and Z all
+           represent the same time zone in dateTime.
+
+       (c) The canonical format (see below) of data-and-time values
+           differs from the canonical format used by the dateTime XML
+           schema type, which requires all times to be in UTC using the
+           time-offset 'Z'.
+
+       This type is not equivalent to the DateAndTime textual
+       convention of the SMIv2 since RFC 3339 uses a different
+       separator between full-date and full-time and provides
+       higher resolution of time-secfrac.
+
+       The canonical format for date-and-time values with a known time
+       zone uses a numeric time zone offset that is calculated using
+       the device's configured known offset to UTC time.  A change of
+       the device's offset to UTC time will cause date-and-time values
+       to change accordingly.  Such changes might happen periodically
+       in case a server follows automatically daylight saving time
+       (DST) time zone offset changes.  The canonical format for
+       date-and-time values with an unknown time zone (usually referring
+       to the notion of local time) uses the time-offset -00:00.";
+     reference
+      "RFC 3339: Date and Time on the Internet: Timestamps
+       RFC 2579: Textual Conventions for SMIv2
+       XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
+   }
+
+   typedef timeticks {
+     type uint32;
+     description
+      "The timeticks type represents a non-negative integer that
+       represents the time, modulo 2^32 (4294967296 decimal), in
+       hundredths of a second between two epochs.  When a schema
+       node is defined that uses this type, the description of
+       the schema node identifies both of the reference epochs.
+
+       In the value set and its semantics, this type is equivalent
+       to the TimeTicks type of the SMIv2.";
+     reference
+      "RFC 2578: Structure of Management Information Version 2 (SMIv2)";
+   }
+
+   typedef timestamp {
+     type yang:timeticks;
+     description
+      "The timestamp type represents the value of an associated
+       timeticks schema node at which a specific occurrence happened.
+       The specific occurrence must be defined in the description
+       of any schema node defined using this type.  When the specific
+       occurrence occurred prior to the last time the associated
+       timeticks attribute was zero, then the timestamp value is
+       zero.  Note that this requires all timestamp values to be
+       reset to zero when the value of the associated timeticks
+       attribute reaches 497+ days and wraps around to zero.
+
+       The associated timeticks schema node must be specified
+       in the description of any schema node using this type.
+
+       In the value set and its semantics, this type is equivalent
+       to the TimeStamp textual convention of the SMIv2.";
+     reference
+      "RFC 2579: Textual Conventions for SMIv2";
+   }
+
+   /*** collection of generic address types ***/
+
+   typedef phys-address {
+     type string {
+       pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+     }
+     description
+      "Represents media- or physical-level addresses represented
+       as a sequence octets, each octet represented by two hexadecimal
+       numbers.  Octets are separated by colons.  The canonical
+       representation uses lowercase characters.
+
+       In the value set and its semantics, this type is equivalent
+       to the PhysAddress textual convention of the SMIv2.";
+     reference
+      "RFC 2579: Textual Conventions for SMIv2";
+   }
+
+   typedef mac-address {
+     type string {
+       pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+     }
+     description
+      "The mac-address type represents an IEEE 802 MAC address.
+       The canonical representation uses lowercase characters.
+
+       In the value set and its semantics, this type is equivalent
+       to the MacAddress textual convention of the SMIv2.";
+     reference
+      "IEEE 802: IEEE Standard for Local and Metropolitan Area
+                 Networks: Overview and Architecture
+       RFC 2579: Textual Conventions for SMIv2";
+   }
+
+   /*** collection of XML specific types ***/
+
+   typedef xpath1.0 {
+     type string;
+     description
+      "This type represents an XPATH 1.0 expression.
+
+       When a schema node is defined that uses this type, the
+       description of the schema node MUST specify the XPath
+       context in which the XPath expression is evaluated.";
+     reference
+      "XPATH: XML Path Language (XPath) Version 1.0";
+   }
+
+ }
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-yang-types@2013-05-16.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-yang-types@2013-05-16.yang
new file mode 100644 (file)
index 0000000..6c82d9d
--- /dev/null
@@ -0,0 +1,471 @@
+module ietf-yang-types {
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+  prefix "yang";
+
+  organization
+   "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+   "WG Web:   <http://tools.ietf.org/wg/netmod/>
+    WG List:  <mailto:netmod@ietf.org>
+
+    WG Chair: David Kessens
+              <mailto:david.kessens@nsn.com>
+
+    WG Chair: Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>
+
+    Editor:   Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>";
+
+  description
+   "This module contains a collection of generally useful derived
+    YANG data types.
+
+    Copyright (c) 2013 IETF Trust and the persons identified as
+    authors of the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or
+    without modification, is permitted pursuant to, and subject
+    to the license terms contained in, the Simplified BSD License
+    set forth in Section 4.c of the IETF Trust's Legal Provisions
+    Relating to IETF Documents
+    (http://trustee.ietf.org/license-info).
+
+    This version of this YANG module is part of RFC XXXX; see
+    the RFC itself for full legal notices.";
+
+  revision 2013-05-16 {
+    description
+     "This revision adds the following new data types:
+      - yang-identifier
+      - hex-string
+      - uuid
+      - dotted-quad";
+    reference
+     "RFC XXXX: Common YANG Data Types";
+  }
+
+  revision 2010-09-24 {
+    description
+     "Initial revision.";
+    reference
+     "RFC 6021: Common YANG Data Types";
+  }
+
+  /*** collection of counter and gauge types ***/
+
+  typedef counter32 {
+    type uint32;
+    description
+     "The counter32 type represents a non-negative integer
+      that monotonically increases until it reaches a
+      maximum value of 2^32-1 (4294967295 decimal), when it
+      wraps around and starts increasing again from zero.
+
+      Counters have no defined 'initial' value, and thus, a
+      single value of a counter has (in general) no information
+      content.  Discontinuities in the monotonically increasing
+      value normally occur at re-initialization of the
+      management system, and at other times as specified in the
+      description of a schema node using this type.  If such
+      other times can occur, for example, the creation of
+      a schema node of type counter32 at times other than
+      re-initialization, then a corresponding schema node
+      should be defined, with an appropriate type, to indicate
+      the last discontinuity.
+
+      The counter32 type should not be used for configuration
+      schema nodes.  A default statement SHOULD NOT be used in
+      combination with the type counter32.
+
+      In the value set and its semantics, this type is equivalent
+      to the Counter32 type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef zero-based-counter32 {
+    type yang:counter32;
+    default "0";
+    description
+     "The zero-based-counter32 type represents a counter32
+      that has the defined 'initial' value zero.
+
+      A schema node of this type will be set to zero (0) on creation
+      and will thereafter increase monotonically until it reaches
+      a maximum value of 2^32-1 (4294967295 decimal), when it
+      wraps around and starts increasing again from zero.
+
+      Provided that an application discovers a new schema node
+      of this type within the minimum time to wrap, it can use the
+      'initial' value as a delta.  It is important for a management
+      station to be aware of this minimum time and the actual time
+      between polls, and to discard data if the actual time is too
+      long or there is no defined minimum time.
+
+      In the value set and its semantics, this type is equivalent
+      to the ZeroBasedCounter32 textual convention of the SMIv2.";
+    reference
+      "RFC 4502: Remote Network Monitoring Management Information
+                 Base Version 2";
+  }
+
+  typedef counter64 {
+    type uint64;
+    description
+     "The counter64 type represents a non-negative integer
+      that monotonically increases until it reaches a
+      maximum value of 2^64-1 (18446744073709551615 decimal),
+      when it wraps around and starts increasing again from zero.
+
+      Counters have no defined 'initial' value, and thus, a
+      single value of a counter has (in general) no information
+      content.  Discontinuities in the monotonically increasing
+      value normally occur at re-initialization of the
+      management system, and at other times as specified in the
+      description of a schema node using this type.  If such
+      other times can occur, for example, the creation of
+      a schema node of type counter64 at times other than
+      re-initialization, then a corresponding schema node
+      should be defined, with an appropriate type, to indicate
+      the last discontinuity.
+
+      The counter64 type should not be used for configuration
+      schema nodes.  A default statement SHOULD NOT be used in
+      combination with the type counter64.
+
+      In the value set and its semantics, this type is equivalent
+      to the Counter64 type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef zero-based-counter64 {
+    type yang:counter64;
+    default "0";
+    description
+     "The zero-based-counter64 type represents a counter64 that
+      has the defined 'initial' value zero.
+
+      A schema node of this type will be set to zero (0) on creation
+      and will thereafter increase monotonically until it reaches
+      a maximum value of 2^64-1 (18446744073709551615 decimal),
+      when it wraps around and starts increasing again from zero.
+
+      Provided that an application discovers a new schema node
+      of this type within the minimum time to wrap, it can use the
+      'initial' value as a delta.  It is important for a management
+      station to be aware of this minimum time and the actual time
+      between polls, and to discard data if the actual time is too
+      long or there is no defined minimum time.
+
+      In the value set and its semantics, this type is equivalent
+      to the ZeroBasedCounter64 textual convention of the SMIv2.";
+    reference
+     "RFC 2856: Textual Conventions for Additional High Capacity
+                Data Types";
+  }
+
+  typedef gauge32 {
+    type uint32;
+    description
+     "The gauge32 type represents a non-negative integer, which
+      may increase or decrease, but shall never exceed a maximum
+      value, nor fall below a minimum value.  The maximum value
+      cannot be greater than 2^32-1 (4294967295 decimal), and
+      the minimum value cannot be smaller than 0.  The value of
+      a gauge32 has its maximum value whenever the information
+      being modeled is greater than or equal to its maximum
+      value, and has its minimum value whenever the information
+      being modeled is smaller than or equal to its minimum value.
+      If the information being modeled subsequently decreases
+      below (increases above) the maximum (minimum) value, the
+      gauge32 also decreases (increases).
+
+      In the value set and its semantics, this type is equivalent
+      to the Gauge32 type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef gauge64 {
+    type uint64;
+    description
+     "The gauge64 type represents a non-negative integer, which
+      may increase or decrease, but shall never exceed a maximum
+      value, nor fall below a minimum value.  The maximum value
+      cannot be greater than 2^64-1 (18446744073709551615), and
+      the minimum value cannot be smaller than 0.  The value of
+      a gauge64 has its maximum value whenever the information
+      being modeled is greater than or equal to its maximum
+      value, and has its minimum value whenever the information
+      being modeled is smaller than or equal to its minimum value.
+      If the information being modeled subsequently decreases
+      below (increases above) the maximum (minimum) value, the
+      gauge64 also decreases (increases).
+
+      In the value set and its semantics, this type is equivalent
+      to the CounterBasedGauge64 SMIv2 textual convention defined
+      in RFC 2856";
+    reference
+     "RFC 2856: Textual Conventions for Additional High Capacity
+                Data Types";
+  }
+
+  /*** collection of identifier related types ***/
+
+  typedef object-identifier {
+    type string {
+      pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+            + '(\.(0|([1-9]\d*)))*';
+    }
+    description
+     "The object-identifier type represents administratively
+      assigned names in a registration-hierarchical-name tree.
+
+      Values of this type are denoted as a sequence of numerical
+      non-negative sub-identifier values.  Each sub-identifier
+      value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers
+      are separated by single dots and without any intermediate
+      whitespace.
+
+      The ASN.1 standard restricts the value space of the first
+      sub-identifier to 0, 1, or 2.  Furthermore, the value space
+      of the second sub-identifier is restricted to the range
+      0 to 39 if the first sub-identifier is 0 or 1.  Finally,
+      the ASN.1 standard requires that an object identifier
+      has always at least two sub-identifier.  The pattern
+      captures these restrictions.
+
+      Although the number of sub-identifiers is not limited,
+      module designers should realize that there may be
+      implementations that stick with the SMIv2 limit of 128
+      sub-identifiers.
+
+      This type is a superset of the SMIv2 OBJECT IDENTIFIER type
+      since it is not restricted to 128 sub-identifiers.  Hence,
+      this type SHOULD NOT be used to represent the SMIv2 OBJECT
+      IDENTIFIER type, the object-identifier-128 type SHOULD be
+      used instead.";
+    reference
+     "ISO9834-1: Information technology -- Open Systems
+      Interconnection -- Procedures for the operation of OSI
+      Registration Authorities: General procedures and top
+      arcs of the ASN.1 Object Identifier tree";
+  }
+
+  typedef object-identifier-128 {
+    type object-identifier {
+      pattern '\d*(\.\d*){1,127}';
+    }
+    description
+     "This type represents object-identifiers restricted to 128
+      sub-identifiers.
+
+      In the value set and its semantics, this type is equivalent
+      to the OBJECT IDENTIFIER type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef yang-identifier {
+    type string {
+      length "1..max";
+      pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
+      pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*';
+    }
+    description
+      "A YANG identifier string as defined in RFC 6020, page 163.
+       An identifier must start with an alphabetic character or
+       an underscore followed by an arbitrary sequence of
+       alphabetic or numeric characters, underscores, hyphens
+       or dots.
+
+       A YANG identifier MUST NOT start with any possible
+       combination of the lower-case or upper-case character
+       sequence 'xml'.";
+    reference
+      "RFC 6020: YANG - A Data Modeling Language for the Network
+                 Configuration Protocol (NETCONF)";
+  }
+
+  /*** collection of date and time related types ***/
+
+  typedef date-and-time {
+    type string {
+      pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+            + '(Z|[\+\-]\d{2}:\d{2})';
+    }
+    description
+     "The date-and-time type is a profile of the ISO 8601
+      standard for representation of dates and times using the
+      Gregorian calendar.  The profile is defined by the
+      date-time production in Section 5.6 of RFC 3339.
+
+      The date-and-time type is compatible with the dateTime XML
+      schema type with the following notable exceptions:
+
+      (a) The date-and-time type does not allow negative years.
+
+      (b) The date-and-time time-offset -00:00 indicates an unknown
+          time zone (see RFC 3339) while -00:00 and +00:00 and Z all
+          represent the same time zone in dateTime.
+
+      (c) The canonical format (see below) of data-and-time values
+          differs from the canonical format used by the dateTime XML
+          schema type, which requires all times to be in UTC using
+          the time-offset 'Z'.
+
+      This type is not equivalent to the DateAndTime textual
+      convention of the SMIv2 since RFC 3339 uses a different
+      separator between full-date and full-time and provides
+      higher resolution of time-secfrac.
+      The canonical format for date-and-time values with a known time
+      zone uses a numeric time zone offset that is calculated using
+      the device's configured known offset to UTC time.  A change of
+      the device's offset to UTC time will cause date-and-time values
+      to change accordingly.  Such changes might happen periodically
+      in case a server follows automatically daylight saving time
+      (DST) time zone offset changes.  The canonical format for
+      date-and-time values with an unknown time zone (usually
+      referring to the notion of local time) uses the time-offset
+      -00:00.";
+    reference
+     "RFC 3339: Date and Time on the Internet: Timestamps
+      RFC 2579: Textual Conventions for SMIv2
+      XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
+  }
+
+  typedef timeticks {
+    type uint32;
+    description
+     "The timeticks type represents a non-negative integer that
+      represents the time, modulo 2^32 (4294967296 decimal), in
+      hundredths of a second between two epochs.  When a schema
+      node is defined that uses this type, the description of
+      the schema node identifies both of the reference epochs.
+
+      In the value set and its semantics, this type is equivalent
+      to the TimeTicks type of the SMIv2.";
+    reference
+     "RFC 2578: Structure of Management Information Version 2
+                (SMIv2)";
+  }
+
+  typedef timestamp {
+    type yang:timeticks;
+    description
+     "The timestamp type represents the value of an associated
+      timeticks schema node at which a specific occurrence
+      happened. The specific occurrence must be defined in the
+      description of any schema node defined using this type.  When
+      the specific occurrence occurred prior to the last time the
+      associated timeticks attribute was zero, then the timestamp
+      value is zero.  Note that this requires all timestamp values
+      to be reset to zero when the value of the associated timeticks
+      attribute reaches 497+ days and wraps around to zero.
+
+      The associated timeticks schema node must be specified
+      in the description of any schema node using this type.
+      In the value set and its semantics, this type is equivalent
+      to the TimeStamp textual convention of the SMIv2.";
+    reference
+     "RFC 2579: Textual Conventions for SMIv2";
+  }
+
+  /*** collection of generic address types ***/
+
+  typedef phys-address {
+    type string {
+      pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+    }
+    description
+     "Represents media- or physical-level addresses represented
+      as a sequence octets, each octet represented by two hexadecimal
+      numbers.  Octets are separated by colons.  The canonical
+      representation uses lowercase characters.
+
+      In the value set and its semantics, this type is equivalent
+      to the PhysAddress textual convention of the SMIv2.";
+    reference
+     "RFC 2579: Textual Conventions for SMIv2";
+  }
+
+  typedef mac-address {
+    type string {
+      pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+    }
+    description
+     "The mac-address type represents an IEEE 802 MAC address.
+      The canonical representation uses lowercase characters.
+
+      In the value set and its semantics, this type is equivalent
+      to the MacAddress textual convention of the SMIv2.";
+    reference
+     "IEEE 802: IEEE Standard for Local and Metropolitan Area
+                Networks: Overview and Architecture
+      RFC 2579: Textual Conventions for SMIv2";
+  }
+
+  /*** collection of XML specific types ***/
+
+  typedef xpath1.0 {
+    type string;
+    description
+     "This type represents an XPATH 1.0 expression.
+
+      When a schema node is defined that uses this type, the
+      description of the schema node MUST specify the XPath
+      context in which the XPath expression is evaluated.";
+    reference
+     "XPATH: XML Path Language (XPath) Version 1.0";
+  }
+
+  /*** collection of string types ***/
+
+  typedef hex-string {
+    type string {
+      pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+    }
+    description
+     "A hexadecimal string with octets represented as hex digits
+      separated by colons.  The canonical representation uses
+      lowercase characters.";
+  }
+
+  typedef uuid {
+    type string {
+      pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-'
+            + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
+    }
+    description
+     "A Universally Unique IDentifier in the string representation
+      defined in RFC 4122.  The canonical representation uses
+      lowercase characters.
+
+      The following is an example of a UUID in string representation:
+      f81d4fae-7dec-11d0-a765-00a0c91e6bf6
+      ";
+    reference
+     "RFC 4122: A Universally Unique IDentifier (UUID) URN
+                Namespace";
+  }
+
+  typedef dotted-quad {
+    type string {
+      pattern
+        '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+      + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
+    }
+    description
+      "An unsigned 32-bit number expressed in the dotted-quad
+       notation, i.e., four octets written as decimal numbers
+       and separated with the '.' (full stop) character.";
+  }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-yang-types@2013-07-15.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/full/ietf-yang-types@2013-07-15.yang
new file mode 100644 (file)
index 0000000..bdff18c
--- /dev/null
@@ -0,0 +1,467 @@
+module ietf-yang-types {
+
+     namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+     prefix "yang";
+
+     organization
+      "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+     contact
+      "WG Web:   <http://tools.ietf.org/wg/netmod/>
+       WG List:  <mailto:netmod@ietf.org>
+       WG Chair: David Kessens
+                 <mailto:david.kessens@nsn.com>
+
+       WG Chair: Juergen Schoenwaelder
+                 <mailto:j.schoenwaelder@jacobs-university.de>
+
+       Editor:   Juergen Schoenwaelder
+                 <mailto:j.schoenwaelder@jacobs-university.de>";
+
+     description
+      "This module contains a collection of generally useful derived
+       YANG data types.
+
+       Copyright (c) 2013 IETF Trust and the persons identified as
+       authors of the code.  All rights reserved.
+
+       Redistribution and use in source and binary forms, with or
+       without modification, is permitted pursuant to, and subject
+       to the license terms contained in, the Simplified BSD License
+       set forth in Section 4.c of the IETF Trust's Legal Provisions
+       Relating to IETF Documents
+       (http://trustee.ietf.org/license-info).
+
+       This version of this YANG module is part of RFC 6991; see
+       the RFC itself for full legal notices.";
+
+     revision 2013-07-15 {
+       description
+        "This revision adds the following new data types:
+         - yang-identifier
+         - hex-string
+         - uuid
+         - dotted-quad";
+       reference
+        "RFC 6991: Common YANG Data Types";
+     }
+
+     revision 2010-09-24 {
+       description
+        "Initial revision.";
+       reference
+        "RFC 6021: Common YANG Data Types";
+     }
+
+     /*** collection of counter and gauge types ***/
+
+     typedef counter32 {
+       type uint32;
+       description
+        "The counter32 type represents a non-negative integer
+         that monotonically increases until it reaches a
+         maximum value of 2^32-1 (4294967295 decimal), when it
+         wraps around and starts increasing again from zero.
+
+         Counters have no defined 'initial' value, and thus, a
+         single value of a counter has (in general) no information
+         content.  Discontinuities in the monotonically increasing
+         value normally occur at re-initialization of the
+         management system, and at other times as specified in the
+         description of a schema node using this type.  If such
+         other times can occur, for example, the creation of
+         a schema node of type counter32 at times other than
+         re-initialization, then a corresponding schema node
+         should be defined, with an appropriate type, to indicate
+         the last discontinuity.
+
+         The counter32 type should not be used for configuration
+         schema nodes.  A default statement SHOULD NOT be used in
+         combination with the type counter32.
+
+         In the value set and its semantics, this type is equivalent
+         to the Counter32 type of the SMIv2.";
+       reference
+        "RFC 2578: Structure of Management Information Version 2
+                   (SMIv2)";
+     }
+
+     typedef zero-based-counter32 {
+       type yang:counter32;
+       default "0";
+       description
+        "The zero-based-counter32 type represents a counter32
+         that has the defined 'initial' value zero.
+
+         A schema node of this type will be set to zero (0) on creation
+         and will thereafter increase monotonically until it reaches
+         a maximum value of 2^32-1 (4294967295 decimal), when it
+         wraps around and starts increasing again from zero.
+
+         Provided that an application discovers a new schema node
+         of this type within the minimum time to wrap, it can use the
+         'initial' value as a delta.  It is important for a management
+         station to be aware of this minimum time and the actual time
+         between polls, and to discard data if the actual time is too
+         long or there is no defined minimum time.
+        In the value set and its semantics, this type is equivalent
+         to the ZeroBasedCounter32 textual convention of the SMIv2.";
+       reference
+         "RFC 4502: Remote Network Monitoring Management Information
+                    Base Version 2";
+     }
+
+     typedef counter64 {
+       type uint64;
+       description
+        "The counter64 type represents a non-negative integer
+         that monotonically increases until it reaches a
+         maximum value of 2^64-1 (18446744073709551615 decimal),
+         when it wraps around and starts increasing again from zero.
+
+         Counters have no defined 'initial' value, and thus, a
+         single value of a counter has (in general) no information
+         content.  Discontinuities in the monotonically increasing
+         value normally occur at re-initialization of the
+         management system, and at other times as specified in the
+         description of a schema node using this type.  If such
+         other times can occur, for example, the creation of
+         a schema node of type counter64 at times other than
+         re-initialization, then a corresponding schema node
+         should be defined, with an appropriate type, to indicate
+         the last discontinuity.
+
+         The counter64 type should not be used for configuration
+         schema nodes.  A default statement SHOULD NOT be used in
+         combination with the type counter64.
+
+         In the value set and its semantics, this type is equivalent
+         to the Counter64 type of the SMIv2.";
+       reference
+        "RFC 2578: Structure of Management Information Version 2
+                   (SMIv2)";
+     }
+
+     typedef zero-based-counter64 {
+       type yang:counter64;
+       default "0";
+       description
+        "The zero-based-counter64 type represents a counter64 that
+         has the defined 'initial' value zero.
+         A schema node of this type will be set to zero (0) on creation
+         and will thereafter increase monotonically until it reaches
+         a maximum value of 2^64-1 (18446744073709551615 decimal),
+         when it wraps around and starts increasing again from zero.
+
+         Provided that an application discovers a new schema node
+         of this type within the minimum time to wrap, it can use the
+         'initial' value as a delta.  It is important for a management
+         station to be aware of this minimum time and the actual time
+         between polls, and to discard data if the actual time is too
+         long or there is no defined minimum time.
+
+         In the value set and its semantics, this type is equivalent
+         to the ZeroBasedCounter64 textual convention of the SMIv2.";
+       reference
+        "RFC 2856: Textual Conventions for Additional High Capacity
+                   Data Types";
+     }
+
+     typedef gauge32 {
+       type uint32;
+       description
+        "The gauge32 type represents a non-negative integer, which
+         may increase or decrease, but shall never exceed a maximum
+         value, nor fall below a minimum value.  The maximum value
+         cannot be greater than 2^32-1 (4294967295 decimal), and
+         the minimum value cannot be smaller than 0.  The value of
+         a gauge32 has its maximum value whenever the information
+         being modeled is greater than or equal to its maximum
+         value, and has its minimum value whenever the information
+         being modeled is smaller than or equal to its minimum value.
+         If the information being modeled subsequently decreases
+         below (increases above) the maximum (minimum) value, the
+         gauge32 also decreases (increases).
+
+         In the value set and its semantics, this type is equivalent
+         to the Gauge32 type of the SMIv2.";
+       reference
+        "RFC 2578: Structure of Management Information Version 2
+                   (SMIv2)";
+     }
+
+     typedef gauge64 {
+       type uint64;
+       description
+        "The gauge64 type represents a non-negative integer, which
+         may increase or decrease, but shall never exceed a maximum
+         value, nor fall below a minimum value.  The maximum value
+         cannot be greater than 2^64-1 (18446744073709551615), and
+         the minimum value cannot be smaller than 0.  The value of
+         a gauge64 has its maximum value whenever the information
+         being modeled is greater than or equal to its maximum
+         value, and has its minimum value whenever the information
+         being modeled is smaller than or equal to its minimum value.
+         If the information being modeled subsequently decreases
+         below (increases above) the maximum (minimum) value, the
+         gauge64 also decreases (increases).
+
+         In the value set and its semantics, this type is equivalent
+         to the CounterBasedGauge64 SMIv2 textual convention defined
+         in RFC 2856";
+       reference
+        "RFC 2856: Textual Conventions for Additional High Capacity
+                   Data Types";
+     }
+
+     /*** collection of identifier-related types ***/
+
+     typedef object-identifier {
+       type string {
+         pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+               + '(\.(0|([1-9]\d*)))*';
+       }
+       description
+        "The object-identifier type represents administratively
+         assigned names in a registration-hierarchical-name tree.
+
+         Values of this type are denoted as a sequence of numerical
+         non-negative sub-identifier values.  Each sub-identifier
+         value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers
+         are separated by single dots and without any intermediate
+         whitespace.
+
+         The ASN.1 standard restricts the value space of the first
+         sub-identifier to 0, 1, or 2.  Furthermore, the value space
+         of the second sub-identifier is restricted to the range
+         0 to 39 if the first sub-identifier is 0 or 1.  Finally,
+         the ASN.1 standard requires that an object identifier
+         has always at least two sub-identifiers.  The pattern
+         captures these restrictions.
+
+         Although the number of sub-identifiers is not limited,
+         module designers should realize that there may be
+         implementations that stick with the SMIv2 limit of 128
+         sub-identifiers.
+         This type is a superset of the SMIv2 OBJECT IDENTIFIER type
+         since it is not restricted to 128 sub-identifiers.  Hence,
+         this type SHOULD NOT be used to represent the SMIv2 OBJECT
+         IDENTIFIER type; the object-identifier-128 type SHOULD be
+         used instead.";
+       reference
+        "ISO9834-1: Information technology -- Open Systems
+         Interconnection -- Procedures for the operation of OSI
+         Registration Authorities: General procedures and top
+         arcs of the ASN.1 Object Identifier tree";
+     }
+
+     typedef object-identifier-128 {
+       type object-identifier {
+         pattern '\d*(\.\d*){1,127}';
+       }
+       description
+        "This type represents object-identifiers restricted to 128
+         sub-identifiers.
+
+         In the value set and its semantics, this type is equivalent
+         to the OBJECT IDENTIFIER type of the SMIv2.";
+       reference
+        "RFC 2578: Structure of Management Information Version 2
+                   (SMIv2)";
+     }
+
+     typedef yang-identifier {
+       type string {
+         length "1..max";
+         pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
+         pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*';
+       }
+       description
+         "A YANG identifier string as defined by the 'identifier'
+          rule in Section 12 of RFC 6020.  An identifier must
+          start with an alphabetic character or an underscore
+          followed by an arbitrary sequence of alphabetic or
+          numeric characters, underscores, hyphens, or dots.
+
+          A YANG identifier MUST NOT start with any possible
+          combination of the lowercase or uppercase character
+          sequence 'xml'.";
+       reference
+         "RFC 6020: YANG - A Data Modeling Language for the Network
+                    Configuration Protocol (NETCONF)";
+     }
+     /*** collection of types related to date and time***/
+
+     typedef date-and-time {
+       type string {
+         pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+               + '(Z|[\+\-]\d{2}:\d{2})';
+       }
+       description
+        "The date-and-time type is a profile of the ISO 8601
+         standard for representation of dates and times using the
+         Gregorian calendar.  The profile is defined by the
+         date-time production in Section 5.6 of RFC 3339.
+
+         The date-and-time type is compatible with the dateTime XML
+         schema type with the following notable exceptions:
+
+         (a) The date-and-time type does not allow negative years.
+
+         (b) The date-and-time time-offset -00:00 indicates an unknown
+             time zone (see RFC 3339) while -00:00 and +00:00 and Z
+             all represent the same time zone in dateTime.
+
+         (c) The canonical format (see below) of data-and-time values
+             differs from the canonical format used by the dateTime XML
+             schema type, which requires all times to be in UTC using
+             the time-offset 'Z'.
+
+         This type is not equivalent to the DateAndTime textual
+         convention of the SMIv2 since RFC 3339 uses a different
+         separator between full-date and full-time and provides
+         higher resolution of time-secfrac.
+
+         The canonical format for date-and-time values with a known time
+         zone uses a numeric time zone offset that is calculated using
+         the device's configured known offset to UTC time.  A change of
+         the device's offset to UTC time will cause date-and-time values
+         to change accordingly.  Such changes might happen periodically
+         in case a server follows automatically daylight saving time
+         (DST) time zone offset changes.  The canonical format for
+         date-and-time values with an unknown time zone (usually
+         referring to the notion of local time) uses the time-offset
+         -00:00.";
+       reference
+        "RFC 3339: Date and Time on the Internet: Timestamps
+         RFC 2579: Textual Conventions for SMIv2
+         XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
+     }
+     typedef timeticks {
+       type uint32;
+       description
+        "The timeticks type represents a non-negative integer that
+         represents the time, modulo 2^32 (4294967296 decimal), in
+         hundredths of a second between two epochs.  When a schema
+         node is defined that uses this type, the description of
+         the schema node identifies both of the reference epochs.
+
+         In the value set and its semantics, this type is equivalent
+         to the TimeTicks type of the SMIv2.";
+       reference
+        "RFC 2578: Structure of Management Information Version 2
+                   (SMIv2)";
+     }
+
+     typedef timestamp {
+       type yang:timeticks;
+       description
+        "The timestamp type represents the value of an associated
+         timeticks schema node at which a specific occurrence
+         happened.  The specific occurrence must be defined in the
+         description of any schema node defined using this type.  When
+         the specific occurrence occurred prior to the last time the
+         associated timeticks attribute was zero, then the timestamp
+         value is zero.  Note that this requires all timestamp values
+         to be reset to zero when the value of the associated timeticks
+         attribute reaches 497+ days and wraps around to zero.
+
+         The associated timeticks schema node must be specified
+         in the description of any schema node using this type.
+
+         In the value set and its semantics, this type is equivalent
+         to the TimeStamp textual convention of the SMIv2.";
+       reference
+        "RFC 2579: Textual Conventions for SMIv2";
+     }
+
+     /*** collection of generic address types ***/
+
+     typedef phys-address {
+       type string {
+         pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+       }
+       description
+        "Represents media- or physical-level addresses represented
+         as a sequence octets, each octet represented by two hexadecimal
+         numbers.  Octets are separated by colons.  The canonical
+         representation uses lowercase characters.
+
+         In the value set and its semantics, this type is equivalent
+         to the PhysAddress textual convention of the SMIv2.";
+       reference
+        "RFC 2579: Textual Conventions for SMIv2";
+     }
+
+     typedef mac-address {
+       type string {
+         pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
+       }
+       description
+        "The mac-address type represents an IEEE 802 MAC address.
+         The canonical representation uses lowercase characters.
+
+         In the value set and its semantics, this type is equivalent
+         to the MacAddress textual convention of the SMIv2.";
+       reference
+        "IEEE 802: IEEE Standard for Local and Metropolitan Area
+                   Networks: Overview and Architecture
+         RFC 2579: Textual Conventions for SMIv2";
+     }
+
+     /*** collection of XML-specific types ***/
+
+     typedef xpath1.0 {
+       type string;
+       description
+        "This type represents an XPATH 1.0 expression.
+
+         When a schema node is defined that uses this type, the
+         description of the schema node MUST specify the XPath
+         context in which the XPath expression is evaluated.";
+       reference
+        "XPATH: XML Path Language (XPath) Version 1.0";
+     }
+
+     /*** collection of string types ***/
+
+     typedef hex-string {
+       type string {
+         pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
+       }
+       description
+        "A hexadecimal string with octets represented as hex digits
+         separated by colons.  The canonical representation uses
+         lowercase characters.";
+     }
+
+     typedef uuid {
+       type string {
+         pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-'
+               + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
+       }
+       description
+        "A Universally Unique IDentifier in the string representation
+         defined in RFC 4122.  The canonical representation uses
+         lowercase characters.
+
+         The following is an example of a UUID in string representation:
+         f81d4fae-7dec-11d0-a765-00a0c91e6bf6
+         ";
+       reference
+        "RFC 4122: A Universally Unique IDentifier (UUID) URN
+                   Namespace";
+     }
+
+     typedef dotted-quad {
+       type string {
+         pattern
+           '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+         + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
+       }
+       description
+         "An unsigned 32-bit number expressed in the dotted-quad
+          notation, i.e., four octets written as decimal numbers
+          and separated with the '.' (full stop) character.";
+     }
+   }
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-04-02.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-04-02.yang
new file mode 100644 (file)
index 0000000..ea68793
--- /dev/null
@@ -0,0 +1,15 @@
+module importedrevtest {
+    yang-version 1;
+    namespace "urn:imprevtst:yang";
+    prefix "imprevtst";
+
+    description "This is a test module";
+
+    revision 2015-04-02;
+
+    typedef date-and-time {
+        type string {
+            pattern "[0-9]+";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-05-02.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-05-02.yang
new file mode 100644 (file)
index 0000000..1c8210e
--- /dev/null
@@ -0,0 +1,15 @@
+module importedrevtest {
+    yang-version 1;
+    namespace "urn:imprevtst:yang";
+    prefix "imprevtst";
+
+    description "This is a test module";
+
+    revision 2015-05-02;
+
+    typedef date-and-time {
+        type string {
+            pattern "[0-9]+";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-06-02.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-06-02.yang
new file mode 100644 (file)
index 0000000..b0104da
--- /dev/null
@@ -0,0 +1,15 @@
+module importedrevtest {
+    yang-version 1;
+    namespace "urn:imprevtst:yang";
+    prefix "imprevtst";
+
+    description "This is a test module";
+
+    revision 2015-06-02;
+
+    typedef date-and-time {
+        type string {
+            pattern "[0-9]+";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-08-02.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-08-02.yang
new file mode 100644 (file)
index 0000000..f50d90b
--- /dev/null
@@ -0,0 +1,15 @@
+module importedrevtest {
+    yang-version 1;
+    namespace "urn:imprevtst:yang";
+    prefix "imprevtst";
+
+    description "This is a test module";
+
+    revision 2015-08-02;
+
+    typedef date-and-time {
+        type string {
+            pattern "[0-9]+";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-09-02.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/importedtestrev@2015-09-02.yang
new file mode 100644 (file)
index 0000000..e4046a4
--- /dev/null
@@ -0,0 +1,15 @@
+module importedrevtest {
+    yang-version 1;
+    namespace "urn:imprevtst:yang";
+    prefix "imprevtst";
+
+    description "This is a test module";
+
+    revision 2015-09-02;
+
+    typedef date-and-time {
+        type string {
+            pattern "[0-9]+";
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/node-test/bar@2016-04-04.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/node-test/bar@2016-04-04.yang
new file mode 100644 (file)
index 0000000..c1fa624
--- /dev/null
@@ -0,0 +1,14 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    revision 2016-04-04 {
+        description "test";
+    }
+
+    grouping grp {
+        container con20160404 {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/node-test/bar@2016-04-05.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/node-test/bar@2016-04-05.yang
new file mode 100644 (file)
index 0000000..a7c5fb6
--- /dev/null
@@ -0,0 +1,16 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    revision 2016-04-05 {
+        description "test";
+    }
+
+    grouping grp {
+        container con20160404 {
+        }
+        container con20160405 {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/node-test/bar@2016-04-06.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/node-test/bar@2016-04-06.yang
new file mode 100644 (file)
index 0000000..30739a3
--- /dev/null
@@ -0,0 +1,18 @@
+module bar {
+    namespace "bar";
+    prefix bar;
+    yang-version 1;
+
+    revision 2016-04-06 {
+        description "test";
+    }
+
+    grouping grp {
+        container con20160404 {
+        }
+        container con20160405 {
+        }
+        container con20160406 {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/node-test/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/node-test/foo.yang
new file mode 100644 (file)
index 0000000..631e726
--- /dev/null
@@ -0,0 +1,15 @@
+module foo {
+    namespace "foo";
+    prefix foo;
+    yang-version 1;
+
+    import bar { prefix bar; revision-date 2016-04-05; }
+
+    revision 2016-04-06 {
+        description "test";
+    }
+
+    container foo-root {
+        uses bar:grp;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-interfaces.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-interfaces.yang
new file mode 100644 (file)
index 0000000..24afaad
--- /dev/null
@@ -0,0 +1,13 @@
+module ietf-interfaces {
+
+    namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces";
+    prefix if;
+
+    import ietf-yang-types { prefix yang; revision-date 2010-09-24; }
+
+    revision 2012-11-15;
+
+    leaf last-change {
+        type yang:date-and-time;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-netconf-monitoring.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-netconf-monitoring.yang
new file mode 100644 (file)
index 0000000..5fb888b
--- /dev/null
@@ -0,0 +1,15 @@
+module ietf-netconf-monitoring {
+
+    yang-version 1;
+
+    namespace
+        "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
+
+    prefix ncm;
+
+    import ietf-yang-types { prefix yang; }
+
+    leaf locked-time {
+        type yang:date-and-time;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-yang-types.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-yang-types.yang
new file mode 100644 (file)
index 0000000..071a24f
--- /dev/null
@@ -0,0 +1,14 @@
+ module ietf-yang-types {
+
+    namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+    prefix "yang";
+
+    revision 2010-09-24;
+
+    typedef date-and-time {
+        type string {
+            pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+            + '(Z|[\+\-]\d{2}:\d{2})';
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-yang-types@2013-05-16.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-yang-types@2013-05-16.yang
new file mode 100644 (file)
index 0000000..21e9279
--- /dev/null
@@ -0,0 +1,16 @@
+module ietf-yang-types {
+
+    namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+    prefix "yang";
+
+    revision 2013-05-16;
+
+    revision 2010-09-24;
+
+    typedef date-and-time {
+        type string {
+            pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+            + '(Z|[\+\-]\d{2}:\d{2})';
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-yang-types@2013-07-15.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/simple/ietf-yang-types@2013-07-15.yang
new file mode 100644 (file)
index 0000000..3ee48ab
--- /dev/null
@@ -0,0 +1,16 @@
+module ietf-yang-types {
+
+    namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
+    prefix "yang";
+
+    revision 2013-07-15;
+
+    revision 2010-09-24;
+
+    typedef date-and-time {
+        type string {
+            pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+            + '(Z|[\+\-]\d{2}:\d{2})';
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/testrev.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/multiple-revisions/testrev.yang
new file mode 100644 (file)
index 0000000..a643c1f
--- /dev/null
@@ -0,0 +1,19 @@
+module testrev {
+    yang-version 1;
+    namespace "urn:tstrev:yang";
+    prefix "tstrev";
+
+    import importedrevtest { prefix "imprevtst"; }
+
+    description "This is a test module";
+
+    revision 2001-01-01 {
+        description "initial version";
+    }
+
+    container mycontainer {
+        leaf myleaf {
+            type imprevtst:date-and-time;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/revisions/more-revisions-test.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/revisions/more-revisions-test.yang
new file mode 100644 (file)
index 0000000..50d76db
--- /dev/null
@@ -0,0 +1,14 @@
+module more-revisions {
+    namespace "urn.mr";
+    prefix "mr";
+
+    //RFC6020 says that all revisions SHOULD be in reverse chronological order, but it doesn't say they MUST
+    //so, we have to take care of it in Java code (ModuleStatementSupport class)
+    revision "2015-05-07";
+
+    revision "2015-05-08";
+
+    revision "2015-05-01";
+
+    revision "2015-06-07";
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/root.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/root.yang
new file mode 100644 (file)
index 0000000..2e72ad1
--- /dev/null
@@ -0,0 +1,14 @@
+module root {
+       prefix root_pref;
+       namespace root_ns;
+
+       import augmented {
+               prefix imp;
+       }
+
+       augment /imp:cont1/imp:cont2/imp:cont-target {
+               leaf added {
+                       type string;
+               }
+       }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/simple-nodes-semantic.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/simple-nodes-semantic.yang
new file mode 100644 (file)
index 0000000..1e7e991
--- /dev/null
@@ -0,0 +1,52 @@
+module simple-nodes {
+    yang-version 1;
+    namespace "urn:opendaylight:"+
+        "simple-nodes";
+    prefix "sn";
+
+    organization "opendaylight";
+
+    import test {
+        prefix tst;
+    }
+
+    description
+        "test file containing yang data nodes";
+
+    revision "2013-07-30" {
+        description
+            "Initial revision.";
+        reference "will be defined";
+    }
+
+
+    typedef mytype {
+        type string;
+    }
+
+    // NOTE: simple comment
+
+    feature has-name;
+
+    anyxml data {
+         config false;
+         description "anyxml desc";
+         if-feature has-name;
+         mandatory true;
+         must "ifType != 'ethernet' or " +
+                      "(ifType = 'ethernet' and ifMTU = 1500)" {
+            error-message "An ethernet MTU must be 1500";
+         }
+         must "ifType != 'atm' or " +
+                      "(ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)" {
+                     error-app-tag "anyxml data error-app-tag";
+                     error-message "An atm MTU must be  64 .. 17966";
+                     description "an error occured in data";
+                     reference "data must ref";
+                }
+         reference "data ref";
+         status obsolete;
+         when "class != 'wheel'";
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/simple-types.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/simple-types.yang
new file mode 100644 (file)
index 0000000..5512b1a
--- /dev/null
@@ -0,0 +1,103 @@
+module simple-types {
+
+    yang-version 1;
+    namespace "urn:simple.types";
+    prefix "st";
+
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+    revision 2015-04-16;
+
+    typedef myint8 {
+        type int8 {
+            range "1..20";
+        }
+    }
+
+    identity my-base;
+
+    typedef myint32 {
+         type int32 {
+            range "1..2000";
+         }
+    }
+
+    container mycontainer {
+       leaf mynumerical {
+          type int32 {
+            range "1..3000";
+          }
+       }
+
+       leaf mynorestrictions {
+           type string;
+       }
+
+       leaf mytypedefuse1 {
+           type myint32;
+       }
+
+       leaf mytypedefuse2 {
+           type myint8;
+       }
+
+        leaf myunion {
+            type union {
+                type int8;
+                type int32;
+            }
+        }
+
+        leaf mydecimal64 {
+            type decimal64 {
+                fraction-digits 2;
+                range "1 .. 3.14 | 10 | 20..max";
+            }
+        }
+
+        leaf mystring {
+            type string {
+                length "1..255";
+                pattern "[0-9a-fA-F]*";
+            }
+        }
+
+        leaf myenum {
+            type enumeration {
+                enum zero;
+                enum one;
+                enum seven {
+                    value 7;
+                }
+            }
+        }
+
+        leaf mybits {
+            type bits {
+                bit disable-nagle {
+                    position 0;
+                }
+                bit auto-sense-speed {
+                    position 1;
+                }
+                bit onyl-10-Mb {
+                    position 2;
+                }
+            }
+            default "auto-sense-speed";
+        }
+
+        leaf mgmt-interface {
+            type leafref {
+               path "../myenum";
+            }
+        }
+
+        leaf crypto {
+            type identityref {
+                base "my-base";
+            }
+        }
+
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/test.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/test.yang
new file mode 100644 (file)
index 0000000..6c5fef5
--- /dev/null
@@ -0,0 +1,19 @@
+module test {
+    yang-version 1;
+    namespace "urn:tst:yang";
+    prefix "tst";
+
+    import importedtest {
+            prefix "imptst";
+    }
+
+    description "This is a test module";
+
+    revision 2015-03-04;
+
+    container mycontainer {
+        leaf myleaf {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/isis-topology@2013-07-12.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/isis-topology@2013-07-12.yang
new file mode 100644 (file)
index 0000000..23e9dbc
--- /dev/null
@@ -0,0 +1,143 @@
+module isis-topology {
+       yang-version 1;
+       namespace "urn:TBD:params:xml:ns:yang:network:isis-topology";
+       // replace with IANA namespace when assigned
+       prefix "isis";
+       import network-topology {
+           prefix nt;
+           revision-date 2013-07-12;
+       }
+       import l3-unicast-igp-topology {
+               prefix igp;
+               revision-date 2013-07-12;
+       }
+       import ted {
+               prefix ted;
+               revision-date 2013-07-12;
+       }
+       
+       organization "TBD";
+       contact "TBD";
+       description "ISIS Topology model";
+
+       revision "2013-07-12" {
+               description "Initial version";
+       }
+    typedef iso-system-id {
+        description "ISO System ID. RFC 1237";
+        type string {
+            pattern '[0-9a-fA-F]{4}(.[0-9a-fA-F]{4}){2}';
+        }
+    }
+
+    typedef iso-pseudonode-id {
+        description "ISO pseudonode id for broadcast network";
+        type string {
+            pattern '[0-9a-fA-F]{2}';
+        }
+    }
+
+       typedef iso-net-id {
+        description "ISO NET ID. RFC 1237";
+        type string {
+            pattern '[0-9a-fA-F]{2}((.[0-9a-fA-F]{4}){6})';
+        }
+    }
+
+       grouping isis-topology-type {
+           container isis {
+               presence "Indicates ISIS Topology";
+           }
+       }
+
+       augment "/nt:network-topology/nt:topology/nt:topology-types/igp:l3-unicast-igp-topology" {
+           uses isis-topology-type;
+       }
+
+       augment "/nt:network-topology/nt:topology/igp:igp-topology-attributes" {
+           when "../../topology-types/isis";
+           container isis-topogloy-attributes {
+                   leaf net {
+                       type iso-net-id;
+                   }
+           }
+       }
+
+       augment "/nt:network-topology/nt:topology/nt:node/igp:igp-node-attributes" {
+           when "../../../topology-types/isis";
+           uses isis-node-attributes;
+       }
+
+       augment "/nt:network-topology/nt:topology/nt:link/igp:igp-link-attributes" {
+           when "../../../../topology-types/isis";
+           uses isis-link-attributes;
+       }
+
+       grouping isis-node-attributes {
+               container isis-node-attributes {
+                       container iso {
+                               leaf iso-system-id {
+                                       type iso-system-id;
+                               }
+                               leaf iso-pseudonode-id {
+                                       default "0";
+                                       type iso-pseudonode-id;
+                               }
+            }
+            leaf-list net {
+               max-elements 3;
+                       type iso-net-id;
+            }
+            leaf-list multi-topology-id {
+                description "List of Multi Topology Identifier upto 128 (0-127). RFC 4915";
+                max-elements "128";
+                type uint8 {
+                       range "0..127";
+                }
+            }
+            choice router-type {
+               case level-2 {
+                       leaf level-2 {
+                               type empty;
+                       }
+               }
+                case level-1 {
+                    leaf level-1 {
+                        type empty;
+                       }
+                }
+                case level-1-2 {
+                    leaf level-1-2 {
+                               type empty;
+                    }
+               }
+               }
+                       container ted {
+                           uses ted:ted-node-attributes;
+                       }
+               }
+       }
+
+       grouping isis-link-attributes {
+               container isis-link-attributes {
+                       leaf multi-topology-id {
+                               type uint8 {
+                                       range "0..127";
+                               }
+                       }
+                       container ted {
+                           uses ted:ted-link-attributes;       
+                       }
+               }
+       }
+       /*
+       augment "/igp:igp-node-event" {
+               uses isis-topology-type;
+               uses isis-node-attributes;
+       }
+
+       augment "/igp:igp-link-event" {
+               uses isis-topology-type;
+               uses isis-link-attributes;
+       }*/
+} // Module isis-topology
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/isis-topology@2013-10-21.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/isis-topology@2013-10-21.yang
new file mode 100644 (file)
index 0000000..edf75ca
--- /dev/null
@@ -0,0 +1,143 @@
+module isis-topology {
+    yang-version 1;
+    namespace "urn:TBD:params:xml:ns:yang:network:isis-topology";
+    // replace with IANA namespace when assigned
+    prefix "isis";
+    import network-topology {
+        prefix nt;
+        revision-date 2013-10-21;
+    }
+    import l3-unicast-igp-topology {
+        prefix igp;
+        revision-date 2013-10-21;
+    }
+    import ted {
+        prefix ted;
+        revision-date 2013-10-21;
+    }
+
+    organization "TBD";
+    contact "TBD";
+    description "ISIS Topology model";
+
+    revision "2013-10-21" {
+        description "Initial version";
+    }
+    typedef iso-system-id {
+        description "ISO System ID. RFC 1237";
+        type string {
+            pattern '[0-9a-fA-F]{4}(\.[0-9a-fA-F]{4}){2}';
+        }
+    }
+
+    typedef iso-pseudonode-id {
+        description "ISO pseudonode id for broadcast network";
+        type string {
+            pattern '[0-9a-fA-F]{2}';
+        }
+    }
+    typedef iso-net-id {
+        description "ISO NET ID. RFC 1237";
+        type string {
+            pattern '[0-9a-fA-F]{2}((\.[0-9a-fA-F]{4}){6})';
+        }
+    }
+
+    grouping isis-topology-type {
+        container isis {
+            presence "Indicates ISIS Topology";
+        }
+    }
+
+    augment "/nt:network-topology/nt:topology/nt:topology-types/igp:l3-unicast-igp-topology" {
+        uses isis-topology-type;
+    }
+
+    augment "/nt:network-topology/nt:topology/igp:igp-topology-attributes" {
+        when "../nt:topology-types/igp:l3-unicast-igp-topology/isis";
+        container isis-topology-attributes {
+            leaf net {
+                type iso-net-id;
+            }
+        }
+    }
+
+    augment "/nt:network-topology/nt:topology/nt:node/igp:igp-node-attributes" {
+        when "../../nt:topology-types/igp:l3-unicast-igp-topology/isis";
+        uses isis-node-attributes;
+    }
+
+    augment "/nt:network-topology/nt:topology/nt:link/igp:igp-link-attributes" {
+
+        when "../../nt:topology-types/igp:l3-unicast-igp-topology/isis";
+        uses isis-link-attributes;
+    }
+
+    grouping isis-node-attributes {
+        container isis-node-attributes {
+            container iso {
+                leaf iso-system-id {
+                    type iso-system-id;
+                }
+                leaf iso-pseudonode-id {
+                    default "0";
+                    type iso-pseudonode-id;
+                }
+            }
+            leaf-list net {
+                max-elements 3;
+                type iso-net-id;
+            }
+            leaf-list multi-topology-id {
+                description "List of Multi Topology Identifier upto 128 (0-127). RFC 4915";
+                max-elements "128";
+                type uint8 {
+                    range "0..127";
+                }
+            }
+            choice router-type {
+                case level-2 {
+                    leaf level-2 {
+                        type empty;
+                    }
+                }
+                case level-1 {
+                    leaf level-1 {
+                        type empty;
+                    }
+                }
+                case level-1-2 {
+                    leaf level-1-2 {
+                        type empty;
+                    }
+                }
+            }
+            container ted {
+                uses ted:ted-node-attributes;
+            }
+        }
+    }
+
+    grouping isis-link-attributes {
+        container isis-link-attributes {
+            leaf multi-topology-id {
+                type uint8 {
+                    range "0..127";
+                }
+            }
+            container ted {
+                uses ted:ted-link-attributes;
+            }
+        }
+    }
+
+    augment "/igp:igp-node-event" {
+        uses isis-topology-type;
+        uses isis-node-attributes;
+    }
+
+    augment "/igp:igp-link-event" {
+        uses isis-topology-type;
+        uses isis-link-attributes;
+    }
+} // Module isis-topology
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/l3-unicast-igp-topology@2013-07-12.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/l3-unicast-igp-topology@2013-07-12.yang
new file mode 100644 (file)
index 0000000..99df396
--- /dev/null
@@ -0,0 +1,233 @@
+module l3-unicast-igp-topology {
+    yang-version 1;
+    namespace "urn:TBD:params:xml:ns:yang:nt:l3-unicast-igp-topology";
+    // replace with IANA namespace when assigned
+    prefix "l3t";
+    import network-topology {
+        prefix "nt";
+        revision-date 2013-07-12;
+    }
+    
+    import ietf-inet-types {
+        prefix "inet";
+    }
+
+    organization "TBD";
+    contact "TBD";
+
+    revision "2013-07-12" {
+        description "Initial revision";
+        reference "TBD";
+    }
+    
+    typedef igp-event-type {
+        description "IGP Event type for notifications";
+        type enumeration {
+            enum "add" {
+                value 0;
+                    description "An IGP node or link or prefix or termination-point has been added";
+            }
+            enum "remove" {
+                value 1;
+                description "An IGP node or link or prefix or termination-point has been removed";
+            }
+            enum "update" {
+                value 2;
+                description "An IGP node or link or prefix or termination-point has been updated";
+            }
+        }
+    } // igp-event-type
+
+    identity flag-identity {
+        description "Base type for flags";
+    }
+    identity undefined-flag {
+        base "flag-identity";
+    }
+    
+    typedef flag-type {
+        type identityref {
+            base "flag-identity";
+        }
+    }
+    
+    grouping igp-prefix-attributes {
+        leaf prefix {
+            type inet:ip-prefix;
+        }
+        leaf metric {
+            type uint32;
+        }
+        leaf-list flag {
+            type flag-type;
+        }
+    }
+    
+    grouping l3-unicast-igp-topology-type {
+        container l3-unicast-igp-topology {
+            presence "indicates L3 Unicast IGP Topology";
+        }
+    }
+    
+    grouping igp-topology-attributes {
+        container igp-topology-attributes { 
+            leaf name {
+                description "Name of the topology";
+                    type string;
+            }
+            leaf-list flag {
+                description "Topology flags";
+                type flag-type;
+            }
+        }
+    }
+
+    grouping igp-node-attributes {
+        container igp-node-attributes {
+            leaf name {
+                description "Node name";
+                type inet:domain-name; 
+            }
+            leaf-list flag {
+                description "Node operational flags";
+                type flag-type; 
+            }
+            
+            leaf-list router-id {
+                description "Router-id for the node";
+                type inet:ip-address;
+            }
+            
+            list prefix {  
+                key "prefix";
+                uses igp-prefix-attributes; 
+            }
+        }
+    }
+
+    grouping igp-link-attributes {
+        container igp-link-attributes { 
+            leaf name {
+                description "Link Name";
+                type string;
+            }
+            leaf-list flag {
+                description "Link flags";
+                type flag-type; 
+            }
+            leaf metric {
+                description "Link Metric";
+                type uint32 {
+                    range "0..16777215"  {
+                        description "
+                        "; 
+                        // OSPF/ISIS supports max 3 byte metric. 
+                        // Ideally we would like this restriction to be
+                        // defined in the derived models, however,  
+                        // we are not allowed to augment a "must" statement. 
+                    }
+                }
+            }
+        }
+    } // grouping igp-link-attributes
+
+    grouping igp-termination-point-attributes {
+        container igp-termination-point-attributes {
+            choice termination-point-type {  
+                case ip {
+                   leaf-list ip-address {
+                    description "IPv4 or IPv6 address";
+                    type inet:ip-address;
+                    }
+                }
+                case unnumbered {
+                    leaf unnumbered-id {  
+                    description "Unnumbered interface identifier";
+                    type uint32;
+                    }
+                }
+            }
+        }
+    } // grouping igp-termination-point-attributes
+    
+    
+    augment "/nt:network-topology/nt:topology/nt:topology-types" {  
+        uses l3-unicast-igp-topology-type; 
+    }
+    
+    
+    augment "/nt:network-topology/nt:topology" {
+        when "topology-types/l3-unicast-igp-topology";
+        uses igp-topology-attributes;  
+    }
+    
+    augment "/nt:network-topology/nt:topology/nt:node" {
+        when "../../topology-types/l3-unicast-igp-topology";
+        uses igp-node-attributes; 
+    }
+    
+
+    augment "/nt:network-topology/nt:topology/nt:link" {
+        when "../../topology-types/l3-unicast-igp-topology";
+        uses igp-link-attributes; 
+    }
+    augment "/nt:network-topology/nt:topology/nt:node/nt:termination-point" {
+        when "../../topology-types/l3-unicast-igp-topology";
+        uses igp-termination-point-attributes; 
+    }
+    
+    notification igp-node-event {
+        leaf igp-event-type {
+            type igp-event-type;
+        }
+        leaf topology-ref {
+            type nt:topology-ref;
+        }
+        uses l3-unicast-igp-topology-type; 
+        uses nt:node-attributes;
+        uses igp-node-attributes;
+    }
+
+    notification igp-link-event {
+        leaf igp-event-type {
+            type igp-event-type;
+        }
+        leaf topology-ref {
+            type nt:topology-ref;
+        }
+        uses l3-unicast-igp-topology-type; 
+        uses nt:link-attributes; 
+        uses igp-link-attributes; 
+    }
+
+    notification igp-prefix-event {
+        leaf igp-event-type {
+            type igp-event-type;
+        }
+        leaf topology-ref {
+            type nt:topology-ref;
+        }
+        leaf node-ref {
+            type nt:node-ref;
+        }
+        uses l3-unicast-igp-topology-type; 
+        container prefix {
+            uses igp-prefix-attributes;
+        }
+    }   
+
+    notification termination-point-event {
+        leaf igp-event-type {
+            type igp-event-type;
+        }
+        leaf topology-ref {
+            type nt:topology-ref;
+        }
+        leaf node-ref {
+            type nt:node-ref;
+        }
+        uses l3-unicast-igp-topology-type;
+        uses nt:tp-attributes;
+        uses igp-termination-point-attributes;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/l3-unicast-igp-topology@2013-10-21.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/l3-unicast-igp-topology@2013-10-21.yang
new file mode 100644 (file)
index 0000000..00db145
--- /dev/null
@@ -0,0 +1,233 @@
+module l3-unicast-igp-topology {
+    yang-version 1;
+    namespace "urn:TBD:params:xml:ns:yang:nt:l3-unicast-igp-topology";
+    // replace with IANA namespace when assigned
+    prefix "l3t";
+    import network-topology {
+        prefix "nt";
+        revision-date 2013-10-21;
+    }
+
+    import ietf-inet-types {
+        prefix "inet";
+       revision-date 2010-09-24;
+    }
+
+    organization "TBD";
+    contact "TBD";
+
+    revision "2013-10-21" {
+        description "Initial revision";
+        reference "TBD";
+    }
+
+    typedef igp-event-type {
+        description "IGP Event type for notifications";
+            type enumeration {
+                enum "add" {
+                    value 0;
+                        description "An IGP node or link or prefix or termination-point has been added";
+                }
+                enum "remove" {
+                    value 1;
+                    description "An IGP node or link or prefix or termination-point has been removed";
+                }
+                enum "update" {
+                    value 2;
+                    description "An IGP node or link or prefix or termination-point has been updated";
+                }
+            }
+        } // igp-event-type
+
+    identity flag-identity {
+        description "Base type for flags";
+    }
+    identity undefined-flag {
+        base "flag-identity";
+    }
+
+    typedef flag-type {
+
+        type identityref {
+            base "flag-identity";
+        }
+    }
+
+    grouping igp-prefix-attributes {
+        leaf prefix {
+            type inet:ip-prefix;
+        }
+        leaf metric {
+            type uint32;
+        }
+        leaf-list flag {
+            type flag-type;
+        }
+    }
+
+    grouping l3-unicast-igp-topology-type {
+        container l3-unicast-igp-topology {
+            presence "indicates L3 Unicast IGP Topology";
+        }
+    }
+
+    grouping igp-topology-attributes {
+        container igp-topology-attributes {
+            leaf name {
+                description "Name of the topology";
+                    type string;
+            }
+            leaf-list flag {
+                description "Topology flags";
+                type flag-type;
+            }
+        }
+    }
+
+    grouping igp-node-attributes {
+        container igp-node-attributes {
+            leaf name {
+                description "Node name";
+                type inet:domain-name;
+            }
+            leaf-list flag {
+                description "Node operational flags";
+                type flag-type;
+            }
+            leaf-list router-id {
+                description "Router-id for the node";
+
+                type inet:ip-address;
+            }
+            list prefix {
+                key "prefix";
+                uses igp-prefix-attributes;
+            }
+        }
+    }
+
+    grouping igp-link-attributes {
+        container igp-link-attributes {
+            leaf name {
+                description "Link Name";
+                type string;
+            }
+            leaf-list flag {
+                description "Link flags";
+                type flag-type;
+            }
+            leaf metric {
+                description "Link Metric";
+                type uint32 {
+                    range "0..16777215"  {
+                        description "
+                        ";
+                        // OSPF/ISIS supports max 3 byte metric.
+                        // Ideally we would like this restriction to be
+                        // defined in the derived models, however,
+                        // we are not allowed to augment a "must" statement.
+                    }
+                }
+            }
+        }
+    } // grouping igp-link-attributes
+
+    grouping igp-termination-point-attributes {
+        container igp-termination-point-attributes {
+            choice termination-point-type {
+                case ip {
+                   leaf-list ip-address {
+                    description "IPv4 or IPv6 address";
+                    type inet:ip-address;
+                    }
+                }
+                case unnumbered {
+                    leaf unnumbered-id {
+                    description "Unnumbered interface identifier";
+                    type uint32;
+
+                    }
+                }
+            }
+        }
+    } // grouping igp-termination-point-attributes
+
+
+    augment "/nt:network-topology/nt:topology/nt:topology-types" {
+        uses l3-unicast-igp-topology-type;
+    }
+
+    augment "/nt:network-topology/nt:topology" {
+        when "nt:topology-types/l3-unicast-igp-topology";
+        uses igp-topology-attributes;
+    }
+
+    augment "/nt:network-topology/nt:topology/nt:node" {
+        when "../nt:topology-types/l3-unicast-igp-topology";
+        uses igp-node-attributes;
+    }
+    augment "/nt:network-topology/nt:topology/nt:link" {
+        when "../nt:topology-types/l3-unicast-igp-topology";
+        uses igp-link-attributes;
+    }
+    augment "/nt:network-topology/nt:topology/nt:node/nt:termination-point" {
+        when "../../nt:topology-types/l3-unicast-igp-topology";
+        uses igp-termination-point-attributes;
+    }
+
+    notification igp-node-event {
+        leaf igp-event-type {
+            type igp-event-type;
+        }
+        leaf topology-ref {
+            type nt:topology-ref;
+        }
+        uses l3-unicast-igp-topology-type;
+        uses nt:node-attributes;
+        uses igp-node-attributes;
+    }
+
+    notification igp-link-event {
+        leaf igp-event-type {
+            type igp-event-type;
+        }
+        leaf topology-ref {
+            type nt:topology-ref;
+        }
+
+        uses l3-unicast-igp-topology-type;
+        uses nt:link-attributes;
+        uses igp-link-attributes;
+    }
+
+    notification igp-prefix-event {
+        leaf igp-event-type {
+            type igp-event-type;
+        }
+        leaf topology-ref {
+            type nt:topology-ref;
+        }
+        leaf node-ref {
+            type nt:node-ref;
+        }
+        uses l3-unicast-igp-topology-type;
+        container prefix {
+            uses igp-prefix-attributes;
+        }
+    }
+
+    notification termination-point-event {
+        leaf igp-event-type {
+            type igp-event-type;
+        }
+        leaf topology-ref {
+            type nt:topology-ref;
+        }
+        leaf node-ref {
+            type nt:node-ref;
+        }
+        uses l3-unicast-igp-topology-type;
+        uses nt:tp-attributes;
+        uses igp-termination-point-attributes;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/ted@2013-07-12.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/ted@2013-07-12.yang
new file mode 100644 (file)
index 0000000..0d0063d
--- /dev/null
@@ -0,0 +1,301 @@
+module ted {
+  yang-version 1;
+  namespace "urn:TBD:params:xml:ns:yang:network:ted";
+  // replace with IANA namespace when assigned
+  prefix ted;
+
+  import ietf-inet-types {
+    prefix inet;
+  }
+
+  organization "TBD";
+  contact
+    "TBD";
+  description
+    "Helper module to hold TED attributes for OSPF/ISIS";
+
+  revision 2013-07-12 {
+    description
+      "Initial revision";
+  }
+
+  typedef switching-capabilities {
+    description
+      "Switching Capabilities of an interface.";
+    reference
+      "RFC 5307: IS-IS Extensions in Support of Generalized
+       Multi-Protocol Label Switching (GMPLS)";
+    type enumeration {
+      enum "PSC-1" {
+        description
+          "Packet-Switch Capable-1 (PSC-1)";
+        value 1;
+      }
+      enum "PSC-2" {
+        description
+          "Packet-Switch Capable-2 (PSC-2)";
+        value 2;
+      }
+      enum "PSC-3" {
+        description
+          "Packet-Switch Capable-3 (PSC-3)";
+        value 3;
+      }
+      enum "PSC-4" {
+        description
+          "Packet-Switch Capable-4 (PSC-4)";
+        value 4;
+      }
+      enum "L2SC" {
+        description
+          "Layer-2 Switch Capable (L2SC)";
+        value 51;
+      }
+      enum "TDM" {
+        description
+          "Time-Division-Multiplex Capable (TDM)";
+        value 100;
+      }
+      enum "LSC" {
+        description
+          "Lambda-Switch Capable (LSC)";
+        value 150;
+      }
+      enum "FSC" {
+        description
+          "Fiber-Switch Capable (FSC)";
+        value 200;
+      }
+    }
+  }
+
+  typedef pcc-capabilities {
+    description
+      "Path Computation Capabilities.";
+    reference
+      "RFC 5088, draft-ietf-pce-disco-protoc-isis-07.txt
+       OSPF/ISIS Protocol Extensions for Path Computation Element (PCE) Discovery.";
+    type bits {
+      bit path-computation-with-gmpls-link-constraints {
+        position 0;
+      }
+      bit bidirectional-path-computation {
+        position 1;
+      }
+      bit diverse-path-computation {
+        position 2;
+      }
+      bit load-balanced-path-computation {
+        position 3;
+      }
+      bit synchronized-path-computation {
+        position 4;
+      }
+      bit support-for-multiple-objective-functions {
+        position 5;
+      }
+      bit support-for-additive-path-constraints {
+        position 6;
+      }
+      bit support-for-request-prioritization {
+        position 7;
+      }
+      bit support-for-multiple-requests-per-message {
+        position 8;
+      }
+    }
+  }
+
+
+grouping srlg-attributes {
+    description
+      "Shared Risk Link Group Attributes";
+    reference
+      "RFC 5307, RFC 4203: ISIS / OSPF Extensions in Support of
+       Generalized Multi-Protocol Label Switching (GMPLS)";
+    list interface-switching-capabilities {
+      description
+        "List of interface capabilities for this interface";
+      key "switching-capability";
+      leaf switching-capability {
+        description
+          "Switching Capability for this interface";
+        type ted:switching-capabilities;
+      }
+      leaf encoding {
+        description
+          "Encoding supported by this interface";
+        type uint8;
+      }
+      list max-lsp-bandwidth {
+        description
+          "Maximum LSP Bandwidth at priorities 0-7";
+        max-elements "8";
+        key "priority";
+        leaf priority {
+          type uint8 {
+            range "0..7";
+          }
+        }
+        leaf bandwidth {
+          description
+            "Max LSP Bandwidth for this level";
+          type decimal64 {
+            fraction-digits 2;
+          }
+        }
+      }
+      container packet-switch-capable {
+        when "../switching-capability = PSC-1 or ../switching-capability = PSC-2 or ../switching-capability = PSC-3 or ../switching-capability = PSC-4";
+        description
+          "Interface has packet-switching capabilities";
+        leaf minimum-lsp-bandwidth {
+          description
+            "Minimum LSP Bandwidth. Units in bytes per second";
+          type decimal64 {
+            fraction-digits 2;
+          }
+        }
+        leaf interface-mtu {
+          description
+            "Interface MTU";
+          type uint16;
+        }
+      }
+      container time-division-multiplex-capable {
+        when "../switching-capability = TDM";
+        description
+          "Interface has time-division multiplex capabilities";
+        leaf minimum-lsp-bandwidth {
+          description
+            "Minimum LSP Bandwidth. Units in bytes per second";
+          type decimal64 {
+            fraction-digits 2;
+          }
+        }
+        leaf indication {
+          description
+            "Indication whether the interface supports Standard or Arbitrary SONET/SDH";
+          type uint16;
+        }
+      }
+    }
+    list srlg-values {
+      description
+        "List of Shared Risk Link Group this interface belongs to.";
+      key "srlg-value";
+      leaf srlg-value {
+        description
+          "Shared Risk Link Group value";
+        type uint32;
+      }
+    }
+    leaf link-protection-type {
+      description
+        "Link Protection Type desired for this link";
+      type uint16;
+    }
+  }
+
+  grouping ted-node-attributes {
+    description
+      "Identifier to uniquely identify a node in TED";
+    reference "RFC 5305, RFC 6119: IPv6 Traffic Engineering in IS-IS/OSPF";
+    leaf te-router-id-ipv4 {
+      description
+        "Globally unique IPv4 Traffic Engineering Router ID.";
+      type inet:ipv4-address;
+    }
+    leaf te-router-id-ipv6 {
+      description
+        "Globally unique IPv6 Traffic Engineering Router ID";
+      type inet:ipv6-address;
+    }
+    list ipv4-local-address {
+      description
+        "List of IPv4 Local Address(OSPF). RFC 5786";
+      key "ipv4-prefix";
+      leaf ipv4-prefix {
+        description
+          "Local IPv4 address for the node";
+        type inet:ipv4-prefix;
+      }
+    }
+    list ipv6-local-address {
+      description
+        "List of IPv6 Local Address.";
+      reference
+        "RFC 5786: Advertising a Router's Local Addresses
+         in OSPF Traffic Engineering (TE) Extensions";
+      key "ipv6-prefix";
+      leaf ipv6-prefix {
+        description
+          "Local IPv6 address for the node";
+        type inet:ipv6-prefix;
+      }
+      leaf prefix-option {
+        description
+          "IPv6 prefix option.";
+        type uint8;
+      }
+    }
+    leaf pcc-capabilities {
+      description
+        "OSPF/ISIS PCC capabilities";
+      type pcc-capabilities;
+    }
+  }
+
+  grouping ted-link-attributes {
+    description
+      "TED Attributes associated with the link.";
+    reference "RFC 3630, RFC 3784: IS-IS / OSPF Traffic Engineering (TE)";
+    leaf color {
+      description
+        "Administrative group or color of the link";
+      type uint32;
+    }
+    leaf max-link-bandwidth {
+      description
+        "Maximum bandwidth that can be see on this link in this direction. Units in bytes per second";
+      type decimal64 {
+        fraction-digits 2;
+      }
+    }
+    leaf max-resv-link-bandwidth {
+      description
+        "Maximum amount of bandwidth that can be reserved in this direction in this link. Units in bytes per second";
+      type decimal64 {
+        fraction-digits 2;
+      }
+    }
+    list unreserved-bandwidth {
+      description
+        "Unreserved bandwidth for 0-7 priority levels. Units in bytes per second";
+      max-elements "8";
+      key "priority";
+      leaf priority {
+        type uint8 {
+          range "0..7";
+        }
+      }
+      leaf bandwidth {
+        description
+          "Unreserved bandwidth for this level";
+        type decimal64 {
+          fraction-digits 2;
+        }
+      }
+    }
+    leaf te-default-metric {
+      description
+        "Traffic Engineering Metric";
+      type uint32;
+    }
+    container srlg {
+      description
+        "Shared Risk Link Group Attributes";
+      uses srlg-attributes;
+    }
+  }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/ted@2013-10-21.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/two-revisions/ted@2013-10-21.yang
new file mode 100644 (file)
index 0000000..7456623
--- /dev/null
@@ -0,0 +1,306 @@
+module ted {
+  yang-version 1;
+  namespace "urn:TBD:params:xml:ns:yang:network:ted";
+  // replace with IANA namespace when assigned
+  prefix ted;
+
+  import ietf-inet-types {
+    prefix inet;
+    revision-date 2010-09-24;
+  }
+
+  organization "TBD";
+  contact
+    "TBD";
+  description
+    "Helper module to hold TED attributes for OSPF/ISIS";
+
+  revision 2013-10-21 {
+
+    description
+      "Initial revision";
+  }
+
+  typedef switching-capabilities {
+    description
+      "Switching Capabilities of an interface.";
+    reference
+      "RFC 5307: IS-IS Extensions in Support of Generalized
+       Multi-Protocol Label Switching (GMPLS)";
+    type enumeration {
+      enum "PSC-1" {
+        description
+          "Packet-Switch Capable-1 (PSC-1)";
+        value 1;
+      }
+      enum "PSC-2" {
+        description
+          "Packet-Switch Capable-2 (PSC-2)";
+        value 2;
+      }
+      enum "PSC-3" {
+        description
+          "Packet-Switch Capable-3 (PSC-3)";
+        value 3;
+      }
+      enum "PSC-4" {
+        description
+          "Packet-Switch Capable-4 (PSC-4)";
+        value 4;
+      }
+      enum "L2SC" {
+        description
+          "Layer-2 Switch Capable (L2SC)";
+        value 51;
+      }
+      enum "TDM" {
+        description
+          "Time-Division-Multiplex Capable (TDM)";
+        value 100;
+      }
+      enum "LSC" {
+        description
+          "Lambda-Switch Capable (LSC)";
+        value 150;
+      }
+      enum "FSC" {
+        description
+
+          "Fiber-Switch Capable (FSC)";
+        value 200;
+      }
+    }
+  }
+
+  typedef pcc-capabilities {
+    description
+      "Path Computation Capabilities.";
+    reference
+      "RFC 5088, draft-ietf-pce-disco-protoc-isis-07.txt
+       OSPF/ISIS Protocol Extensions for Path Computation Element (PCE) Discovery.";
+    type bits {
+      bit path-computation-with-gmpls-link-constraints {
+        position 0;
+      }
+      bit bidirectional-path-computation {
+        position 1;
+      }
+      bit diverse-path-computation {
+        position 2;
+      }
+      bit load-balanced-path-computation {
+        position 3;
+      }
+      bit synchronized-path-computation {
+        position 4;
+      }
+      bit support-for-multiple-objective-functions {
+        position 5;
+      }
+      bit support-for-additive-path-constraints {
+        position 6;
+      }
+      bit support-for-request-prioritization {
+        position 7;
+      }
+      bit support-for-multiple-requests-per-message {
+        position 8;
+      }
+    }
+  }
+
+  grouping ted-node-attributes {
+    description
+      "Identifier to uniquely identify a node in TED";
+    reference "RFC 5305, RFC 6119: IPv6 Traffic Engineering in IS-IS/OSPF";
+    leaf te-router-id-ipv4 {
+
+      description
+        "Globally unique IPv4 Traffic Engineering Router ID.";
+      type inet:ipv4-address;
+    }
+    leaf te-router-id-ipv6 {
+      description
+        "Globally unique IPv6 Traffic Engineering Router ID";
+      type inet:ipv6-address;
+    }
+    list ipv4-local-address {
+      description
+        "List of IPv4 Local Address(OSPF). RFC 5786";
+      key "ipv4-prefix";
+      leaf ipv4-prefix {
+        description
+          "Local IPv4 address for the node";
+        type inet:ipv4-prefix;
+      }
+    }
+    list ipv6-local-address {
+      description
+        "List of IPv6 Local Address.";
+      reference
+        "RFC 5786: Advertising a Router's Local Addresses
+         in OSPF Traffic Engineering (TE) Extensions";
+      key "ipv6-prefix";
+      leaf ipv6-prefix {
+        description
+          "Local IPv6 address for the node";
+        type inet:ipv6-prefix;
+      }
+      leaf prefix-option {
+        description
+          "IPv6 prefix option.";
+        type uint8;
+      }
+    }
+    leaf pcc-capabilities {
+      description
+        "OSPF/ISIS PCC capabilities";
+      type pcc-capabilities;
+    }
+  }
+
+  grouping ted-link-attributes {
+    description
+      "TED Attributes associated with the link.";
+    reference "RFC 3630, RFC 3784: IS-IS / OSPF Traffic Engineering (TE)";
+
+    leaf color {
+      description
+        "Administrative group or color of the link";
+      type uint32;
+    }
+    leaf max-link-bandwidth {
+      description
+        "Maximum bandwidth that can be see on this link in this direction. Units in bytes per second";
+      type decimal64 {
+        fraction-digits 2;
+      }
+    }
+    leaf max-resv-link-bandwidth {
+      description
+        "Maximum amount of bandwidth that can be reserved in this direction in this link. Units in bytes per second";
+      type decimal64 {
+        fraction-digits 2;
+      }
+    }
+    list unreserved-bandwidth {
+      description
+        "Unreserved bandwidth for 0-7 priority levels. Units in bytes per second";
+      max-elements "8";
+      key "priority";
+      leaf priority {
+        type uint8 {
+          range "0..7";
+        }
+      }
+      leaf bandwidth {
+        description
+          "Unreserved bandwidth for this level";
+        type decimal64 {
+          fraction-digits 2;
+        }
+      }
+    }
+    leaf te-default-metric {
+      description
+        "Traffic Engineering Metric";
+      type uint32;
+    }
+    container srlg {
+      description
+        "Shared Risk Link Group Attributes";
+      uses srlg-attributes;
+    }
+  }
+
+  grouping srlg-attributes {
+    description
+      "Shared Risk Link Group Attributes";
+    reference
+      "RFC 5307, RFC 4203: ISIS / OSPF Extensions in Support of
+       Generalized Multi-Protocol Label Switching (GMPLS)";
+    list interface-switching-capabilities {
+      description
+        "List of interface capabilities for this interface";
+      key "switching-capability";
+      leaf switching-capability {
+        description
+          "Switching Capability for this interface";
+        type ted:switching-capabilities;
+      }
+      leaf encoding {
+        description
+          "Encoding supported by this interface";
+        type uint8;
+      }
+      list max-lsp-bandwidth {
+        description
+          "Maximum LSP Bandwidth at priorities 0-7";
+        max-elements "8";
+        key "priority";
+        leaf priority {
+          type uint8 {
+            range "0..7";
+          }
+        }
+        leaf bandwidth {
+          description
+            "Max LSP Bandwidth for this level";
+          type decimal64 {
+            fraction-digits 2;
+          }
+        }
+      }
+      container packet-switch-capable {
+        when "../switching-capability = PSC-1 or ../switching-capability = PSC-2 or ../switching-capability = PSC-3 or ../switching-capability = PSC-4";
+        description
+          "Interface has packet-switching capabilities";
+        leaf minimum-lsp-bandwidth {
+          description
+            "Minimum LSP Bandwidth. Units in bytes per second";
+          type decimal64 {
+            fraction-digits 2;
+          }
+
+        }
+        leaf interface-mtu {
+          description
+            "Interface MTU";
+          type uint16;
+        }
+      }
+      container time-division-multiplex-capable {
+        when "../switching-capability = TDM";
+        description
+          "Interface has time-division multiplex capabilities";
+        leaf minimum-lsp-bandwidth {
+          description
+            "Minimum LSP Bandwidth. Units in bytes per second";
+          type decimal64 {
+            fraction-digits 2;
+          }
+        }
+        leaf indication {
+          description
+            "Indication whether the interface supports Standard or Arbitrary SONET/SDH";
+          type uint16;
+        }
+      }
+    }
+    list srlg-values {
+      description
+        "List of Shared Risk Link Group this interface belongs to.";
+      key "srlg-value";
+      leaf srlg-value {
+        description
+          "Shared Risk Link Group value";
+        type uint32;
+      }
+    }
+    leaf link-protection-type {
+      description
+        "Link Protection Type desired for this link";
+      type uint16;
+    }
+  }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/types.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/types.yang
new file mode 100644 (file)
index 0000000..5b0dfbf
--- /dev/null
@@ -0,0 +1,158 @@
+module custom-types-test {
+
+    yang-version 1;
+    namespace "urn:custom.types.demo";
+    prefix "iit";
+
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+        revision 2012-04-16 {
+    }
+
+    import identity-ref-test {
+            prefix "irt";
+    }
+
+    extension mountpoint {
+        description "enter point";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+    typedef access-operations-type {
+        type bits {
+            bit create {
+                description "Any protocol operation that creates a new data node.";
+            }
+            bit read {
+                description "Any protocol operation or notification that returns the value of a data node.";
+                position 500;
+            }
+            bit update {
+                description "Any protocol operation that alters an existing data node.";
+            }
+            bit delete {
+                description "Any protocol operation that removes a data node.";
+                position 365;
+            }
+            bit exec {
+                description "Execution access to the specified protocol operation.";
+            }
+        }
+        description "NETCONF Access Operation.";
+    }
+
+    leaf inst-id-leaf1 {
+        type instance-identifier {
+            require-instance false;
+        }
+        iit:mountpoint "mnt-extension";
+    }
+
+    leaf inst-id-leaf2 {
+        type instance-identifier;
+    }
+
+    leaf type {
+        type service-type-ref;
+    }
+
+    leaf type2 {
+        type service-type2-ref;
+    }
+
+    leaf type3 {
+        type service-type3-ref;
+    }
+
+    identity crypto-id {
+        base "crypto-base";
+        description "crypto-id description";
+        iit:mountpoint "mnt-extension";
+    }
+
+    identity crypto-base {
+        description "crypto-base description";
+    }
+
+    identity crypto-alg {
+        base "crypto-base";
+        description "crypto-alg description";
+    }
+
+    identity crypto-def {
+        base "crypto-base";
+        description "crypto-def description";
+    }
+
+    leaf mybits {
+        type bits {
+            bit disable-nagle {
+                position 0;
+            }
+            bit auto-sense-speed {
+                position 1;
+            }
+            bit only-10-Mb {
+                position 2;
+            }
+        }
+        default "auto-sense-speed";
+    }
+
+    typedef ip-version {
+        type enumeration {
+            enum unknown {
+                description "An unknown or unspecified version of the Internet protocol.";
+            }
+            enum ipv4 {
+                value "19";
+                description "The IPv4 protocol as defined in RFC 791.";
+            }
+            enum ipv6 {
+                value "7";
+                description "The IPv6 protocol as defined in RFC 2460.";
+            }
+            enum default {
+                description "default ip";
+            }
+        }
+    }
+
+    identity service-type {
+        description
+            "Service identity base type. All service identities must be
+             derived from this type. A service type uniquely defines a single
+             atomic API contract, such as a Java interface, a set of C
+             function declarations, or similar.
+
+             If the service type has a corresponding Java interface, the name
+             of that interface should be attached to the derived identity MUST
+             include a java-class keyword, whose name argument points to that
+             interface.";
+    }
+
+    typedef service-type-ref {
+        description
+            "Internal type of references to service type identity.";
+        type identityref {
+            base service-type;
+        }
+    }
+
+    typedef service-type2-ref {
+        description
+            "Internal type of references to service type2 identity.";
+        type identityref {
+            base irt:my-type;
+        }
+    }
+
+    typedef service-type3-ref {
+        description
+                "Internal type of references to service type3 identity.";
+        type service-type-ref;
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/types2.yang b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/types2.yang
new file mode 100644 (file)
index 0000000..01412ce
--- /dev/null
@@ -0,0 +1,92 @@
+module types2 {
+    namespace "types2";
+    prefix "t2";
+
+    leaf lf-decimal {
+        type decimal64 {
+            fraction-digits 2;
+            range "1 .. 3.14 | 10 | 15..16 | 20..max";
+        }
+    }
+
+    leaf lf-my-string {
+        type string {
+            length "1..255 | 320..max";
+            pattern "[0-9a-fA-F]*";
+            pattern "aa*";
+        }
+    }
+
+    leaf my-bits {
+        type bits {
+            bit disable-nagle {
+                position 0;
+                status current;
+            }
+            bit auto-sense-speed {
+                position 1;
+            }
+            bit only-10-Mb {
+                position 2;
+            }
+        }
+        default "auto-sense-speed";
+    }
+
+    grouping grp1 {
+        leaf myenum {
+            type enumeration {
+                enum zero;
+                enum one;
+                enum seven {
+                    value 7;
+                }
+            }
+            default zero;
+        }
+    }
+
+    container uses-cont {
+        uses t2:grp1 {
+            refine myenum {
+                default one;
+            }
+        }
+    }
+
+    leaf lf-int8 {
+        type int8;
+    }
+
+    leaf lf-int16 {
+        type int16;
+    }
+
+    leaf lf-int32 {
+        type int32;
+    }
+
+    leaf lf-int64 {
+        type int64;
+    }
+
+    leaf lf-uint8 {
+        type uint8;
+    }
+
+    leaf lf-uint16 {
+        type uint16;
+    }
+
+    leaf lf-uint32 {
+        type uint32;
+    }
+
+    leaf lf-uint64 {
+        type uint64;
+    }
+
+    leaf lf-bool {
+        type boolean;
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/extension-use.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/extension-use.yin
new file mode 100644 (file)
index 0000000..f430b81
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module name="extension-use"
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1"
+        xmlns:extension="urn:opendaylight:params:xml:ns:yang:controller:extension">
+    <yang-version value="1"/>
+    <namespace uri="urn:opendaylight:params:xml:ns:yang:controller:extension"/>
+    <prefix value="extension-use"/>
+
+    <extension:provided-service name="extension:cont"/>
+
+    <import module="extension">
+        <prefix value="extension"/>
+        <revision-date date="2013-04-05"/>
+    </import>
+
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/extension.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/extension.yin
new file mode 100644 (file)
index 0000000..b5cc074
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module name="extension"
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1">
+    <yang-version value="1"/>
+    <namespace uri="urn:opendaylight:params:xml:ns:yang:controller:extension"/>
+    <prefix value="extension"/>
+    <revision date="2013-04-05">
+        <description>
+            <text>Reworked to give modules their own space.</text>
+        </description>
+    </revision>
+    <extension name="provided-service">
+        <description>
+            <text>YANG language extension which indicates that a particular
+                module provides certain service. This extension can be placed
+                on identities that are based on module-type. Zero or more services
+                can be provided.
+                This extension takes one argument - name - which MUST be the name
+                of an identity. Furthermore, this identity MUST be based on
+                service-type.
+            </text>
+        </description>
+        <argument name="name"/>
+    </extension>
+
+    <container name="cont"></container>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/feature-test/yang-with-features.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/feature-test/yang-with-features.yin
new file mode 100644 (file)
index 0000000..fc0f09c
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module name="yang-with-features"
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1">
+
+    <yang-version value="1"/>
+    <namespace uri="urn:opendaylight/yang-with-features"/>
+    <prefix value="feats"/>
+
+    <feature name="arbitrary-names">
+        <description>
+            <text>This feature indicates that the device allows user-controlled
+                interfaces to be named arbitrarily.</text>
+        </description>
+    </feature>
+    <feature name="pre-provisioning">
+        <description>
+            <text>This feature indicates that the device supports
+                pre-provisioning of interface configuration, i.e., it is
+                possible to configure an interface whose physical interface
+                hardware is not present on the device.</text>
+        </description>
+    </feature>
+
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/include-belongs-to-test/child.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/include-belongs-to-test/child.yin
new file mode 100644 (file)
index 0000000..a9fd5ba
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<submodule name="child" xmlns="urn:ietf:params:xml:ns:yang:yin:1">
+
+    <belongs-to module="parent">
+        <prefix value="parent"/>
+    </belongs-to>
+
+    <revision date="2010-05-18">
+        <description>
+            <text>Initial module draft.</text>
+        </description>
+        <reference>
+            <text>Random reference text</text>
+        </reference>
+    </revision>
+
+    <list name="testing-list">
+        <key value="testing-string"/>
+        <leaf name="testing-string">
+            <type name="string"/>
+        </leaf>
+        <leaf name="testing-number">
+            <type name="int32"/>
+            <description>
+                <text>Description of the number leaf.</text>
+            </description>
+        </leaf>
+    </list>
+
+</submodule>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/include-belongs-to-test/parent.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/include-belongs-to-test/parent.yin
new file mode 100644 (file)
index 0000000..3a089bb
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module name="parent" xmlns="urn:ietf:params:xml:ns:yang:yin:1">
+
+    <namespace uri="urn:opendaylight/parent"/>
+    <prefix value="parent"/>
+
+    <include module="child">
+        <revision-date date="2010-05-18"/>
+    </include>
+
+    <revision date="2010-05-14">
+        <description>
+            <text>Initial module draft.</text>
+        </description>
+        <reference>
+            <text>Random reference text</text>
+        </reference>
+    </revision>
+
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/incorrect-bar.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/incorrect-bar.yin
new file mode 100644 (file)
index 0000000..3ffc9dd
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<module name="bar" xmlns="urn:ietf:params:xml:ns:yang:yin:1">
+
+    <namespace uri="urn:opendaylight/bar"/>
+    <prefix value="bar"/>
+
+    <list name="testing-list">
+        <key value="testing-string testing-string"/>
+        <leaf name="testing-string">
+            <type name="string"/>
+        </leaf>
+        <leaf name="testing-number">
+            <type name="int32"/>
+            <description>
+                <text>Description of the number leaf.</text>
+            </description>
+        </leaf>
+    </list>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/incorrect-foo.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/incorrect-foo.yin
new file mode 100644 (file)
index 0000000..b207a3c
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<module name="foo" xmlns="urn:ietf:params:xml:ns:yang:yin:1">
+
+    <namespace uri="urn:opendaylight/foo"/>
+    <prefix value="foo"/>
+
+    <import module="baar">
+        <prefix value="bar"/>
+    </import>
+
+    <revision date="2010-04-15"></revision>
+
+    <list name="testing-list">
+        <key value="name"/>
+        <leaf name="name">
+            <type name="string"/>
+        </leaf>
+        <leaf name="number">
+            <type name="uint32"/>
+            <description>
+                <text>Description of the number leaf.</text>
+            </description>
+        </leaf>
+    </list>
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/config.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/config.yin
new file mode 100644 (file)
index 0000000..ed5c142
--- /dev/null
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<module name="config"\r
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1"\r
+        xmlns:config="urn:opendaylight:params:xml:ns:yang:controller:config">\r
+  <yang-version value="1"/>\r
+  <namespace uri="urn:opendaylight:params:xml:ns:yang:controller:config"/>\r
+  <prefix value="config"/>\r
+  <description>\r
+    <text>This module contains the base YANG definitions for NS-OS\r
+configuration subsystem. The system modeled revolves around two\r
+major concepts: modules and services.</text>\r
+  </description>\r
+  <revision date="2013-04-05">\r
+    <description>\r
+      <text>Reworked to give modules their own space.</text>\r
+    </description>\r
+  </revision>\r
+  <revision date="2013-04-03">\r
+    <description>\r
+      <text>Initial revision.</text>\r
+    </description>\r
+  </revision>\r
+  <extension name="java-class">\r
+    <description>\r
+      <text>YANG language extension carrying the fully-qualified name of\r
+a Java class. Code generation tools use the provided reference\r
+to tie a specific construct to its Java representation.</text>\r
+    </description>\r
+    <argument name="name"/>\r
+  </extension>\r
+  <extension name="required-identity">\r
+    <description>\r
+      <text>YANG language extension which indicates that a particular\r
+leafref, which points to a identityref, should additionally\r
+require the target node is actually set to a descendant to\r
+of a particular identity.\r
+\r
+This is a workaround to two YANG deficiencies:\r
+1) not being able to leafref instances of identityref\r
+2) not being able to refine an identityref\r
+\r
+This extension takes one argument, name, which MUST be the name\r
+of an identity. Furthermore, that identity MUST be based,\r
+directly or indirectly, on the identity, which is referenced by\r
+the leaf reference, which is annotated with this extension.</text>\r
+    </description>\r
+    <argument name="name"/>\r
+  </extension>\r
+  <extension name="inner-state-bean">\r
+    <description>\r
+      <text>YANG language extension which indicates that a particular\r
+list located under module's state should be treated as a list\r
+of child state beans instead of just an ordinary list attribute</text>\r
+    </description>\r
+  </extension>\r
+  <extension name="provided-service">\r
+    <description>\r
+      <text>YANG language extension which indicates that a particular\r
+module provides certain service. This extension can be placed\r
+on identities that are based on module-type. Zero or more services\r
+can be provided.\r
+This extension takes one argument - name - which MUST be the name\r
+of an identity. Furthermore, this identity MUST be based on\r
+service-type.</text>\r
+    </description>\r
+    <argument name="name"/>\r
+  </extension>\r
+  <extension name="java-name-prefix">\r
+    <description>\r
+      <text>YANG language extension carrying java simple class name prefix\r
+that will be taken into account when generating java code from\r
+identities that are based on module-type.</text>\r
+    </description>\r
+    <argument name="java-prefix"/>\r
+  </extension>\r
+  <identity name="module-type">\r
+    <description>\r
+      <text>Module identity base type. All module identities must be derived\r
+from this type. A module type uniquely defines a single atomic\r
+component, such as an application. Each such component is assumed\r
+to have its unique, stable and versioned configuration structure.</text>\r
+    </description>\r
+  </identity>\r
+  <identity name="service-type">\r
+    <description>\r
+      <text>Service identity base type. All service identities must be\r
+derived from this type. A service type uniquely defines a single\r
+atomic API contract, such as a Java interface, a set of C\r
+function declarations, or similar.\r
+\r
+If the service type has a corresponding Java interface, the name\r
+of that interface should be attached to the derived identity MUST\r
+include a java-class keyword, whose name argument points to that\r
+interface.</text>\r
+    </description>\r
+  </identity>\r
+  <typedef name="service-type-ref">\r
+    <description>\r
+      <text>Internal type of references to service type identity.</text>\r
+    </description>\r
+    <type name="identityref">\r
+      <base name="service-type"/>\r
+    </type>\r
+  </typedef>\r
+  <grouping name="service-ref">\r
+    <description>\r
+      <text>Type of references to a particular service instance. This type\r
+can be used when defining module configuration to refer to a\r
+particular service instance. Containers using this grouping\r
+should not define anything else. The run-time implementation\r
+is expected to inject a reference to the service as the value\r
+of the container.</text>\r
+    </description>\r
+    <leaf name="type">\r
+      <description>\r
+        <text>Type of the service being referenced. Users of this grouping\r
+should refine this leaf with required-identity pointing to\r
+the actual service-type which is actually required.</text>\r
+      </description>\r
+      <mandatory value="true"/>\r
+      <type name="service-type-ref"/>\r
+    </leaf>\r
+    <leaf name="name">\r
+      <mandatory value="true"/>\r
+      <type name="leafref">\r
+        <path value="/config:services/config:service[config:type=current()/../type]/config:instance/config:name"/>\r
+      </type>\r
+    </leaf>\r
+  </grouping>\r
+  <container name="modules">\r
+    <description>\r
+      <text>Top level container encapsulating configuration of all modules.</text>\r
+    </description>\r
+    <list name="module">\r
+      <key value="name"/>\r
+      <leaf name="name">\r
+        <description>\r
+          <text>Unique module instance name</text>\r
+        </description>\r
+        <type name="string"/>\r
+        <mandatory value="true"/>\r
+      </leaf>\r
+      <leaf name="type">\r
+        <type name="identityref">\r
+          <base name="module-type"/>\r
+        </type>\r
+        <mandatory value="true"/>\r
+      </leaf>\r
+      <choice name="configuration">\r
+        <mandatory value="true"/>\r
+        <config value="true"/>\r
+      </choice>\r
+      <choice name="state">\r
+        <config value="false"/>\r
+      </choice>\r
+    </list>\r
+  </container>\r
+  <container name="services">\r
+    <list name="service">\r
+      <key value="type"/>\r
+      <leaf name="type">\r
+        <type name="service-type-ref"/>\r
+      </leaf>\r
+      <list name="instance">\r
+        <key value="name"/>\r
+        <leaf name="name">\r
+          <type name="string"/>\r
+        </leaf>\r
+        <leaf name="provider">\r
+          <mandatory value="true"/>\r
+          <type name="leafref">\r
+            <path value="/modules/module/name"/>\r
+          </type>\r
+        </leaf>\r
+      </list>\r
+    </list>\r
+  </container>\r
+</module>\r
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/iana-if-type.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/iana-if-type.yin
new file mode 100644 (file)
index 0000000..756961b
--- /dev/null
@@ -0,0 +1,1806 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<module name="iana-if-type"\r
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1"\r
+        xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">\r
+  <namespace uri="urn:ietf:params:xml:ns:yang:iana-if-type"/>\r
+  <prefix value="ianaift"/>\r
+  <organization>\r
+    <text>IANA</text>\r
+  </organization>\r
+  <contact>\r
+    <text>        Internet Assigned Numbers Authority\r
+\r
+Postal: ICANN\r
+        4676 Admiralty Way, Suite 330\r
+        Marina del Rey, CA 90292\r
+\r
+Tel:    +1 310 823 9358\r
+E-Mail: iana&amp;iana.org</text>\r
+  </contact>\r
+  <description>\r
+    <text>This YANG module defines the iana-if-type typedef, which\r
+contains YANG definitions for IANA-registered interface types.\r
+\r
+This YANG module is maintained by IANA, and reflects the\r
+'ifType definitions' registry.\r
+\r
+The latest revision of this YANG module can be obtained from\r
+the IANA web site.\r
+\r
+Copyright (c) 2011 IETF Trust and the persons identified as\r
+authors of the code.  All rights reserved.\r
+\r
+Redistribution and use in source and binary forms, with or\r
+without modification, is permitted pursuant to, and subject\r
+to the license terms contained in, the Simplified BSD License\r
+set forth in Section 4.c of the IETF Trust's Legal Provisions\r
+Relating to IETF Documents\r
+(http://trustee.ietf.org/license-info).\r
+\r
+This version of this YANG module is part of RFC XXXX; see\r
+the RFC itself for full legal notices.</text>\r
+  </description>\r
+  <revision date="2013-07-04">\r
+    <description>\r
+      <text>Initial revision.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC XXXX: IANA Interface Type YANG Module</text>\r
+    </reference>\r
+  </revision>\r
+  <typedef name="iana-if-type">\r
+    <type name="enumeration">\r
+      <enum name="other">\r
+        <value value="1"/>\r
+        <description>\r
+          <text>None of the following</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="regular1822">\r
+        <value value="2"/>\r
+      </enum>\r
+      <enum name="hdh1822">\r
+        <value value="3"/>\r
+      </enum>\r
+      <enum name="ddnX25">\r
+        <value value="4"/>\r
+      </enum>\r
+      <enum name="rfc877x25">\r
+        <value value="5"/>\r
+        <reference>\r
+          <text>RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="ethernetCsmacd">\r
+        <value value="6"/>\r
+        <description>\r
+          <text>For all ethernet-like interfaces, regardless of speed,\r
+as per RFC3635.</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 3635 - Definitions of Managed Objects for the\r
+           Ethernet-like Interface Types.</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="iso88023Csmacd">\r
+        <value value="7"/>\r
+        <status value="deprecated"/>\r
+        <description>\r
+          <text>Deprecated via RFC3635.\r
+Use ethernetCsmacd(6) instead.</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 3635 - Definitions of Managed Objects for the\r
+           Ethernet-like Interface Types.</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="iso88024TokenBus">\r
+        <value value="8"/>\r
+      </enum>\r
+      <enum name="iso88025TokenRing">\r
+        <value value="9"/>\r
+      </enum>\r
+      <enum name="iso88026Man">\r
+        <value value="10"/>\r
+      </enum>\r
+      <enum name="starLan">\r
+        <value value="11"/>\r
+        <status value="deprecated"/>\r
+        <description>\r
+          <text>Deprecated via RFC3635.\r
+Use ethernetCsmacd(6) instead.</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 3635 - Definitions of Managed Objects for the\r
+           Ethernet-like Interface Types.</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="proteon10Mbit">\r
+        <value value="12"/>\r
+      </enum>\r
+      <enum name="proteon80Mbit">\r
+        <value value="13"/>\r
+      </enum>\r
+      <enum name="hyperchannel">\r
+        <value value="14"/>\r
+      </enum>\r
+      <enum name="fddi">\r
+        <value value="15"/>\r
+        <reference>\r
+          <text>RFC 1512 - FDDI Management Information Base</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="lapb">\r
+        <value value="16"/>\r
+        <reference>\r
+          <text>RFC 1381 - SNMP MIB Extension for X.25 LAPB</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="sdlc">\r
+        <value value="17"/>\r
+      </enum>\r
+      <enum name="ds1">\r
+        <value value="18"/>\r
+        <description>\r
+          <text>DS1-MIB</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 4805 - Definitions of Managed Objects for the\r
+           DS1, J1, E1, DS2, and E2 Interface Types</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="e1">\r
+        <value value="19"/>\r
+        <status value="obsolete"/>\r
+        <description>\r
+          <text>Obsolete see DS1-MIB</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 4805 - Definitions of Managed Objects for the\r
+           DS1, J1, E1, DS2, and E2 Interface Types</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="basicISDN">\r
+        <value value="20"/>\r
+        <description>\r
+          <text>see also RFC2127</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="primaryISDN">\r
+        <value value="21"/>\r
+      </enum>\r
+      <enum name="propPointToPointSerial">\r
+        <value value="22"/>\r
+        <description>\r
+          <text>proprietary serial</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ppp">\r
+        <value value="23"/>\r
+      </enum>\r
+      <enum name="softwareLoopback">\r
+        <value value="24"/>\r
+      </enum>\r
+      <enum name="eon">\r
+        <value value="25"/>\r
+        <description>\r
+          <text>CLNP over IP</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ethernet3Mbit">\r
+        <value value="26"/>\r
+      </enum>\r
+      <enum name="nsip">\r
+        <value value="27"/>\r
+        <description>\r
+          <text>XNS over IP</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="slip">\r
+        <value value="28"/>\r
+        <description>\r
+          <text>generic SLIP</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ultra">\r
+        <value value="29"/>\r
+        <description>\r
+          <text>ULTRA technologies</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ds3">\r
+        <value value="30"/>\r
+        <description>\r
+          <text>DS3-MIB</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 3896 - Definitions of Managed Objects for the\r
+           DS3/E3 Interface Type</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="sip">\r
+        <value value="31"/>\r
+        <description>\r
+          <text>SMDS, coffee</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 1694 - Definitions of Managed Objects for SMDS\r
+           Interfaces using SMIv2</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="frameRelay">\r
+        <value value="32"/>\r
+        <description>\r
+          <text>DTE only.</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 2115 - Management Information Base for Frame Relay\r
+           DTEs Using SMIv2</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="rs232">\r
+        <value value="33"/>\r
+        <reference>\r
+          <text>RFC 1659 - Definitions of Managed Objects for RS-232-like\r
+           Hardware Devices using SMIv2</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="para">\r
+        <value value="34"/>\r
+        <description>\r
+          <text>parallel-port</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 1660 - Definitions of Managed Objects for\r
+           Parallel-printer-like Hardware Devices using\r
+           SMIv2</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="arcnet">\r
+        <value value="35"/>\r
+        <description>\r
+          <text>arcnet</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="arcnetPlus">\r
+        <value value="36"/>\r
+        <description>\r
+          <text>arcnet plus</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="atm">\r
+        <value value="37"/>\r
+        <description>\r
+          <text>ATM cells</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="miox25">\r
+        <value value="38"/>\r
+        <reference>\r
+          <text>RFC 1461 - SNMP MIB extension for Multiprotocol\r
+           Interconnect over X.25</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="sonet">\r
+        <value value="39"/>\r
+        <description>\r
+          <text>SONET or SDH</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="x25ple">\r
+        <value value="40"/>\r
+        <reference>\r
+          <text>RFC 2127 - ISDN Management Information Base using SMIv2</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="iso88022llc">\r
+        <value value="41"/>\r
+      </enum>\r
+      <enum name="localTalk">\r
+        <value value="42"/>\r
+      </enum>\r
+      <enum name="smdsDxi">\r
+        <value value="43"/>\r
+      </enum>\r
+      <enum name="frameRelayService">\r
+        <value value="44"/>\r
+        <description>\r
+          <text>FRNETSERV-MIB</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 2954 - Definitions of Managed Objects for Frame\r
+           Relay Service</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="v35">\r
+        <value value="45"/>\r
+      </enum>\r
+      <enum name="hssi">\r
+        <value value="46"/>\r
+      </enum>\r
+      <enum name="hippi">\r
+        <value value="47"/>\r
+      </enum>\r
+      <enum name="modem">\r
+        <value value="48"/>\r
+        <description>\r
+          <text>Generic modem</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="aal5">\r
+        <value value="49"/>\r
+        <description>\r
+          <text>AAL5 over ATM</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="sonetPath">\r
+        <value value="50"/>\r
+      </enum>\r
+      <enum name="sonetVT">\r
+        <value value="51"/>\r
+      </enum>\r
+      <enum name="smdsIcip">\r
+        <value value="52"/>\r
+        <description>\r
+          <text>SMDS InterCarrier Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="propVirtual">\r
+        <value value="53"/>\r
+        <description>\r
+          <text>proprietary virtual/internal</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 2863 - The Interfaces Group MIB</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="propMultiplexor">\r
+        <value value="54"/>\r
+        <description>\r
+          <text>proprietary multiplexing</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 2863 - The Interfaces Group MIB</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="ieee80212">\r
+        <value value="55"/>\r
+        <description>\r
+          <text>100BaseVG</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="fibreChannel">\r
+        <value value="56"/>\r
+        <description>\r
+          <text>Fibre Channel</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="hippiInterface">\r
+        <value value="57"/>\r
+        <description>\r
+          <text>HIPPI interfaces</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="frameRelayInterconnect">\r
+        <value value="58"/>\r
+        <status value="obsolete"/>\r
+        <description>\r
+          <text>Obsolete use either\r
+frameRelay(32) or frameRelayService(44).</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="aflane8023">\r
+        <value value="59"/>\r
+        <description>\r
+          <text>ATM Emulated LAN for 802.3</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="aflane8025">\r
+        <value value="60"/>\r
+        <description>\r
+          <text>ATM Emulated LAN for 802.5</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="cctEmul">\r
+        <value value="61"/>\r
+        <description>\r
+          <text>ATM Emulated circuit</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="fastEther">\r
+        <value value="62"/>\r
+        <status value="deprecated"/>\r
+        <description>\r
+          <text>Obsoleted via RFC3635.\r
+ethernetCsmacd(6) should be used instead</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 3635 - Definitions of Managed Objects for the\r
+           Ethernet-like Interface Types.</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="isdn">\r
+        <value value="63"/>\r
+        <description>\r
+          <text>ISDN and X.25</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN\r
+           in the Packet Mode</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="v11">\r
+        <value value="64"/>\r
+        <description>\r
+          <text>CCITT V.11/X.21</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="v36">\r
+        <value value="65"/>\r
+        <description>\r
+          <text>CCITT V.36</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="g703at64k">\r
+        <value value="66"/>\r
+        <description>\r
+          <text>CCITT G703 at 64Kbps</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="g703at2mb">\r
+        <value value="67"/>\r
+        <status value="obsolete"/>\r
+        <description>\r
+          <text>Obsolete see DS1-MIB</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="qllc">\r
+        <value value="68"/>\r
+        <description>\r
+          <text>SNA QLLC</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="fastEtherFX">\r
+        <value value="69"/>\r
+        <status value="deprecated"/>\r
+        <description>\r
+          <text>Obsoleted via RFC3635\r
+ethernetCsmacd(6) should be used instead</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 3635 - Definitions of Managed Objects for the\r
+           Ethernet-like Interface Types.</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="channel">\r
+        <value value="70"/>\r
+        <description>\r
+          <text>channel</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ieee80211">\r
+        <value value="71"/>\r
+        <description>\r
+          <text>radio spread spectrum</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ibm370parChan">\r
+        <value value="72"/>\r
+        <description>\r
+          <text>IBM System 360/370 OEMI Channel</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="escon">\r
+        <value value="73"/>\r
+        <description>\r
+          <text>IBM Enterprise Systems Connection</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="dlsw">\r
+        <value value="74"/>\r
+        <description>\r
+          <text>Data Link Switching</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="isdns">\r
+        <value value="75"/>\r
+        <description>\r
+          <text>ISDN S/T interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="isdnu">\r
+        <value value="76"/>\r
+        <description>\r
+          <text>ISDN U interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="lapd">\r
+        <value value="77"/>\r
+        <description>\r
+          <text>Link Access Protocol D</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ipSwitch">\r
+        <value value="78"/>\r
+        <description>\r
+          <text>IP Switching Objects</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="rsrb">\r
+        <value value="79"/>\r
+        <description>\r
+          <text>Remote Source Route Bridging</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="atmLogical">\r
+        <value value="80"/>\r
+        <description>\r
+          <text>ATM Logical Port</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 3606 - Definitions of Supplemental Managed Objects\r
+           for ATM Interface</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="ds0">\r
+        <value value="81"/>\r
+        <description>\r
+          <text>Digital Signal Level 0</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 2494 - Definitions of Managed Objects for the DS0\r
+           and DS0 Bundle Interface Type</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="ds0Bundle">\r
+        <value value="82"/>\r
+        <description>\r
+          <text>group of ds0s on the same ds1</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 2494 - Definitions of Managed Objects for the DS0\r
+           and DS0 Bundle Interface Type</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="bsc">\r
+        <value value="83"/>\r
+        <description>\r
+          <text>Bisynchronous Protocol</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="async">\r
+        <value value="84"/>\r
+        <description>\r
+          <text>Asynchronous Protocol</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="cnr">\r
+        <value value="85"/>\r
+        <description>\r
+          <text>Combat Net Radio</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="iso88025Dtr">\r
+        <value value="86"/>\r
+        <description>\r
+          <text>ISO 802.5r DTR</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="eplrs">\r
+        <value value="87"/>\r
+        <description>\r
+          <text>Ext Pos Loc Report Sys</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="arap">\r
+        <value value="88"/>\r
+        <description>\r
+          <text>Appletalk Remote Access Protocol</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="propCnls">\r
+        <value value="89"/>\r
+        <description>\r
+          <text>Proprietary Connectionless Protocol</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="hostPad">\r
+        <value value="90"/>\r
+        <description>\r
+          <text>CCITT-ITU X.29 PAD Protocol</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="termPad">\r
+        <value value="91"/>\r
+        <description>\r
+          <text>CCITT-ITU X.3 PAD Facility</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="frameRelayMPI">\r
+        <value value="92"/>\r
+        <description>\r
+          <text>Multiproto Interconnect over FR</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="x213">\r
+        <value value="93"/>\r
+        <description>\r
+          <text>CCITT-ITU X213</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="adsl">\r
+        <value value="94"/>\r
+        <description>\r
+          <text>Asymmetric Digital Subscriber Loop</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="radsl">\r
+        <value value="95"/>\r
+        <description>\r
+          <text>Rate-Adapt. Digital Subscriber Loop</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="sdsl">\r
+        <value value="96"/>\r
+        <description>\r
+          <text>Symmetric Digital Subscriber Loop</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="vdsl">\r
+        <value value="97"/>\r
+        <description>\r
+          <text>Very H-Speed Digital Subscrib. Loop</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="iso88025CRFPInt">\r
+        <value value="98"/>\r
+        <description>\r
+          <text>ISO 802.5 CRFP</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="myrinet">\r
+        <value value="99"/>\r
+        <description>\r
+          <text>Myricom Myrinet</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceEM">\r
+        <value value="100"/>\r
+        <description>\r
+          <text>voice recEive and transMit</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceFXO">\r
+        <value value="101"/>\r
+        <description>\r
+          <text>voice Foreign Exchange Office</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceFXS">\r
+        <value value="102"/>\r
+        <description>\r
+          <text>voice Foreign Exchange Station</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceEncap">\r
+        <value value="103"/>\r
+        <description>\r
+          <text>voice encapsulation</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceOverIp">\r
+        <value value="104"/>\r
+        <description>\r
+          <text>voice over IP encapsulation</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="atmDxi">\r
+        <value value="105"/>\r
+        <description>\r
+          <text>ATM DXI</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="atmFuni">\r
+        <value value="106"/>\r
+        <description>\r
+          <text>ATM FUNI</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="atmIma">\r
+        <value value="107"/>\r
+        <description>\r
+          <text>ATM IMA</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="pppMultilinkBundle">\r
+        <value value="108"/>\r
+        <description>\r
+          <text>PPP Multilink Bundle</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ipOverCdlc">\r
+        <value value="109"/>\r
+        <description>\r
+          <text>IBM ipOverCdlc</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ipOverClaw">\r
+        <value value="110"/>\r
+        <description>\r
+          <text>IBM Common Link Access to Workstn</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="stackToStack">\r
+        <value value="111"/>\r
+        <description>\r
+          <text>IBM stackToStack</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="virtualIpAddress">\r
+        <value value="112"/>\r
+        <description>\r
+          <text>IBM VIPA</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="mpc">\r
+        <value value="113"/>\r
+        <description>\r
+          <text>IBM multi-protocol channel support</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ipOverAtm">\r
+        <value value="114"/>\r
+        <description>\r
+          <text>IBM ipOverAtm</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 2320 - Definitions of Managed Objects for Classical IP\r
+           and ARP Over ATM Using SMIv2 (IPOA-MIB)</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="iso88025Fiber">\r
+        <value value="115"/>\r
+        <description>\r
+          <text>ISO 802.5j Fiber Token Ring</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="tdlc">\r
+        <value value="116"/>\r
+        <description>\r
+          <text>IBM twinaxial data link control</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="gigabitEthernet">\r
+        <value value="117"/>\r
+        <status value="deprecated"/>\r
+        <description>\r
+          <text>Obsoleted via RFC3635\r
+ethernetCsmacd(6) should be used instead</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 3635 - Definitions of Managed Objects for the\r
+           Ethernet-like Interface Types.</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="hdlc">\r
+        <value value="118"/>\r
+        <description>\r
+          <text>HDLC</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="lapf">\r
+        <value value="119"/>\r
+        <description>\r
+          <text>LAP F</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="v37">\r
+        <value value="120"/>\r
+        <description>\r
+          <text>V.37</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="x25mlp">\r
+        <value value="121"/>\r
+        <description>\r
+          <text>Multi-Link Protocol</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="x25huntGroup">\r
+        <value value="122"/>\r
+        <description>\r
+          <text>X25 Hunt Group</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="transpHdlc">\r
+        <value value="123"/>\r
+        <description>\r
+          <text>Transp HDLC</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="interleave">\r
+        <value value="124"/>\r
+        <description>\r
+          <text>Interleave channel</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="fast">\r
+        <value value="125"/>\r
+        <description>\r
+          <text>Fast channel</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ip">\r
+        <value value="126"/>\r
+        <description>\r
+          <text>IP (for APPN HPR in IP networks)</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="docsCableMaclayer">\r
+        <value value="127"/>\r
+        <description>\r
+          <text>CATV Mac Layer</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="docsCableDownstream">\r
+        <value value="128"/>\r
+        <description>\r
+          <text>CATV Downstream interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="docsCableUpstream">\r
+        <value value="129"/>\r
+        <description>\r
+          <text>CATV Upstream interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="a12MppSwitch">\r
+        <value value="130"/>\r
+        <description>\r
+          <text>Avalon Parallel Processor</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="tunnel">\r
+        <value value="131"/>\r
+        <description>\r
+          <text>Encapsulation interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="coffee">\r
+        <value value="132"/>\r
+        <description>\r
+          <text>coffee pot</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 2325 - Coffee MIB</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="ces">\r
+        <value value="133"/>\r
+        <description>\r
+          <text>Circuit Emulation Service</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="atmSubInterface">\r
+        <value value="134"/>\r
+        <description>\r
+          <text>ATM Sub Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="l2vlan">\r
+        <value value="135"/>\r
+        <description>\r
+          <text>Layer 2 Virtual LAN using 802.1Q</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="l3ipvlan">\r
+        <value value="136"/>\r
+        <description>\r
+          <text>Layer 3 Virtual LAN using IP</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="l3ipxvlan">\r
+        <value value="137"/>\r
+        <description>\r
+          <text>Layer 3 Virtual LAN using IPX</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="digitalPowerline">\r
+        <value value="138"/>\r
+        <description>\r
+          <text>IP over Power Lines</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="mediaMailOverIp">\r
+        <value value="139"/>\r
+        <description>\r
+          <text>Multimedia Mail over IP</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="dtm">\r
+        <value value="140"/>\r
+        <description>\r
+          <text>Dynamic syncronous Transfer Mode</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="dcn">\r
+        <value value="141"/>\r
+        <description>\r
+          <text>Data Communications Network</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ipForward">\r
+        <value value="142"/>\r
+        <description>\r
+          <text>IP Forwarding Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="msdsl">\r
+        <value value="143"/>\r
+        <description>\r
+          <text>Multi-rate Symmetric DSL</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ieee1394">\r
+        <value value="144"/>\r
+        <description>\r
+          <text>IEEE1394 High Performance Serial Bus</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="if-gsn">\r
+        <value value="145"/>\r
+        <description>\r
+          <text>HIPPI-6400</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="dvbRccMacLayer">\r
+        <value value="146"/>\r
+        <description>\r
+          <text>DVB-RCC MAC Layer</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="dvbRccDownstream">\r
+        <value value="147"/>\r
+        <description>\r
+          <text>DVB-RCC Downstream Channel</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="dvbRccUpstream">\r
+        <value value="148"/>\r
+        <description>\r
+          <text>DVB-RCC Upstream Channel</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="atmVirtual">\r
+        <value value="149"/>\r
+        <description>\r
+          <text>ATM Virtual Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="mplsTunnel">\r
+        <value value="150"/>\r
+        <description>\r
+          <text>MPLS Tunnel Virtual Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="srp">\r
+        <value value="151"/>\r
+        <description>\r
+          <text>Spatial Reuse Protocol</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceOverAtm">\r
+        <value value="152"/>\r
+        <description>\r
+          <text>Voice Over ATM</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceOverFrameRelay">\r
+        <value value="153"/>\r
+        <description>\r
+          <text>Voice Over Frame Relay</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="idsl">\r
+        <value value="154"/>\r
+        <description>\r
+          <text>Digital Subscriber Loop over ISDN</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="compositeLink">\r
+        <value value="155"/>\r
+        <description>\r
+          <text>Avici Composite Link Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ss7SigLink">\r
+        <value value="156"/>\r
+        <description>\r
+          <text>SS7 Signaling Link</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="propWirelessP2P">\r
+        <value value="157"/>\r
+        <description>\r
+          <text>Prop. P2P wireless interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="frForward">\r
+        <value value="158"/>\r
+        <description>\r
+          <text>Frame Forward Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="rfc1483">\r
+        <value value="159"/>\r
+        <description>\r
+          <text>Multiprotocol over ATM AAL5</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 1483 - Multiprotocol Encapsulation over ATM\r
+           Adaptation Layer 5</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="usb">\r
+        <value value="160"/>\r
+        <description>\r
+          <text>USB Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ieee8023adLag">\r
+        <value value="161"/>\r
+        <description>\r
+          <text>IEEE 802.3ad Link Aggregate</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="bgppolicyaccounting">\r
+        <value value="162"/>\r
+        <description>\r
+          <text>BGP Policy Accounting</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="frf16MfrBundle">\r
+        <value value="163"/>\r
+        <description>\r
+          <text>FRF .16 Multilink Frame Relay</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="h323Gatekeeper">\r
+        <value value="164"/>\r
+        <description>\r
+          <text>H323 Gatekeeper</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="h323Proxy">\r
+        <value value="165"/>\r
+        <description>\r
+          <text>H323 Voice and Video Proxy</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="mpls">\r
+        <value value="166"/>\r
+        <description>\r
+          <text>MPLS</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="mfSigLink">\r
+        <value value="167"/>\r
+        <description>\r
+          <text>Multi-frequency signaling link</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="hdsl2">\r
+        <value value="168"/>\r
+        <description>\r
+          <text>High Bit-Rate DSL - 2nd generation</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="shdsl">\r
+        <value value="169"/>\r
+        <description>\r
+          <text>Multirate HDSL2</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ds1FDL">\r
+        <value value="170"/>\r
+        <description>\r
+          <text>Facility Data Link 4Kbps on a DS1</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="pos">\r
+        <value value="171"/>\r
+        <description>\r
+          <text>Packet over SONET/SDH Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="dvbAsiIn">\r
+        <value value="172"/>\r
+        <description>\r
+          <text>DVB-ASI Input</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="dvbAsiOut">\r
+        <value value="173"/>\r
+        <description>\r
+          <text>DVB-ASI Output</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="plc">\r
+        <value value="174"/>\r
+        <description>\r
+          <text>Power Line Communtications</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="nfas">\r
+        <value value="175"/>\r
+        <description>\r
+          <text>Non Facility Associated Signaling</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="tr008">\r
+        <value value="176"/>\r
+        <description>\r
+          <text>TR008</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="gr303RDT">\r
+        <value value="177"/>\r
+        <description>\r
+          <text>Remote Digital Terminal</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="gr303IDT">\r
+        <value value="178"/>\r
+        <description>\r
+          <text>Integrated Digital Terminal</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="isup">\r
+        <value value="179"/>\r
+        <description>\r
+          <text>ISUP</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="propDocsWirelessMaclayer">\r
+        <value value="180"/>\r
+        <description>\r
+          <text>Cisco proprietary Maclayer</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="propDocsWirelessDownstream">\r
+        <value value="181"/>\r
+        <description>\r
+          <text>Cisco proprietary Downstream</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="propDocsWirelessUpstream">\r
+        <value value="182"/>\r
+        <description>\r
+          <text>Cisco proprietary Upstream</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="hiperlan2">\r
+        <value value="183"/>\r
+        <description>\r
+          <text>HIPERLAN Type 2 Radio Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="propBWAp2Mp">\r
+        <value value="184"/>\r
+        <description>\r
+          <text>PropBroadbandWirelessAccesspt2multipt use of this value\r
+for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f\r
+is deprecated and ieee80216WMAN(237) should be used\r
+instead.</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="sonetOverheadChannel">\r
+        <value value="185"/>\r
+        <description>\r
+          <text>SONET Overhead Channel</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="digitalWrapperOverheadChannel">\r
+        <value value="186"/>\r
+        <description>\r
+          <text>Digital Wrapper</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="aal2">\r
+        <value value="187"/>\r
+        <description>\r
+          <text>ATM adaptation layer 2</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="radioMAC">\r
+        <value value="188"/>\r
+        <description>\r
+          <text>MAC layer over radio links</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="atmRadio">\r
+        <value value="189"/>\r
+        <description>\r
+          <text>ATM over radio links</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="imt">\r
+        <value value="190"/>\r
+        <description>\r
+          <text>Inter Machine Trunks</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="mvl">\r
+        <value value="191"/>\r
+        <description>\r
+          <text>Multiple Virtual Lines DSL</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="reachDSL">\r
+        <value value="192"/>\r
+        <description>\r
+          <text>Long Reach DSL</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="frDlciEndPt">\r
+        <value value="193"/>\r
+        <description>\r
+          <text>Frame Relay DLCI End Point</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="atmVciEndPt">\r
+        <value value="194"/>\r
+        <description>\r
+          <text>ATM VCI End Point</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="opticalChannel">\r
+        <value value="195"/>\r
+        <description>\r
+          <text>Optical Channel</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="opticalTransport">\r
+        <value value="196"/>\r
+        <description>\r
+          <text>Optical Transport</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="propAtm">\r
+        <value value="197"/>\r
+        <description>\r
+          <text>Proprietary ATM</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceOverCable">\r
+        <value value="198"/>\r
+        <description>\r
+          <text>Voice Over Cable Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="infiniband">\r
+        <value value="199"/>\r
+        <description>\r
+          <text>Infiniband</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="teLink">\r
+        <value value="200"/>\r
+        <description>\r
+          <text>TE Link</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="q2931">\r
+        <value value="201"/>\r
+        <description>\r
+          <text>Q.2931</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="virtualTg">\r
+        <value value="202"/>\r
+        <description>\r
+          <text>Virtual Trunk Group</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="sipTg">\r
+        <value value="203"/>\r
+        <description>\r
+          <text>SIP Trunk Group</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="sipSig">\r
+        <value value="204"/>\r
+        <description>\r
+          <text>SIP Signaling</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="docsCableUpstreamChannel">\r
+        <value value="205"/>\r
+        <description>\r
+          <text>CATV Upstream Channel</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="econet">\r
+        <value value="206"/>\r
+        <description>\r
+          <text>Acorn Econet</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="pon155">\r
+        <value value="207"/>\r
+        <description>\r
+          <text>FSAN 155Mb Symetrical PON interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="pon622">\r
+        <value value="208"/>\r
+        <description>\r
+          <text>FSAN622Mb Symetrical PON interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="bridge">\r
+        <value value="209"/>\r
+        <description>\r
+          <text>Transparent bridge interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="linegroup">\r
+        <value value="210"/>\r
+        <description>\r
+          <text>Interface common to multiple lines</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceEMFGD">\r
+        <value value="211"/>\r
+        <description>\r
+          <text>voice E&amp;M Feature Group D</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceFGDEANA">\r
+        <value value="212"/>\r
+        <description>\r
+          <text>voice FGD Exchange Access North American</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceDID">\r
+        <value value="213"/>\r
+        <description>\r
+          <text>voice Direct Inward Dialing</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="mpegTransport">\r
+        <value value="214"/>\r
+        <description>\r
+          <text>MPEG transport interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="sixToFour">\r
+        <value value="215"/>\r
+        <status value="deprecated"/>\r
+        <description>\r
+          <text>6to4 interface (DEPRECATED)</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 4087 - IP Tunnel MIB</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="gtp">\r
+        <value value="216"/>\r
+        <description>\r
+          <text>GTP (GPRS Tunneling Protocol)</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="pdnEtherLoop1">\r
+        <value value="217"/>\r
+        <description>\r
+          <text>Paradyne EtherLoop 1</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="pdnEtherLoop2">\r
+        <value value="218"/>\r
+        <description>\r
+          <text>Paradyne EtherLoop 2</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="opticalChannelGroup">\r
+        <value value="219"/>\r
+        <description>\r
+          <text>Optical Channel Group</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="homepna">\r
+        <value value="220"/>\r
+        <description>\r
+          <text>HomePNA ITU-T G.989</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="gfp">\r
+        <value value="221"/>\r
+        <description>\r
+          <text>Generic Framing Procedure (GFP)</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ciscoISLvlan">\r
+        <value value="222"/>\r
+        <description>\r
+          <text>Layer 2 Virtual LAN using Cisco ISL</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="actelisMetaLOOP">\r
+        <value value="223"/>\r
+        <description>\r
+          <text>Acteleis proprietary MetaLOOP High Speed Link</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="fcipLink">\r
+        <value value="224"/>\r
+        <description>\r
+          <text>FCIP Link</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="rpr">\r
+        <value value="225"/>\r
+        <description>\r
+          <text>Resilient Packet Ring Interface Type</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="qam">\r
+        <value value="226"/>\r
+        <description>\r
+          <text>RF Qam Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="lmp">\r
+        <value value="227"/>\r
+        <description>\r
+          <text>Link Management Protocol</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 4327 - Link Management Protocol (LMP) Management\r
+           Information Base (MIB)</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="cblVectaStar">\r
+        <value value="228"/>\r
+        <description>\r
+          <text>Cambridge Broadband Networks Limited VectaStar</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="docsCableMCmtsDownstream">\r
+        <value value="229"/>\r
+        <description>\r
+          <text>CATV Modular CMTS Downstream Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="adsl2">\r
+        <value value="230"/>\r
+        <status value="deprecated"/>\r
+        <description>\r
+          <text>Asymmetric Digital Subscriber Loop Version 2\r
+(DEPRECATED/OBSOLETED - please use adsl2plus(238)\r
+instead)</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 4706 - Definitions of Managed Objects for Asymmetric\r
+           Digital Subscriber Line 2 (ADSL2)</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="macSecControlledIF">\r
+        <value value="231"/>\r
+        <description>\r
+          <text>MACSecControlled</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="macSecUncontrolledIF">\r
+        <value value="232"/>\r
+        <description>\r
+          <text>MACSecUncontrolled</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="aviciOpticalEther">\r
+        <value value="233"/>\r
+        <description>\r
+          <text>Avici Optical Ethernet Aggregate</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="atmbond">\r
+        <value value="234"/>\r
+        <description>\r
+          <text>atmbond</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceFGDOS">\r
+        <value value="235"/>\r
+        <description>\r
+          <text>voice FGD Operator Services</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="mocaVersion1">\r
+        <value value="236"/>\r
+        <description>\r
+          <text>MultiMedia over Coax Alliance (MoCA) Interface\r
+as documented in information provided privately to IANA</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ieee80216WMAN">\r
+        <value value="237"/>\r
+        <description>\r
+          <text>IEEE 802.16 WMAN interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="adsl2plus">\r
+        <value value="238"/>\r
+        <description>\r
+          <text>Asymmetric Digital Subscriber Loop Version 2,\r
+Version 2 Plus and all variants</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="dvbRcsMacLayer">\r
+        <value value="239"/>\r
+        <description>\r
+          <text>DVB-RCS MAC Layer</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 5728 - The SatLabs Group DVB-RCS MIB</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="dvbTdm">\r
+        <value value="240"/>\r
+        <description>\r
+          <text>DVB Satellite TDM</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 5728 - The SatLabs Group DVB-RCS MIB</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="dvbRcsTdma">\r
+        <value value="241"/>\r
+        <description>\r
+          <text>DVB-RCS TDMA</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 5728 - The SatLabs Group DVB-RCS MIB</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="x86Laps">\r
+        <value value="242"/>\r
+        <description>\r
+          <text>LAPS based on ITU-T X.86/Y.1323</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="wwanPP">\r
+        <value value="243"/>\r
+        <description>\r
+          <text>3GPP WWAN</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="wwanPP2">\r
+        <value value="244"/>\r
+        <description>\r
+          <text>3GPP2 WWAN</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="voiceEBS">\r
+        <value value="245"/>\r
+        <description>\r
+          <text>voice P-phone EBS physical interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ifPwType">\r
+        <value value="246"/>\r
+        <description>\r
+          <text>Pseudowire interface type</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 5601 - Pseudowire (PW) Management Information Base</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="ilan">\r
+        <value value="247"/>\r
+        <description>\r
+          <text>Internal LAN on a bridge per IEEE 802.1ap</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="pip">\r
+        <value value="248"/>\r
+        <description>\r
+          <text>Provider Instance Port on a bridge per IEEE 802.1ah PBB</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="aluELP">\r
+        <value value="249"/>\r
+        <description>\r
+          <text>Alcatel-Lucent Ethernet Link Protection</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="gpon">\r
+        <value value="250"/>\r
+        <description>\r
+          <text>Gigabit-capable passive optical networks (G-PON) as per\r
+ITU-T G.948</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="vdsl2">\r
+        <value value="251"/>\r
+        <description>\r
+          <text>Very high speed digital subscriber line Version 2\r
+(as per ITU-T Recommendation G.993.2)</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 5650 - Definitions of Managed Objects for Very High\r
+           Speed Digital Subscriber Line 2 (VDSL2)</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="capwapDot11Profile">\r
+        <value value="252"/>\r
+        <description>\r
+          <text>WLAN Profile Interface</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 5834 - Control and Provisioning of Wireless Access\r
+           Points (CAPWAP) Protocol Binding MIB for\r
+           IEEE 802.11</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="capwapDot11Bss">\r
+        <value value="253"/>\r
+        <description>\r
+          <text>WLAN BSS Interface</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 5834 - Control and Provisioning of Wireless Access\r
+           Points (CAPWAP) Protocol Binding MIB for\r
+           IEEE 802.11</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="capwapWtpVirtualRadio">\r
+        <value value="254"/>\r
+        <description>\r
+          <text>WTP Virtual Radio Interface</text>\r
+        </description>\r
+        <reference>\r
+          <text>RFC 5833 - Control and Provisioning of Wireless Access\r
+           Points (CAPWAP) Protocol Base MIB</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="bits">\r
+        <value value="255"/>\r
+        <description>\r
+          <text>bitsport</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="docsCableUpstreamRfPort">\r
+        <value value="256"/>\r
+        <description>\r
+          <text>DOCSIS CATV Upstream RF Port</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="cableDownstreamRfPort">\r
+        <value value="257"/>\r
+        <description>\r
+          <text>CATV downstream RF port</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="vmwareVirtualNic">\r
+        <value value="258"/>\r
+        <description>\r
+          <text>VMware Virtual Network Interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ieee802154">\r
+        <value value="259"/>\r
+        <description>\r
+          <text>IEEE 802.15.4 WPAN interface</text>\r
+        </description>\r
+        <reference>\r
+          <text>IEEE 802.15.4-2006</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="otnOdu">\r
+        <value value="260"/>\r
+        <description>\r
+          <text>OTN Optical Data Unit</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="otnOtu">\r
+        <value value="261"/>\r
+        <description>\r
+          <text>OTN Optical channel Transport Unit</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ifVfiType">\r
+        <value value="262"/>\r
+        <description>\r
+          <text>VPLS Forwarding Instance Interface Type</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="g9981">\r
+        <value value="263"/>\r
+        <description>\r
+          <text>G.998.1 bonded interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="g9982">\r
+        <value value="264"/>\r
+        <description>\r
+          <text>G.998.2 bonded interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="g9983">\r
+        <value value="265"/>\r
+        <description>\r
+          <text>G.998.3 bonded interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="aluEpon">\r
+        <value value="266"/>\r
+        <description>\r
+          <text>Ethernet Passive Optical Networks (E-PON)</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="aluEponOnu">\r
+        <value value="267"/>\r
+        <description>\r
+          <text>EPON Optical Network Unit</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="aluEponPhysicalUni">\r
+        <value value="268"/>\r
+        <description>\r
+          <text>EPON physical User to Network interface</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="aluEponLogicalLink">\r
+        <value value="269"/>\r
+        <description>\r
+          <text>The emulation of a point-to-point link over the EPON\r
+layer</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="aluGponOnu">\r
+        <value value="270"/>\r
+        <description>\r
+          <text>GPON Optical Network Unit</text>\r
+        </description>\r
+        <reference>\r
+          <text>ITU-T G.984.2</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="aluGponPhysicalUni">\r
+        <value value="271"/>\r
+        <description>\r
+          <text>GPON physical User to Network interface</text>\r
+        </description>\r
+        <reference>\r
+          <text>ITU-T G.984.2</text>\r
+        </reference>\r
+      </enum>\r
+      <enum name="vmwareNicTeam">\r
+        <value value="272"/>\r
+        <description>\r
+          <text>VMware NIC Team</text>\r
+        </description>\r
+      </enum>\r
+    </type>\r
+    <description>\r
+      <text>This data type is used as the syntax of the 'type'\r
+leaf in the 'interface' list in the YANG module\r
+ietf-interface.\r
+\r
+The definition of this typedef with the\r
+addition of newly assigned values is published\r
+periodically by the IANA, in either the Assigned\r
+Numbers RFC, or some derivative of it specific to\r
+Internet Network Management number assignments.  (The\r
+latest arrangements can be obtained by contacting the\r
+IANA.)\r
+\r
+Requests for new values should be made to IANA via\r
+email (iana&amp;iana.org).</text>\r
+    </description>\r
+    <reference>\r
+      <text>IANA ifType definitions registry.\r
+&lt;http://www.iana.org/assignments/smi-numbers&gt;</text>\r
+    </reference>\r
+  </typedef>\r
+</module>\r
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-inet-types.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-inet-types.yin
new file mode 100644 (file)
index 0000000..39f6b14
--- /dev/null
@@ -0,0 +1,412 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<module name="ietf-inet-types"\r
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1"\r
+        xmlns:inet="urn:ietf:params:xml:ns:yang:ietf-inet-types">\r
+  <namespace uri="urn:ietf:params:xml:ns:yang:ietf-inet-types"/>\r
+  <prefix value="inet"/>\r
+  <organization>\r
+    <text>IETF NETMOD (NETCONF Data Modeling Language) Working Group</text>\r
+  </organization>\r
+  <contact>\r
+    <text>WG Web:   &lt;http://tools.ietf.org/wg/netmod/&gt;\r
+WG List:  &lt;mailto:netmod@ietf.org&gt;\r
+\r
+WG Chair: David Partain\r
+          &lt;mailto:david.partain@ericsson.com&gt;\r
+\r
+WG Chair: David Kessens\r
+          &lt;mailto:david.kessens@nsn.com&gt;\r
+\r
+Editor:   Juergen Schoenwaelder\r
+          &lt;mailto:j.schoenwaelder@jacobs-university.de&gt;</text>\r
+  </contact>\r
+  <description>\r
+    <text>This module contains a collection of generally useful derived\r
+YANG data types for Internet addresses and related things.\r
+\r
+Copyright (c) 2010 IETF Trust and the persons identified as\r
+authors of the code.  All rights reserved.\r
+\r
+\r
+\r
+Redistribution and use in source and binary forms, with or without\r
+modification, is permitted pursuant to, and subject to the license\r
+terms contained in, the Simplified BSD License set forth in Section\r
+4.c of the IETF Trust's Legal Provisions Relating to IETF Documents\r
+(http://trustee.ietf.org/license-info).\r
+\r
+This version of this YANG module is part of RFC 6021; see\r
+the RFC itself for full legal notices.</text>\r
+  </description>\r
+  <revision date="2010-09-24">\r
+    <description>\r
+      <text>Initial revision.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 6021: Common YANG Data Types</text>\r
+    </reference>\r
+  </revision>\r
+  <typedef name="ip-version">\r
+    <type name="enumeration">\r
+      <enum name="unknown">\r
+        <value value="0"/>\r
+        <description>\r
+          <text>An unknown or unspecified version of the Internet protocol.</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ipv4">\r
+        <value value="1"/>\r
+        <description>\r
+          <text>The IPv4 protocol as defined in RFC 791.</text>\r
+        </description>\r
+      </enum>\r
+      <enum name="ipv6">\r
+        <value value="2"/>\r
+        <description>\r
+          <text>The IPv6 protocol as defined in RFC 2460.</text>\r
+        </description>\r
+      </enum>\r
+    </type>\r
+    <description>\r
+      <text>This value represents the version of the IP protocol.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the InetVersion textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC  791: Internet Protocol\r
+RFC 2460: Internet Protocol, Version 6 (IPv6) Specification\r
+RFC 4001: Textual Conventions for Internet Network Addresses</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="dscp">\r
+    <type name="uint8">\r
+      <range value="0..63"/>\r
+    </type>\r
+    <description>\r
+      <text>The dscp type represents a Differentiated Services Code-Point\r
+that may be used for marking packets in a traffic stream.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the Dscp textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 3289: Management Information Base for the Differentiated\r
+          Services Architecture\r
+RFC 2474: Definition of the Differentiated Services Field\r
+          (DS Field) in the IPv4 and IPv6 Headers\r
+RFC 2780: IANA Allocation Guidelines For Values In\r
+          the Internet Protocol and Related Headers</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="ipv6-flow-label">\r
+    <type name="uint32">\r
+      <range value="0..1048575"/>\r
+    </type>\r
+    <description>\r
+      <text>The flow-label type represents flow identifier or Flow Label\r
+in an IPv6 packet header that may be used to discriminate\r
+traffic flows.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the IPv6FlowLabel textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 3595: Textual Conventions for IPv6 Flow Label\r
+RFC 2460: Internet Protocol, Version 6 (IPv6) Specification</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="port-number">\r
+    <type name="uint16">\r
+      <range value="0..65535"/>\r
+    </type>\r
+    <description>\r
+      <text>The port-number type represents a 16-bit port number of an\r
+Internet transport layer protocol such as UDP, TCP, DCCP, or\r
+SCTP.  Port numbers are assigned by IANA.  A current list of\r
+all assignments is available from &lt;http://www.iana.org/&gt;.\r
+\r
+Note that the port number value zero is reserved by IANA.  In\r
+situations where the value zero does not make sense, it can\r
+be excluded by subtyping the port-number type.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the InetPortNumber textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC  768: User Datagram Protocol\r
+RFC  793: Transmission Control Protocol\r
+RFC 4960: Stream Control Transmission Protocol\r
+RFC 4340: Datagram Congestion Control Protocol (DCCP)\r
+RFC 4001: Textual Conventions for Internet Network Addresses</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="as-number">\r
+    <type name="uint32"/>\r
+    <description>\r
+      <text>The as-number type represents autonomous system numbers\r
+which identify an Autonomous System (AS).  An AS is a set\r
+of routers under a single technical administration, using\r
+an interior gateway protocol and common metrics to route\r
+packets within the AS, and using an exterior gateway\r
+protocol to route packets to other ASs'.  IANA maintains\r
+the AS number space and has delegated large parts to the\r
+regional registries.\r
+\r
+Autonomous system numbers were originally limited to 16\r
+bits.  BGP extensions have enlarged the autonomous system\r
+number space to 32 bits.  This type therefore uses an uint32\r
+base type without a range restriction in order to support\r
+a larger autonomous system number space.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the InetAutonomousSystemNumber textual convention of\r
+the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 1930: Guidelines for creation, selection, and registration\r
+          of an Autonomous System (AS)\r
+RFC 4271: A Border Gateway Protocol 4 (BGP-4)\r
+RFC 4893: BGP Support for Four-octet AS Number Space\r
+RFC 4001: Textual Conventions for Internet Network Addresses</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="ip-address">\r
+    <type name="union">\r
+      <type name="inet:ipv4-address"/>\r
+      <type name="inet:ipv6-address"/>\r
+    </type>\r
+    <description>\r
+      <text>The ip-address type represents an IP address and is IP\r
+version neutral.  The format of the textual representations\r
+implies the IP version.</text>\r
+    </description>\r
+  </typedef>\r
+  <typedef name="ipv4-address">\r
+    <type name="string">\r
+      <pattern value="(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(%[\p{N}\p{L}]+)?"/>\r
+    </type>\r
+    <description>\r
+      <text>The ipv4-address type represents an IPv4 address in\r
+dotted-quad notation.  The IPv4 address may include a zone\r
+index, separated by a % sign.\r
+\r
+The zone index is used to disambiguate identical address\r
+values.  For link-local addresses, the zone index will\r
+typically be the interface index number or the name of an\r
+interface.  If the zone index is not present, the default\r
+zone of the device will be used.\r
+\r
+The canonical format for the zone index is the numerical\r
+format</text>\r
+    </description>\r
+  </typedef>\r
+  <typedef name="ipv6-address">\r
+    <type name="string">\r
+      <pattern value="((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))(%[\p{N}\p{L}]+)?"/>\r
+      <pattern value="(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)(%.+)?"/>\r
+    </type>\r
+    <description>\r
+      <text>The ipv6-address type represents an IPv6 address in full,\r
+mixed, shortened, and shortened-mixed notation.  The IPv6\r
+address may include a zone index, separated by a % sign.\r
+\r
+\r
+\r
+\r
+\r
+The zone index is used to disambiguate identical address\r
+values.  For link-local addresses, the zone index will\r
+typically be the interface index number or the name of an\r
+interface.  If the zone index is not present, the default\r
+zone of the device will be used.\r
+\r
+The canonical format of IPv6 addresses uses the compressed\r
+format described in RFC 4291, Section 2.2, item 2 with the\r
+following additional rules: the :: substitution must be\r
+applied to the longest sequence of all-zero 16-bit chunks\r
+in an IPv6 address.  If there is a tie, the first sequence\r
+of all-zero 16-bit chunks is replaced by ::.  Single\r
+all-zero 16-bit chunks are not compressed.  The canonical\r
+format uses lowercase characters and leading zeros are\r
+not allowed.  The canonical format for the zone index is\r
+the numerical format as described in RFC 4007, Section\r
+11.2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 4291: IP Version 6 Addressing Architecture\r
+RFC 4007: IPv6 Scoped Address Architecture\r
+RFC 5952: A Recommendation for IPv6 Address Text Representation</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="ip-prefix">\r
+    <type name="union">\r
+      <type name="inet:ipv4-prefix"/>\r
+      <type name="inet:ipv6-prefix"/>\r
+    </type>\r
+    <description>\r
+      <text>The ip-prefix type represents an IP prefix and is IP\r
+version neutral.  The format of the textual representations\r
+implies the IP version.</text>\r
+    </description>\r
+  </typedef>\r
+  <typedef name="ipv4-prefix">\r
+    <type name="string">\r
+      <pattern value="(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])/(([0-9])|([1-2][0-9])|(3[0-2]))"/>\r
+    </type>\r
+    <description>\r
+      <text>The ipv4-prefix type represents an IPv4 address prefix.\r
+The prefix length is given by the number following the\r
+slash character and must be less than or equal to 32.\r
+\r
+\r
+\r
+A prefix length value of n corresponds to an IP address\r
+mask that has n contiguous 1-bits from the most\r
+significant bit (MSB) and all other bits set to 0.\r
+\r
+The canonical format of an IPv4 prefix has all bits of\r
+the IPv4 address set to zero that are not part of the\r
+IPv4 prefix.</text>\r
+    </description>\r
+  </typedef>\r
+  <typedef name="ipv6-prefix">\r
+    <type name="string">\r
+      <pattern value="((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))"/>\r
+      <pattern value="(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)(/.+)"/>\r
+    </type>\r
+    <description>\r
+      <text>The ipv6-prefix type represents an IPv6 address prefix.\r
+The prefix length is given by the number following the\r
+slash character and must be less than or equal 128.\r
+\r
+A prefix length value of n corresponds to an IP address\r
+mask that has n contiguous 1-bits from the most\r
+significant bit (MSB) and all other bits set to 0.\r
+\r
+The IPv6 address should have all bits that do not belong\r
+to the prefix set to zero.\r
+\r
+The canonical format of an IPv6 prefix has all bits of\r
+the IPv6 address set to zero that are not part of the\r
+IPv6 prefix.  Furthermore, IPv6 address is represented\r
+in the compressed format described in RFC 4291, Section\r
+2.2, item 2 with the following additional rules: the ::\r
+substitution must be applied to the longest sequence of\r
+all-zero 16-bit chunks in an IPv6 address.  If there is\r
+a tie, the first sequence of all-zero 16-bit chunks is\r
+replaced by ::.  Single all-zero 16-bit chunks are not\r
+compressed.  The canonical format uses lowercase\r
+characters and leading zeros are not allowed.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 4291: IP Version 6 Addressing Architecture</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="domain-name">\r
+    <type name="string">\r
+      <pattern value="((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)|\."/>\r
+      <length value="1..253"/>\r
+    </type>\r
+    <description>\r
+      <text>The domain-name type represents a DNS domain name.  The\r
+name SHOULD be fully qualified whenever possible.\r
+\r
+Internet domain names are only loosely specified.  Section\r
+3.5 of RFC 1034 recommends a syntax (modified in Section\r
+2.1 of RFC 1123).  The pattern above is intended to allow\r
+for current practice in domain name use, and some possible\r
+future expansion.  It is designed to hold various types of\r
+domain names, including names used for A or AAAA records\r
+(host names) and other records, such as SRV records.  Note\r
+that Internet host names have a stricter syntax (described\r
+in RFC 952) than the DNS recommendations in RFCs 1034 and\r
+1123, and that systems that want to store host names in\r
+schema nodes using the domain-name type are recommended to\r
+adhere to this stricter standard to ensure interoperability.\r
+\r
+The encoding of DNS names in the DNS protocol is limited\r
+to 255 characters.  Since the encoding consists of labels\r
+prefixed by a length bytes and there is a trailing NULL\r
+byte, only 253 characters can appear in the textual dotted\r
+notation.\r
+\r
+The description clause of schema nodes using the domain-name\r
+type MUST describe when and how these names are resolved to\r
+IP addresses.  Note that the resolution of a domain-name value\r
+may require to query multiple DNS records (e.g., A for IPv4\r
+and AAAA for IPv6).  The order of the resolution process and\r
+which DNS record takes precedence can either be defined\r
+explicitely or it may depend on the configuration of the\r
+resolver.\r
+\r
+Domain-name values use the US-ASCII encoding.  Their canonical\r
+format uses lowercase US-ASCII characters.  Internationalized\r
+domain names MUST be encoded in punycode as described in RFC\r
+3492</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC  952: DoD Internet Host Table Specification\r
+RFC 1034: Domain Names - Concepts and Facilities\r
+RFC 1123: Requirements for Internet Hosts -- Application\r
+          and Support\r
+RFC 2782: A DNS RR for specifying the location of services\r
+          (DNS SRV)\r
+RFC 3492: Punycode: A Bootstring encoding of Unicode for\r
+          Internationalized Domain Names in Applications\r
+          (IDNA)\r
+RFC 5891: Internationalizing Domain Names in Applications\r
+          (IDNA): Protocol</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="host">\r
+    <type name="union">\r
+      <type name="inet:ip-address"/>\r
+      <type name="inet:domain-name"/>\r
+    </type>\r
+    <description>\r
+      <text>The host type represents either an IP address or a DNS\r
+domain name.</text>\r
+    </description>\r
+  </typedef>\r
+  <typedef name="uri">\r
+    <type name="string"/>\r
+    <description>\r
+      <text>The uri type represents a Uniform Resource Identifier\r
+(URI) as defined by STD 66.\r
+\r
+Objects using the uri type MUST be in US-ASCII encoding,\r
+and MUST be normalized as described by RFC 3986 Sections\r
+6.2.1, 6.2.2.1, and 6.2.2.2.  All unnecessary\r
+percent-encoding is removed, and all case-insensitive\r
+characters are set to lowercase except for hexadecimal\r
+digits, which are normalized to uppercase as described in\r
+Section 6.2.2.1.\r
+\r
+The purpose of this normalization is to help provide\r
+unique URIs.  Note that this normalization is not\r
+sufficient to provide uniqueness.  Two URIs that are\r
+textually distinct after this normalization may still be\r
+equivalent.\r
+\r
+Objects using the uri type may restrict the schemes that\r
+they permit.  For example, 'data:' and 'urn:' schemes\r
+might not be appropriate.\r
+\r
+A zero-length URI is not a valid URI.  This can be used to\r
+express 'URI absent' where required.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the Uri SMIv2 textual convention defined in RFC 5017.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 3986: Uniform Resource Identifier (URI): Generic Syntax\r
+RFC 3305: Report from the Joint W3C/IETF URI Planning Interest\r
+          Group: Uniform Resource Identifiers (URIs), URLs,\r
+          and Uniform Resource Names (URNs): Clarifications\r
+          and Recommendations\r
+RFC 5017: MIB Textual Conventions for Uniform Resource\r
+          Identifiers (URIs)</text>\r
+    </reference>\r
+  </typedef>\r
+</module>\r
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-netconf-monitoring.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-netconf-monitoring.yin
new file mode 100644 (file)
index 0000000..cea6521
--- /dev/null
@@ -0,0 +1,609 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<module name="ietf-netconf-monitoring"\r
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1"\r
+        xmlns:ncm="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"\r
+        xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types"\r
+        xmlns:inet="urn:ietf:params:xml:ns:yang:ietf-inet-types">\r
+  <yang-version value="1"/>\r
+  <namespace uri="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"/>\r
+  <prefix value="ncm"/>\r
+  <import module="ietf-yang-types">\r
+    <prefix value="yang"/>\r
+  </import>\r
+  <import module="ietf-inet-types">\r
+    <prefix value="inet"/>\r
+  </import>\r
+  <organization>\r
+    <text>IETF NETCONF (Network Configuration) Working Group</text>\r
+  </organization>\r
+  <contact>\r
+    <text>WG Web:   &lt;http://tools.ietf.org/wg/netconf/&gt;\r
+WG List:  &lt;mailto:netconf@ietf.org&gt;\r
+\r
+WG Chair: Mehmet Ersue\r
+        &lt;mailto:mehmet.ersue@nsn.com&gt;\r
+\r
+WG Chair: Bert Wijnen\r
+        &lt;mailto:bertietf@bwijnen.net&gt;\r
+\r
+Editor:   Mark Scott\r
+        &lt;mailto:mark.scott@ericsson.com&gt;\r
+\r
+Editor:   Martin Bjorklund\r
+        &lt;mailto:mbj@tail-f.com&gt;</text>\r
+  </contact>\r
+  <description>\r
+    <text>NETCONF Monitoring Module.\r
+All elements in this module are read-only.\r
+\r
+Copyright (c) 2010 IETF Trust and the persons identified as\r
+authors of the code. All rights reserved.\r
+\r
+Redistribution and use in source and binary forms, with or\r
+without modification, is permitted pursuant to, and subject\r
+to the license terms contained in, the Simplified BSD\r
+License set forth in Section 4.c of the IETF Trust's\r
+Legal Provisions Relating to IETF Documents\r
+(http://trustee.ietf.org/license-info).\r
+\r
+This version of this YANG module is part of RFC 6022; see\r
+the RFC itself for full legal notices.</text>\r
+  </description>\r
+  <revision date="2010-10-04">\r
+    <description>\r
+      <text>Initial revision.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 6022: YANG Module for NETCONF Monitoring</text>\r
+    </reference>\r
+  </revision>\r
+  <typedef name="netconf-datastore-type">\r
+    <type name="enumeration">\r
+      <enum name="running">\r
+        <value value="0"/>\r
+      </enum>\r
+      <enum name="candidate">\r
+        <value value="1"/>\r
+      </enum>\r
+      <enum name="startup">\r
+        <value value="2"/>\r
+      </enum>\r
+    </type>\r
+    <description>\r
+      <text>Enumeration of possible NETCONF datastore types.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 4741: NETCONF Configuration Protocol</text>\r
+    </reference>\r
+  </typedef>\r
+  <identity name="transport">\r
+    <description>\r
+      <text>Base identity for NETCONF transport types.</text>\r
+    </description>\r
+  </identity>\r
+  <identity name="netconf-ssh">\r
+    <base name="transport"/>\r
+    <description>\r
+      <text>NETCONF over Secure Shell (SSH).</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 4742: Using the NETCONF Configuration Protocol\r
+  over Secure SHell (SSH)</text>\r
+    </reference>\r
+  </identity>\r
+  <identity name="netconf-soap-over-beep">\r
+    <base name="transport"/>\r
+    <description>\r
+      <text>NETCONF over Simple Object Access Protocol (SOAP) over\r
+Blocks Extensible Exchange Protocol (BEEP).</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 4743: Using NETCONF over the Simple Object\r
+  Access Protocol (SOAP)</text>\r
+    </reference>\r
+  </identity>\r
+  <identity name="netconf-soap-over-https">\r
+    <base name="transport"/>\r
+    <description>\r
+      <text>NETCONF over Simple Object Access Protocol (SOAP)\r
+over Hypertext Transfer Protocol Secure (HTTPS).</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 4743: Using NETCONF over the Simple Object\r
+  Access Protocol (SOAP)</text>\r
+    </reference>\r
+  </identity>\r
+  <identity name="netconf-beep">\r
+    <base name="transport"/>\r
+    <description>\r
+      <text>NETCONF over Blocks Extensible Exchange Protocol (BEEP).</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 4744: Using the NETCONF Protocol over the\r
+  Blocks Extensible Exchange Protocol (BEEP)</text>\r
+    </reference>\r
+  </identity>\r
+  <identity name="netconf-tls">\r
+    <base name="transport"/>\r
+    <description>\r
+      <text>NETCONF over Transport Layer Security (TLS).</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 5539: NETCONF over Transport Layer Security (TLS)</text>\r
+    </reference>\r
+  </identity>\r
+  <identity name="schema-format">\r
+    <description>\r
+      <text>Base identity for data model schema languages.</text>\r
+    </description>\r
+  </identity>\r
+  <identity name="xsd">\r
+    <base name="schema-format"/>\r
+    <description>\r
+      <text>W3C XML Schema Definition.</text>\r
+    </description>\r
+    <reference>\r
+      <text>W3C REC REC-xmlschema-1-20041028:\r
+ XML Schema Part 1: Structures</text>\r
+    </reference>\r
+  </identity>\r
+  <identity name="yang">\r
+    <base name="schema-format"/>\r
+    <description>\r
+      <text>The YANG data modeling language for NETCONF.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 6020:  YANG - A Data Modeling Language for the\r
+   Network Configuration Protocol (NETCONF)</text>\r
+    </reference>\r
+  </identity>\r
+  <identity name="yin">\r
+    <base name="schema-format"/>\r
+    <description>\r
+      <text>The YIN syntax for YANG.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 6020:  YANG - A Data Modeling Language for the\r
+   Network Configuration Protocol (NETCONF)</text>\r
+    </reference>\r
+  </identity>\r
+  <identity name="rng">\r
+    <base name="schema-format"/>\r
+    <description>\r
+      <text>Regular Language for XML Next Generation (RELAX NG).</text>\r
+    </description>\r
+    <reference>\r
+      <text>ISO/IEC 19757-2:2008: RELAX NG</text>\r
+    </reference>\r
+  </identity>\r
+  <identity name="rnc">\r
+    <base name="schema-format"/>\r
+    <description>\r
+      <text>Relax NG Compact Syntax</text>\r
+    </description>\r
+    <reference>\r
+      <text>ISO/IEC 19757-2:2008: RELAX NG</text>\r
+    </reference>\r
+  </identity>\r
+  <grouping name="common-counters">\r
+    <description>\r
+      <text>Counters that exist both per session, and also globally,\r
+accumulated from all sessions.</text>\r
+    </description>\r
+    <leaf name="in-rpcs">\r
+      <type name="yang:zero-based-counter32"/>\r
+      <description>\r
+        <text>Number of correct &lt;rpc&gt; messages received.</text>\r
+      </description>\r
+    </leaf>\r
+    <leaf name="in-bad-rpcs">\r
+      <type name="yang:zero-based-counter32"/>\r
+      <description>\r
+        <text>Number of messages received when an &lt;rpc&gt; message was expected,\r
+that were not correct &lt;rpc&gt; messages.  This includes XML parse\r
+errors and errors on the rpc layer.</text>\r
+      </description>\r
+    </leaf>\r
+    <leaf name="out-rpc-errors">\r
+      <type name="yang:zero-based-counter32"/>\r
+      <description>\r
+        <text>Number of &lt;rpc-reply&gt; messages sent that contained an\r
+&lt;rpc-error&gt; element.</text>\r
+      </description>\r
+    </leaf>\r
+    <leaf name="out-notifications">\r
+      <type name="yang:zero-based-counter32"/>\r
+      <description>\r
+        <text>Number of &lt;notification&gt; messages sent.</text>\r
+      </description>\r
+    </leaf>\r
+  </grouping>\r
+  <container name="netconf-state">\r
+    <config value="false"/>\r
+    <description>\r
+      <text>The netconf-state container is the root of the monitoring\r
+data model.</text>\r
+    </description>\r
+    <container name="capabilities">\r
+      <description>\r
+        <text>Contains the list of NETCONF capabilities supported by the\r
+server.</text>\r
+      </description>\r
+      <leaf-list name="capability">\r
+        <type name="inet:uri"/>\r
+        <description>\r
+          <text>List of NETCONF capabilities supported by the server.</text>\r
+        </description>\r
+      </leaf-list>\r
+    </container>\r
+    <container name="datastores">\r
+      <description>\r
+        <text>Contains the list of NETCONF configuration datastores.</text>\r
+      </description>\r
+      <list name="datastore">\r
+        <key value="name"/>\r
+        <description>\r
+          <text>List of NETCONF configuration datastores supported by\r
+the NETCONF server and related information.</text>\r
+        </description>\r
+        <leaf name="name">\r
+          <type name="netconf-datastore-type"/>\r
+          <description>\r
+            <text>Name of the datastore associated with this list entry.</text>\r
+          </description>\r
+        </leaf>\r
+        <container name="locks">\r
+          <presence value="This container is present only if the datastore&#10;is locked."/>\r
+          <description>\r
+            <text>The NETCONF &lt;lock&gt; and &lt;partial-lock&gt; operations allow\r
+a client to lock specific resources in a datastore.  The\r
+NETCONF server will prevent changes to the locked\r
+resources by all sessions except the one that acquired\r
+the lock(s).\r
+\r
+Monitoring information is provided for each datastore\r
+entry including details such as the session that acquired\r
+the lock, the type of lock (global or partial) and the\r
+list of locked resources.  Multiple locks per datastore\r
+are supported.</text>\r
+          </description>\r
+          <grouping name="lock-info">\r
+            <description>\r
+              <text>Lock related parameters, common to both global and\r
+partial locks.</text>\r
+            </description>\r
+            <leaf name="locked-by-session">\r
+              <type name="uint32"/>\r
+              <mandatory value="true"/>\r
+              <description>\r
+                <text>The session ID of the session that has locked\r
+this resource.  Both a global lock and a partial\r
+lock MUST contain the NETCONF session-id.\r
+\r
+If the lock is held by a session that is not managed\r
+by the NETCONF server (e.g., a CLI session), a session\r
+id of 0 (zero) is reported.</text>\r
+              </description>\r
+              <reference>\r
+                <text>RFC 4741: NETCONF Configuration Protocol</text>\r
+              </reference>\r
+            </leaf>\r
+            <leaf name="locked-time">\r
+              <type name="yang:date-and-time"/>\r
+              <mandatory value="true"/>\r
+              <description>\r
+                <text>The date and time of when the resource was\r
+locked.</text>\r
+              </description>\r
+            </leaf>\r
+          </grouping>\r
+          <choice name="lock-type">\r
+            <description>\r
+              <text>Indicates if a global lock or a set of partial locks\r
+are set.</text>\r
+            </description>\r
+            <container name="global-lock">\r
+              <description>\r
+                <text>Present if the global lock is set.</text>\r
+              </description>\r
+              <uses name="lock-info"/>\r
+            </container>\r
+            <list name="partial-lock">\r
+              <key value="lock-id"/>\r
+              <description>\r
+                <text>List of partial locks.</text>\r
+              </description>\r
+              <reference>\r
+                <text>RFC 5717: Partial Lock Remote Procedure Call (RPC) for\r
+  NETCONF</text>\r
+              </reference>\r
+              <leaf name="lock-id">\r
+                <type name="uint32"/>\r
+                <description>\r
+                  <text>This is the lock id returned in the &lt;partial-lock&gt;\r
+response.</text>\r
+                </description>\r
+              </leaf>\r
+              <uses name="lock-info"/>\r
+              <leaf-list name="select">\r
+                <type name="yang:xpath1.0"/>\r
+                <min-elements value="1"/>\r
+                <description>\r
+                  <text>The xpath expression that was used to request\r
+the lock.  The select expression indicates the\r
+original intended scope of the lock.</text>\r
+                </description>\r
+              </leaf-list>\r
+              <leaf-list name="locked-node">\r
+                <type name="instance-identifier"/>\r
+                <description>\r
+                  <text>The list of instance-identifiers (i.e., the\r
+locked nodes).\r
+\r
+The scope of the partial lock is defined by the list\r
+of locked nodes.</text>\r
+                </description>\r
+              </leaf-list>\r
+            </list>\r
+          </choice>\r
+        </container>\r
+      </list>\r
+    </container>\r
+    <container name="schemas">\r
+      <description>\r
+        <text>Contains the list of data model schemas supported by the\r
+server.</text>\r
+      </description>\r
+      <list name="schema">\r
+        <key value="identifier version format"/>\r
+        <description>\r
+          <text>List of data model schemas supported by the server.</text>\r
+        </description>\r
+        <leaf name="identifier">\r
+          <type name="string"/>\r
+          <description>\r
+            <text>Identifier to uniquely reference the schema.  The\r
+identifier is used in the &lt;get-schema&gt; operation and may\r
+be used for other purposes such as file retrieval.\r
+\r
+For modeling languages that support or require a data\r
+model name (e.g., YANG module name) the identifier MUST\r
+match that name.  For YANG data models, the identifier is\r
+the name of the module or submodule.  In other cases, an\r
+identifier such as a filename MAY be used instead.</text>\r
+          </description>\r
+        </leaf>\r
+        <leaf name="version">\r
+          <type name="string"/>\r
+          <description>\r
+            <text>Version of the schema supported.  Multiple versions MAY be\r
+supported simultaneously by a NETCONF server.  Each\r
+version MUST be reported individually in the schema list,\r
+i.e., with same identifier, possibly different location,\r
+but different version.\r
+\r
+For YANG data models, version is the value of the most\r
+recent YANG 'revision' statement in the module or\r
+submodule, or the empty string if no 'revision' statement\r
+is present.</text>\r
+          </description>\r
+        </leaf>\r
+        <leaf name="format">\r
+          <type name="identityref">\r
+            <base name="schema-format"/>\r
+          </type>\r
+          <description>\r
+            <text>The data modeling language the schema is written\r
+in (currently xsd, yang, yin, rng, or rnc).\r
+For YANG data models, 'yang' format MUST be supported and\r
+'yin' format MAY also be provided.</text>\r
+          </description>\r
+        </leaf>\r
+        <leaf name="namespace">\r
+          <type name="inet:uri"/>\r
+          <mandatory value="true"/>\r
+          <description>\r
+            <text>The XML namespace defined by the data model.\r
+\r
+For YANG data models, this is the module's namespace.\r
+If the list entry describes a submodule, this field\r
+contains the namespace of the module to which the\r
+submodule belongs.</text>\r
+          </description>\r
+        </leaf>\r
+        <leaf-list name="location">\r
+          <type name="union">\r
+            <type name="enumeration">\r
+              <enum name="NETCONF">\r
+                <value value="0"/>\r
+              </enum>\r
+            </type>\r
+            <type name="inet:uri"/>\r
+          </type>\r
+          <description>\r
+            <text>One or more locations from which the schema can be\r
+retrieved.  This list SHOULD contain at least one\r
+entry per schema.\r
+\r
+A schema entry may be located on a remote file system\r
+(e.g., reference to file system for ftp retrieval) or\r
+retrieved directly from a server supporting the\r
+&lt;get-schema&gt; operation (denoted by the value 'NETCONF').</text>\r
+          </description>\r
+        </leaf-list>\r
+      </list>\r
+    </container>\r
+    <container name="sessions">\r
+      <description>\r
+        <text>The sessions container includes session-specific data for\r
+NETCONF management sessions.  The session list MUST include\r
+all currently active NETCONF sessions.</text>\r
+      </description>\r
+      <list name="session">\r
+        <key value="session-id"/>\r
+        <description>\r
+          <text>All NETCONF sessions managed by the NETCONF server\r
+MUST be reported in this list.</text>\r
+        </description>\r
+        <leaf name="session-id">\r
+          <type name="uint32">\r
+            <range value="1..max"/>\r
+          </type>\r
+          <description>\r
+            <text>Unique identifier for the session.  This value is the\r
+NETCONF session identifier, as defined in RFC 4741.</text>\r
+          </description>\r
+          <reference>\r
+            <text>RFC 4741: NETCONF Configuration Protocol</text>\r
+          </reference>\r
+        </leaf>\r
+        <leaf name="transport">\r
+          <type name="identityref">\r
+            <base name="transport"/>\r
+          </type>\r
+          <mandatory value="true"/>\r
+          <description>\r
+            <text>Identifies the transport for each session, e.g.,\r
+'netconf-ssh', 'netconf-soap', etc.</text>\r
+          </description>\r
+        </leaf>\r
+        <leaf name="username">\r
+          <type name="string"/>\r
+          <mandatory value="true"/>\r
+          <description>\r
+            <text>The username is the client identity that was authenticated\r
+by the NETCONF transport protocol.  The algorithm used to\r
+derive the username is NETCONF transport protocol specific\r
+and in addition specific to the authentication mechanism\r
+used by the NETCONF transport protocol.</text>\r
+          </description>\r
+        </leaf>\r
+        <leaf name="source-host">\r
+          <type name="inet:host"/>\r
+          <description>\r
+            <text>Host identifier of the NETCONF client.  The value\r
+returned is implementation specific (e.g., hostname,\r
+IPv4 address, IPv6 address)</text>\r
+          </description>\r
+        </leaf>\r
+        <leaf name="login-time">\r
+          <type name="yang:date-and-time"/>\r
+          <mandatory value="true"/>\r
+          <description>\r
+            <text>Time at the server at which the session was established.</text>\r
+          </description>\r
+        </leaf>\r
+        <uses name="common-counters">\r
+          <description>\r
+            <text>Per-session counters.  Zero based with following reset\r
+behaviour:\r
+- at start of a session\r
+- when max value is reached</text>\r
+          </description>\r
+        </uses>\r
+      </list>\r
+    </container>\r
+    <container name="statistics">\r
+      <description>\r
+        <text>Statistical data pertaining to the NETCONF server.</text>\r
+      </description>\r
+      <leaf name="netconf-start-time">\r
+        <type name="yang:date-and-time"/>\r
+        <description>\r
+          <text>Date and time at which the management subsystem was\r
+started.</text>\r
+        </description>\r
+      </leaf>\r
+      <leaf name="in-bad-hellos">\r
+        <type name="yang:zero-based-counter32"/>\r
+        <description>\r
+          <text>Number of sessions silently dropped because an\r
+invalid &lt;hello&gt; message was received.  This includes &lt;hello&gt;\r
+messages with a 'session-id' attribute, bad namespace, and\r
+bad capability declarations.</text>\r
+        </description>\r
+      </leaf>\r
+      <leaf name="in-sessions">\r
+        <type name="yang:zero-based-counter32"/>\r
+        <description>\r
+          <text>Number of sessions started.  This counter is incremented\r
+when a &lt;hello&gt; message with a &lt;session-id&gt; is sent.\r
+\r
+'in-sessions' - 'in-bad-hellos' =\r
+ 'number of correctly started netconf sessions'</text>\r
+        </description>\r
+      </leaf>\r
+      <leaf name="dropped-sessions">\r
+        <type name="yang:zero-based-counter32"/>\r
+        <description>\r
+          <text>Number of sessions that were abnormally terminated, e.g.,\r
+due to idle timeout or transport close.  This counter is not\r
+incremented when a session is properly closed by a\r
+&lt;close-session&gt; operation, or killed by a &lt;kill-session&gt;\r
+operation.</text>\r
+        </description>\r
+      </leaf>\r
+      <uses name="common-counters">\r
+        <description>\r
+          <text>Global counters, accumulated from all sessions.\r
+Zero based with following reset behaviour:\r
+- re-initialization of NETCONF server\r
+- when max value is reached</text>\r
+        </description>\r
+      </uses>\r
+    </container>\r
+  </container>\r
+  <rpc name="get-schema">\r
+    <description>\r
+      <text>This operation is used to retrieve a schema from the\r
+NETCONF server.\r
+\r
+Positive Response:\r
+The NETCONF server returns the requested schema.\r
+\r
+Negative Response:\r
+If requested schema does not exist, the &lt;error-tag&gt; is\r
+'invalid-value'.\r
+\r
+If more than one schema matches the requested parameters, the\r
+&lt;error-tag&gt; is 'operation-failed', and &lt;error-app-tag&gt; is\r
+'data-not-unique'.</text>\r
+    </description>\r
+    <input>\r
+      <leaf name="identifier">\r
+        <type name="string"/>\r
+        <mandatory value="true"/>\r
+        <description>\r
+          <text>Identifier for the schema list entry.</text>\r
+        </description>\r
+      </leaf>\r
+      <leaf name="version">\r
+        <type name="string"/>\r
+        <description>\r
+          <text>Version of the schema requested.  If this parameter is not\r
+present, and more than one version of the schema exists on\r
+the server, a 'data-not-unique' error is returned, as\r
+described above.</text>\r
+        </description>\r
+      </leaf>\r
+      <leaf name="format">\r
+        <type name="identityref">\r
+          <base name="schema-format"/>\r
+        </type>\r
+        <description>\r
+          <text>The data modeling language of the schema.  If this\r
+parameter is not present, and more than one formats of\r
+the schema exists on the server, a 'data-not-unique' error\r
+is returned, as described above.</text>\r
+        </description>\r
+      </leaf>\r
+    </input>\r
+    <output>\r
+      <anyxml name="data">\r
+        <description>\r
+          <text>Contains the schema content.</text>\r
+        </description>\r
+      </anyxml>\r
+    </output>\r
+  </rpc>\r
+</module>\r
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-yang-types.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-yang-types.yin
new file mode 100644 (file)
index 0000000..2edfcce
--- /dev/null
@@ -0,0 +1,397 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<module name="ietf-yang-types"\r
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1"\r
+        xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types">\r
+  <namespace uri="urn:ietf:params:xml:ns:yang:ietf-yang-types"/>\r
+  <prefix value="yang"/>\r
+  <organization>\r
+    <text>IETF NETMOD (NETCONF Data Modeling Language) Working Group</text>\r
+  </organization>\r
+  <contact>\r
+    <text>WG Web:   &lt;http://tools.ietf.org/wg/netmod/&gt;\r
+WG List:  &lt;mailto:netmod@ietf.org&gt;\r
+\r
+WG Chair: David Partain\r
+          &lt;mailto:david.partain@ericsson.com&gt;\r
+\r
+WG Chair: David Kessens\r
+          &lt;mailto:david.kessens@nsn.com&gt;\r
+\r
+Editor:   Juergen Schoenwaelder\r
+          &lt;mailto:j.schoenwaelder@jacobs-university.de&gt;</text>\r
+  </contact>\r
+  <description>\r
+    <text>This module contains a collection of generally useful derived\r
+YANG data types.\r
+\r
+Copyright (c) 2010 IETF Trust and the persons identified as\r
+authors of the code.  All rights reserved.\r
+\r
+Redistribution and use in source and binary forms, with or without\r
+modification, is permitted pursuant to, and subject to the license\r
+terms contained in, the Simplified BSD License set forth in Section\r
+4.c of the IETF Trust's Legal Provisions Relating to IETF Documents\r
+(http://trustee.ietf.org/license-info).\r
+\r
+This version of this YANG module is part of RFC 6021; see\r
+the RFC itself for full legal notices.</text>\r
+  </description>\r
+  <revision date="2010-09-24">\r
+    <description>\r
+      <text>Initial revision.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 6021: Common YANG Data Types</text>\r
+    </reference>\r
+  </revision>\r
+  <typedef name="counter32">\r
+    <type name="uint32"/>\r
+    <description>\r
+      <text>The counter32 type represents a non-negative integer\r
+that monotonically increases until it reaches a\r
+maximum value of 2^32-1 (4294967295 decimal), when it\r
+wraps around and starts increasing again from zero.\r
+\r
+Counters have no defined 'initial' value, and thus, a\r
+single value of a counter has (in general) no information\r
+content.  Discontinuities in the monotonically increasing\r
+value normally occur at re-initialization of the\r
+management system, and at other times as specified in the\r
+description of a schema node using this type.  If such\r
+other times can occur, for example, the creation of\r
+a schema node of type counter32 at times other than\r
+re-initialization, then a corresponding schema node\r
+should be defined, with an appropriate type, to indicate\r
+the last discontinuity.\r
+\r
+The counter32 type should not be used for configuration\r
+schema nodes.  A default statement SHOULD NOT be used in\r
+combination with the type counter32.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the Counter32 type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2 (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="zero-based-counter32">\r
+    <type name="yang:counter32"/>\r
+    <default value="0"/>\r
+    <description>\r
+      <text>The zero-based-counter32 type represents a counter32\r
+that has the defined 'initial' value zero.\r
+\r
+A schema node of this type will be set to zero (0) on creation\r
+and will thereafter increase monotonically until it reaches\r
+a maximum value of 2^32-1 (4294967295 decimal), when it\r
+wraps around and starts increasing again from zero.\r
+\r
+Provided that an application discovers a new schema node\r
+of this type within the minimum time to wrap, it can use the\r
+'initial' value as a delta.  It is important for a management\r
+station to be aware of this minimum time and the actual time\r
+between polls, and to discard data if the actual time is too\r
+long or there is no defined minimum time.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the ZeroBasedCounter32 textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 4502: Remote Network Monitoring Management Information\r
+          Base Version 2</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="counter64">\r
+    <type name="uint64"/>\r
+    <description>\r
+      <text>The counter64 type represents a non-negative integer\r
+that monotonically increases until it reaches a\r
+maximum value of 2^64-1 (18446744073709551615 decimal),\r
+when it wraps around and starts increasing again from zero.\r
+\r
+Counters have no defined 'initial' value, and thus, a\r
+single value of a counter has (in general) no information\r
+content.  Discontinuities in the monotonically increasing\r
+value normally occur at re-initialization of the\r
+management system, and at other times as specified in the\r
+description of a schema node using this type.  If such\r
+other times can occur, for example, the creation of\r
+a schema node of type counter64 at times other than\r
+re-initialization, then a corresponding schema node\r
+should be defined, with an appropriate type, to indicate\r
+the last discontinuity.\r
+\r
+The counter64 type should not be used for configuration\r
+schema nodes.  A default statement SHOULD NOT be used in\r
+combination with the type counter64.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the Counter64 type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2 (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="zero-based-counter64">\r
+    <type name="yang:counter64"/>\r
+    <default value="0"/>\r
+    <description>\r
+      <text>The zero-based-counter64 type represents a counter64 that\r
+has the defined 'initial' value zero.\r
+\r
+A schema node of this type will be set to zero (0) on creation\r
+and will thereafter increase monotonically until it reaches\r
+a maximum value of 2^64-1 (18446744073709551615 decimal),\r
+when it wraps around and starts increasing again from zero.\r
+\r
+Provided that an application discovers a new schema node\r
+of this type within the minimum time to wrap, it can use the\r
+'initial' value as a delta.  It is important for a management\r
+station to be aware of this minimum time and the actual time\r
+between polls, and to discard data if the actual time is too\r
+long or there is no defined minimum time.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the ZeroBasedCounter64 textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2856: Textual Conventions for Additional High Capacity\r
+          Data Types</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="gauge32">\r
+    <type name="uint32"/>\r
+    <description>\r
+      <text>The gauge32 type represents a non-negative integer, which\r
+may increase or decrease, but shall never exceed a maximum\r
+value, nor fall below a minimum value.  The maximum value\r
+cannot be greater than 2^32-1 (4294967295 decimal), and\r
+the minimum value cannot be smaller than 0.  The value of\r
+a gauge32 has its maximum value whenever the information\r
+being modeled is greater than or equal to its maximum\r
+value, and has its minimum value whenever the information\r
+being modeled is smaller than or equal to its minimum value.\r
+If the information being modeled subsequently decreases\r
+below (increases above) the maximum (minimum) value, the\r
+gauge32 also decreases (increases).\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the Gauge32 type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2 (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="gauge64">\r
+    <type name="uint64"/>\r
+    <description>\r
+      <text>The gauge64 type represents a non-negative integer, which\r
+may increase or decrease, but shall never exceed a maximum\r
+value, nor fall below a minimum value.  The maximum value\r
+cannot be greater than 2^64-1 (18446744073709551615), and\r
+the minimum value cannot be smaller than 0.  The value of\r
+a gauge64 has its maximum value whenever the information\r
+being modeled is greater than or equal to its maximum\r
+value, and has its minimum value whenever the information\r
+being modeled is smaller than or equal to its minimum value.\r
+If the information being modeled subsequently decreases\r
+below (increases above) the maximum (minimum) value, the\r
+gauge64 also decreases (increases).\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the CounterBasedGauge64 SMIv2 textual convention defined\r
+in RFC 2856</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2856: Textual Conventions for Additional High Capacity\r
+          Data Types</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="object-identifier">\r
+    <type name="string">\r
+      <pattern value="(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))(\.(0|([1-9]\d*)))*"/>\r
+    </type>\r
+    <description>\r
+      <text>The object-identifier type represents administratively\r
+assigned names in a registration-hierarchical-name tree.\r
+\r
+Values of this type are denoted as a sequence of numerical\r
+non-negative sub-identifier values.  Each sub-identifier\r
+value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers\r
+are separated by single dots and without any intermediate\r
+whitespace.\r
+\r
+The ASN.1 standard restricts the value space of the first\r
+sub-identifier to 0, 1, or 2.  Furthermore, the value space\r
+of the second sub-identifier is restricted to the range\r
+0 to 39 if the first sub-identifier is 0 or 1.  Finally,\r
+the ASN.1 standard requires that an object identifier\r
+has always at least two sub-identifier.  The pattern\r
+captures these restrictions.\r
+\r
+Although the number of sub-identifiers is not limited,\r
+module designers should realize that there may be\r
+implementations that stick with the SMIv2 limit of 128\r
+sub-identifiers.\r
+\r
+This type is a superset of the SMIv2 OBJECT IDENTIFIER type\r
+since it is not restricted to 128 sub-identifiers.  Hence,\r
+this type SHOULD NOT be used to represent the SMIv2 OBJECT\r
+IDENTIFIER type, the object-identifier-128 type SHOULD be\r
+used instead.</text>\r
+    </description>\r
+    <reference>\r
+      <text>ISO9834-1: Information technology -- Open Systems\r
+Interconnection -- Procedures for the operation of OSI\r
+Registration Authorities: General procedures and top\r
+arcs of the ASN.1 Object Identifier tree</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="object-identifier-128">\r
+    <type name="object-identifier">\r
+      <pattern value="\d*(\.\d*){1,127}"/>\r
+    </type>\r
+    <description>\r
+      <text>This type represents object-identifiers restricted to 128\r
+sub-identifiers.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the OBJECT IDENTIFIER type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2 (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="date-and-time">\r
+    <type name="string">\r
+      <pattern value="\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2})"/>\r
+    </type>\r
+    <description>\r
+      <text>The date-and-time type is a profile of the ISO 8601\r
+standard for representation of dates and times using the\r
+Gregorian calendar.  The profile is defined by the\r
+date-time production in Section 5.6 of RFC 3339.\r
+\r
+The date-and-time type is compatible with the dateTime XML\r
+schema type with the following notable exceptions:\r
+\r
+(a) The date-and-time type does not allow negative years.\r
+\r
+(b) The date-and-time time-offset -00:00 indicates an unknown\r
+    time zone (see RFC 3339) while -00:00 and +00:00 and Z all\r
+    represent the same time zone in dateTime.\r
+\r
+(c) The canonical format (see below) of data-and-time values\r
+    differs from the canonical format used by the dateTime XML\r
+    schema type, which requires all times to be in UTC using the\r
+    time-offset 'Z'.\r
+\r
+This type is not equivalent to the DateAndTime textual\r
+convention of the SMIv2 since RFC 3339 uses a different\r
+separator between full-date and full-time and provides\r
+higher resolution of time-secfrac.\r
+\r
+The canonical format for date-and-time values with a known time\r
+zone uses a numeric time zone offset that is calculated using\r
+the device's configured known offset to UTC time.  A change of\r
+the device's offset to UTC time will cause date-and-time values\r
+to change accordingly.  Such changes might happen periodically\r
+in case a server follows automatically daylight saving time\r
+(DST) time zone offset changes.  The canonical format for\r
+date-and-time values with an unknown time zone (usually referring\r
+to the notion of local time) uses the time-offset -00:00.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 3339: Date and Time on the Internet: Timestamps\r
+RFC 2579: Textual Conventions for SMIv2\r
+XSD-TYPES: XML Schema Part 2: Datatypes Second Edition</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="timeticks">\r
+    <type name="uint32"/>\r
+    <description>\r
+      <text>The timeticks type represents a non-negative integer that\r
+represents the time, modulo 2^32 (4294967296 decimal), in\r
+hundredths of a second between two epochs.  When a schema\r
+node is defined that uses this type, the description of\r
+the schema node identifies both of the reference epochs.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the TimeTicks type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2 (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="timestamp">\r
+    <type name="yang:timeticks"/>\r
+    <description>\r
+      <text>The timestamp type represents the value of an associated\r
+timeticks schema node at which a specific occurrence happened.\r
+The specific occurrence must be defined in the description\r
+of any schema node defined using this type.  When the specific\r
+occurrence occurred prior to the last time the associated\r
+timeticks attribute was zero, then the timestamp value is\r
+zero.  Note that this requires all timestamp values to be\r
+reset to zero when the value of the associated timeticks\r
+attribute reaches 497+ days and wraps around to zero.\r
+\r
+The associated timeticks schema node must be specified\r
+in the description of any schema node using this type.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the TimeStamp textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2579: Textual Conventions for SMIv2</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="phys-address">\r
+    <type name="string">\r
+      <pattern value="([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?"/>\r
+    </type>\r
+    <description>\r
+      <text>Represents media- or physical-level addresses represented\r
+as a sequence octets, each octet represented by two hexadecimal\r
+numbers.  Octets are separated by colons.  The canonical\r
+representation uses lowercase characters.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the PhysAddress textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2579: Textual Conventions for SMIv2</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="mac-address">\r
+    <type name="string">\r
+      <pattern value="[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}"/>\r
+    </type>\r
+    <description>\r
+      <text>The mac-address type represents an IEEE 802 MAC address.\r
+The canonical representation uses lowercase characters.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the MacAddress textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>IEEE 802: IEEE Standard for Local and Metropolitan Area\r
+          Networks: Overview and Architecture\r
+RFC 2579: Textual Conventions for SMIv2</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="xpath1.0">\r
+    <type name="string"/>\r
+    <description>\r
+      <text>This type represents an XPATH 1.0 expression.\r
+\r
+When a schema node is defined that uses this type, the\r
+description of the schema node MUST specify the XPath\r
+context in which the XPath expression is evaluated.</text>\r
+    </description>\r
+    <reference>\r
+      <text>XPATH: XML Path Language (XPath) Version 1.0</text>\r
+    </reference>\r
+  </typedef>\r
+</module>\r
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-yang-types@2013-05-16.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-yang-types@2013-05-16.yin
new file mode 100644 (file)
index 0000000..d539311
--- /dev/null
@@ -0,0 +1,474 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<module name="ietf-yang-types"\r
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1"\r
+        xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types">\r
+  <namespace uri="urn:ietf:params:xml:ns:yang:ietf-yang-types"/>\r
+  <prefix value="yang"/>\r
+  <organization>\r
+    <text>IETF NETMOD (NETCONF Data Modeling Language) Working Group</text>\r
+  </organization>\r
+  <contact>\r
+    <text>WG Web:   &lt;http://tools.ietf.org/wg/netmod/&gt;\r
+WG List:  &lt;mailto:netmod@ietf.org&gt;\r
+\r
+WG Chair: David Kessens\r
+          &lt;mailto:david.kessens@nsn.com&gt;\r
+\r
+WG Chair: Juergen Schoenwaelder\r
+          &lt;mailto:j.schoenwaelder@jacobs-university.de&gt;\r
+\r
+Editor:   Juergen Schoenwaelder\r
+          &lt;mailto:j.schoenwaelder@jacobs-university.de&gt;</text>\r
+  </contact>\r
+  <description>\r
+    <text>This module contains a collection of generally useful derived\r
+YANG data types.\r
+\r
+Copyright (c) 2013 IETF Trust and the persons identified as\r
+authors of the code.  All rights reserved.\r
+\r
+Redistribution and use in source and binary forms, with or\r
+without modification, is permitted pursuant to, and subject\r
+to the license terms contained in, the Simplified BSD License\r
+set forth in Section 4.c of the IETF Trust's Legal Provisions\r
+Relating to IETF Documents\r
+(http://trustee.ietf.org/license-info).\r
+\r
+This version of this YANG module is part of RFC XXXX; see\r
+the RFC itself for full legal notices.</text>\r
+  </description>\r
+  <revision date="2013-05-16">\r
+    <description>\r
+      <text>This revision adds the following new data types:\r
+- yang-identifier\r
+- hex-string\r
+- uuid\r
+- dotted-quad</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC XXXX: Common YANG Data Types</text>\r
+    </reference>\r
+  </revision>\r
+  <revision date="2010-09-24">\r
+    <description>\r
+      <text>Initial revision.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 6021: Common YANG Data Types</text>\r
+    </reference>\r
+  </revision>\r
+  <typedef name="counter32">\r
+    <type name="uint32"/>\r
+    <description>\r
+      <text>The counter32 type represents a non-negative integer\r
+that monotonically increases until it reaches a\r
+maximum value of 2^32-1 (4294967295 decimal), when it\r
+wraps around and starts increasing again from zero.\r
+\r
+Counters have no defined 'initial' value, and thus, a\r
+single value of a counter has (in general) no information\r
+content.  Discontinuities in the monotonically increasing\r
+value normally occur at re-initialization of the\r
+management system, and at other times as specified in the\r
+description of a schema node using this type.  If such\r
+other times can occur, for example, the creation of\r
+a schema node of type counter32 at times other than\r
+re-initialization, then a corresponding schema node\r
+should be defined, with an appropriate type, to indicate\r
+the last discontinuity.\r
+\r
+The counter32 type should not be used for configuration\r
+schema nodes.  A default statement SHOULD NOT be used in\r
+combination with the type counter32.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the Counter32 type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2\r
+          (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="zero-based-counter32">\r
+    <type name="yang:counter32"/>\r
+    <default value="0"/>\r
+    <description>\r
+      <text>The zero-based-counter32 type represents a counter32\r
+that has the defined 'initial' value zero.\r
+\r
+A schema node of this type will be set to zero (0) on creation\r
+and will thereafter increase monotonically until it reaches\r
+a maximum value of 2^32-1 (4294967295 decimal), when it\r
+wraps around and starts increasing again from zero.\r
+\r
+Provided that an application discovers a new schema node\r
+of this type within the minimum time to wrap, it can use the\r
+'initial' value as a delta.  It is important for a management\r
+station to be aware of this minimum time and the actual time\r
+between polls, and to discard data if the actual time is too\r
+long or there is no defined minimum time.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the ZeroBasedCounter32 textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 4502: Remote Network Monitoring Management Information\r
+          Base Version 2</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="counter64">\r
+    <type name="uint64"/>\r
+    <description>\r
+      <text>The counter64 type represents a non-negative integer\r
+that monotonically increases until it reaches a\r
+maximum value of 2^64-1 (18446744073709551615 decimal),\r
+when it wraps around and starts increasing again from zero.\r
+\r
+Counters have no defined 'initial' value, and thus, a\r
+single value of a counter has (in general) no information\r
+content.  Discontinuities in the monotonically increasing\r
+value normally occur at re-initialization of the\r
+management system, and at other times as specified in the\r
+description of a schema node using this type.  If such\r
+other times can occur, for example, the creation of\r
+a schema node of type counter64 at times other than\r
+re-initialization, then a corresponding schema node\r
+should be defined, with an appropriate type, to indicate\r
+the last discontinuity.\r
+\r
+The counter64 type should not be used for configuration\r
+schema nodes.  A default statement SHOULD NOT be used in\r
+combination with the type counter64.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the Counter64 type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2\r
+          (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="zero-based-counter64">\r
+    <type name="yang:counter64"/>\r
+    <default value="0"/>\r
+    <description>\r
+      <text>The zero-based-counter64 type represents a counter64 that\r
+has the defined 'initial' value zero.\r
+\r
+A schema node of this type will be set to zero (0) on creation\r
+and will thereafter increase monotonically until it reaches\r
+a maximum value of 2^64-1 (18446744073709551615 decimal),\r
+when it wraps around and starts increasing again from zero.\r
+\r
+Provided that an application discovers a new schema node\r
+of this type within the minimum time to wrap, it can use the\r
+'initial' value as a delta.  It is important for a management\r
+station to be aware of this minimum time and the actual time\r
+between polls, and to discard data if the actual time is too\r
+long or there is no defined minimum time.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the ZeroBasedCounter64 textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2856: Textual Conventions for Additional High Capacity\r
+          Data Types</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="gauge32">\r
+    <type name="uint32"/>\r
+    <description>\r
+      <text>The gauge32 type represents a non-negative integer, which\r
+may increase or decrease, but shall never exceed a maximum\r
+value, nor fall below a minimum value.  The maximum value\r
+cannot be greater than 2^32-1 (4294967295 decimal), and\r
+the minimum value cannot be smaller than 0.  The value of\r
+a gauge32 has its maximum value whenever the information\r
+being modeled is greater than or equal to its maximum\r
+value, and has its minimum value whenever the information\r
+being modeled is smaller than or equal to its minimum value.\r
+If the information being modeled subsequently decreases\r
+below (increases above) the maximum (minimum) value, the\r
+gauge32 also decreases (increases).\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the Gauge32 type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2\r
+          (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="gauge64">\r
+    <type name="uint64"/>\r
+    <description>\r
+      <text>The gauge64 type represents a non-negative integer, which\r
+may increase or decrease, but shall never exceed a maximum\r
+value, nor fall below a minimum value.  The maximum value\r
+cannot be greater than 2^64-1 (18446744073709551615), and\r
+the minimum value cannot be smaller than 0.  The value of\r
+a gauge64 has its maximum value whenever the information\r
+being modeled is greater than or equal to its maximum\r
+value, and has its minimum value whenever the information\r
+being modeled is smaller than or equal to its minimum value.\r
+If the information being modeled subsequently decreases\r
+below (increases above) the maximum (minimum) value, the\r
+gauge64 also decreases (increases).\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the CounterBasedGauge64 SMIv2 textual convention defined\r
+in RFC 2856</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2856: Textual Conventions for Additional High Capacity\r
+          Data Types</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="object-identifier">\r
+    <type name="string">\r
+      <pattern value="(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))(\.(0|([1-9]\d*)))*"/>\r
+    </type>\r
+    <description>\r
+      <text>The object-identifier type represents administratively\r
+assigned names in a registration-hierarchical-name tree.\r
+\r
+Values of this type are denoted as a sequence of numerical\r
+non-negative sub-identifier values.  Each sub-identifier\r
+value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers\r
+are separated by single dots and without any intermediate\r
+whitespace.\r
+\r
+The ASN.1 standard restricts the value space of the first\r
+sub-identifier to 0, 1, or 2.  Furthermore, the value space\r
+of the second sub-identifier is restricted to the range\r
+0 to 39 if the first sub-identifier is 0 or 1.  Finally,\r
+the ASN.1 standard requires that an object identifier\r
+has always at least two sub-identifier.  The pattern\r
+captures these restrictions.\r
+\r
+Although the number of sub-identifiers is not limited,\r
+module designers should realize that there may be\r
+implementations that stick with the SMIv2 limit of 128\r
+sub-identifiers.\r
+\r
+This type is a superset of the SMIv2 OBJECT IDENTIFIER type\r
+since it is not restricted to 128 sub-identifiers.  Hence,\r
+this type SHOULD NOT be used to represent the SMIv2 OBJECT\r
+IDENTIFIER type, the object-identifier-128 type SHOULD be\r
+used instead.</text>\r
+    </description>\r
+    <reference>\r
+      <text>ISO9834-1: Information technology -- Open Systems\r
+Interconnection -- Procedures for the operation of OSI\r
+Registration Authorities: General procedures and top\r
+arcs of the ASN.1 Object Identifier tree</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="object-identifier-128">\r
+    <type name="object-identifier">\r
+      <pattern value="\d*(\.\d*){1,127}"/>\r
+    </type>\r
+    <description>\r
+      <text>This type represents object-identifiers restricted to 128\r
+sub-identifiers.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the OBJECT IDENTIFIER type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2\r
+          (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="yang-identifier">\r
+    <type name="string">\r
+      <length value="1..max"/>\r
+      <pattern value="[a-zA-Z_][a-zA-Z0-9\-_.]*"/>\r
+      <pattern value=".|..|[^xX].*|.[^mM].*|..[^lL].*"/>\r
+    </type>\r
+    <description>\r
+      <text>A YANG identifier string as defined in RFC 6020, page 163.\r
+An identifier must start with an alphabetic character or\r
+an underscore followed by an arbitrary sequence of\r
+alphabetic or numeric characters, underscores, hyphens\r
+or dots.\r
+\r
+A YANG identifier MUST NOT start with any possible\r
+combination of the lower-case or upper-case character\r
+sequence 'xml'.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 6020: YANG - A Data Modeling Language for the Network\r
+          Configuration Protocol (NETCONF)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="date-and-time">\r
+    <type name="string">\r
+      <pattern value="\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2})"/>\r
+    </type>\r
+    <description>\r
+      <text>The date-and-time type is a profile of the ISO 8601\r
+standard for representation of dates and times using the\r
+Gregorian calendar.  The profile is defined by the\r
+date-time production in Section 5.6 of RFC 3339.\r
+\r
+The date-and-time type is compatible with the dateTime XML\r
+schema type with the following notable exceptions:\r
+\r
+(a) The date-and-time type does not allow negative years.\r
+\r
+(b) The date-and-time time-offset -00:00 indicates an unknown\r
+    time zone (see RFC 3339) while -00:00 and +00:00 and Z all\r
+    represent the same time zone in dateTime.\r
+\r
+(c) The canonical format (see below) of data-and-time values\r
+    differs from the canonical format used by the dateTime XML\r
+    schema type, which requires all times to be in UTC using\r
+    the time-offset 'Z'.\r
+\r
+This type is not equivalent to the DateAndTime textual\r
+convention of the SMIv2 since RFC 3339 uses a different\r
+separator between full-date and full-time and provides\r
+higher resolution of time-secfrac.\r
+The canonical format for date-and-time values with a known time\r
+zone uses a numeric time zone offset that is calculated using\r
+the device's configured known offset to UTC time.  A change of\r
+the device's offset to UTC time will cause date-and-time values\r
+to change accordingly.  Such changes might happen periodically\r
+in case a server follows automatically daylight saving time\r
+(DST) time zone offset changes.  The canonical format for\r
+date-and-time values with an unknown time zone (usually\r
+referring to the notion of local time) uses the time-offset\r
+-00:00.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 3339: Date and Time on the Internet: Timestamps\r
+RFC 2579: Textual Conventions for SMIv2\r
+XSD-TYPES: XML Schema Part 2: Datatypes Second Edition</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="timeticks">\r
+    <type name="uint32"/>\r
+    <description>\r
+      <text>The timeticks type represents a non-negative integer that\r
+represents the time, modulo 2^32 (4294967296 decimal), in\r
+hundredths of a second between two epochs.  When a schema\r
+node is defined that uses this type, the description of\r
+the schema node identifies both of the reference epochs.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the TimeTicks type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2\r
+          (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="timestamp">\r
+    <type name="yang:timeticks"/>\r
+    <description>\r
+      <text>The timestamp type represents the value of an associated\r
+timeticks schema node at which a specific occurrence\r
+happened. The specific occurrence must be defined in the\r
+description of any schema node defined using this type.  When\r
+the specific occurrence occurred prior to the last time the\r
+associated timeticks attribute was zero, then the timestamp\r
+value is zero.  Note that this requires all timestamp values\r
+to be reset to zero when the value of the associated timeticks\r
+attribute reaches 497+ days and wraps around to zero.\r
+\r
+The associated timeticks schema node must be specified\r
+in the description of any schema node using this type.\r
+In the value set and its semantics, this type is equivalent\r
+to the TimeStamp textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2579: Textual Conventions for SMIv2</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="phys-address">\r
+    <type name="string">\r
+      <pattern value="([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?"/>\r
+    </type>\r
+    <description>\r
+      <text>Represents media- or physical-level addresses represented\r
+as a sequence octets, each octet represented by two hexadecimal\r
+numbers.  Octets are separated by colons.  The canonical\r
+representation uses lowercase characters.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the PhysAddress textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2579: Textual Conventions for SMIv2</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="mac-address">\r
+    <type name="string">\r
+      <pattern value="[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}"/>\r
+    </type>\r
+    <description>\r
+      <text>The mac-address type represents an IEEE 802 MAC address.\r
+The canonical representation uses lowercase characters.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the MacAddress textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>IEEE 802: IEEE Standard for Local and Metropolitan Area\r
+          Networks: Overview and Architecture\r
+RFC 2579: Textual Conventions for SMIv2</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="xpath1.0">\r
+    <type name="string"/>\r
+    <description>\r
+      <text>This type represents an XPATH 1.0 expression.\r
+\r
+When a schema node is defined that uses this type, the\r
+description of the schema node MUST specify the XPath\r
+context in which the XPath expression is evaluated.</text>\r
+    </description>\r
+    <reference>\r
+      <text>XPATH: XML Path Language (XPath) Version 1.0</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="hex-string">\r
+    <type name="string">\r
+      <pattern value="([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?"/>\r
+    </type>\r
+    <description>\r
+      <text>A hexadecimal string with octets represented as hex digits\r
+separated by colons.  The canonical representation uses\r
+lowercase characters.</text>\r
+    </description>\r
+  </typedef>\r
+  <typedef name="uuid">\r
+    <type name="string">\r
+      <pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"/>\r
+    </type>\r
+    <description>\r
+      <text>A Universally Unique IDentifier in the string representation\r
+defined in RFC 4122.  The canonical representation uses\r
+lowercase characters.\r
+\r
+The following is an example of a UUID in string representation:\r
+f81d4fae-7dec-11d0-a765-00a0c91e6bf6\r
+</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 4122: A Universally Unique IDentifier (UUID) URN\r
+          Namespace</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="dotted-quad">\r
+    <type name="string">\r
+      <pattern value="(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"/>\r
+    </type>\r
+    <description>\r
+      <text>An unsigned 32-bit number expressed in the dotted-quad\r
+notation, i.e., four octets written as decimal numbers\r
+and separated with the '.' (full stop) character.</text>\r
+    </description>\r
+  </typedef>\r
+</module>\r
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-yang-types@2013-07-15.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/ietf-yang-types@2013-07-15.yin
new file mode 100644 (file)
index 0000000..5c90d28
--- /dev/null
@@ -0,0 +1,472 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<module name="ietf-yang-types"\r
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1"\r
+        xmlns:yang="urn:ietf:params:xml:ns:yang:ietf-yang-types">\r
+  <namespace uri="urn:ietf:params:xml:ns:yang:ietf-yang-types"/>\r
+  <prefix value="yang"/>\r
+  <organization>\r
+    <text>IETF NETMOD (NETCONF Data Modeling Language) Working Group</text>\r
+  </organization>\r
+  <contact>\r
+    <text>WG Web:   &lt;http://tools.ietf.org/wg/netmod/&gt;\r
+WG List:  &lt;mailto:netmod@ietf.org&gt;\r
+WG Chair: David Kessens\r
+          &lt;mailto:david.kessens@nsn.com&gt;\r
+\r
+WG Chair: Juergen Schoenwaelder\r
+          &lt;mailto:j.schoenwaelder@jacobs-university.de&gt;\r
+\r
+Editor:   Juergen Schoenwaelder\r
+          &lt;mailto:j.schoenwaelder@jacobs-university.de&gt;</text>\r
+  </contact>\r
+  <description>\r
+    <text>This module contains a collection of generally useful derived\r
+YANG data types.\r
+\r
+Copyright (c) 2013 IETF Trust and the persons identified as\r
+authors of the code.  All rights reserved.\r
+\r
+Redistribution and use in source and binary forms, with or\r
+without modification, is permitted pursuant to, and subject\r
+to the license terms contained in, the Simplified BSD License\r
+set forth in Section 4.c of the IETF Trust's Legal Provisions\r
+Relating to IETF Documents\r
+(http://trustee.ietf.org/license-info).\r
+\r
+This version of this YANG module is part of RFC 6991; see\r
+the RFC itself for full legal notices.</text>\r
+  </description>\r
+  <revision date="2013-07-15">\r
+    <description>\r
+      <text>This revision adds the following new data types:\r
+- yang-identifier\r
+- hex-string\r
+- uuid\r
+- dotted-quad</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 6991: Common YANG Data Types</text>\r
+    </reference>\r
+  </revision>\r
+  <revision date="2010-09-24">\r
+    <description>\r
+      <text>Initial revision.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 6021: Common YANG Data Types</text>\r
+    </reference>\r
+  </revision>\r
+  <typedef name="counter32">\r
+    <type name="uint32"/>\r
+    <description>\r
+      <text>The counter32 type represents a non-negative integer\r
+that monotonically increases until it reaches a\r
+maximum value of 2^32-1 (4294967295 decimal), when it\r
+wraps around and starts increasing again from zero.\r
+\r
+Counters have no defined 'initial' value, and thus, a\r
+single value of a counter has (in general) no information\r
+content.  Discontinuities in the monotonically increasing\r
+value normally occur at re-initialization of the\r
+management system, and at other times as specified in the\r
+description of a schema node using this type.  If such\r
+other times can occur, for example, the creation of\r
+a schema node of type counter32 at times other than\r
+re-initialization, then a corresponding schema node\r
+should be defined, with an appropriate type, to indicate\r
+the last discontinuity.\r
+\r
+The counter32 type should not be used for configuration\r
+schema nodes.  A default statement SHOULD NOT be used in\r
+combination with the type counter32.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the Counter32 type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2\r
+          (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="zero-based-counter32">\r
+    <type name="yang:counter32"/>\r
+    <default value="0"/>\r
+    <description>\r
+      <text>The zero-based-counter32 type represents a counter32\r
+that has the defined 'initial' value zero.\r
+\r
+A schema node of this type will be set to zero (0) on creation\r
+and will thereafter increase monotonically until it reaches\r
+a maximum value of 2^32-1 (4294967295 decimal), when it\r
+wraps around and starts increasing again from zero.\r
+\r
+Provided that an application discovers a new schema node\r
+of this type within the minimum time to wrap, it can use the\r
+'initial' value as a delta.  It is important for a management\r
+station to be aware of this minimum time and the actual time\r
+between polls, and to discard data if the actual time is too\r
+long or there is no defined minimum time.\r
+In the value set and its semantics, this type is equivalent\r
+to the ZeroBasedCounter32 textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 4502: Remote Network Monitoring Management Information\r
+          Base Version 2</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="counter64">\r
+    <type name="uint64"/>\r
+    <description>\r
+      <text>The counter64 type represents a non-negative integer\r
+that monotonically increases until it reaches a\r
+maximum value of 2^64-1 (18446744073709551615 decimal),\r
+when it wraps around and starts increasing again from zero.\r
+\r
+Counters have no defined 'initial' value, and thus, a\r
+single value of a counter has (in general) no information\r
+content.  Discontinuities in the monotonically increasing\r
+value normally occur at re-initialization of the\r
+management system, and at other times as specified in the\r
+description of a schema node using this type.  If such\r
+other times can occur, for example, the creation of\r
+a schema node of type counter64 at times other than\r
+re-initialization, then a corresponding schema node\r
+should be defined, with an appropriate type, to indicate\r
+the last discontinuity.\r
+\r
+The counter64 type should not be used for configuration\r
+schema nodes.  A default statement SHOULD NOT be used in\r
+combination with the type counter64.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the Counter64 type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2\r
+          (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="zero-based-counter64">\r
+    <type name="yang:counter64"/>\r
+    <default value="0"/>\r
+    <description>\r
+      <text>The zero-based-counter64 type represents a counter64 that\r
+has the defined 'initial' value zero.\r
+A schema node of this type will be set to zero (0) on creation\r
+and will thereafter increase monotonically until it reaches\r
+a maximum value of 2^64-1 (18446744073709551615 decimal),\r
+when it wraps around and starts increasing again from zero.\r
+\r
+Provided that an application discovers a new schema node\r
+of this type within the minimum time to wrap, it can use the\r
+'initial' value as a delta.  It is important for a management\r
+station to be aware of this minimum time and the actual time\r
+between polls, and to discard data if the actual time is too\r
+long or there is no defined minimum time.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the ZeroBasedCounter64 textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2856: Textual Conventions for Additional High Capacity\r
+          Data Types</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="gauge32">\r
+    <type name="uint32"/>\r
+    <description>\r
+      <text>The gauge32 type represents a non-negative integer, which\r
+may increase or decrease, but shall never exceed a maximum\r
+value, nor fall below a minimum value.  The maximum value\r
+cannot be greater than 2^32-1 (4294967295 decimal), and\r
+the minimum value cannot be smaller than 0.  The value of\r
+a gauge32 has its maximum value whenever the information\r
+being modeled is greater than or equal to its maximum\r
+value, and has its minimum value whenever the information\r
+being modeled is smaller than or equal to its minimum value.\r
+If the information being modeled subsequently decreases\r
+below (increases above) the maximum (minimum) value, the\r
+gauge32 also decreases (increases).\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the Gauge32 type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2\r
+          (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="gauge64">\r
+    <type name="uint64"/>\r
+    <description>\r
+      <text>The gauge64 type represents a non-negative integer, which\r
+may increase or decrease, but shall never exceed a maximum\r
+value, nor fall below a minimum value.  The maximum value\r
+cannot be greater than 2^64-1 (18446744073709551615), and\r
+the minimum value cannot be smaller than 0.  The value of\r
+a gauge64 has its maximum value whenever the information\r
+being modeled is greater than or equal to its maximum\r
+value, and has its minimum value whenever the information\r
+being modeled is smaller than or equal to its minimum value.\r
+If the information being modeled subsequently decreases\r
+below (increases above) the maximum (minimum) value, the\r
+gauge64 also decreases (increases).\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the CounterBasedGauge64 SMIv2 textual convention defined\r
+in RFC 2856</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2856: Textual Conventions for Additional High Capacity\r
+          Data Types</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="object-identifier">\r
+    <type name="string">\r
+      <pattern value="(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))(\.(0|([1-9]\d*)))*"/>\r
+    </type>\r
+    <description>\r
+      <text>The object-identifier type represents administratively\r
+assigned names in a registration-hierarchical-name tree.\r
+\r
+Values of this type are denoted as a sequence of numerical\r
+non-negative sub-identifier values.  Each sub-identifier\r
+value MUST NOT exceed 2^32-1 (4294967295).  Sub-identifiers\r
+are separated by single dots and without any intermediate\r
+whitespace.\r
+\r
+The ASN.1 standard restricts the value space of the first\r
+sub-identifier to 0, 1, or 2.  Furthermore, the value space\r
+of the second sub-identifier is restricted to the range\r
+0 to 39 if the first sub-identifier is 0 or 1.  Finally,\r
+the ASN.1 standard requires that an object identifier\r
+has always at least two sub-identifiers.  The pattern\r
+captures these restrictions.\r
+\r
+Although the number of sub-identifiers is not limited,\r
+module designers should realize that there may be\r
+implementations that stick with the SMIv2 limit of 128\r
+sub-identifiers.\r
+This type is a superset of the SMIv2 OBJECT IDENTIFIER type\r
+since it is not restricted to 128 sub-identifiers.  Hence,\r
+this type SHOULD NOT be used to represent the SMIv2 OBJECT\r
+IDENTIFIER type; the object-identifier-128 type SHOULD be\r
+used instead.</text>\r
+    </description>\r
+    <reference>\r
+      <text>ISO9834-1: Information technology -- Open Systems\r
+Interconnection -- Procedures for the operation of OSI\r
+Registration Authorities: General procedures and top\r
+arcs of the ASN.1 Object Identifier tree</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="object-identifier-128">\r
+    <type name="object-identifier">\r
+      <pattern value="\d*(\.\d*){1,127}"/>\r
+    </type>\r
+    <description>\r
+      <text>This type represents object-identifiers restricted to 128\r
+sub-identifiers.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the OBJECT IDENTIFIER type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2\r
+          (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="yang-identifier">\r
+    <type name="string">\r
+      <length value="1..max"/>\r
+      <pattern value="[a-zA-Z_][a-zA-Z0-9\-_.]*"/>\r
+      <pattern value=".|..|[^xX].*|.[^mM].*|..[^lL].*"/>\r
+    </type>\r
+    <description>\r
+      <text>A YANG identifier string as defined by the 'identifier'\r
+rule in Section 12 of RFC 6020.  An identifier must\r
+start with an alphabetic character or an underscore\r
+followed by an arbitrary sequence of alphabetic or\r
+numeric characters, underscores, hyphens, or dots.\r
+\r
+A YANG identifier MUST NOT start with any possible\r
+combination of the lowercase or uppercase character\r
+sequence 'xml'.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 6020: YANG - A Data Modeling Language for the Network\r
+          Configuration Protocol (NETCONF)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="date-and-time">\r
+    <type name="string">\r
+      <pattern value="\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2})"/>\r
+    </type>\r
+    <description>\r
+      <text>The date-and-time type is a profile of the ISO 8601\r
+standard for representation of dates and times using the\r
+Gregorian calendar.  The profile is defined by the\r
+date-time production in Section 5.6 of RFC 3339.\r
+\r
+The date-and-time type is compatible with the dateTime XML\r
+schema type with the following notable exceptions:\r
+\r
+(a) The date-and-time type does not allow negative years.\r
+\r
+(b) The date-and-time time-offset -00:00 indicates an unknown\r
+    time zone (see RFC 3339) while -00:00 and +00:00 and Z\r
+    all represent the same time zone in dateTime.\r
+\r
+(c) The canonical format (see below) of data-and-time values\r
+    differs from the canonical format used by the dateTime XML\r
+    schema type, which requires all times to be in UTC using\r
+    the time-offset 'Z'.\r
+\r
+This type is not equivalent to the DateAndTime textual\r
+convention of the SMIv2 since RFC 3339 uses a different\r
+separator between full-date and full-time and provides\r
+higher resolution of time-secfrac.\r
+\r
+The canonical format for date-and-time values with a known time\r
+zone uses a numeric time zone offset that is calculated using\r
+the device's configured known offset to UTC time.  A change of\r
+the device's offset to UTC time will cause date-and-time values\r
+to change accordingly.  Such changes might happen periodically\r
+in case a server follows automatically daylight saving time\r
+(DST) time zone offset changes.  The canonical format for\r
+date-and-time values with an unknown time zone (usually\r
+referring to the notion of local time) uses the time-offset\r
+-00:00.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 3339: Date and Time on the Internet: Timestamps\r
+RFC 2579: Textual Conventions for SMIv2\r
+XSD-TYPES: XML Schema Part 2: Datatypes Second Edition</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="timeticks">\r
+    <type name="uint32"/>\r
+    <description>\r
+      <text>The timeticks type represents a non-negative integer that\r
+represents the time, modulo 2^32 (4294967296 decimal), in\r
+hundredths of a second between two epochs.  When a schema\r
+node is defined that uses this type, the description of\r
+the schema node identifies both of the reference epochs.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the TimeTicks type of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2578: Structure of Management Information Version 2\r
+          (SMIv2)</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="timestamp">\r
+    <type name="yang:timeticks"/>\r
+    <description>\r
+      <text>The timestamp type represents the value of an associated\r
+timeticks schema node at which a specific occurrence\r
+happened.  The specific occurrence must be defined in the\r
+description of any schema node defined using this type.  When\r
+the specific occurrence occurred prior to the last time the\r
+associated timeticks attribute was zero, then the timestamp\r
+value is zero.  Note that this requires all timestamp values\r
+to be reset to zero when the value of the associated timeticks\r
+attribute reaches 497+ days and wraps around to zero.\r
+\r
+The associated timeticks schema node must be specified\r
+in the description of any schema node using this type.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the TimeStamp textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2579: Textual Conventions for SMIv2</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="phys-address">\r
+    <type name="string">\r
+      <pattern value="([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?"/>\r
+    </type>\r
+    <description>\r
+      <text>Represents media- or physical-level addresses represented\r
+as a sequence octets, each octet represented by two hexadecimal\r
+numbers.  Octets are separated by colons.  The canonical\r
+representation uses lowercase characters.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the PhysAddress textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 2579: Textual Conventions for SMIv2</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="mac-address">\r
+    <type name="string">\r
+      <pattern value="[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}"/>\r
+    </type>\r
+    <description>\r
+      <text>The mac-address type represents an IEEE 802 MAC address.\r
+The canonical representation uses lowercase characters.\r
+\r
+In the value set and its semantics, this type is equivalent\r
+to the MacAddress textual convention of the SMIv2.</text>\r
+    </description>\r
+    <reference>\r
+      <text>IEEE 802: IEEE Standard for Local and Metropolitan Area\r
+          Networks: Overview and Architecture\r
+RFC 2579: Textual Conventions for SMIv2</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="xpath1.0">\r
+    <type name="string"/>\r
+    <description>\r
+      <text>This type represents an XPATH 1.0 expression.\r
+\r
+When a schema node is defined that uses this type, the\r
+description of the schema node MUST specify the XPath\r
+context in which the XPath expression is evaluated.</text>\r
+    </description>\r
+    <reference>\r
+      <text>XPATH: XML Path Language (XPath) Version 1.0</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="hex-string">\r
+    <type name="string">\r
+      <pattern value="([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?"/>\r
+    </type>\r
+    <description>\r
+      <text>A hexadecimal string with octets represented as hex digits\r
+separated by colons.  The canonical representation uses\r
+lowercase characters.</text>\r
+    </description>\r
+  </typedef>\r
+  <typedef name="uuid">\r
+    <type name="string">\r
+      <pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"/>\r
+    </type>\r
+    <description>\r
+      <text>A Universally Unique IDentifier in the string representation\r
+defined in RFC 4122.  The canonical representation uses\r
+lowercase characters.\r
+\r
+The following is an example of a UUID in string representation:\r
+f81d4fae-7dec-11d0-a765-00a0c91e6bf6\r
+</text>\r
+    </description>\r
+    <reference>\r
+      <text>RFC 4122: A Universally Unique IDentifier (UUID) URN\r
+          Namespace</text>\r
+    </reference>\r
+  </typedef>\r
+  <typedef name="dotted-quad">\r
+    <type name="string">\r
+      <pattern value="(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"/>\r
+    </type>\r
+    <description>\r
+      <text>An unsigned 32-bit number expressed in the dotted-quad\r
+notation, i.e., four octets written as decimal numbers\r
+and separated with the '.' (full stop) character.</text>\r
+    </description>\r
+  </typedef>\r
+</module>\r
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/main-impl.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/main-impl.yin
new file mode 100644 (file)
index 0000000..5a684ec
--- /dev/null
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<module name="main-impl"\r
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1"\r
+        xmlns:main-impl="urn:opendaylight:l2switch:main-impl"\r
+        xmlns:config="urn:opendaylight:params:xml:ns:yang:controller:config"\r
+        xmlns:mdsal="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">\r
+  <yang-version value="1"/>\r
+  <namespace uri="urn:opendaylight:l2switch:main-impl"/>\r
+  <prefix value="main-impl"/>\r
+  <import module="config">\r
+    <prefix value="config"/>\r
+    <revision-date date="2013-04-05"/>\r
+  </import>\r
+  <import module="opendaylight-md-sal-binding">\r
+    <prefix value="mdsal"/>\r
+    <revision-date date="2013-10-28"/>\r
+  </import>\r
+  <description>\r
+    <text>This module contains the base YANG definitions for\r
+main-impl implementation.</text>\r
+  </description>\r
+  <revision date="2014-05-28">\r
+    <description>\r
+      <text>Initial module draft.</text>\r
+    </description>\r
+  </revision>\r
+  <identity name="main-impl">\r
+    <base name="config:module-type"/>\r
+    <config:java-name-prefix java-prefix="L2SwitchMain"/>\r
+  </identity>\r
+  <augment target-node="/config:modules/config:module/config:configuration">\r
+    <case name="main-impl">\r
+      <when condition="/config:modules/config:module/config:type = 'main-impl'"/>\r
+      <leaf name="is-learning-only-mode">\r
+        <type name="boolean"/>\r
+      </leaf>\r
+      <leaf name="is-install-dropall-flow">\r
+        <type name="boolean"/>\r
+      </leaf>\r
+      <leaf name="dropall-flow-table-id">\r
+        <type name="uint8"/>\r
+      </leaf>\r
+      <leaf name="dropall-flow-priority">\r
+        <type name="uint16"/>\r
+      </leaf>\r
+      <leaf name="dropall-flow-hard-timeout">\r
+        <type name="uint16"/>\r
+      </leaf>\r
+      <leaf name="dropall-flow-idle-timeout">\r
+        <type name="uint16"/>\r
+      </leaf>\r
+      <leaf name="reactive-flow-table-id">\r
+        <type name="uint8"/>\r
+      </leaf>\r
+      <leaf name="reactive-flow-priority">\r
+        <type name="uint16"/>\r
+      </leaf>\r
+      <leaf name="reactive-flow-hard-timeout">\r
+        <type name="uint16"/>\r
+      </leaf>\r
+      <leaf name="reactive-flow-idle-timeout">\r
+        <type name="uint16"/>\r
+      </leaf>\r
+      <container name="notification-service">\r
+        <uses name="config:service-ref">\r
+          <refine target-node="type">\r
+            <mandatory value="true"/>\r
+            <config:required-identity name="mdsal:binding-notification-service"/>\r
+          </refine>\r
+        </uses>\r
+      </container>\r
+      <container name="data-broker">\r
+        <uses name="config:service-ref">\r
+          <refine target-node="type">\r
+            <mandatory value="false"/>\r
+            <config:required-identity name="mdsal:binding-async-data-broker"/>\r
+          </refine>\r
+        </uses>\r
+      </container>\r
+      <container name="rpc-registry">\r
+        <uses name="config:service-ref">\r
+          <refine target-node="type">\r
+            <mandatory value="true"/>\r
+            <config:required-identity name="mdsal:binding-rpc-registry"/>\r
+          </refine>\r
+        </uses>\r
+      </container>\r
+    </case>\r
+  </augment>\r
+</module>\r
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/opendaylight-md-sal-binding.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/modules/opendaylight-md-sal-binding.yin
new file mode 100644 (file)
index 0000000..7882d14
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<module name="opendaylight-md-sal-binding"\r
+        xmlns="urn:ietf:params:xml:ns:yang:yin:1"\r
+        xmlns:md-sal-binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding"\r
+        xmlns:config="urn:opendaylight:params:xml:ns:yang:controller:config">\r
+  <yang-version value="1"/>\r
+  <namespace uri="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding"/>\r
+  <prefix value="md-sal-binding"/>\r
+  <import module="config">\r
+    <prefix value="config"/>\r
+    <revision-date date="2013-04-05"/>\r
+  </import>\r
+  <description>\r
+    <text>Service definition for Binding Aware MD-SAL.</text>\r
+  </description>\r
+  <revision date="2013-10-28">\r
+    <description>\r
+      <text>Initial revision</text>\r
+    </description>\r
+  </revision>\r
+  <identity name="binding-broker-osgi-registry">\r
+    <base name="config:service-type"/>\r
+    <config:java-class name="org.opendaylight.controller.sal.binding.api.BindingAwareBroker"/>\r
+  </identity>\r
+  <identity name="binding-data-broker">\r
+    <base name="config:service-type"/>\r
+    <config:java-class name="org.opendaylight.controller.sal.binding.api.data.DataProviderService"/>\r
+  </identity>\r
+  <identity name="binding-async-data-broker">\r
+    <base name="config:service-type"/>\r
+    <config:java-class name="org.opendaylight.controller.md.sal.binding.api.DataBroker"/>\r
+  </identity>\r
+  <identity name="binding-data-consumer-broker">\r
+    <base name="config:service-type"/>\r
+    <config:java-class name="org.opendaylight.controller.sal.binding.api.data.DataBrokerService"/>\r
+  </identity>\r
+  <identity name="binding-rpc-registry">\r
+    <base name="config:service-type"/>\r
+    <config:java-class name="org.opendaylight.controller.sal.binding.api.RpcProviderRegistry"/>\r
+  </identity>\r
+  <identity name="binding-notification-service">\r
+    <base name="config:service-type"/>\r
+    <config:java-class name="org.opendaylight.controller.sal.binding.api.NotificationProviderService"/>\r
+  </identity>\r
+  <identity name="binding-notification-subscription-service">\r
+    <base name="config:service-type"/>\r
+    <config:java-class name="org.opendaylight.controller.sal.binding.api.NotificationService"/>\r
+  </identity>\r
+</module>\r
diff --git a/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/test.yin b/yang/yang-parser-rfc7950/src/test/resources/semantic-statement-parser/yin/test.yin
new file mode 100644 (file)
index 0000000..0bdd45b
--- /dev/null
@@ -0,0 +1,402 @@
+<module xmlns="urn:ietf:params:xml:ns:yang:yin:1" name="my-test">
+    <yang-version value="1"></yang-version>
+    <namespace uri="urn:ietf:params:xml:ns:yang:ietf-inet-types"></namespace>
+    <prefix value="inet"></prefix>
+
+    <organization><text>IETF NETMOD (NETCONF Data Modeling Language) Working Group"></text></organization>
+    <contact><text>WG Web:   &lt;http://tools.ietf.org/wg/netmod/&gt;
+    WG List:  &lt;mailto:netmod@ietf.org&gt;
+
+    WG Chair: David Partain
+              &lt;mailto:david.partain@ericsson.com&gt;
+
+    WG Chair: David Kessens
+              &lt;mailto:david.kessens@nsn.com&gt;
+
+    Editor:   Juergen Schoenwaelder
+              &lt;mailto:j.schoenwaelder@jacobs-university.de&gt;</text></contact>
+
+    <description><text>This module contains a collection of generally useful derived
+    YANG data types for Internet addresses and related things.
+
+    Copyright (c) 2010 IETF Trust and the persons identified as
+    authors of the code.  All rights reserved.
+
+
+
+    Redistribution and use in source and binary forms, with or without
+    modification, is permitted pursuant to, and subject to the license
+    terms contained in, the Simplified BSD License set forth in Section
+    4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+    (http://trustee.ietf.org/license-info).
+
+    This version of this YANG module is part of RFC 6021; see
+    the RFC itself for full legal notices.</text></description>
+
+    <reference><text>RFC 6021: Common YANG Data Types</text></reference>
+    <revision date="2010-09-24"></revision>
+
+    <typedef name="as-number">
+        <type name="uint32"></type>
+        <status value="current"></status>
+        <description><text>The as-number type represents autonomous system numbers
+      which identify an Autonomous System (AS).  An AS is a set
+      of routers under a single technical administration, using
+      an interior gateway protocol and common metrics to route
+      packets within the AS, and using an exterior gateway
+      protocol to route packets to other ASs'.  IANA maintains
+      the AS number space and has delegated large parts to the
+      regional registries.
+
+      Autonomous system numbers were originally limited to 16
+      bits.  BGP extensions have enlarged the autonomous system
+      number space to 32 bits.  This type therefore uses an uint32
+      base type without a range restriction in order to support
+      a larger autonomous system number space.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetAutonomousSystemNumber textual convention of
+      the SMIv2.</text></description>
+
+        <reference><text>RFC 1930: Guidelines for creation, selection, and registration
+                of an Autonomous System (AS)
+      RFC 4271: A Border Gateway Protocol 4 (BGP-4)
+      RFC 4893: BGP Support for Four-octet AS Number Space
+      RFC 4001: Textual Conventions for Internet Network Addresses</text></reference>
+    </typedef>
+
+    <typedef name="domain-name">
+        <type name="string">
+            <length value="1..253">
+                <error-message><value>The argument is out of bounds &lt;1, 253&gt;</value></error-message>
+                <error-app-tag value="length-out-of-specified-bounds"></error-app-tag>
+            </length>
+            <pattern
+                    value="^((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)|\.$">
+                <error-app-tag value="invalid-regular-expression"></error-app-tag>
+            </pattern>
+        </type>
+        <status value="current"></status>
+
+        <description><text>The domain-name type represents a DNS domain name.  The
+      name SHOULD be fully qualified whenever possible.
+
+      Internet domain names are only loosely specified.  Section
+      3.5 of RFC 1034 recommends a syntax (modified in Section
+      2.1 of RFC 1123).  The pattern above is intended to allow
+      for current practice in domain name use, and some possible
+      future expansion.  It is designed to hold various types of
+      domain names, including names used for A or AAAA records
+      (host names) and other records, such as SRV records.  Note
+      that Internet host names have a stricter syntax (described
+      in RFC 952) than the DNS recommendations in RFCs 1034 and
+      1123, and that systems that want to store host names in
+      schema nodes using the domain-name type are recommended to
+      adhere to this stricter standard to ensure interoperability.
+
+      The encoding of DNS names in the DNS protocol is limited
+      to 255 characters.  Since the encoding consists of labels
+      prefixed by a length bytes and there is a trailing NULL
+      byte, only 253 characters can appear in the textual dotted
+      notation.
+
+      The description clause of schema nodes using the domain-name
+      type MUST describe when and how these names are resolved to
+      IP addresses.  Note that the resolution of a domain-name value
+      may require to query multiple DNS records (e.g., A for IPv4
+      and AAAA for IPv6).  The order of the resolution process and
+      which DNS record takes precedence can either be defined
+      explicitely or it may depend on the configuration of the
+      resolver.
+
+      Domain-name values use the US-ASCII encoding.  Their canonical
+      format uses lowercase US-ASCII characters.  Internationalized
+      domain names MUST be encoded in punycode as described in RFC
+      3492</text></description>
+
+        <reference><text>RFC  952: DoD Internet Host Table Specification
+      RFC 1034: Domain Names - Concepts and Facilities
+      RFC 1123: Requirements for Internet Hosts -- Application
+                and Support
+      RFC 2782: A DNS RR for specifying the location of services
+                (DNS SRV)
+      RFC 3492: Punycode: A Bootstring encoding of Unicode for
+                Internationalized Domain Names in Applications
+                (IDNA)
+      RFC 5891: Internationalizing Domain Names in Applications
+                (IDNA): Protocol</text></reference>
+
+    </typedef>
+
+    <typedef name="dscp">
+        <type name="uint8">
+            <range value="0..63">
+                <error-message><value>The argument is out of bounds &lt;0, 63&gt;</value></error-message>
+                <error-app-tag value="range-out-of-specified-bounds"></error-app-tag>
+            </range>
+        </type>
+        <status value="current"></status>
+        <description><text>The dscp type represents a Differentiated Services Code-Point
+      that may be used for marking packets in a traffic stream.
+
+      In the value set and its semantics, this type is equivalent
+      to the Dscp textual convention of the SMIv2.</text></description>
+        <reference><text>RFC 3289: Management Information Base for the Differentiated
+                Services Architecture
+      RFC 2474: Definition of the Differentiated Services Field
+                (DS Field) in the IPv4 and IPv6 Headers
+      RFC 2780: IANA Allocation Guidelines For Values In
+                the Internet Protocol and Related Headers</text></reference>
+    </typedef>
+    <typedef name="host">
+        <type name="union">
+            <type name="ip-address"></type>
+            <type name="domain-name"></type>
+        </type>
+        <status value="current"></status>
+        <description><text>The host type represents either an IP address or a DNS
+      domain name.</text></description>
+    </typedef>
+    <typedef name="ip-address">
+        <type name="union">
+            <type name="ipv4-address"></type>
+            <type name="ipv6-address"></type>
+        </type>
+        <status value="current"></status>
+        <description><text>The ip-address type represents an IP address and is IP
+      version neutral.  The format of the textual representations
+      implies the IP version.</text></description>
+    </typedef>
+    <typedef name="ip-prefix">
+        <type name="union">
+            <type name="ipv4-prefix"></type>
+            <type name="ipv6-prefix"></type>
+        </type>
+        <status value="current"></status>
+        <description><text>The ip-prefix type represents an IP prefix and is IP
+      version neutral.  The format of the textual representations
+      implies the IP version.</text></description>
+    </typedef>
+    <typedef name="ip-version">
+        <type name="enumeration">
+            <enum name="unknown">
+                <value value="0"></value>
+                <description><text>An unknown or unspecified version of the Internet protocol.</text></description>
+            </enum>
+            <enum name="ipv4">
+                <value value="1"></value>
+                <description><text>The IPv4 protocol as defined in RFC 791.</text></description>
+            </enum>
+            <enum name="ipv6">
+                <value value="2"></value>
+                <description><text>The IPv6 protocol as defined in RFC 2460.</text></description>
+            </enum>
+        </type>
+        <status value="current"></status>
+        <description><text>This value represents the version of the IP protocol.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetVersion textual convention of the SMIv2.</text></description>
+        <reference><text>RFC  791: Internet Protocol
+      RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+      RFC 4001: Textual Conventions for Internet Network Addresses</text></reference>
+    </typedef>
+    <typedef name="ipv4-address">
+        <type name="string">
+            <pattern
+                    value="^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(%[\p{N}\p{L}]+)?$">
+                <error-app-tag value="invalid-regular-expression"></error-app-tag>
+            </pattern>
+        </type>
+        <status value="current"></status>
+        <description><text>The ipv4-address type represents an IPv4 address in
+       dotted-quad notation.  The IPv4 address may include a zone
+       index, separated by a % sign.
+
+       The zone index is used to disambiguate identical address
+       values.  For link-local addresses, the zone index will
+       typically be the interface index number or the name of an
+       interface.  If the zone index is not present, the default
+       zone of the device will be used.
+
+       The canonical format for the zone index is the numerical
+       format</text></description>
+    </typedef>
+    <typedef name="ipv4-prefix">
+        <type name="string">
+            <pattern
+                    value="^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])/(([0-9])|([1-2][0-9])|(3[0-2]))$">
+                <error-app-tag value="invalid-regular-expression"></error-app-tag>
+            </pattern>
+        </type>
+        <status value="current"></status>
+        <description><text>The ipv4-prefix type represents an IPv4 address prefix.
+      The prefix length is given by the number following the
+      slash character and must be less than or equal to 32.
+
+
+
+      A prefix length value of n corresponds to an IP address
+      mask that has n contiguous 1-bits from the most
+      significant bit (MSB) and all other bits set to 0.
+
+      The canonical format of an IPv4 prefix has all bits of
+      the IPv4 address set to zero that are not part of the
+      IPv4 prefix.</text></description>
+    </typedef>
+    <typedef name="ipv6-address">
+        <type name="string">
+            <pattern
+                    value="^((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))(%[\p{N}\p{L}]+)?$">
+                <error-app-tag value="invalid-regular-expression"></error-app-tag>
+            </pattern>
+            <pattern value="^(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)(%.+)?$">
+                <error-app-tag value="invalid-regular-expression"></error-app-tag>
+            </pattern>
+        </type>
+        <status value="current"></status>
+        <description><text>The ipv6-address type represents an IPv6 address in full,
+      mixed, shortened, and shortened-mixed notation.  The IPv6
+      address may include a zone index, separated by a % sign.
+
+
+
+
+
+      The zone index is used to disambiguate identical address
+      values.  For link-local addresses, the zone index will
+      typically be the interface index number or the name of an
+      interface.  If the zone index is not present, the default
+      zone of the device will be used.
+
+      The canonical format of IPv6 addresses uses the compressed
+      format described in RFC 4291, Section 2.2, item 2 with the
+      following additional rules: the :: substitution must be
+      applied to the longest sequence of all-zero 16-bit chunks
+      in an IPv6 address.  If there is a tie, the first sequence
+      of all-zero 16-bit chunks is replaced by ::.  Single
+      all-zero 16-bit chunks are not compressed.  The canonical
+      format uses lowercase characters and leading zeros are
+      not allowed.  The canonical format for the zone index is
+      the numerical format as described in RFC 4007, Section
+      11.2.</text></description>
+        <reference><text>RFC 4291: IP Version 6 Addressing Architecture
+      RFC 4007: IPv6 Scoped Address Architecture
+      RFC 5952: A Recommendation for IPv6 Address Text Representation</text></reference>
+    </typedef>
+    <typedef name="ipv6-flow-label">
+        <type name="uint32">
+            <range value="0..1048575">
+                <error-message><value>The argument is out of bounds &lt;0, 1048575&gt;</value></error-message>
+                <error-app-tag value="range-out-of-specified-bounds"></error-app-tag>
+            </range>
+        </type>
+        <status value="current"></status>
+        <description><text>The flow-label type represents flow identifier or Flow Label
+      in an IPv6 packet header that may be used to discriminate
+      traffic flows.
+
+      In the value set and its semantics, this type is equivalent
+      to the IPv6FlowLabel textual convention of the SMIv2.</text></description>
+        <reference><text>RFC 3595: Textual Conventions for IPv6 Flow Label
+      RFC 2460: Internet Protocol, Version 6 (IPv6) Specification</text></reference>
+    </typedef>
+    <typedef name="ipv6-prefix">
+        <type name="string">
+            <pattern
+                    value="^((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))$">
+                <error-app-tag value="invalid-regular-expression"></error-app-tag>
+            </pattern>
+            <pattern value="^(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)(/.+)$">
+                <error-app-tag value="invalid-regular-expression"></error-app-tag>
+            </pattern>
+        </type>
+        <status value="current"></status>
+        <description><text>The ipv6-prefix type represents an IPv6 address prefix.
+      The prefix length is given by the number following the
+      slash character and must be less than or equal 128.
+
+      A prefix length value of n corresponds to an IP address
+      mask that has n contiguous 1-bits from the most
+      significant bit (MSB) and all other bits set to 0.
+
+      The IPv6 address should have all bits that do not belong
+      to the prefix set to zero.
+
+      The canonical format of an IPv6 prefix has all bits of
+      the IPv6 address set to zero that are not part of the
+      IPv6 prefix.  Furthermore, IPv6 address is represented
+      in the compressed format described in RFC 4291, Section
+      2.2, item 2 with the following additional rules: the ::
+      substitution must be applied to the longest sequence of
+      all-zero 16-bit chunks in an IPv6 address.  If there is
+      a tie, the first sequence of all-zero 16-bit chunks is
+      replaced by ::.  Single all-zero 16-bit chunks are not
+      compressed.  The canonical format uses lowercase
+      characters and leading zeros are not allowed.</text></description>
+        <reference><text>RFC 4291: IP Version 6 Addressing Architecture</text></reference>
+    </typedef>
+    <typedef name="port-number">
+        <type name="uint16">
+            <range value="0..65535">
+                <error-message><value>The argument is out of bounds &lt;0, 65535&gt;</value></error-message>
+                <error-app-tag value="range-out-of-specified-bounds"></error-app-tag>
+            </range>
+        </type>
+        <status value="current"></status>
+        <description><text>The port-number type represents a 16-bit port number of an
+      Internet transport layer protocol such as UDP, TCP, DCCP, or
+      SCTP.  Port numbers are assigned by IANA.  A current list of
+      all assignments is available from &lt;http://www.iana.org/&gt;.
+
+      Note that the port number value zero is reserved by IANA.  In
+      situations where the value zero does not make sense, it can
+      be excluded by subtyping the port-number type.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetPortNumber textual convention of the SMIv2.</text></description>
+        <reference><text>RFC  768: User Datagram Protocol
+      RFC  793: Transmission Control Protocol
+      RFC 4960: Stream Control Transmission Protocol
+      RFC 4340: Datagram Congestion Control Protocol (DCCP)
+      RFC 4001: Textual Conventions for Internet Network Addresses</text></reference>
+    </typedef>
+    <typedef name="uri">
+        <type name="string"></type>
+        <status value="current"></status>
+        <description><text>The uri type represents a Uniform Resource Identifier
+      (URI) as defined by STD 66.
+
+      Objects using the uri type MUST be in US-ASCII encoding,
+      and MUST be normalized as described by RFC 3986 Sections
+      6.2.1, 6.2.2.1, and 6.2.2.2.  All unnecessary
+      percent-encoding is removed, and all case-insensitive
+      characters are set to lowercase except for hexadecimal
+      digits, which are normalized to uppercase as described in
+      Section 6.2.2.1.
+
+      The purpose of this normalization is to help provide
+      unique URIs.  Note that this normalization is not
+      sufficient to provide uniqueness.  Two URIs that are
+      textually distinct after this normalization may still be
+      equivalent.
+
+      Objects using the uri type may restrict the schemes that
+      they permit.  For example, 'data:' and 'urn:' schemes
+      might not be appropriate.
+
+      A zero-length URI is not a valid URI.  This can be used to
+      express 'URI absent' where required.
+
+      In the value set and its semantics, this type is equivalent
+      to the Uri SMIv2 textual convention defined in RFC 5017.</text></description>
+        <reference><text>RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
+      RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
+                Group: Uniform Resource Identifiers (URIs), URLs,
+                and Uniform Resource Names (URNs): Clarifications
+                and Recommendations
+      RFC 5017: MIB Textual Conventions for Uniform Resource
+                Identifiers (URIs)</text></reference>
+    </typedef>
+
+</module>
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/simple-test/simple-nodes.yang b/yang/yang-parser-rfc7950/src/test/resources/simple-test/simple-nodes.yang
new file mode 100644 (file)
index 0000000..dd44975
--- /dev/null
@@ -0,0 +1,153 @@
+module simple-nodes {
+    yang-version 1.1;
+    namespace "urn:opendaylight:"+
+        "simple-nodes";
+    prefix "sn";
+
+    organization "opendaylight";
+
+    description
+        "test file containing yang data nodes";
+
+    revision "2013-07-30" {
+        description
+            "Initial revision.";
+        reference "will be defined";
+    }
+
+    typedef mytype {
+        type string;
+    }
+
+    // NOTE: simple comment
+
+    feature has-name;
+
+    anyxml data {
+        config false;
+        description "anyxml desc";
+        if-feature has-name;
+        mandatory true;
+        must "ifType != 'ethernet' or " +
+              "(ifType = 'ethernet' and ifMTU = 1500)" {
+             error-message "An ethernet MTU must be 1500";
+        }
+        must "ifType != 'atm' or " +
+              "(ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)" {
+             error-app-tag "anyxml data error-app-tag";
+             error-message "An atm MTU must be  64 .. 17966";
+             description "an error occured in data";
+             reference "data must ref";
+        }
+        reference "data ref";
+        status obsolete;
+        when "class != 'wheel'";
+    }
+
+    anydata data2 {
+        config false;
+        description "anydata desc";
+        if-feature has-name;
+        mandatory true;
+        must "ifType != 'ethernet' or " +
+              "(ifType = 'ethernet' and ifMTU = 1500)" {
+             error-message "An ethernet MTU must be 1500";
+        }
+        must "ifType != 'atm' or " +
+              "(ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)" {
+             error-app-tag "anydata data error-app-tag";
+             error-message "An atm MTU must be  64 .. 17966";
+             description "an error occured in data";
+             reference "data must ref";
+        }
+        reference "data ref";
+        status obsolete;
+        when "class != 'wheel'";
+    }
+
+    container nodes {
+        anyxml text;
+        choice level;
+        config false;
+        container node;
+        description "nodes collection";
+        grouping node-group {
+            leaf source {
+                type int8;
+            }
+            leaf target {
+                type int16;
+            }
+        }
+        if-feature has-name;
+        leaf nodes-id {
+            type int32;
+        }
+        leaf-list added {
+            type mytype;
+        }
+        list links {
+        }
+        must "ifType != 'atm' or " +
+              "(ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)" {
+             error-app-tag "anyxml data error-app-tag";
+             error-message "An atm MTU must be  64 .. 17966";
+             description "an error occured in data";
+             reference "data must ref";
+        }
+        must "ifId != 0";
+        presence "required";
+        reference "nodes ref";
+        status current;
+        typedef nodes-type {
+            type string;
+        }
+        uses node-group;
+        when "class != 'wheel'";
+    }
+
+    list links {
+        anyxml text;
+        choice level;
+        config false;
+        container link;
+        description "links collection";
+        grouping link-group {
+            leaf source {
+                type int8;
+            }
+            leaf target {
+                type int16;
+            }
+        }
+        if-feature has-name;
+        key links-id;
+        leaf links-id {
+            type int32;
+        }
+        leaf-list added {
+            type int64;
+        }
+        list nodes {
+        }
+        max-elements 100;
+        min-elements 1;
+        must "ifType != 'atm' or " +
+              "(ifType = 'atm' and ifMTU <= 17966 and ifMTU >= 64)" {
+             error-app-tag "anyxml data error-app-tag";
+             error-message "An atm MTU must be  64 .. 17966";
+             description "an error occured in data";
+             reference "data must ref";
+        }
+        must "ifId != 0";
+        ordered-by user;
+        reference "links ref";
+        status current;
+        typedef nodes-type {
+            type string;
+        }
+        uses link-group;
+        when "class != 'wheel'";
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m1.yang b/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m1.yang
new file mode 100644 (file)
index 0000000..48c5e9b
--- /dev/null
@@ -0,0 +1,15 @@
+module m1 {
+
+    yang-version 1;
+    namespace "urn:test.m1";
+    prefix "m1";
+
+    import m3 {
+        prefix "m3";
+        revision-date 2014-01-13;
+    }
+
+    revision 2014-01-13 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m2.yang b/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m2.yang
new file mode 100644 (file)
index 0000000..0d6942a
--- /dev/null
@@ -0,0 +1,10 @@
+module m2 {
+
+    yang-version 1;
+    namespace "urn:test.m2";
+    prefix "m2";
+
+    revision 2014-01-13 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m3.yang b/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m3.yang
new file mode 100644 (file)
index 0000000..fd8f2da
--- /dev/null
@@ -0,0 +1,15 @@
+module m3 {
+
+    yang-version 1;
+    namespace "urn:test.m3";
+    prefix "m3";
+
+    import m5 {
+        prefix "m5";
+        revision-date 2014-01-13;
+    }
+
+    revision 2014-01-13 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m4.yang b/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m4.yang
new file mode 100644 (file)
index 0000000..117f36e
--- /dev/null
@@ -0,0 +1,15 @@
+module m4 {
+
+    yang-version 1;
+    namespace "urn:test.m4";
+    prefix "m4";
+
+    import m2 {
+        prefix "m2";
+        revision-date 2014-01-13;
+    }
+
+    revision 2014-01-13 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m5.yang b/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m5.yang
new file mode 100644 (file)
index 0000000..843cb52
--- /dev/null
@@ -0,0 +1,15 @@
+module m5 {
+
+    yang-version 1;
+    namespace "urn:test.m5";
+    prefix "m5";
+
+    import m7 {
+        prefix "m7";
+        revision-date 2014-01-13;
+    }
+
+    revision 2014-01-13 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m6.yang b/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m6.yang
new file mode 100644 (file)
index 0000000..26b0877
--- /dev/null
@@ -0,0 +1,15 @@
+module m6 {
+
+    yang-version 1;
+    namespace "urn:test.m6";
+    prefix "m6";
+
+    import m4 {
+        prefix "m4";
+        revision-date 2014-01-13;
+    }
+
+    revision 2014-01-13 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m7.yang b/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m7.yang
new file mode 100644 (file)
index 0000000..b78c40d
--- /dev/null
@@ -0,0 +1,15 @@
+module m7 {
+
+    yang-version 1;
+    namespace "urn:test.m7";
+    prefix "m7";
+
+    import m8 {
+        prefix "m8";
+        revision-date 2014-01-13;
+    }
+
+    revision 2014-01-13 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m8.yang b/yang/yang-parser-rfc7950/src/test/resources/sorting-test/m8.yang
new file mode 100644 (file)
index 0000000..6518a35
--- /dev/null
@@ -0,0 +1,15 @@
+module m8 {
+
+    yang-version 1;
+    namespace "urn:test.m8";
+    prefix "m8";
+
+    import m6 {
+        prefix "m6";
+        revision-date 2014-01-13;
+    }
+
+    revision 2014-01-13 {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/aug-root.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/aug-root.yang
new file mode 100644 (file)
index 0000000..42ced8d
--- /dev/null
@@ -0,0 +1,37 @@
+module aug-root {
+    namespace root;
+    prefix root;
+
+    import augmented { prefix aug; }
+
+    augment "/aug:aug-parent1/aug:aug-parent2/aug:cont-target" {
+        container cont-added1 {
+            list list1 {
+            }
+        }
+
+        container cont-added2 {
+            anyxml axml;
+        }
+
+        uses aug:grp;
+    }
+
+    grouping grp2 {
+        container grp-cont2 {
+            container grp-cont22 {
+            }
+        }
+    }
+
+    container aug-cont1 {
+        container aug-cont2 {
+            uses grp2 {
+                augment "grp-cont2/grp-cont22" {
+                    container grp-add {
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/augmented.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/augmented.yang
new file mode 100644 (file)
index 0000000..6ff2422
--- /dev/null
@@ -0,0 +1,17 @@
+module augmented {
+    namespace aug;
+    prefix aug;
+
+    container aug-parent1 {
+        container aug-parent2 {
+            container cont-target {
+            }
+        }
+    }
+
+    grouping grp {
+        container cont-grp {
+            anyxml axml-grp;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-imported.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-imported.yang
new file mode 100644 (file)
index 0000000..26ec3c8
--- /dev/null
@@ -0,0 +1,51 @@
+module multiple-augment-imported {
+
+    namespace "multiple-augment-imported";
+    prefix imp;
+
+
+    container root-container {
+        uses grp1 {
+            augment container-from-grp1/sub-container-from-grp1 {
+                container container-in-uses-augment {
+                }
+            }
+        }
+    }
+
+    grouping grp1 {
+        container container-from-grp1 {
+            container sub-container-from-grp1 {
+                uses grp2 {
+                    augment container-from-grp2/sub-container-from-grp2 {
+                        container augmented-container-in-uses-grp2;
+                    }
+                }
+            }
+        }
+    }
+
+    grouping grp2 {
+        container container-from-grp2 {
+            container sub-container-from-grp2 {
+            }
+        }
+        uses grp3;
+    }
+
+    grouping grp3 {
+        container container-from-grp3 {
+        }
+    }
+
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-in-uses-augment {
+        container sub-container-from-augment2 {
+        }
+    }
+
+    grouping grp-from-import {
+        container container-from-grp-from-import {
+
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-incorrect.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-incorrect.yang
new file mode 100644 (file)
index 0000000..61ad3e3
--- /dev/null
@@ -0,0 +1,80 @@
+module multiple-augment-incorrect {
+
+    namespace "multiple-augment-test";
+    prefix mpa;
+    
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-in-uses-augment/sub-container-from-augment2/sub-container-from-augment3 {
+        container sub-container-from-augment4 {
+        }
+    }
+    
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-in-uses-augment/sub-container-from-augment2 {
+        container sub-container-from-augment3 {
+        }
+    }
+
+    augment /root-container/container-from-grp1/sub-container-from-grp1 {
+        container sub-container-from-augment {
+        }
+    }
+
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-from-grp2/sub-container-from-grp2/augmented-container-in-uses-grp2 {
+        container sub-container-from-augment6 {
+        }
+    }
+    
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-from-grp2/sub-container-from-grp2/sub-container-from-augment5 {
+        container sub-container-from-augment7 {
+        }
+    }
+    
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-from-grp2/sub-container-from-grp2 {
+        container sub-container-from-augment5 {
+        }
+    }
+
+    augment /root-container/added-container-1 {
+        container added-container-2 {
+        }
+    }
+
+    augment /root-container {
+        container added-container-1 {
+        }
+    }
+
+    grouping grp1 {
+        container container-from-grp1 {
+            container sub-container-from-grp1 {
+                uses grp2 {
+                    augment container-from-grp2/sub-container-from-grp2 {
+                        container augmented-container-in-uses-grp2;
+                    }
+                }
+            }
+        }
+    }
+
+    grouping grp2 {
+        container container-from-grp2 {
+            container sub-container-from-grp2 {
+            }
+        }
+        uses grp3;
+    }
+    
+    grouping grp3 {
+      container container-from-grp3 {
+          
+      }
+    }
+    
+    container root-container {
+        uses grp1 {
+            augment container-from-grp1/sub-container-from-grp1 {
+                container container-in-uses-augment {
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-incorrect2.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-incorrect2.yang
new file mode 100644 (file)
index 0000000..c0e51c8
--- /dev/null
@@ -0,0 +1,89 @@
+module multiple-augment-incorrect2 {
+
+    namespace "multiple-augment-test";
+    prefix mpa;
+
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-in-uses-augment/sub-container-from-augment2/sub-container-from-augment3 {
+        container sub-container-from-augment4 {
+        }
+    }
+
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-in-uses-augment/sub-container-from-augment2 {
+        container sub-container-from-augment3 {
+        }
+    }
+
+    augment /root-container/container-from-grp1/sub-container-from-grp1 {
+        container sub-container-from-augment {
+        }
+    }
+
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-from-grp2/sub-container-from-grp2/augmented-container-in-uses-grp2 {
+        container sub-container-from-augment6 {
+        }
+    }
+
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-from-grp2/sub-container-from-grp2/sub-container-from-augment5 {
+        container sub-container-from-augment7 {
+        }
+    }
+
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-from-grp2/sub-container-from-grp2 {
+        container sub-container-from-augment5 {
+        }
+    }
+
+    augment /root-container/added-container-1 {
+        container added-container-2 {
+        }
+    }
+
+    augment /root-container {
+        container added-container-1 {
+        }
+    }
+
+    grouping grp1 {
+        container container-from-grp1 {
+            container sub-container-from-grp1 {
+                uses grp2 {
+                    augment container-from-grp2/sub-container-from-grp2 {
+                        container augmented-container-in-uses-grp2;
+                    }
+                }
+            }
+        }
+    }
+
+    grouping grp2 {
+        container container-from-grp2 {
+            container sub-container-from-grp2 {
+            }
+        }
+        uses grp3;
+    }
+
+    grouping grp3 {
+        container container-from-grp3 {
+        }
+    }
+
+    container root-container {
+        uses grp1 {
+            augment container-from-grp1/sub-container-from-grp1 {
+                container container-in-uses-augment {
+                }
+            }
+        }
+    }
+    
+    container container-with-incorrect-uses {
+        uses grp4 {
+            augment unknown {
+                container new {
+                    
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-root.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-root.yang
new file mode 100644 (file)
index 0000000..d27d804
--- /dev/null
@@ -0,0 +1,54 @@
+module multiple-augment-root {
+
+    namespace "multiple-augment-root";
+    prefix root;
+
+    import multiple-augment-imported { prefix imp; }
+
+    include multiple-augment-submodule;
+
+    augment /imp:root-container/imp:container-from-grp1/imp:sub-container-from-grp1/imp:container-in-uses-augment/imp:sub-container-from-augment2 {
+        container sub-container-from-augment3 {
+        }
+    }
+
+    augment /imp:root-container/imp:container-from-grp1/imp:sub-container-from-grp1 {
+        container sub-container-from-augment {
+        }
+    }
+
+    augment /imp:root-container/imp:container-from-grp1/imp:sub-container-from-grp1/imp:container-from-grp2/imp:sub-container-from-grp2/imp:augmented-container-in-uses-grp2 {
+        container sub-container-from-augment6 {
+        }
+    }
+
+    augment /imp:root-container/imp:container-from-grp1/imp:sub-container-from-grp1/imp:container-from-grp2/imp:sub-container-from-grp2/sub-container-from-augment5 {
+        container sub-container-from-augment7 {
+        }
+    }
+
+    augment /imp:root-container/imp:container-from-grp1/imp:sub-container-from-grp1/imp:container-from-grp2/imp:sub-container-from-grp2 {
+        container sub-container-from-augment5 {
+        }
+    }
+
+    augment /imp:root-container {
+        container added-container-1 {
+        }
+    }
+
+    container container-with-multiple-uses {
+        uses imp:grp-from-import{
+            augment container-from-grp-from-import {
+                container new {
+                }
+            }
+        }
+        uses grp-from-include{
+            augment container-from-grp-from-include {
+                container new {
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-submodule.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-submodule.yang
new file mode 100644 (file)
index 0000000..ec5b70e
--- /dev/null
@@ -0,0 +1,11 @@
+submodule multiple-augment-submodule {
+    
+    belongs-to multiple-augment-root {
+     prefix rt;   
+    }
+    
+    grouping grp-from-include{
+        container container-from-grp-from-include {
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-test.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/augments/multiple-augment-test.yang
new file mode 100644 (file)
index 0000000..7912b7d
--- /dev/null
@@ -0,0 +1,85 @@
+module multiple-augment-test {
+
+    namespace "multiple-augment-test";
+    prefix mpa;
+    
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-in-uses-augment/sub-container-from-augment2/sub-container-from-augment3 {
+        container sub-container-from-augment4 {
+        }
+    }
+    
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-in-uses-augment/sub-container-from-augment2 {
+        container sub-container-from-augment3 {
+        }
+    }
+
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-in-uses-augment {
+        container sub-container-from-augment2 {
+        }
+    }
+
+    augment /root-container/container-from-grp1/sub-container-from-grp1 {
+        container sub-container-from-augment {
+        }
+    }
+
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-from-grp2/sub-container-from-grp2/augmented-container-in-uses-grp2 {
+        container sub-container-from-augment6 {
+        }
+    }
+    
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-from-grp2/sub-container-from-grp2/sub-container-from-augment5 {
+        container sub-container-from-augment7 {
+        }
+    }
+    
+    augment /root-container/container-from-grp1/sub-container-from-grp1/container-from-grp2/sub-container-from-grp2 {
+        container sub-container-from-augment5 {
+        }
+    }
+
+    augment /root-container/added-container-1 {
+        container added-container-2 {
+        }
+    }
+
+    augment /root-container {
+        container added-container-1 {
+        }
+    }
+
+    grouping grp1 {
+        container container-from-grp1 {
+            container sub-container-from-grp1 {
+                uses grp2 {
+                    augment container-from-grp2/sub-container-from-grp2 {
+                        container augmented-container-in-uses-grp2;
+                    }
+                }
+            }
+        }
+    }
+
+    grouping grp2 {
+        container container-from-grp2 {
+            container sub-container-from-grp2 {
+            }
+        }
+        uses grp3;
+    }
+    
+    grouping grp3 {
+      container container-from-grp3 {
+          
+      }
+    }
+    
+    container root-container {
+        uses grp1 {
+            augment container-from-grp1/sub-container-from-grp1 {
+                container container-in-uses-augment {
+                }
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/constraints/foo.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/constraints/foo.yang
new file mode 100644 (file)
index 0000000..a27f9fe
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+module foo {
+    yang-version 1;
+    namespace "urn:opendaylight.foo";
+    prefix "foo";
+
+    revision "2013-10-08" {
+    }
+
+    typedef my-decimal-type {
+        type decimal64 {
+            fraction-digits 6;
+            range "1.5..5.5";
+        }
+    }
+
+    leaf id-decimal64 {
+        type decimal64 {
+            fraction-digits 4;
+            range "1.5..5.5";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/effective-build/simple-module.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/effective-build/simple-module.yang
new file mode 100644 (file)
index 0000000..9993069
--- /dev/null
@@ -0,0 +1,30 @@
+module simple-module {
+
+    namespace "simple.yang";
+    prefix sim;
+
+    container root-container {
+        container sub-container {
+            container sub-sub-container {
+            }
+        }
+    }
+    
+//    container sim:root-container2 {
+//        container sub-container2 {
+//            container "sim:sub-sub-container2" {
+//            }
+//        }
+//    }
+    
+    container root-container2 {
+        uses grp;
+    }
+    
+    grouping grp {
+        container sub-container2 {
+            container "sub-sub-container2" {
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/extensions/mock.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/extensions/mock.yang
new file mode 100644 (file)
index 0000000..5c1602d
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+ module mock {
+
+   namespace "a:b:c";
+   prefix "m";
+
+   revision 2010-09-24 {
+     description
+      "Initial revision.";
+   }
+
+ }
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/extensions/yang-ext.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/extensions/yang-ext.yang
new file mode 100644 (file)
index 0000000..0fbe94d
--- /dev/null
@@ -0,0 +1,79 @@
+module yang-ext {
+    yang-version 1;
+    namespace "urn:opendaylight:yang:extension:yang-ext";
+    prefix "ext";
+    
+    contact "Anton Tkacik <ttkacik@cisco.com>";
+
+    description 
+            "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";
+
+    revision "2013-07-09" {
+        description "";
+    }
+
+    // Augmentation name
+
+    extension "augment-identifier" {
+        description 
+           "YANG language extension which assigns an identifier to 
+            augmentation. Augment identifier is used to identify
+            specific augment statement by name. 
+
+            The identifier syntax is defined formally defined by the rule
+            'identifier' in Section 12 of RFC 6020.
+
+            All augment identifiers defined in a namespace MUST be unique.
+            The namespace of augment identifiers is shared by module and
+            its submodules.";
+
+            /*
+                Discussion:
+                This extension allows for ease of development / debug
+                of YANG modules and it is suitable for code generation,
+                where each augment statement is nicely identified by
+                unique name instead of combination of augment target
+                and when condition. 
+            */
+        argument "identifier";
+    }
+
+
+    // Context-aware RPCs
+
+    grouping rpc-context-ref {
+        description 
+           "A reference to RPC context.";
+        leaf context-instance {
+            type instance-identifier;
+            description "Pointer to the context. ";
+        }
+    }
+
+    extension "rpc-context-instance" {
+        description
+           "YANG language extension which defines enclosing (parent) 
+            schema node as referencable context for RPCs.
+
+            The argument is identity which is used to identify RPC context
+            type.";
+
+        argument "context-type";
+    }
+
+    extension "context-reference" {
+        argument "context-type";
+    }
+
+    extension "context-instance" {
+        argument "context-type";
+    }
+    
+    extension "instance-target" {
+       argument "path";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/identity/cyclic-identity-test.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/identity/cyclic-identity-test.yang
new file mode 100644 (file)
index 0000000..94c3096
--- /dev/null
@@ -0,0 +1,34 @@
+module cyclic-identity-test {
+    namespace "cyclic.identity.test";
+    prefix cid;
+
+    identity child-identity-1 {
+        base child-identity-2;
+    }
+
+    identity child-identity-2 {
+        base child-identity-3;
+    }
+
+    identity child-identity-3 {
+        base child-identity-4;
+    }
+
+    identity child-identity-4 {
+        base child-identity-1;
+    }
+
+    identity base-identity {
+    }
+
+    identity sub-identity {
+        base base-identity;
+    }
+
+    identity sub-identity-2 {
+        base base-identity;
+    }
+
+    container root-container {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/identity/identity-test.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/identity/identity-test.yang
new file mode 100644 (file)
index 0000000..4836f80
--- /dev/null
@@ -0,0 +1,26 @@
+module identity-test {
+    namespace "identity.test";
+    prefix id;
+
+    identity root-identity {
+        description "root";
+    }
+
+    identity child-identity-1 {
+        description "child-identity-1";
+        base root-identity;
+    }
+
+    identity child-identity-2 {
+        description "child-identity-2";
+        base root-identity;
+    }
+
+    identity child-identity-1-2 {
+        description "child-identity-1-2";
+        base child-identity-1;
+    }
+
+    container root-container {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/imported-module.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/imported-module.yang
new file mode 100644 (file)
index 0000000..3fd777a
--- /dev/null
@@ -0,0 +1,8 @@
+module imported-module {
+    namespace "imported-module";
+    prefix imp;
+
+    container container-in-imported-module {
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/root-module.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/root-module.yang
new file mode 100644 (file)
index 0000000..880e6ee
--- /dev/null
@@ -0,0 +1,13 @@
+module root-module {
+    namespace "root-module";
+    prefix root;
+
+    import imported-module { prefix imp; }
+    
+    include submodule-1;
+    include submodule-2;
+
+    container container-in-root-module {
+        description "desc";
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/submodule-1.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/submodule-1.yang
new file mode 100644 (file)
index 0000000..45e1bf2
--- /dev/null
@@ -0,0 +1,11 @@
+submodule submodule-1 {
+
+    belongs-to root-module {
+        prefix root;
+    }
+    
+    include submodule-to-submodule-1;
+    
+    container container-in-submodule-1 {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/submodule-2.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/submodule-2.yang
new file mode 100644 (file)
index 0000000..1d61c46
--- /dev/null
@@ -0,0 +1,9 @@
+submodule submodule-2 {
+
+    belongs-to root-module {
+        prefix root;
+    }
+
+    container container-in-submodule-2 {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/submodule-to-submodule-1.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/submodules/submodule-to-submodule-1.yang
new file mode 100644 (file)
index 0000000..d99ecbf
--- /dev/null
@@ -0,0 +1,9 @@
+submodule submodule-to-submodule-1 {
+
+    belongs-to root-module {
+        prefix root;
+    }
+
+    container container-in-submodule-to-submodule-1 {
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/stmt-test/uses/refine-test.yang b/yang/yang-parser-rfc7950/src/test/resources/stmt-test/uses/refine-test.yang
new file mode 100644 (file)
index 0000000..3d418a1
--- /dev/null
@@ -0,0 +1,57 @@
+module refine-test {
+    namespace "root-module";
+    prefix root;
+
+    container root-container {
+        container sub-container1 {
+        }
+
+        uses grp-1 {
+            refine container-from-grouping/list-in-container {
+                min-elements 5;
+                max-elements 7;
+                description "new description";
+                reference "new reference";
+                config true;
+                must "2 = 2";
+            }
+            refine container-from-grouping2/presence-container {
+                presence "presence desc";
+                description "new description";
+                reference "new reference";
+                config true;
+                must "1 = 1";
+            }
+            refine container-from-grouping/choice-from-grp {
+                mandatory true;
+            }
+        }
+
+        container sub-container2 {
+        }
+    }
+
+    grouping grp-1 {
+        container container-from-grouping {
+            list list-in-container {
+                min-elements 10;
+                max-elements 20;
+                description "original description";
+                reference "original reference";
+                config false;
+                must "1 = 1";
+            }
+            choice choice-from-grp {
+                case one {
+                }
+                case two {
+                }
+                mandatory false;
+            }
+        }
+        container container-from-grouping2 {
+            container presence-container {
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/bug-4310/bug4310.yang b/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/bug-4310/bug4310.yang
new file mode 100644 (file)
index 0000000..13315e9
--- /dev/null
@@ -0,0 +1,19 @@
+module bug4310 {
+    yang-version 1;
+    namespace "urn:opendaylight.bug4310";
+    prefix "bug";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    leaf datapath-id {
+        type union {
+            bug:simple-ext-w-arg "key:value";
+        }
+    }
+
+    extension simple-ext-w-arg {
+        argument "ext-arg";
+        description "Extension with single argument definition.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/empty-element/empty.yang b/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/empty-element/empty.yang
new file mode 100644 (file)
index 0000000..35b2844
--- /dev/null
@@ -0,0 +1,19 @@
+module empty {
+    namespace "root-module";
+    prefix empty;
+
+    container root-container {
+        uses grp-1 {
+            refine container-from-grouping/list-in-container {
+            }
+        }
+    }
+
+    grouping grp-1 {
+        container container-from-grouping {
+            list list-in-container {
+                description "original description";
+            }
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/maximal-element/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/maximal-element/baz.yang
new file mode 100644 (file)
index 0000000..185be60
--- /dev/null
@@ -0,0 +1,25 @@
+module baz {
+    yang-version 1;
+    namespace "urn:opendaylight.baz";
+    prefix "baz";
+
+    import maximal {
+        prefix "max";
+        revision-date 2015-11-11;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2015-11-11" {
+        reference " WILL BE DEFINED LATER";
+    }
+
+    augment "/max:interfaces/max:ifEntry" {
+        description "Test describtion";
+        description "Test describtion";
+        container augment-holder2 {
+            description "Description for augment holder";
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/maximal-element/maximal.yang b/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/maximal-element/maximal.yang
new file mode 100644 (file)
index 0000000..62f2f56
--- /dev/null
@@ -0,0 +1,33 @@
+module maximal {
+    yang-version 1;
+    namespace "urn:opendaylight.maximal";
+    prefix "maximal";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2015-11-11" {
+        reference "WILL BE DEFINED LATER";
+    }
+
+    container interfaces {
+        grouping ifEntry {
+            container augment-holder;
+        }
+        list ifEntry {
+            key "ifIndex";
+
+            leaf ifIndex {
+                type uint32;
+                units minutes;
+            }
+
+            leaf ifMtu {
+                type int32;
+            }
+
+            min-elements 1;
+            max-elements 11;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/missing-element/baz.yang b/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/missing-element/baz.yang
new file mode 100644 (file)
index 0000000..accae92
--- /dev/null
@@ -0,0 +1,16 @@
+module baz {
+    yang-version 1;
+    namespace "urn:opendaylight.baz";
+    prefix "baz";
+
+    import missing {
+        revision-date 2015-11-11;
+    }
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2015-11-11" {
+        reference " WILL BE DEFINED LATER";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/missing-element/missing.yang b/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/missing-element/missing.yang
new file mode 100644 (file)
index 0000000..753238e
--- /dev/null
@@ -0,0 +1,12 @@
+module missing {
+    yang-version 1;
+    namespace "urn:opendaylight.missing";
+    prefix "missing";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2015-11-11" {
+        reference " WILL BE DEFINED LATER";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/undesirable-element/undesirable.yang b/yang/yang-parser-rfc7950/src/test/resources/substatement-validator/undesirable-element/undesirable.yang
new file mode 100644 (file)
index 0000000..449526a
--- /dev/null
@@ -0,0 +1,13 @@
+module undesirable {
+    yang-version 1;
+    namespace "urn:opendaylight.undesirable";
+    prefix "undesirable";
+
+    organization "opendaylight";
+    contact "http://www.opendaylight.org/";
+
+    revision "2015-11-11" {
+        reference " WILL BE DEFINED LATER";
+        type "string";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/type-tests/types.yang b/yang/yang-parser-rfc7950/src/test/resources/type-tests/types.yang
new file mode 100644 (file)
index 0000000..7f8ed0c
--- /dev/null
@@ -0,0 +1,143 @@
+module types {
+
+    yang-version 1;
+    namespace "urn:custom.types.demo";
+    prefix "typ";
+    revision "2015-08-08";
+
+    leaf leaf-int8 {
+        type int8;
+    }
+
+    leaf leaf-int16 {
+        type int16;
+    }
+
+    leaf leaf-int32 {
+        type int32;
+    }
+
+    leaf leaf-int64 {
+        type int64;
+    }
+
+    leaf leaf-uint8 {
+        type uint8;
+    }
+
+    leaf leaf-uint16 {
+        type uint16;
+    }
+
+    leaf leaf-uint32 {
+        type uint32;
+    }
+
+    leaf leaf-uint64 {
+        type uint64;
+    }
+
+    leaf leaf-binary {
+        type binary;
+    }
+
+    leaf leaf-boolean {
+        type boolean;
+    }
+
+    leaf leaf-instance-identifier {
+        type instance-identifier;
+    }
+
+    leaf leaf-identityref {
+        type identityref {
+            base "test-identity";
+        }
+    }
+
+    leaf leaf-string {
+        type string;
+    }
+
+    leaf leaf-length-pattern {
+        type string {
+            length "1..255";
+            pattern "[0-9a-fA-F]*";
+        }
+    }
+
+    leaf leaf-length-pattern-second {
+        type string {
+            length "2..10";
+            pattern "[0-9a-fA-F]";
+        }
+    }
+
+    identity test-identity {
+        description "test identity description";
+    }
+
+    leaf leaf-decimal64 {
+        type decimal64 {
+            fraction-digits 2;
+            range "1 .. 3.14 | 10 | 20..max";
+        }
+    }
+
+    leaf leaf-empty {
+        type empty;
+    }
+
+    leaf leaf-bits {
+        type bits {
+            bit one {
+                position 0;
+                description "test bit";
+                reference "test bit ref";
+                status current;
+            }
+            bit two {
+                position 1;
+            }
+            bit three {
+                position 2;
+                description "test bit";
+                reference "test bit ref";
+                status current;
+            }
+        }
+    default "one";
+    }
+
+    leaf leaf-enum {
+        type enumeration {
+            enum zero {
+                description "test enum";
+                reference "test enum ref";
+                status current;
+                value 0;
+            }
+            enum one;
+            enum two;
+        }
+    }
+
+    leaf leaf-union {
+        type union {
+            type int8;
+            type int16;
+        }
+    }
+
+    leaf leaf-leafref {
+         type leafref {
+             path "/container-test/leaf-test";
+         }
+    }
+
+    container container-test {
+        leaf leaf-test {
+            type int8;
+        }
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/typedef-substmts-test/typedef-substmts-test.yang b/yang/yang-parser-rfc7950/src/test/resources/typedef-substmts-test/typedef-substmts-test.yang
new file mode 100644 (file)
index 0000000..aea20ea
--- /dev/null
@@ -0,0 +1,11 @@
+module typedef-substmts-test {
+    namespace "urn:opendaylight.org/typedef-substmts-test";
+    prefix "tdst";
+
+    typedef time-of-the-day {
+        type string;
+        units 24-hour-clock;
+        default 1am;
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/types/custom-types-test@2012-04-04.yang b/yang/yang-parser-rfc7950/src/test/resources/types/custom-types-test@2012-04-04.yang
new file mode 100644 (file)
index 0000000..dd9af18
--- /dev/null
@@ -0,0 +1,143 @@
+module custom-types-test {
+
+    yang-version 1;
+    namespace "urn:custom.types.demo";
+    prefix "iit";
+
+    // imports added only for testing purposes
+    import iana-timezones {
+        prefix "tz";
+    }
+    import ietf-inet-types {
+        prefix "inet";
+    }
+    import ietf-yang-types {
+        prefix "yang";
+    }
+
+    organization "opendaylight";
+    contact "WILL-BE-DEFINED-LATER";
+        revision 2012-04-16 {
+    }
+
+    extension mountpoint {
+        description "enter point";
+        argument "name" {
+            yin-element "true";
+        }
+    }
+
+    typedef access-operations-type {
+        type bits {
+            bit create {
+                description "Any protocol operation that creates a new data node.";
+            }
+            bit read {
+                description "Any protocol operation or notification that returns the value of a data node.";
+                position 500;
+            }
+            bit update {
+                description "Any protocol operation that alters an existing data node.";
+            }
+            bit delete {
+                description "Any protocol operation that removes a data node.";
+                position 365;
+            }
+            bit exec {
+                description "Execution access to the specified protocol operation.";
+            }
+        }
+        description "NETCONF Access Operation.";
+    }
+
+    leaf inst-id-leaf1 {
+        type instance-identifier {
+            require-instance false;
+        }
+        iit:mountpoint "mnt-extension";
+    }
+
+    leaf inst-id-leaf2 {
+        type instance-identifier;
+    }
+
+    leaf type {
+        type service-type-ref;
+    }
+
+    identity crypto-id {
+        base "crypto-base";
+        description "crypto-id description";
+        iit:mountpoint "mnt-extension";
+    }
+
+    identity crypto-base {
+        description "crypto-base description";
+    }
+     
+    identity crypto-alg {
+        base "crypto-base";
+        description "crypto-alg description";
+    }
+
+    identity crypto-def {
+        base "crypto-base";
+        description "crypto-def description";
+    }
+
+    leaf mybits {
+        type bits {
+            bit disable-nagle {
+                position 0;
+            }
+            bit auto-sense-speed {
+                position 1;
+            }
+            bit only-10-Mb {
+                position 2;
+            }
+        }
+        default "auto-sense-speed";
+    }
+
+    typedef ip-version {
+        type enumeration {
+            enum unknown {
+                description "An unknown or unspecified version of the Internet protocol.";
+            }
+            enum ipv4 {
+                value "19";
+                description "The IPv4 protocol as defined in RFC 791.";
+            }
+            enum ipv6 {
+                value "7";
+                description "The IPv6 protocol as defined in RFC 2460.";
+            }
+            enum default {
+                description "default ip";
+            }
+        }
+    }
+
+    identity service-type {
+        description
+            "Service identity base type. All service identities must be
+             derived from this type. A service type uniquely defines a single
+             atomic API contract, such as a Java interface, a set of C
+             function declarations, or similar.
+
+             If the service type has a corresponding Java interface, the name
+             of that interface should be attached to the derived identity MUST
+             include a java-class keyword, whose name argument points to that
+             interface.";
+    }
+
+    typedef service-type-ref {
+        description
+            "Internal type of references to service type identity.";
+        type identityref {
+            base service-type;
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/types/union-in-list/unioninlisttest.yang b/yang/yang-parser-rfc7950/src/test/resources/types/union-in-list/unioninlisttest.yang
new file mode 100644 (file)
index 0000000..1db8726
--- /dev/null
@@ -0,0 +1,32 @@
+module unioninlisttest {
+
+    namespace "urn:uilt";
+    prefix "uilt";
+
+    revision 2015-01-13 {
+    }
+
+    list foo {
+        key "name";
+        unique "ip port";
+
+        leaf name {
+            type string;
+        }
+
+        union {
+            type int32;
+            type enumeration {
+                enum "test";
+            }
+        }
+
+        leaf ip {
+            type string;
+        }
+
+        leaf port {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/types/union-with-bits/union-bits-model.yang b/yang/yang-parser-rfc7950/src/test/resources/types/union-with-bits/union-bits-model.yang
new file mode 100644 (file)
index 0000000..2c99807
--- /dev/null
@@ -0,0 +1,59 @@
+module unionbitsmodel {
+    namespace "urn:ubt";
+    prefix "ubt";
+
+    revision 2014-12-10 {
+    }
+
+    typedef port-state-v13 {
+        description
+            "Current state of the physical port. These are not configurable from the controller.";
+
+        type bits {
+            bit link_down {
+                //description "No physical link present.";
+                position 0;
+            }
+
+            bit blocked {
+                //description "Port is blocked";
+                position 1;
+            }
+
+            bit live {
+                //description "Live for Fast Failover Group.";
+                position 2;
+            }
+        }
+    }
+
+    typedef port-state-v10 {
+        description "Current state of the physical port. These are not configurable from the controller.";
+
+        type bits {
+            bit link_down {
+                description "No physical link present.";
+                position 0;
+            }
+
+            bit blocked {
+                    description "Port is blocked";
+                    position 1;
+                }
+                bit live {
+                    description "Live for Fast Failover Group.";
+                    position 2;
+                }
+                bit stp_listen {
+                    description "Not learning or relaying frames.";
+                }
+            }
+        }
+
+    typedef my-union-type {
+        type union {
+            type port-state-v13;
+            type port-state-v10;
+        }
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/types/union-with-ext/extdef.yang b/yang/yang-parser-rfc7950/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-rfc7950/src/test/resources/types/union-with-ext/unionbug.yang b/yang/yang-parser-rfc7950/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";
+            }
+        }
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/revisions-extension.yang b/yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/revisions-extension.yang
new file mode 100644 (file)
index 0000000..3527eff
--- /dev/null
@@ -0,0 +1,24 @@
+module revisions-extension {
+    namespace "my-namespace";
+    prefix pre;
+
+    include submodule-header-extension {
+        revision-date 2007-06-09;
+    }
+
+    revision "2007-06-09" {
+        description "Initial revision.";
+    }
+
+    pre:my-extension 1;
+
+    revision "2008-06-09" {
+        description "Revision 2.";
+    }
+
+    extension my-extension {
+        description "my description ...";
+        argument "number";
+    }
+
+}
diff --git a/yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/stmtsep-in-statements-sub.yang b/yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/stmtsep-in-statements-sub.yang
new file mode 100644 (file)
index 0000000..434a5ce
--- /dev/null
@@ -0,0 +1,28 @@
+submodule subtest {
+    aaa {
+       bbb {
+        yang-version 1;
+       }
+    }
+
+    yang-version 1;
+
+    belongs-to test {
+        aaa {
+           bbb {
+            yang-version 1;
+           }
+        }
+       prefix test;
+    }
+
+    revision 2015-01-14 {
+        ccc {
+            ddd {
+                yang-version 1;
+            }
+        }
+
+        description "Initial revision.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/stmtsep-in-statements.yang b/yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/stmtsep-in-statements.yang
new file mode 100644 (file)
index 0000000..b9e94ed
--- /dev/null
@@ -0,0 +1,40 @@
+module test {
+    aaa {
+        bbb {
+            yang-version 1;
+        }
+    }
+
+    yang-version 1;
+    namespace "tst";
+    prefix test;
+
+    include subtest {
+        ccc {
+            ddd {
+                yang-version 1;
+            }
+        }
+        revision-date 2015-01-14;
+    }
+
+    import test2 {
+        eee {
+           fff {
+            yang-version 1;
+           }
+        }
+        prefix test2;
+        revision-date 2015-01-14;
+    }
+
+    revision 2015-01-14 {
+        ggg {
+            hhh {
+                yang-version 1;
+            }
+        }
+
+        description "Initial revision.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/stmtsep-in-statements2.yang b/yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/stmtsep-in-statements2.yang
new file mode 100644 (file)
index 0000000..aa50e4f
--- /dev/null
@@ -0,0 +1,21 @@
+module test2 {
+    aaa {
+        bbb {
+            yang-version 1;
+        }
+    }
+
+    yang-version 1;
+    namespace "tst2";
+    prefix test2;
+
+    revision 2015-01-14 {
+        ccc {
+            ddd {
+                yang-version 1;
+            }
+        }
+
+        description "Initial revision.";
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/submodule-header-extension.yang b/yang/yang-parser-rfc7950/src/test/resources/yang-grammar-test/submodule-header-extension.yang
new file mode 100644 (file)
index 0000000..cfda3cb
--- /dev/null
@@ -0,0 +1,18 @@
+submodule submodule-header-extension {
+
+    pre:my-extension2 2;
+
+    belongs-to revisions-extension {
+        prefix pre;
+    }
+
+    revision "2007-06-09" {
+        description "Initial revision.";
+    }
+
+    extension my-extension2 {
+        description "my description ...";
+        argument "number";
+    }
+
+}
diff --git a/yang/yang-parser-spi/pom.xml b/yang/yang-parser-spi/pom.xml
new file mode 100644 (file)
index 0000000..13162ac
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-parser-spi</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>YANG parser SPI</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-model-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.parser.spi</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/ExtensionNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/ExtensionNamespace.java
new file mode 100644 (file)
index 0000000..6bd2bd2
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 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.spi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Extension namespace. All extension names defined in a module and its submodules share the same
+ * extension identifier namespace, where each extension is identified by a QName formed from the
+ * defining module's QNameModule and the identifier specified in extension statement's argument.
+ */
+public interface ExtensionNamespace extends StatementNamespace<QName, ExtensionStatement, ExtensionEffectiveStatement> {
+    NamespaceBehaviour<QName, StmtContext<?, ExtensionStatement, ExtensionEffectiveStatement>,
+            @NonNull ExtensionNamespace> BEHAVIOUR = NamespaceBehaviour.global(ExtensionNamespace.class);
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/FeatureNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/FeatureNamespace.java
new file mode 100644 (file)
index 0000000..0e7b050
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 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.spi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Feature namespace. All feature names defined in a module and its submodules share the
+ * same feature identifier namespace. Each feature is identified by a QName formed from
+ * the defining module's QNameModule and the feature name.
+ */
+public interface FeatureNamespace extends StatementNamespace<QName, FeatureStatement, FeatureEffectiveStatement> {
+    NamespaceBehaviour<QName, StmtContext<?, FeatureStatement, FeatureEffectiveStatement>,
+            @NonNull FeatureNamespace> BEHAVIOUR = NamespaceBehaviour.global(FeatureNamespace.class);
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/GroupingNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/GroupingNamespace.java
new file mode 100644 (file)
index 0000000..c407ef9
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 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.spi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.GroupingEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.GroupingStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace.TreeScoped;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Grouping namespace. * All grouping names defined within a parent node or at the top level of the module
+ * or its submodules share the same grouping identifier namespace. This namespace is scoped to all
+ * descendant nodes of the parent node or module.
+ *
+ * <p>
+ * This means that any descendant node may use that grouping, and it MUST NOT define a grouping with the same name.
+ */
+public interface GroupingNamespace extends TreeScoped<QName, GroupingStatement, GroupingEffectiveStatement> {
+    NamespaceBehaviour<QName, StmtContext<?, GroupingStatement, GroupingEffectiveStatement>,
+            @NonNull GroupingNamespace> BEHAVIOUR = NamespaceBehaviour.treeScoped(GroupingNamespace.class);
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/IdentityNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/IdentityNamespace.java
new file mode 100644 (file)
index 0000000..b092406
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 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.spi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.IdentityStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Identity namespace. All identity names defined in a module and its submodules share the same identity identifier
+ * namespace.
+ */
+public interface IdentityNamespace extends
+        StatementNamespace<QName, IdentityStatement, IdentityEffectiveStatement> {
+    NamespaceBehaviour<QName, StmtContext<?, IdentityStatement, IdentityEffectiveStatement>,
+            @NonNull IdentityNamespace> BEHAVIOUR = NamespaceBehaviour.global(IdentityNamespace.class);
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/ModuleNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/ModuleNamespace.java
new file mode 100644 (file)
index 0000000..9788fde
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 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.spi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Module namespace. All modules known to the reactor are populated to this namespace. Each module is identified
+ * by a {@link SourceIdentifier}.
+ */
+public interface ModuleNamespace
+        extends StatementNamespace<SourceIdentifier, ModuleStatement, ModuleEffectiveStatement> {
+    NamespaceBehaviour<SourceIdentifier, StmtContext<?, ModuleStatement, ModuleEffectiveStatement>,
+            @NonNull ModuleNamespace> BEHAVIOUR = NamespaceBehaviour.global(ModuleNamespace.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/NamespaceToModule.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/NamespaceToModule.java
new file mode 100644 (file)
index 0000000..dc81edf
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.spi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * A derived namespace allowing lookup of modules based on their {@link QNameModule}.
+ */
+public interface NamespaceToModule extends StatementNamespace<QNameModule, ModuleStatement, ModuleEffectiveStatement> {
+    NamespaceBehaviour<QNameModule, StmtContext<?, ModuleStatement, ModuleEffectiveStatement>,
+            @NonNull NamespaceToModule> BEHAVIOUR = NamespaceBehaviour.global(NamespaceToModule.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/PreLinkageModuleNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/PreLinkageModuleNamespace.java
new file mode 100644 (file)
index 0000000..3eb1278
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016 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.spi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Intermediate-stage namespace equivalent to ModuleNamespace except it is keyed by module names. This namespace is
+ * used to resolve inter-module references before actual linkage occurs.
+ */
+public interface PreLinkageModuleNamespace
+        extends StatementNamespace<String, ModuleStatement, ModuleEffectiveStatement> {
+    NamespaceBehaviour<String, StmtContext<?, ModuleStatement, ModuleEffectiveStatement>,
+            @NonNull PreLinkageModuleNamespace> BEHAVIOUR = NamespaceBehaviour.global(PreLinkageModuleNamespace.class);
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/SchemaNodeIdentifierNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/SchemaNodeIdentifierNamespace.java
new file mode 100644 (file)
index 0000000..cbed70a
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 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.spi;
+
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace.TreeBased;
+
+/**
+ * Schema node identifier namespace. All leafs, leaf-lists, lists, containers, choices, rpcs, notifications,
+ * anydata and anyxmls defined (directly or through a uses statement) within a parent node or at the top level
+ * of the module (or its submodules) share the same identifier namespace.
+ *
+ * <p>
+ * This namespace is scoped to the parent node or module, unless the parent node is a case node. In that case,
+ * the namespace is scoped to the closest ancestor node that is not a case or choice node.
+ */
+public interface SchemaNodeIdentifierNamespace
+    extends TreeBased<SchemaNodeIdentifier, DeclaredStatement<?>, EffectiveStatement<?, DeclaredStatement<?>>> {
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/SubmoduleNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/SubmoduleNamespace.java
new file mode 100644 (file)
index 0000000..0fbb318
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 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.spi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Submodule equivalent of ModuleNamespace.
+ */
+// FIXME: describe scoping of this namespace
+public interface SubmoduleNamespace
+    extends StatementNamespace<SourceIdentifier, SubmoduleStatement, SubmoduleEffectiveStatement> {
+    NamespaceBehaviour<SourceIdentifier, StmtContext<?, SubmoduleStatement, SubmoduleEffectiveStatement>,
+            @NonNull SubmoduleNamespace> BEHAVIOUR =
+            NamespaceBehaviour.global(SubmoduleNamespace.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/TypeNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/TypeNamespace.java
new file mode 100644 (file)
index 0000000..52ea101
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015 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.spi;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.TypedefStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace.TreeScoped;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Derived types namespace. All derived type names defined within a parent node or at the top level of the module
+ * (or its submodules) share the same type identifier namespace.
+ *
+ * <p>
+ * This namespace is scoped to all descendant nodes of the parent node or module. This means that any descendant node
+ * may use that typedef, and it MUST NOT define a typedef with the same name.
+ *
+ * <p>
+ * This namespace includes all type definitions implied by the language in which the current statement resides
+ * (e.g. RFC6020/RFC7950 for YANG 1.0/1.1).
+ */
+public interface TypeNamespace extends TreeScoped<QName, TypedefStatement, TypedefEffectiveStatement> {
+    NamespaceBehaviour<QName, StmtContext<?, TypedefStatement, TypedefEffectiveStatement>, @NonNull TypeNamespace>
+        BEHAVIOUR = NamespaceBehaviour.treeScoped(TypeNamespace.class);
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractDeclaredStatement.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractDeclaredStatement.java
new file mode 100644 (file)
index 0000000..c10cf9e
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+
+/**
+ * Utility abstract base class for implementing declared statements.
+ *
+ * @param <A> Argument type.
+ */
+public abstract class AbstractDeclaredStatement<A> implements DeclaredStatement<A> {
+    private final @NonNull ImmutableList<? extends DeclaredStatement<?>> substatements;
+    private final @NonNull StatementDefinition definition;
+    private final @NonNull StatementSource source;
+
+    private final A argument;
+    private final String rawArgument;
+
+    protected AbstractDeclaredStatement(final StmtContext<A, ?, ?> context) {
+        rawArgument = context.rawStatementArgument();
+        argument = context.getStatementArgument();
+        source = context.getStatementSource();
+        definition = context.getPublicDefinition();
+        /*
+         * Perform an explicit copy, because Collections2.transform() is lazily transformed and retains pointer to
+         * original collection, which may contains references to mutable context.
+         */
+        substatements = ImmutableList.copyOf(Collections2.transform(context.declaredSubstatements(),
+            StmtContext::buildDeclared));
+    }
+
+    @Override
+    public String rawArgument() {
+        return rawArgument;
+    }
+
+    @Override
+    public A argument() {
+        return argument;
+    }
+
+    @Override
+    public StatementDefinition statementDefinition() {
+        return definition;
+    }
+
+    @Override
+    public Collection<? extends DeclaredStatement<?>> declaredSubstatements() {
+        return substatements;
+    }
+
+    @Override
+    public StatementSource getStatementSource() {
+        return source;
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractQNameStatementSupport.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractQNameStatementSupport.java
new file mode 100644 (file)
index 0000000..74b8e75
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.spi.meta;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+/**
+ * Class providing necessary support for processing a YANG statements which has a {@link QName} argument. In addition
+ * to functions of {@link AbstractStatementSupport}, it takes care of adapting QNames across modules.
+ *
+ * @param <D>
+ *            Declared Statement representation
+ * @param <E>
+ *            Effective Statement representation
+ */
+@Beta
+public abstract class AbstractQNameStatementSupport<D extends DeclaredStatement<QName>,
+        E extends EffectiveStatement<QName, D>> extends AbstractStatementSupport<QName, D, E> {
+
+    protected AbstractQNameStatementSupport(final StatementDefinition publicDefinition) {
+        super(publicDefinition);
+    }
+
+    @Override
+    public QName adaptArgumentValue(final StmtContext<QName, D, E> ctx, final QNameModule targetModule) {
+        return ctx.getFromNamespace(QNameCacheNamespace.class, ctx.getStatementArgument().withModule(targetModule));
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractStatementSupport.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractStatementSupport.java
new file mode 100644 (file)
index 0000000..a12cd19
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+/**
+ * Class providing necessary support for processing a YANG statement. This class is intended to be subclassed
+ * by developers who want to add semantic support for a statement to a parser reactor.
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ * @param <E> Effective Statement representation
+ */
+public abstract class AbstractStatementSupport<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+        implements StatementDefinition, StatementFactory<A, D, E>, StatementSupport<A, D, E> {
+
+    private final @NonNull StatementDefinition type;
+
+    protected AbstractStatementSupport(final StatementDefinition publicDefinition) {
+        this.type = requireNonNull(publicDefinition);
+        checkArgument(publicDefinition != this);
+    }
+
+    @Override
+    public final StatementDefinition getPublicView() {
+        return type;
+    }
+
+    @Override
+    public void onStatementAdded(final StmtContext.Mutable<A, D, E> stmt) {
+        // NOOP for most implementations
+    }
+
+    /**
+     * {@inheritDoc}.
+     *
+     * <p>
+     * Subclasses of this class may override this method to perform actions on this event or register a modification
+     * action using {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
+     */
+    @Override
+    public void onPreLinkageDeclared(final StmtContext.Mutable<A, D, E> stmt) {
+        // NOOP for most implementations
+    }
+
+    /**
+     * {@inheritDoc}.
+     *
+     * <p>
+     * Subclasses of this class may override this method to perform actions on this event or register a modification
+     * action using {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
+     */
+    @Override
+    public void onLinkageDeclared(final StmtContext.Mutable<A, D, E> stmt) {
+        // NOOP for most implementations
+    }
+
+    /**
+     * {@inheritDoc}.
+     *
+     * <p>
+     * Subclasses of this class may override this method to perform actions on this event or register a modification
+     * action using {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
+     */
+    @Override
+    public void onStatementDefinitionDeclared(final StmtContext.Mutable<A, D, E> stmt) {
+        // NOOP for most implementations
+    }
+
+    /**
+     * {@inheritDoc}.
+     *
+     * <p>
+     * Subclasses of this class may override this method to perform actions on this event or register a modification
+     * action using {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
+     */
+    @Override
+    public void onFullDefinitionDeclared(final StmtContext.Mutable<A, D, E> stmt) {
+        final SubstatementValidator validator = getSubstatementValidator();
+        if (validator != null) {
+            validator.validate(stmt);
+        }
+    }
+
+    @Override
+    public boolean hasArgumentSpecificSupports() {
+        // Most of statement supports don't have any argument specific
+        // supports, so return 'false'.
+        return false;
+    }
+
+    @Override
+    public StatementSupport<?, ?, ?> getSupportSpecificForArgument(final String argument) {
+        // Most of statement supports don't have any argument specific
+        // supports, so return null.
+        return null;
+    }
+
+    /**
+     * Returns corresponding substatement validator of a statement support.
+     *
+     * @return substatement validator or null, if substatement validator is not defined
+     */
+    protected abstract @Nullable SubstatementValidator getSubstatementValidator();
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractVoidStatementSupport.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/AbstractVoidStatementSupport.java
new file mode 100644 (file)
index 0000000..8a3c642
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.spi.meta;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+/**
+ * Class providing necessary support for processing a YANG statements which does not have an argument. In addition
+ * to functions of {@link AbstractStatementSupport}, it provide the argument value.
+ *
+ * @param <D> Declared Statement representation
+ * @param <E> Effective Statement representation
+ */
+@Beta
+public abstract class AbstractVoidStatementSupport<D extends DeclaredStatement<Void>,
+        E extends EffectiveStatement<Void, D>> extends AbstractStatementSupport<Void, D, E> {
+
+    protected AbstractVoidStatementSupport(final StatementDefinition publicDefinition) {
+        super(publicDefinition);
+    }
+
+    @Override
+    public final Void parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return null;
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/CopyHistory.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/CopyHistory.java
new file mode 100644 (file)
index 0000000..5b0e134
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2016 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.spi.meta;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Verify;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+@Beta
+public final class CopyHistory implements Immutable {
+    private static final CopyType[] VALUES = CopyType.values();
+
+    private static final CopyHistory[][] CACHE = new CopyHistory[VALUES.length][];
+
+    static {
+        /*
+         * Cache size is dependent on number of items in CopyType, it costs N * 2^N objects.
+         * For 4 types that boils down to 4 * 16 = 64 objects.
+         * For 5 types that boils down to 5 * 32 = 160 objects.
+         * For 6 types that boils down to 6 * 64 = 384 objects.
+         *
+         * If we ever hit 6 types, the caching strategy needs to be revisited.
+         */
+        Verify.verify(VALUES.length < 6);
+    }
+
+    private static final CopyHistory ORIGINAL = cacheObject(CopyType.ORIGINAL, CopyType.ORIGINAL.bit());
+
+    private final short operations;
+    private final short lastOperation;
+
+    private CopyHistory(final int operations, final CopyType lastOperation) {
+        this.operations = (short) operations;
+        this.lastOperation = (short) lastOperation.ordinal();
+    }
+
+    public static CopyHistory original() {
+        return ORIGINAL;
+    }
+
+    public static CopyHistory of(final CopyType copyType, final CopyHistory copyHistory) {
+        return ORIGINAL.append(copyType, copyHistory);
+    }
+
+    private static CopyHistory[] cacheArray(final CopyType lastOperation) {
+        final int ordinal = lastOperation.ordinal();
+        CopyHistory[] ret = CACHE[ordinal];
+        if (ret == null) {
+            synchronized (CACHE) {
+                ret = CACHE[ordinal];
+                if (ret == null) {
+                    ret = new CopyHistory[1 << VALUES.length];
+                    CACHE[ordinal] = ret;
+                }
+            }
+        }
+
+        return ret;
+    }
+
+    private static CopyHistory cacheObject(final CopyType lastOperation, final int operations) {
+        final CopyHistory[] array = cacheArray(lastOperation);
+        CopyHistory ret = array[operations];
+        if (ret == null) {
+            synchronized (array) {
+                ret = array[operations];
+                if (ret == null) {
+                    ret = new CopyHistory(operations, lastOperation);
+                    array[operations] = ret;
+                }
+            }
+        }
+
+        return ret;
+    }
+
+    public boolean contains(final CopyType type) {
+        return (operations & type.bit()) != 0;
+    }
+
+    public CopyType getLastOperation() {
+        return VALUES[lastOperation];
+    }
+
+    @VisibleForTesting
+    CopyHistory append(final CopyType typeOfCopy, final CopyHistory toAppend) {
+        final int newOperations = operations | toAppend.operations | typeOfCopy.bit();
+        if (newOperations == operations && typeOfCopy.ordinal() == lastOperation) {
+            return this;
+        }
+
+        return cacheObject(typeOfCopy, newOperations);
+    }
+
+    @Override
+    public int hashCode() {
+        return Integer.hashCode(operations | lastOperation << Short.SIZE);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof CopyHistory)) {
+            return false;
+        }
+        final CopyHistory other = (CopyHistory) obj;
+        return operations == other.operations && lastOperation == other.lastOperation;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("lastOperation", getLastOperation())
+                .add("operations", Arrays.stream(VALUES).filter(value -> (value.bit() & operations) != 0)
+                    .collect(Collectors.toList()))
+                .toString();
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/CopyType.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/CopyType.java
new file mode 100644 (file)
index 0000000..1686cb1
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016 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.spi.meta;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Verify;
+
+@Beta
+public enum CopyType {
+    ORIGINAL,
+    ADDED_BY_USES,
+    ADDED_BY_AUGMENTATION,
+    ADDED_BY_USES_AUGMENTATION;
+
+    private final int bit;
+
+    CopyType() {
+        // CopyHistory relies on the fact that the result fits into a short
+        Verify.verify(ordinal() < Short.SIZE);
+        bit = 1 << ordinal();
+    }
+
+    int bit() {
+        return bit;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/DerivedIdentitiesNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/DerivedIdentitiesNamespace.java
new file mode 100644 (file)
index 0000000..23a8abb
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+
+/**
+ * Identity namespace. All identity names defined in a module and its submodules share the same identity identifier
+ * namespace.
+ */
+// FIXME: describe scoping and value
+public interface DerivedIdentitiesNamespace extends IdentifierNamespace<QName, List<StmtContext<?, ?, ?>>> {
+    NamespaceBehaviour<QName, List<StmtContext<?, ?, ?>>, @NonNull DerivedIdentitiesNamespace> BEHAVIOUR =
+            NamespaceBehaviour.global(DerivedIdentitiesNamespace.class);
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/DerivedNamespaceBehaviour.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/DerivedNamespaceBehaviour.java
new file mode 100644 (file)
index 0000000..a8919aa
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+
+/**
+ * An {@link NamespaceBehaviour} which derives keys from a different namespace.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ * @param <N> Namespace type
+ * @param <L> Original key type
+ * @param <O> Original namespace type
+ */
+public abstract class DerivedNamespaceBehaviour<K, V, L, N extends IdentifierNamespace<K, V>,
+       O extends IdentifierNamespace<L, ?>> extends NamespaceBehaviour<K, V, N> {
+
+    private final Class<O> derivedFrom;
+
+    protected DerivedNamespaceBehaviour(final Class<N> identifier, final Class<O> derivedFrom) {
+        super(identifier);
+        this.derivedFrom = requireNonNull(derivedFrom);
+    }
+
+    public Class<O> getDerivedFrom() {
+        return derivedFrom;
+    }
+
+    @Override
+    public Map<K, V> getAllFrom(final NamespaceStorageNode storage) {
+        throw new UnsupportedOperationException("Virtual namespaces does not support provision of all items.");
+    }
+
+    @Override
+    public abstract V getFrom(NamespaceBehaviour.NamespaceStorageNode storage, K key);
+
+    @Override
+    public void addTo(final NamespaceStorageNode storage, final K key, final V value) {
+        // Intentional noop
+    }
+
+    public abstract L getSignificantKey(K key);
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("derivedFrom", derivedFrom.getName());
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ForwardingStatementSupport.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ForwardingStatementSupport.java
new file mode 100644 (file)
index 0000000..56545cc
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.spi.meta;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ForwardingObject;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+
+/**
+ * Utility forwarding implementation of {@link StatementSupport} contract. This class is useful for implementing
+ * wrapped statements.
+ *
+ * @author Robert Varga
+ *
+ * @param <A> Argument type
+ * @param <D> Declared Statement representation
+ * @param <E> Effective Statement representation
+ */
+@Beta
+public abstract class ForwardingStatementSupport<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+    extends ForwardingObject implements StatementSupport<A, D, E> {
+
+    @Override
+    protected abstract StatementSupport<A, D, E> delegate();
+
+    @Override
+    public D createDeclared(final StmtContext<A, D, ?> ctx) {
+        return delegate().createDeclared(ctx);
+    }
+
+    @Override
+    public E createEffective(final StmtContext<A, D, E> ctx) {
+        return delegate().createEffective(ctx);
+    }
+
+    @Override
+    public StatementDefinition getPublicView() {
+        return delegate().getPublicView();
+    }
+
+    @Override
+    public A parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
+        return delegate().parseArgumentValue(ctx, value);
+    }
+
+    @Override
+    public void onStatementAdded(final Mutable<A, D, E> stmt) {
+        delegate().onStatementAdded(stmt);
+    }
+
+    @Override
+    public void onPreLinkageDeclared(final Mutable<A, D, E> stmt) {
+        delegate().onPreLinkageDeclared(stmt);
+    }
+
+    @Override
+    public void onLinkageDeclared(final Mutable<A, D, E> stmt) {
+        delegate().onLinkageDeclared(stmt);
+    }
+
+    @Override
+    public void onStatementDefinitionDeclared(final Mutable<A, D, E> stmt) {
+        delegate().onStatementDefinitionDeclared(stmt);
+    }
+
+    @Override
+    public void onFullDefinitionDeclared(final Mutable<A, D, E> stmt) {
+        delegate().onFullDefinitionDeclared(stmt);
+    }
+
+    @Override
+    public boolean hasArgumentSpecificSupports() {
+        return delegate().hasArgumentSpecificSupports();
+    }
+
+    @Override
+    public StatementSupport<?, ?, ?> getSupportSpecificForArgument(final String argument) {
+        return delegate().getSupportSpecificForArgument(argument);
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ImplicitParentAwareStatementSupport.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ImplicitParentAwareStatementSupport.java
new file mode 100644 (file)
index 0000000..0ca4129
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.spi.meta;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+/**
+ * {@link StatementSupport} trait for statements which create intermediate parent node for some of its child nodes.
+ * An example of this is RFC6020/RFC7950 choice statement, which creates implicit case statements for child containers
+ * and others.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface ImplicitParentAwareStatementSupport {
+    /**
+     * Returns implicit parent statement support for supplied statement definition, if it is defined. This allows
+     * implementations of this interface add implicit parent to the build context hierarchy before a substatement
+     * is created.
+     *
+     * @param stmtDef statement definition of substatement
+     * @return optional of implicit parent statement support
+     */
+    Optional<StatementSupport<?, ?, ?>> getImplicitParentFor(StatementDefinition stmtDef);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ImportedNamespaceContext.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ImportedNamespaceContext.java
new file mode 100644 (file)
index 0000000..d02d4e8
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+
+/**
+ * Special namespace which allows import of namespaces from other sources.
+ *
+ * <p>
+ * This namespace and its subclasses are used by model processor to
+ * link / import namespaces to context node from supplied {@link StmtContext}.
+ *
+ * <p>
+ * This abstraction allows for imports and includes be implement as derived
+ * namespaces of this, but is not tied only for import and include statements.
+ *
+ * @param <K> Imported context identifier
+ */
+public interface ImportedNamespaceContext<K> extends IdentifierNamespace<K, StmtContext<?, ?, ?>> {
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/InferenceException.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/InferenceException.java
new file mode 100644 (file)
index 0000000..35f49d0
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+
+/**
+ * Thrown when there is an inference error.
+ */
+public class InferenceException extends SourceException {
+    private static final long serialVersionUID = 1L;
+
+    public InferenceException(final @NonNull String message, final @NonNull StatementSourceReference source,
+            final Throwable cause) {
+        super(message, source, cause);
+    }
+
+    public InferenceException(final @NonNull String message, final @NonNull StatementSourceReference source) {
+        super(message, source);
+    }
+
+    public InferenceException(final @NonNull StatementSourceReference source, final @NonNull String format,
+            final Object... args) {
+        this(String.format(format, args), source);
+    }
+
+    /**
+     * Throw an instance of this exception if an expression evaluates to true. If the expression evaluates to false,
+     * this method does nothing.
+     *
+     * @param expression Expression to be evaluated
+     * @param source Statement source reference
+     * @param format Format string, according to {@link String#format(String, Object...)}.
+     * @param args Format string arguments, according to {@link String#format(String, Object...)}
+     * @throws InferenceException if the expression evaluates to true.
+     */
+    public static void throwIf(final boolean expression, final @NonNull StatementSourceReference source,
+            final @NonNull String format, final Object... args) {
+        if (expression) {
+            throw new InferenceException(source, format, args);
+        }
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/InvalidSubstatementException.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/InvalidSubstatementException.java
new file mode 100644 (file)
index 0000000..9c94716
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+
+/**
+ * Thrown when there was invalid element in YANG file.
+ */
+public class InvalidSubstatementException extends SourceException {
+    private static final long serialVersionUID = 1L;
+
+    public InvalidSubstatementException(final @NonNull String message, final @NonNull StatementSourceReference source) {
+        super(message, source);
+    }
+
+    public InvalidSubstatementException(final @NonNull String message, final @NonNull StatementSourceReference source,
+            final Throwable cause) {
+        super(message, source, cause);
+    }
+
+    public InvalidSubstatementException(final @NonNull StatementSourceReference source, final @NonNull String format,
+            final Object... args) {
+        this(String.format(format, args), source);
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/MissingSubstatementException.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/MissingSubstatementException.java
new file mode 100644 (file)
index 0000000..4919fb7
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+
+/**
+ * Thrown when there was Missing element in yang file.
+ */
+public class MissingSubstatementException extends SourceException {
+    private static final long serialVersionUID = 1L;
+
+    public MissingSubstatementException(final @NonNull String message, final @NonNull StatementSourceReference source) {
+        super(message, source);
+    }
+
+    public MissingSubstatementException(final @NonNull String message, final @NonNull StatementSourceReference source,
+            final Throwable cause) {
+        super(message, source, cause);
+    }
+
+    public MissingSubstatementException(final @NonNull StatementSourceReference source, final @NonNull String format,
+            final Object... args) {
+        this(String.format(format, args), source);
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ModelActionBuilder.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ModelActionBuilder.java
new file mode 100644 (file)
index 0000000..011b991
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.EFFECTIVE_MODEL;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+
+/**
+ * Builder for effective model inference action. Model inference action is core principle of transforming
+ * declared model into effective model.
+ *
+ * <p>
+ * Since YANG allows forward references, some inference actions need to be taken at a later point, where reference is
+ * actually resolved. Referenced objects are not retrieved directly but are represented as {@link Prerequisite}
+ * (prerequisite) for inference action to be taken.
+ *
+ * <p>
+ * Some existing YANG statements are more complex and also object, for which effective model may be inferred is also
+ * represented as a {@link Prerequisite} which, when reference is available, will contain target context, which may be
+ * used for inference action.
+ *
+ * <h2>Implementing inference action</h2>
+ * Effective inference action could always be splitted into two separate tasks:
+ * <ol>
+ * <li>Declaration of inference action and its prerequisites</li>
+ * <li>Execution of inference action</li>
+ * </ol>
+ *
+ * <p>
+ * In order to declare inference action following steps needs
+ * to be taken:
+ * <ol>
+ * <li>Use {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)} to obtain
+ * {@link ModelActionBuilder}.
+ * <li>Use builder to specify concrete prerequisites of inference action
+ * (other statements, values from identifier namespaces)
+ * <li>Use builder to specify concrete set of nodes (or forward references to nodes)
+ * which will inference action mutate.
+ * <li>Use {@link #apply(InferenceAction)} with {@link InferenceAction} implementation
+ * to register inference action.
+ * </ol>
+ *
+ * <p>
+ * An action will be executed when:
+ * <ul>
+ * <li> {@link InferenceAction#apply(InferenceContext)} - all prerequisites (and declared forward references) are met,
+ * action could dereference them and start applying changes.
+ * </li>
+ * <li>{@link InferenceAction#prerequisiteFailed(Collection)} - semantic parser finished all other satisfied
+ * inference actions and some of declared prerequisites was still not met.
+ * </li>
+ * </ul>
+ *
+ * <p>
+ * TODO: Insert real word example
+ *
+ * <h2>Design notes</h2>
+ * {@link java.util.concurrent.Future} seems as viable and more standard alternative to {@link Prerequisite}, but
+ * Futures also carries promise that resolution of it is carried in other thread, which will actually put additional
+ * constraints on semantic parser.
+ *
+ * <p>
+ * Also listening on multiple futures is costly, so we opted out of future and designed API, which later may introduce
+ * futures.
+ */
+public interface ModelActionBuilder {
+    interface InferenceContext {
+
+    }
+
+    @FunctionalInterface
+    interface Prerequisite<T> {
+        /**
+         * Returns associated prerequisite once it is resolved.
+         *
+         * @param ctx Inference context in which the prerequisite was satisfied
+         * @return associated prerequisite once it is resolved.
+         */
+        T resolve(InferenceContext ctx);
+    }
+
+    /**
+     * User-defined inference action.
+     */
+    interface InferenceAction {
+
+        /**
+         * Invoked once all prerequisites were met and forward references were resolved and inference action should be
+         * applied. Implementors may perform necessary changes to mutable objects which were declared.
+         *
+         * @throws InferenceException If inference action can not be processed. Note that this exception be used for
+         *         user to debug YANG sources, so should provide helpful context to fix issue in sources.
+         */
+        void apply(InferenceContext ctx);
+
+        /**
+         * Invoked once one of prerequisites was not met, even after all other satisfiable inference actions were
+         * processed.
+         *
+         * <p>
+         * Implementors MUST throw {@link InferenceException} if semantic processing of model should be stopped
+         * and failed.
+         *
+         * <p>
+         * List of failed prerequisites should be used to select right message / error type to debug problem in YANG
+         * sources.
+         *
+         * @param failed collection of prerequisites which were not met
+         * @throws InferenceException If inference action can not be processed. Note that this exception be used
+         *                            by user to debug YANG sources, hence it should provide helpful context to fix
+         *                            the issue in sources.
+         */
+        void prerequisiteFailed(Collection<? extends Prerequisite<?>> failed);
+
+        /**
+         * Invoked once the prerequisite is deemed unavailable due to conformance reasons. This typically happens when
+         * a feature-dependent prerequisite does not have the appropriate feature activated.
+         *
+         * <p>
+         * The default implementation invokes {@link #prerequisiteFailed(Collection)}, implementations should override
+         * this method if they wish, for example, to ignore the missing prerequisite.
+         *
+         * @param unavail Unavailable prerequisite
+         */
+        @Beta
+        default void prerequisiteUnavailable(final Prerequisite<?> unavail) {
+            prerequisiteFailed(ImmutableList.of(unavail));
+        }
+    }
+
+    /**
+     * Action requires that the specified context transition to complete {@link ModelProcessingPhase#FULL_DECLARATION}
+     * phase and produce a declared statement.
+     *
+     * @param context Statement context which needs to complete the transition.
+     * @return A {@link Prerequisite} returning the declared statement of the requested context.
+     */
+    <D extends DeclaredStatement<?>> @NonNull Prerequisite<D> requiresDeclared(StmtContext<?, ? extends D, ?> context);
+
+    /**
+     * Create a requirement on specified statement to be declared.
+     *
+     * @deprecated Undocumented method. Use at your own risk.
+     */
+    @Deprecated
+    <K, D extends DeclaredStatement<?>, N extends StatementNamespace<K, ? extends D, ?>>
+        @NonNull Prerequisite<D> requiresDeclared(StmtContext<?, ?, ?> context, Class<N> namespace, K key);
+
+    /**
+     * Action requires that the specified context completes specified phase before {@link #apply(InferenceAction)}
+     * may be invoked.
+     *
+     * @param context Statement context which needs to complete the transition.
+     * @param phase ModelProcessingPhase which must have completed
+     * @return A {@link Prerequisite} returning the requested context.
+     */
+    <A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>> @NonNull Prerequisite<StmtContext<A, D, E>>
+        requiresCtx(StmtContext<A, D, E> context, ModelProcessingPhase phase);
+
+    <K, N extends StatementNamespace<K, ?, ?>> @NonNull Prerequisite<StmtContext<?, ?, ?>> requiresCtx(
+        StmtContext<?, ?, ?> context, Class<N> namespace, K key, ModelProcessingPhase phase);
+
+    <K, N extends StatementNamespace<K, ?, ?>> @NonNull Prerequisite<StmtContext<?, ?, ?>> requiresCtx(
+            StmtContext<?, ?, ?> context, Class<N> namespace, NamespaceKeyCriterion<K> criterion,
+            ModelProcessingPhase phase);
+
+    /**
+     * Action mutates the effective model of specified statement. This is a shorthand for
+     * {@code mutatesCtx(context, EFFECTIVE_MODEL}.
+     *
+     * @param context Target statement context
+     * @return A {@link Prerequisite} returning the requested context.
+     */
+    default <T extends Mutable<?, ?, ?>> @NonNull Prerequisite<T> mutatesEffectiveCtx(final T context) {
+        return mutatesCtx(context, EFFECTIVE_MODEL);
+    }
+
+    <K, E extends EffectiveStatement<?, ?>, N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>>
+        @NonNull Prerequisite<Mutable<?, ?, E>> mutatesEffectiveCtx(StmtContext<?, ?, ?> context, Class<N> namespace,
+                K key);
+
+    default <K, E extends EffectiveStatement<?, ?>, N extends IdentifierNamespace<K, ? extends StmtContext<?, ?, ?>>>
+        @NonNull Prerequisite<Mutable<?, ?, E>> mutatesEffectiveCtxPath(final StmtContext<?, ?, ?> context,
+                final Class<N> namespace, final Iterable<K> keys) {
+        throw new UnsupportedOperationException(getClass() + " does not implement mutatesEffectiveCtxPath()");
+    }
+
+    /**
+     * Action mutates the specified statement in the specified phase. Target statement cannot complete specified
+     * phase before this action is applier.
+     *
+     * @param context Target statement context
+     * @return A {@link Prerequisite} returning the requested context.
+     */
+    <C extends Mutable<?, ?, ?>, T extends C> @NonNull Prerequisite<C> mutatesCtx(T context,
+            ModelProcessingPhase phase);
+
+    /**
+     * Apply an {@link InferenceAction} when this action's prerequisites are resolved.
+     *
+     * @param action Inference action to apply
+     * @throws InferenceException if the action fails
+     * @throws NullPointerException if {@code action is null}
+     * @throws IllegalStateException if this action has an inference action already associated.
+     */
+    void apply(InferenceAction action);
+
+    /**
+     * Create a requirement on specified statement context to be declared.
+     *
+     * @deprecated Undocumented method. Use at your own risk.
+     */
+    @Deprecated
+    <K, D extends DeclaredStatement<?>, N extends StatementNamespace<K, ? extends D, ?>>
+        @NonNull Prerequisite<StmtContext<?, D, ?>> requiresDeclaredCtx(StmtContext<?, ?, ?> context,
+                Class<N> namespace, K key);
+
+    /**
+     * Create a requirement on specified statement to become effective.
+     *
+     * @deprecated Undocumented method. Use at your own risk.
+     */
+    @Deprecated
+    <E extends EffectiveStatement<?, ?>> @NonNull Prerequisite<E> requiresEffective(
+            StmtContext<?, ?, ? extends E> stmt);
+
+    /**
+     * Create a requirement on specified statement to become effective.
+     *
+     * @deprecated Undocumented method. Use at your own risk.
+     */
+    @Deprecated
+    <K, E extends EffectiveStatement<?, ?>, N extends StatementNamespace<K, ?, ? extends E>>
+        @NonNull Prerequisite<E> requiresEffective(StmtContext<?, ?, ?> context, Class<N> namespace, K key);
+
+    /**
+     * Create a requirement on specified statement context to become effective.
+     *
+     * @deprecated Undocumented method. Use at your own risk.
+     */
+    @Deprecated
+    <K, E extends EffectiveStatement<?, ?>, N extends StatementNamespace<K, ?, ? extends E>>
+        @NonNull Prerequisite<StmtContext<?, ?, E>> requiresEffectiveCtx(StmtContext<?, ?, ?> context,
+                Class<N> namespace, K key);
+
+    /**
+     * Mark the fact that this action is mutating a namespace.
+     *
+     * @deprecated Undocumented method. Use at your own risk.
+     */
+    @Deprecated
+    <N extends IdentifierNamespace<?, ?>> @NonNull Prerequisite<Mutable<?,?,?>> mutatesNs(Mutable<?,?, ?> ctx,
+            Class<N> namespace);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ModelProcessingPhase.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ModelProcessingPhase.java
new file mode 100644 (file)
index 0000000..9181631
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+@NonNullByDefault
+@SuppressFBWarnings("NP_NULL_PARAM_DEREF_NONVIRTUAL")
+public enum ModelProcessingPhase {
+    INIT(null),
+
+    /**
+     * Preliminary cross-source relationship resolution phase which collects available module names and module
+     * namespaces. It is necessary in order to correct resolution of unknown statements used in linkage phase (e.g.
+     * semantic version of yang modules).
+     */
+    SOURCE_PRE_LINKAGE(INIT),
+
+    /**
+     * Cross-source relationship resolution phase.
+     *
+     * <p>
+     * In this phase of processing only statements which affects cross-source relationship (e.g. imports / includes)
+     * are processed.
+     *
+     * <p>
+     * At end of this phase all source related contexts should be bind to their imports and includes to allow
+     * visibility of custom defined statements in subsequent phases.
+     */
+    SOURCE_LINKAGE(SOURCE_PRE_LINKAGE),
+    STATEMENT_DEFINITION(SOURCE_LINKAGE),
+    FULL_DECLARATION(STATEMENT_DEFINITION),
+    EFFECTIVE_MODEL(FULL_DECLARATION);
+
+    private final @Nullable ModelProcessingPhase previousPhase;
+
+    ModelProcessingPhase(final @Nullable ModelProcessingPhase previous) {
+        this.previousPhase = previous;
+    }
+
+    public @Nullable ModelProcessingPhase getPreviousPhase() {
+        return previousPhase;
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/MutableStatement.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/MutableStatement.java
new file mode 100644 (file)
index 0000000..6d4cad1
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 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.spi.meta;
+
+/**
+ * Mutable statement interface. Each mutable statement must be sealed as the
+ * last step of statement parser processing.
+ */
+public interface MutableStatement {
+    /**
+     * Finish statement and make it immutable. After this method is invoked, any
+     * further modifications of current object are not allowed.
+     */
+    void seal();
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/NamespaceBehaviour.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/NamespaceBehaviour.java
new file mode 100644 (file)
index 0000000..dec746e
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Verify;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+
+/**
+ * Definition / implementation of specific Identifier Namespace behaviour. A namespace behaviour is built on top
+ * of a tree of {@link NamespaceStorageNode} which represents local context of one of types defined
+ * n {@link StorageNodeType}.
+ *
+ * <p>
+ * For common behaviour models please use static factories {@link #global(Class)}, {@link #sourceLocal(Class)} and
+ * {@link #treeScoped(Class)}.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ * @param <N> Namespace Type
+ */
+public abstract class NamespaceBehaviour<K, V, N extends IdentifierNamespace<K, V>>
+        extends AbstractIdentifiable<Class<N>> {
+
+    public enum StorageNodeType {
+        /**
+         * Global storage, visible from all sources.
+         */
+        GLOBAL,
+        /**
+         * Storage of the root statement of a particular source and any sources it is importing.
+         */
+        // FIXME: 5.0.0: this is a misnomer and should be renamed
+        SOURCE_LOCAL_SPECIAL,
+        /**
+         * Storage of a single statement.
+         */
+        STATEMENT_LOCAL,
+        /**
+         * Storage of the root statement of a particular source.
+         */
+        ROOT_STATEMENT_LOCAL
+    }
+
+    public interface Registry {
+        /**
+         * Get a namespace behavior.
+         *
+         * @param type Namespace type class
+         * @param <K> key type
+         * @param <V> value type
+         * @param <N> namespace type
+         * @return Namespace behaviour
+         * @throws NamespaceNotAvailableException when the namespace is not available
+         */
+        <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> getNamespaceBehaviour(Class<N> type);
+    }
+
+    public interface NamespaceStorageNode {
+        /**
+         * Return local namespace behaviour type.
+         *
+         * @return local namespace behaviour type {@link NamespaceBehaviour}
+         */
+        StorageNodeType getStorageNodeType();
+
+        @Nullable NamespaceStorageNode getParentNamespaceStorage();
+
+        <K, V, N extends IdentifierNamespace<K, V>> @Nullable V getFromLocalStorage(Class<N> type, K key);
+
+        <K, V, N extends IdentifierNamespace<K, V>> @Nullable Map<K, V> getAllFromLocalStorage(Class<N> type);
+
+        /**
+         * Populate specified namespace with a key/value pair, overwriting previous contents. Similar to
+         * {@link Map#put(Object, Object)}.
+         *
+         * @param type Namespace identifier
+         * @param key Key
+         * @param value Value
+         * @return Previously-stored value, or null if the key was not present
+         */
+        <K, V, N extends IdentifierNamespace<K, V>> @Nullable V putToLocalStorage(Class<N> type, K key, V value);
+
+        /**
+         * Populate specified namespace with a key/value pair unless the key is already associated with a value. Similar
+         * to {@link Map#putIfAbsent(Object, Object)}.
+         *
+         * @param type Namespace identifier
+         * @param key Key
+         * @param value Value
+         * @return Preexisting value or null if there was no previous mapping
+         */
+        <K, V, N extends IdentifierNamespace<K, V>> @Nullable V putToLocalStorageIfAbsent(Class<N> type, K key,
+                V value);
+    }
+
+    protected NamespaceBehaviour(final Class<N> identifier) {
+        super(identifier);
+    }
+
+    /**
+     * Creates a global namespace behaviour for supplied namespace type. Global behaviour stores and loads all values
+     * from root {@link NamespaceStorageNode} with type of {@link StorageNodeType#GLOBAL}.
+     *
+     * @param identifier Namespace identifier.
+     * @param <K> type parameter
+     * @param <V> type parameter
+     * @param <N> type parameter
+     * @return global namespace behaviour for supplied namespace type.
+     */
+    public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> global(
+            final Class<N> identifier) {
+        return new StorageSpecific<>(identifier, StorageNodeType.GLOBAL);
+    }
+
+    /**
+     * Creates source-local namespace behaviour for supplied namespace type. Source-local namespace behaviour stores
+     * and loads all values from closest {@link NamespaceStorageNode} ancestor with type
+     * of {@link StorageNodeType#SOURCE_LOCAL_SPECIAL}.
+     *
+     * @param identifier Namespace identifier.
+     * @param <K> type parameter
+     * @param <V> type parameter
+     * @param <N> type parameter
+     * @return source-local namespace behaviour for supplied namespace type.
+     */
+    public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> sourceLocal(
+            final Class<N> identifier) {
+        return new StorageSpecific<>(identifier, StorageNodeType.SOURCE_LOCAL_SPECIAL);
+    }
+
+    public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> statementLocal(
+           final Class<N> identifier) {
+        return new StorageSpecific<>(identifier, StorageNodeType.STATEMENT_LOCAL);
+    }
+
+    /**
+     * Creates a root-statement-local namespace behaviour for supplied namespace type. Root-statement-local namespace
+     * behaviour stores and loads all values from closest {@link NamespaceStorageNode} ancestor with type
+     * of {@link StorageNodeType#ROOT_STATEMENT_LOCAL}.
+     *
+     * @param identifier Namespace identifier.
+     * @param <K> type parameter
+     * @param <V> type parameter
+     * @param <N> type parameter
+     * @return root-statement-local namespace behaviour for supplied namespace type.
+     */
+    public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> rootStatementLocal(
+            final Class<N> identifier) {
+        return new StorageSpecific<>(identifier, StorageNodeType.ROOT_STATEMENT_LOCAL);
+    }
+
+    /**
+     * Creates tree-scoped namespace behaviour for supplied namespace type. Tree-scoped namespace behaviour searches
+     * for value in all storage nodes up to the root and stores values in supplied node.
+     *
+     * @param identifier
+     *            Namespace identifier.
+     * @param <K> type parameter
+     * @param <V> type parameter
+     * @param <N> type parameter
+     * @return tree-scoped namespace behaviour for supplied namespace type.
+     */
+    public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> treeScoped(
+            final Class<N> identifier) {
+        return new TreeScoped<>(identifier);
+    }
+
+    /**
+     * Returns a value from model namespace storage according to key param class.
+     *
+     * @param storage namespace storage
+     * @param key type parameter
+     * @return value from model namespace storage according to key param class
+     */
+    public abstract V getFrom(NamespaceStorageNode storage, K key);
+
+    /**
+     * Returns the key/value mapping best matching specified criterion.
+     *
+     * @param storage namespace storage
+     * @param criterion selection criterion
+     * @return Selected mapping, if available.
+     */
+    public final Optional<Entry<K, V>> getFrom(final NamespaceStorageNode storage,
+            final NamespaceKeyCriterion<K> criterion) {
+        final Map<K, V> mappings = getAllFrom(storage);
+        if (mappings == null) {
+            return Optional.empty();
+        }
+
+        Entry<K, V> match = null;
+        for (Entry<K, V> entry : mappings.entrySet()) {
+            final K key = entry.getKey();
+            if (criterion.match(key)) {
+                if (match != null) {
+                    final K selected = criterion.select(match.getKey(), key);
+                    if (selected.equals(match.getKey())) {
+                        continue;
+                    }
+
+                    Verify.verify(selected == key, "Criterion %s selected invalid key %s from candidates [%s %s]",
+                            selected, match.getKey(), key);
+                }
+
+                match = entry;
+            }
+        }
+
+        return Optional.ofNullable(match);
+    }
+
+    /**
+     * Returns all values of a keys of param class from model namespace storage.
+     *
+     * @param storage namespace storage
+     * @return all values of keys of param class from model namespace storage
+     */
+    public abstract Map<K, V> getAllFrom(NamespaceStorageNode storage);
+
+    /**
+     * Adds a key/value to corresponding namespace storage according to param class.
+     *
+     * @param storage namespace storage
+     * @param key type parameter
+     * @param value type parameter
+     */
+    public abstract void addTo(NamespaceStorageNode storage, K key, V value);
+
+    protected final V getFromLocalStorage(final NamespaceStorageNode storage, final K key) {
+        return storage.getFromLocalStorage(getIdentifier(), key);
+    }
+
+    protected final Map<K, V> getAllFromLocalStorage(final NamespaceStorageNode storage) {
+        return storage.getAllFromLocalStorage(getIdentifier());
+    }
+
+    protected final void addToStorage(final NamespaceStorageNode storage, final K key, final V value) {
+        storage.putToLocalStorage(getIdentifier(), key, value);
+    }
+
+    static final class StorageSpecific<K, V, N extends IdentifierNamespace<K, V>> extends NamespaceBehaviour<K, V, N> {
+        StorageNodeType storageType;
+
+        StorageSpecific(final Class<N> identifier, final StorageNodeType type) {
+            super(identifier);
+            storageType = requireNonNull(type);
+        }
+
+        @Override
+        public V getFrom(final NamespaceStorageNode storage, final K key) {
+            NamespaceStorageNode current = findClosestTowardsRoot(storage, storageType);
+            return getFromLocalStorage(current, key);
+        }
+
+        @Override
+        public Map<K, V> getAllFrom(final NamespaceStorageNode storage) {
+            NamespaceStorageNode current = storage;
+            while (current.getStorageNodeType() != storageType) {
+                current = current.getParentNamespaceStorage();
+            }
+
+            return getAllFromLocalStorage(current);
+        }
+
+        @Override
+        public void addTo(final NamespaceBehaviour.NamespaceStorageNode storage, final K key, final V value) {
+            NamespaceStorageNode current = findClosestTowardsRoot(storage, storageType);
+            addToStorage(current, key, value);
+        }
+
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            return super.addToStringAttributes(helper.add("type", storageType));
+        }
+    }
+
+    static final class TreeScoped<K, V, N extends IdentifierNamespace<K, V>> extends NamespaceBehaviour<K, V, N> {
+
+        TreeScoped(final Class<N> identifier) {
+            super(identifier);
+        }
+
+        @Override
+        public V getFrom(final NamespaceStorageNode storage, final K key) {
+            NamespaceStorageNode current = storage;
+            while (current != null) {
+                final V val = getFromLocalStorage(current, key);
+                if (val != null) {
+                    return val;
+                }
+                current = current.getParentNamespaceStorage();
+            }
+            return null;
+        }
+
+        @Override
+        public Map<K, V> getAllFrom(final NamespaceStorageNode storage) {
+            NamespaceStorageNode current = storage;
+            while (current != null) {
+                final Map<K, V> val = getAllFromLocalStorage(current);
+                if (val != null) {
+                    return val;
+                }
+                current = current.getParentNamespaceStorage();
+            }
+            return null;
+        }
+
+        @Override
+        public void addTo(final NamespaceStorageNode storage, final K key, final V value) {
+            addToStorage(storage, key, value);
+        }
+
+    }
+
+    protected static NamespaceStorageNode findClosestTowardsRoot(final NamespaceStorageNode storage,
+            final StorageNodeType type) {
+        NamespaceStorageNode current = storage;
+        while (current != null && current.getStorageNodeType() != type) {
+            current = current.getParentNamespaceStorage();
+        }
+        return current;
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("identifier", getIdentifier().getName());
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/NamespaceKeyCriterion.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/NamespaceKeyCriterion.java
new file mode 100644 (file)
index 0000000..a3ea675
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.spi.meta;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Namespace key matching criterion.
+ *
+ * @param <K> Key type
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class NamespaceKeyCriterion<K> {
+    private static final class LatestRevisionModule extends NamespaceKeyCriterion<SourceIdentifier> {
+        private final String moduleName;
+
+        LatestRevisionModule(final String moduleName) {
+            this.moduleName = requireNonNull(moduleName);
+        }
+
+        @Override
+        public boolean match(final SourceIdentifier key) {
+            return moduleName.equals(key.getName());
+        }
+
+        @Override
+        public SourceIdentifier select(final SourceIdentifier first, final SourceIdentifier second) {
+            return Revision.compare(first.getRevision(), second.getRevision()) >= 0 ? first : second;
+        }
+
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+            return toStringHelper.add("moduleName", moduleName);
+        }
+    }
+
+    /**
+     * Return a criterion which selects the latest known revision of a particular module.
+     *
+     * @param moduleName Module name
+     * @return A criterion object.
+     */
+    public static NamespaceKeyCriterion<SourceIdentifier> latestRevisionModule(final String moduleName) {
+        return new LatestRevisionModule(moduleName);
+    }
+
+    /**
+     * Match a key against this criterion.
+     *
+     * @param key Key to be matched
+     * @return True if the key matches this criterion, false otherwise.
+     */
+    public abstract boolean match(@NonNull K key);
+
+    /**
+     * Select the better match from two candidate keys.
+     *
+     * @param first First key
+     * @param second Second key
+     * @return Selected key, must be either first or second key, by identity.
+     */
+    public abstract K select(@NonNull K first, @NonNull K second);
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper;
+    }
+
+    @Override
+    public final int hashCode() {
+        return super.hashCode();
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        return super.equals(obj);
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/NamespaceNotAvailableException.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/NamespaceNotAvailableException.java
new file mode 100644 (file)
index 0000000..93a0a32
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Thrown when identifier namespace is not available (supported) in a specific model processing phase.
+ */
+public class NamespaceNotAvailableException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    public NamespaceNotAvailableException(final String message) {
+        super(requireNonNull(message));
+    }
+
+    public NamespaceNotAvailableException(final String message, final Throwable cause) {
+        super(requireNonNull(message), cause);
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/QNameCacheNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/QNameCacheNamespace.java
new file mode 100644 (file)
index 0000000..89043bb
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import java.util.Map;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+
+/**
+ * An {@link IdentifierNamespace} implementing a {@link QName} internment interface. Lookups in this namespace always
+ * return a non-null object. They capture the first object instance and return that on subsequent lookups.
+ */
+public final class QNameCacheNamespace extends NamespaceBehaviour<QName, QName, QNameCacheNamespace>
+    implements IdentifierNamespace<QName, QName> {
+
+    private static final QNameCacheNamespace INSTANCE = new QNameCacheNamespace();
+
+    private QNameCacheNamespace() {
+        super(QNameCacheNamespace.class);
+    }
+
+    public static QNameCacheNamespace getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public QName get(final QName identifier) {
+        throw new UnsupportedOperationException("Identifier/implementation API borkage");
+    }
+
+    private static NamespaceStorageNode getRoot(final NamespaceStorageNode storage) {
+        NamespaceStorageNode wlk = storage;
+
+        while (wlk.getParentNamespaceStorage() != null) {
+            wlk = wlk.getParentNamespaceStorage();
+        }
+
+        return wlk;
+    }
+
+    @Override
+    public QName getFrom(final NamespaceStorageNode storage, final QName key) {
+        final NamespaceStorageNode root = getRoot(storage);
+        final QName stored = root.getFromLocalStorage(QNameCacheNamespace.class, key);
+        if (stored != null) {
+            return stored;
+        }
+
+        root.putToLocalStorage(QNameCacheNamespace.class, key, key);
+        return key;
+    }
+
+    @Override
+    public Map<QName, QName> getAllFrom(final NamespaceStorageNode storage) {
+        return getRoot(storage).getAllFromLocalStorage(QNameCacheNamespace.class);
+    }
+
+    @Override
+    public void addTo(final NamespaceStorageNode storage, final QName key, final QName value) {
+        throw new UnsupportedOperationException("Automagically populated");
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ReactorException.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/ReactorException.java
new file mode 100644 (file)
index 0000000..476d790
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+public class ReactorException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    private final ModelProcessingPhase phase;
+    private final SourceIdentifier sourceIdentifier;
+
+    public ReactorException(final ModelProcessingPhase phase, final String message, final SourceIdentifier sourceId,
+            final Throwable cause) {
+        super(message, cause);
+        this.phase = requireNonNull(phase);
+        this.sourceIdentifier = sourceId;
+    }
+
+    public ReactorException(final ModelProcessingPhase phase, final String message, final SourceIdentifier sourceId) {
+        super(message);
+        this.phase = requireNonNull(phase);
+        this.sourceIdentifier = sourceId;
+    }
+
+    public final ModelProcessingPhase getPhase() {
+        return phase;
+    }
+
+    public final SourceIdentifier getSourceIdentifier() {
+        return sourceIdentifier;
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SemanticVersionModuleNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SemanticVersionModuleNamespace.java
new file mode 100644 (file)
index 0000000..57eab17
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 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.spi.meta;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
+
+/**
+ * Namespace class for storing Maps of all modules with the same name. This namespace is
+ * used only in case the semantic versioning is enabled, otherwise it is empty.
+ */
+@Beta
+public interface SemanticVersionModuleNamespace
+    extends StatementNamespace<SemVerSourceIdentifier, ModuleStatement, ModuleEffectiveStatement> {
+    NamespaceBehaviour<SemVerSourceIdentifier, StmtContext<?, ModuleStatement, ModuleEffectiveStatement>,
+            @NonNull SemanticVersionModuleNamespace> BEHAVIOUR =
+            NamespaceBehaviour.global(SemanticVersionModuleNamespace.class);
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SemanticVersionNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SemanticVersionNamespace.java
new file mode 100644 (file)
index 0000000..4394b6b
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2016 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.spi.meta;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+
+/**
+ * Namespace class for storing semantic version of yang modules.
+ */
+@Beta
+public interface SemanticVersionNamespace extends IdentifierNamespace<StmtContext<?, ?, ?>, SemVer> {
+    NamespaceBehaviour<StmtContext<?, ?, ?>, SemVer, @NonNull SemanticVersionNamespace> BEHAVIOUR =
+            NamespaceBehaviour.global(SemanticVersionNamespace.class);
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SomeModifiersUnresolvedException.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SomeModifiersUnresolvedException.java
new file mode 100644 (file)
index 0000000..ef3b151
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+public class SomeModifiersUnresolvedException extends ReactorException {
+
+    private static final long serialVersionUID = 1L;
+
+    public SomeModifiersUnresolvedException(ModelProcessingPhase phase, SourceIdentifier sourceId, Throwable cause) {
+        super(phase, "Some of " + phase + " modifiers for statements were not resolved.", sourceId, cause);
+    }
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementDefinitionNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementDefinitionNamespace.java
new file mode 100644 (file)
index 0000000..f851c29
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+/**
+ * Module-specific namespace for holding {@link StatementDefinition}s defined by extension statements. This namespace
+ * is populated before full declaration phase.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface StatementDefinitionNamespace extends IdentifierNamespace<QName, StatementSupport<?, ?, ?>> {
+    NamespaceBehaviour<QName, StatementSupport<?, ?, ?>, @NonNull StatementDefinitionNamespace> BEHAVIOUR =
+            NamespaceBehaviour.global(StatementDefinitionNamespace.class);
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementFactory.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementFactory.java
new file mode 100644 (file)
index 0000000..458a188
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+public interface StatementFactory<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>> {
+    /**
+     * Create a {@link DeclaredStatement} for specified context.
+     *
+     * @param ctx Statement context
+     * @return A declared statement instance.
+     */
+    @NonNull D createDeclared(@NonNull StmtContext<A, D, ?> ctx);
+
+    /**
+     * Create a {@link EffectiveStatement} for specified context.
+     *
+     * @param ctx Statement context
+     * @return An effective statement instance.
+     */
+    @NonNull E createEffective(@NonNull StmtContext<A, D, E> ctx);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementNamespace.java
new file mode 100644 (file)
index 0000000..e108098
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+
+public interface StatementNamespace<K, D extends DeclaredStatement<?>, E extends EffectiveStatement<?, D>> extends
+        IdentifierNamespace<K, StmtContext<?, D, E>> {
+    @Override
+    StmtContext<?, D, E> get(K key);
+
+    interface TreeScoped<K, D extends DeclaredStatement<?>, E extends EffectiveStatement<?, D>> extends
+            StatementNamespace<K, D, E> {
+        TreeScoped<K, D, E> getParentContext();
+    }
+
+    interface TreeBased<K, D extends DeclaredStatement<?>, E extends EffectiveStatement<?, D>> {
+
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementSupport.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementSupport.java
new file mode 100644 (file)
index 0000000..22e20df
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.ArgumentDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+/**
+ * Support for processing concrete YANG statement.
+ *
+ * <p>
+ * This interface is intended to be implemented by developers, which want to
+ * introduce support of statement to parser. Consider subclassing
+ * {@link AbstractStatementSupport} for easier implementation of this interface.
+ *
+ * @param <A>
+ *            Argument type
+ * @param <D>
+ *            Declared Statement representation
+ * @param <E>
+ *            Effective Statement representation
+ */
+public interface StatementSupport<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+        extends StatementDefinition, StatementFactory<A, D, E> {
+
+    /**
+     * Returns public statement definition, which will be present in built statements.
+     *
+     * <p>
+     * Public statement definition may be used to provide different implementation of statement definition,
+     * which will not retain any build specific data or context.
+     *
+     * @return public statement definition, which will be present in built statements.
+     */
+    @NonNull StatementDefinition getPublicView();
+
+    /**
+     * Parses textual representation of argument in object representation.
+     *
+     * @param ctx Context, which may be used to access source-specific namespaces required for parsing.
+     * @param value String representation of value, as was present in text source.
+     * @return Parsed value
+     * @throws SourceException when an inconsistency is detected.
+     */
+    A parseArgumentValue(StmtContext<?, ?, ?> ctx, String value);
+
+    /**
+     * Adapts the argument value to match a new module.
+     *
+     * @param ctx
+     *            Context, which may be used to access source-specific
+     *            namespaces required for parsing.
+     * @param targetModule
+     *            Target module, may not be null.
+     * @return Adapted argument value. The default implementation returns original value stored in context.
+     */
+    default A adaptArgumentValue(final StmtContext<A, D, E> ctx, final QNameModule targetModule) {
+        return ctx.getStatementArgument();
+    }
+
+    /**
+     * Invoked when a statement supported by this instance is added to build context. This allows implementations
+     * of this interface to start tracking the statement and perform any modifications to the build context hierarchy,
+     * accessible via {@link StmtContext#getParentContext()}. One such use is populating the parent's namespaces to
+     * allow it to locate this child statement.
+     *
+     * @param stmt
+     *            Context of added statement. No substatements are available.
+     */
+    void onStatementAdded(StmtContext.Mutable<A, D, E> stmt);
+
+    /**
+     * Invoked when statement is closed during {@link ModelProcessingPhase#SOURCE_PRE_LINKAGE} phase, only substatements
+     * from this and previous phase are available.
+     *
+     * <p>
+     * Implementation may use method to perform actions on this event or register modification action using
+     * {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
+     *
+     * @param stmt
+     *            Context of added statement.
+     */
+    void onPreLinkageDeclared(StmtContext.Mutable<A, D, E> stmt);
+
+    /**
+     * Invoked when statement is closed during {@link ModelProcessingPhase#SOURCE_LINKAGE} phase, only substatements
+     * from this and previous phase are available.
+     *
+     * <p>
+     * Implementation may use method to perform actions on this event or register modification action using
+     * {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
+     *
+     * @param stmt
+     *            Context of added statement.
+     * @throws SourceException
+     *             when an inconsistency is detected.
+     */
+    void onLinkageDeclared(StmtContext.Mutable<A, D, E> stmt);
+
+    /**
+     * Invoked when statement is closed during {@link ModelProcessingPhase#STATEMENT_DEFINITION} phase,
+     * only substatements from this phase are available.
+     *
+     * <p>
+     * Implementation may use method to perform actions on this event or register modification action using
+     * {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
+     *
+     * @param stmt
+     *            Context of added statement. Argument and statement parent is
+     *            accessible.
+     * @throws SourceException
+     *             when an inconsistency is detected.
+     */
+    void onStatementDefinitionDeclared(StmtContext.Mutable<A, D, E> stmt);
+
+    /**
+     * Invoked when statement is closed during {@link ModelProcessingPhase#FULL_DECLARATION} phase,
+     * only substatements from this phase are available.
+     *
+     * <p>
+     * Implementation may use method to perform actions on this event or register modification action using
+     * {@link StmtContext.Mutable#newInferenceAction(ModelProcessingPhase)}.
+     *
+     * @param stmt
+     *            Context of added statement. Argument and statement parent is
+     *            accessible.
+     * @throws SourceException
+     *             when an inconsistency is detected.
+     */
+    void onFullDefinitionDeclared(StmtContext.Mutable<A, D, E> stmt);
+
+    /**
+     * Returns true if this support has argument specific supports.
+     */
+    boolean hasArgumentSpecificSupports();
+
+    /**
+     * If this support has argument specific supports, the method returns support specific for given argument
+     * (e.g. type statement support need to be specialized based on its argument), otherwise returns null.
+     *
+     * @param argument
+     *            argument of statement
+     * @return statement support specific for supplied argument or null
+     */
+    @Nullable StatementSupport<?, ?, ?> getSupportSpecificForArgument(String argument);
+
+    /**
+     * Given a raw string representation of an argument, try to use a shared representation.
+     *
+     * @param rawArgument
+     *            Argument string
+     * @return A potentially-shard instance
+     */
+    default String internArgument(final String rawArgument) {
+        return rawArgument;
+    }
+
+    /**
+     * Returns unknown statement form of a regular YANG statement supplied as a parameter to the method.
+     *
+     * @param yangStmtDef
+     *            statement definition of a regular yang statement
+     * @return Optional of unknown statement form of a regular yang statement or
+     *         Optional.empty() if it is not supported by this statement support
+     */
+    default Optional<StatementSupport<?, ?, ?>> getUnknownStatementDefinitionOf(final StatementDefinition yangStmtDef) {
+        return Optional.empty();
+    }
+
+    /**
+     * Returns true if this statement support and all its substatements ignore if-feature statements (e.g. yang-data
+     * extension defined in <a href="https://tools.ietf.org/html/rfc8040#section-8">RFC 8040</a>). Default
+     * implementation returns false.
+     *
+     * @return true if this statement support ignores if-feature statements,
+     *         otherwise false.
+     */
+    @Beta
+    default boolean isIgnoringIfFeatures() {
+        return false;
+    }
+
+    /**
+     * Returns true if this statement support and all its substatements ignore config statements (e.g. yang-data
+     * extension defined in <a href="https://tools.ietf.org/html/rfc8040#section-8">RFC 8040</a>). Default
+     * implementation returns false.
+     *
+     * @return true if this statement support ignores config statements,
+     *         otherwise false.
+     */
+    @Beta
+    default boolean isIgnoringConfig() {
+        return false;
+    }
+
+    @Override
+    default QName getStatementName() {
+        return getPublicView().getStatementName();
+    }
+
+    @Override
+    default @NonNull Optional<ArgumentDefinition> getArgumentDefinition() {
+        return getPublicView().getArgumentDefinition();
+    }
+
+    @Override
+    default Class<? extends DeclaredStatement<?>> getDeclaredRepresentationClass() {
+        return getPublicView().getDeclaredRepresentationClass();
+    }
+
+    @Override
+    default Class<? extends EffectiveStatement<?,?>> getEffectiveRepresentationClass() {
+        return getPublicView().getEffectiveRepresentationClass();
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementSupportBundle.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StatementSupportBundle.java
new file mode 100644 (file)
index 0000000..10ead73
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class StatementSupportBundle implements Immutable, NamespaceBehaviour.Registry {
+
+    private static final StatementSupportBundle EMPTY = new StatementSupportBundle(null, null, ImmutableMap.of(),
+            ImmutableMap.of(), ImmutableTable.of());
+
+    private final StatementSupportBundle parent;
+    private final ImmutableMap<QName, StatementSupport<?, ?, ?>> commonDefinitions;
+    private final ImmutableTable<YangVersion, QName, StatementSupport<?, ?, ?>> versionSpecificDefinitions;
+    private final ImmutableMap<Class<?>, NamespaceBehaviour<?, ?, ?>> namespaceDefinitions;
+    private final ImmutableSet<YangVersion> supportedVersions;
+
+    private StatementSupportBundle(final StatementSupportBundle parent,
+            final ImmutableSet<YangVersion> supportedVersions,
+            final ImmutableMap<QName, StatementSupport<?, ?, ?>> commonStatements,
+            final ImmutableMap<Class<?>, NamespaceBehaviour<?, ?, ?>> namespaces,
+            final ImmutableTable<YangVersion, QName, StatementSupport<?, ?, ?>> versionSpecificStatements) {
+        this.parent = parent;
+        this.supportedVersions = supportedVersions;
+        this.commonDefinitions = commonStatements;
+        this.namespaceDefinitions = namespaces;
+        this.versionSpecificDefinitions = versionSpecificStatements;
+    }
+
+    /**
+     * Returns statement definitions common for all versions.
+     *
+     * @return map of common statement definitions
+     */
+    public ImmutableMap<QName, StatementSupport<?, ?, ?>> getCommonDefinitions() {
+        return commonDefinitions;
+    }
+
+    /**
+     * Returns statement definitions specific for requested version. Result of this method does nit include common
+     * statement definitions.
+     *
+     * @param version
+     *            requested version
+     * @return map of statement definitions specific for requested version, it
+     *         doesn't include common statement definitions.
+     */
+    public ImmutableMap<QName, StatementSupport<?, ?, ?>> getDefinitionsSpecificForVersion(final YangVersion version) {
+        return versionSpecificDefinitions.row(version);
+    }
+
+    /**
+     * Returns all version specific statement definitions. Result of this method does not include common statement
+     * definitions.
+     *
+     * @return table of all version specific statement definitions, it doesn't
+     *         include common statement definitions.
+     */
+    public ImmutableTable<YangVersion, QName, StatementSupport<?, ?, ?>> getAllVersionSpecificDefinitions() {
+        return versionSpecificDefinitions;
+    }
+
+    public ImmutableMap<Class<?>, NamespaceBehaviour<?, ?, ?>> getNamespaceDefinitions() {
+        return namespaceDefinitions;
+    }
+
+    public static Builder builder(final Set<YangVersion> supportedVersions) {
+        return new Builder(supportedVersions, EMPTY);
+    }
+
+    public static Builder derivedFrom(final StatementSupportBundle parent) {
+        return new Builder(parent.getSupportedVersions(), parent);
+    }
+
+    public Set<YangVersion> getSupportedVersions() {
+        return supportedVersions;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> getNamespaceBehaviour(
+            final Class<N> namespace) {
+        final NamespaceBehaviour<?, ?, ?> potential = namespaceDefinitions.get(namespace);
+        if (potential != null) {
+            checkState(namespace.equals(potential.getIdentifier()));
+
+            // Safe cast, previous checkState checks equivalence of key from which type argument are derived
+            return (NamespaceBehaviour<K, V, N>) potential;
+        }
+        if (parent != null) {
+            return parent.getNamespaceBehaviour(namespace);
+        }
+        return null;
+    }
+
+    public <K, V, N extends IdentifierNamespace<K, V>> boolean hasNamespaceBehaviour(final Class<N> namespace) {
+        if (namespaceDefinitions.containsKey(namespace)) {
+            return true;
+        }
+        if (parent != null) {
+            return parent.hasNamespaceBehaviour(namespace);
+        }
+        return false;
+    }
+
+    public StatementSupport<?, ?, ?> getStatementDefinition(final YangVersion version, final QName stmtName) {
+        StatementSupport<?, ?, ?> result = getVersionSpecificStatementDefinition(version, stmtName);
+        if (result == null) {
+            result = getCommonStatementDefinition(stmtName);
+        }
+
+        return result;
+    }
+
+    private StatementSupport<?, ?, ?> getCommonStatementDefinition(final QName stmtName) {
+        final StatementSupport<?, ?, ?> potential = commonDefinitions.get(stmtName);
+        if (potential != null) {
+            return potential;
+        }
+        if (parent != null) {
+            return parent.getCommonStatementDefinition(stmtName);
+        }
+        return null;
+    }
+
+    private StatementSupport<?, ?, ?> getVersionSpecificStatementDefinition(final YangVersion version,
+            final QName stmtName) {
+        final StatementSupport<?, ?, ?> potential = versionSpecificDefinitions.get(version, stmtName);
+        if (potential != null) {
+            return potential;
+        }
+
+        if (parent != null) {
+            return parent.getVersionSpecificStatementDefinition(version, stmtName);
+        }
+        return null;
+    }
+
+    public static class Builder implements org.opendaylight.yangtools.concepts.Builder<StatementSupportBundle> {
+        private static final Logger LOG = LoggerFactory.getLogger(Builder.class);
+
+        private final Map<QName, StatementSupport<?, ?, ?>> commonStatements = new HashMap<>();
+        private final Table<YangVersion, QName, StatementSupport<?, ?, ?>> versionSpecificStatements = HashBasedTable
+                .create();
+        private final Map<Class<?>, NamespaceBehaviour<?, ?, ?>> namespaces = new HashMap<>();
+
+        private final ImmutableSet<YangVersion> supportedVersions;
+        private StatementSupportBundle parent;
+
+        Builder(final Set<YangVersion> supportedVersions, final StatementSupportBundle parent) {
+            this.parent = requireNonNull(parent);
+            this.supportedVersions = ImmutableSet.copyOf(supportedVersions);
+        }
+
+        public Builder addSupport(final StatementSupport<?, ?, ?> support) {
+            final QName identifier = support.getStatementName();
+            checkNoParentDefinition(identifier);
+
+            checkState(!commonStatements.containsKey(identifier),
+                    "Statement %s already defined in common statement bundle.", identifier);
+            commonStatements.put(identifier, support);
+            return this;
+        }
+
+        public <K, V, N extends IdentifierNamespace<K, V>> Builder addSupport(
+                final NamespaceBehaviour<K, V, N> namespaceSupport) {
+            final Class<N> identifier = namespaceSupport.getIdentifier();
+            checkState(!namespaces.containsKey(identifier));
+            checkState(!parent.hasNamespaceBehaviour(identifier));
+            namespaces.put(identifier, namespaceSupport);
+            return this;
+        }
+
+        public Builder addVersionSpecificSupport(final YangVersion version,
+                final StatementSupport<?, ?, ?> definition) {
+            checkArgument(supportedVersions.contains(requireNonNull(version)));
+
+            final QName identifier = definition.getStatementName();
+            checkState(!commonStatements.containsKey(identifier),
+                    "Statement %s already defined in common statement bundle.", identifier);
+            checkState(!versionSpecificStatements.contains(version, identifier),
+                    "Statement %s already defined for version %s.", identifier, version);
+            checkNoParentDefinition(identifier);
+            checkState(parent.getVersionSpecificStatementDefinition(version, identifier) == null,
+                    "Statement %s already defined for version %s in parent's statement bundle.", identifier, version);
+            versionSpecificStatements.put(version, identifier, definition);
+            return this;
+        }
+
+        public Set<YangVersion> getSupportedVersions() {
+            return supportedVersions;
+        }
+
+        public Builder setParent(final StatementSupportBundle parent) {
+            this.parent = parent;
+            return this;
+        }
+
+        public Builder overrideSupport(final StatementSupport<?, ?, ?> support) {
+            final QName identifier = support.getStatementName();
+            checkNoParentDefinition(identifier);
+
+            final StatementSupport<?, ?, ?> previousSupport = commonStatements.replace(identifier, support);
+            checkState(previousSupport != null, "Statement %s was not previously defined", identifier);
+            LOG.debug("Changed statement {} support from {} to {}", identifier, previousSupport, support);
+            return this;
+        }
+
+        @Override
+        public StatementSupportBundle build() {
+            Preconditions.checkState(parent != null, "Parent must not be null");
+            return new StatementSupportBundle(parent, supportedVersions, ImmutableMap.copyOf(commonStatements),
+                    ImmutableMap.copyOf(namespaces), ImmutableTable.copyOf(versionSpecificStatements));
+        }
+
+        private void checkNoParentDefinition(final QName identifier) {
+            checkState(parent.getCommonStatementDefinition(identifier) == null,
+                    "Statement %s is defined in parent's common statement bundle", identifier);
+        }
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StmtContext.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StmtContext.java
new file mode 100644 (file)
index 0000000..791b3bd
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.base.VerifyException;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Streams;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+
+public interface StmtContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>> {
+
+    @NonNull StatementSource getStatementSource();
+
+    @NonNull StatementSourceReference getStatementSourceReference();
+
+    @NonNull StatementDefinition getPublicDefinition();
+
+    /**
+     * Return the parent statement context, or null if this is the root statement.
+     *
+     * @return context of parent of statement, or null if this is the root statement.
+     */
+    @Nullable StmtContext<?, ?, ?> getParentContext();
+
+    /**
+     * Return the parent statement context, forcing a VerifyException if this is the root statement.
+     *
+     * @return context of parent of statement
+     * @throws VerifyException if this statement is the root statement
+     */
+    default @NonNull StmtContext<?, ?, ?> coerceParentContext() {
+        return verifyNotNull(getParentContext(), "Root context %s does not have a parent", this);
+    }
+
+    /**
+     * Return the statement argument in literal format.
+     *
+     * @return raw statement argument string, or null if this statement does not have an argument.
+     */
+    @Nullable String rawStatementArgument();
+
+    /**
+     * Return the statement argument in literal format.
+     *
+     * @return raw statement argument string
+     * @throws VerifyException if this statement does not have an argument
+     */
+    default @NonNull String coerceRawStatementArgument() {
+        return verifyNotNull(rawStatementArgument(), "Statement context %s does not have an argument", this);
+    }
+
+    /**
+     * Return the statement argument.
+     *
+     * @return statement argument, or null if this statement does not have an argument
+     */
+    @Nullable A getStatementArgument();
+
+    /**
+     * Return the statement argument in literal format.
+     *
+     * @return raw statement argument string
+     * @throws VerifyException if this statement does not have an argument
+     */
+    default @NonNull A coerceStatementArgument() {
+        return verifyNotNull(getStatementArgument(), "Statement context %s does not have an argument", this);
+    }
+
+    /**
+     * Return the {@link SchemaPath} of this statement. Not all statements have a SchemaPath, in which case
+     * {@link Optional#empty()} is returned.
+     *
+     * @return Optional SchemaPath
+     */
+    @NonNull Optional<SchemaPath> getSchemaPath();
+
+    boolean isConfiguration();
+
+    boolean isEnabledSemanticVersioning();
+
+    /**
+     * Return a value associated with specified key within a namespace.
+     *
+     * @param type Namespace type
+     * @param key Key
+     * @param <K> namespace key type
+     * @param <V> namespace value type
+     * @param <N> namespace type
+     * @param <T> key type
+     * @return Value, or null if there is no element
+     * @throws NamespaceNotAvailableException when the namespace is not available.
+     */
+    @NonNull <K, V, T extends K, N extends IdentifierNamespace<K, V>> V getFromNamespace(Class<N> type, T key);
+
+    <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromNamespace(Class<N> type);
+
+    <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromCurrentStmtCtxNamespace(Class<N> type);
+
+    @NonNull StmtContext<?, ?, ?> getRoot();
+
+    /**
+     * Return declared substatements. These are the statements which are explicitly written in the source model.
+     *
+     * @return Collection of declared substatements
+     */
+    @NonNull Collection<? extends StmtContext<?, ?, ?>> declaredSubstatements();
+
+    /**
+     * Return effective substatements. These are the statements which are added as this statement's substatements
+     * complete their effective model phase.
+     *
+     * @return Collection of declared substatements
+     */
+    @NonNull Collection<? extends StmtContext<?, ?, ?>> effectiveSubstatements();
+
+    default Iterable<? extends StmtContext<?, ?, ?>> allSubstatements() {
+        return Iterables.concat(declaredSubstatements(), effectiveSubstatements());
+    }
+
+    default Stream<? extends StmtContext<?, ?, ?>> allSubstatementsStream() {
+        return Streams.concat(declaredSubstatements().stream(), effectiveSubstatements().stream());
+    }
+
+    /**
+     * Builds {@link DeclaredStatement} for statement context.
+     */
+    D buildDeclared();
+
+    /**
+     * Builds {@link EffectiveStatement} for statement context.
+     */
+    E buildEffective();
+
+    boolean isSupportedToBuildEffective();
+
+    boolean isSupportedByFeatures();
+
+    Collection<? extends StmtContext<?, ?, ?>> getEffectOfStatement();
+
+    /*
+     * FIXME: YANGTOOLS-784: the next three methods are closely related to the copy process:
+     *        - getCopyHistory() is a brief summary of what went on
+     *        - getOriginalContext() points to the CopyHistory.ORIGINAL
+     *        - getPreviousCopyCtx() points to the immediate predecessor forming a singly-linked list terminated
+     *          at getOriginalContext()
+     *
+     *        When implementing YANGTOOLS-784, this needs to be taken into account and properly forwarded through
+     *        intermediate MutableTrees. Also note this closely relates to current namespace context, as taken into
+     *        account when creating the argument. At least parts of this are only needed during buildEffective()
+     *        and hence should become arguments to that method.
+     */
+
+    /**
+     * Return the executive summary of the copy process that has produced this context.
+     *
+     * @return A simplified summary of the copy process.
+     */
+    CopyHistory getCopyHistory();
+
+    /**
+     * Return the statement context of the original definition, if this statement is an instantiated copy.
+     *
+     * @return Original definition, if this statement was copied.
+     */
+    Optional<StmtContext<?, ?, ?>> getOriginalCtx();
+
+    /**
+     * Return the context of the previous copy of this statement -- effectively walking towards the source origin
+     * of this statement.
+     *
+     * @return Context of the previous copy of this statement, if this statement has been copied.
+     */
+    Optional<? extends StmtContext<?, ?, ?>> getPreviousCopyCtx();
+
+    ModelProcessingPhase getCompletedPhase();
+
+    /**
+     * Return version of root statement context.
+     *
+     * @return version of root statement context
+     */
+    @NonNull YangVersion getRootVersion();
+
+    interface Mutable<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
+            extends StmtContext<A, D, E> {
+
+        @Override
+        Mutable<?, ?, ?> getParentContext();
+
+        @Override
+        default Mutable<?, ?, ?> coerceParentContext() {
+            return verifyNotNull(getParentContext(), "Root context %s does not have a parent", this);
+        }
+
+        /**
+         * Associate a value with a key within a namespace.
+         *
+         * @param type Namespace type
+         * @param key Key
+         * @param value value
+         * @param <K> namespace key type
+         * @param <V> namespace value type
+         * @param <N> namespace type
+         * @param <T> key type
+         * @param <U> value type
+         * @throws NamespaceNotAvailableException when the namespace is not available.
+         */
+        <K, V, T extends K, U extends V, N extends IdentifierNamespace<K, V>> void addToNs(Class<N> type, T key,
+                U value);
+
+        @Override
+        Mutable<?, ?, ?> getRoot();
+
+        /**
+         * Create a child sub-statement, which is a child of this statement, inheriting all attributes from specified
+         * child and recording copy type. Resulting object may only be added as a child of this statement.
+         *
+         * @param stmt Statement to be used as a template
+         * @param type Type of copy to record in history
+         * @param targetModule Optional new target module
+         * @return copy of statement considering {@link CopyType} (augment, uses)
+         *
+         * @throws IllegalArgumentException if stmt cannot be copied into this statement, for example because it comes
+         *                                  from an alien implementation.
+         * @throws org.opendaylight.yangtools.yang.parser.spi.source.SourceException instance of SourceException
+         */
+        // FIXME: 5.0.0: remove generic arguments X, Y, Z. Callers should not care, as the returned copy can actually
+        //               be an encapsulating implicit statement.
+        <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>> Mutable<X, Y, Z> childCopyOf(
+                StmtContext<X, Y, Z> stmt, CopyType type, @Nullable QNameModule targetModule);
+
+        /**
+         * Create a child sub-statement, which is a child of this statement, inheriting all attributes from specified
+         * child and recording copy type. Resulting object may only be added as a child of this statement.
+         *
+         * @param stmt Statement to be used as a template
+         * @param type Type of copy to record in history
+         * @return copy of statement considering {@link CopyType} (augment, uses)
+         *
+         * @throws IllegalArgumentException if stmt cannot be copied into this statement, for example because it comes
+         *                                  from an alien implementation.
+         * @throws org.opendaylight.yangtools.yang.parser.spi.source.SourceException instance of SourceException
+         */
+        default <X, Y extends DeclaredStatement<X>, Z extends EffectiveStatement<X, Y>> Mutable<X, Y, Z> childCopyOf(
+                final StmtContext<X, Y, Z> stmt, final CopyType type) {
+            return childCopyOf(stmt, type, null);
+        }
+
+        @NonNull Collection<? extends Mutable<?, ?, ?>> mutableDeclaredSubstatements();
+
+        @NonNull Collection<? extends Mutable<?, ?, ?>> mutableEffectiveSubstatements();
+
+        /**
+         * Create a new inference action to be executed during specified phase. The action cannot be cancelled
+         * and will be executed even if its definition remains incomplete. The specified phase cannot complete until
+         * this action is resolved. If the action cannot be resolved, model processing will fail.
+         *
+         * @param phase Target phase in which the action will resolved.
+         * @return A new action builder.
+         * @throws NullPointerException if the specified phase is null
+         */
+        @NonNull ModelActionBuilder newInferenceAction(@NonNull ModelProcessingPhase phase);
+
+        /**
+         * Adds s statement to namespace map with a key.
+         *
+         * @param namespace
+         *            {@link StatementNamespace} child that determines namespace to be added to
+         * @param key
+         *            of type according to namespace class specification
+         * @param stmt
+         *            to be added to namespace map
+         */
+        <K, KT extends K, N extends StatementNamespace<K, ?, ?>> void addContext(Class<N> namespace, KT key,
+                StmtContext<?, ?, ?> stmt);
+
+        /**
+         * Set version of root statement context.
+         *
+         * @param version
+         *            of root statement context
+         */
+        void setRootVersion(YangVersion version);
+
+        /**
+         * Add mutable statement to seal. Each mutable statement must be sealed
+         * as the last step of statement parser processing.
+         *
+         * @param mutableStatement
+         *            mutable statement which should be sealed
+         */
+        void addMutableStmtToSeal(MutableStatement mutableStatement);
+
+        /**
+         * Add required module. Based on these dependencies are collected required sources from library sources.
+         *
+         * @param dependency
+         *            SourceIdentifier of module required by current root
+         *            context
+         */
+        /*
+         * FIXME: this method is used solely during SOURCE_PRE_LINKAGE reactor phase and does not have a corresponding
+         *        getter -- which makes it rather strange. At some point this method needs to be deprecated and its
+         *        users migrated to use proper global namespace.
+         */
+        void addRequiredSource(SourceIdentifier dependency);
+
+        void addAsEffectOfStatement(StmtContext<?, ?, ?> ctx);
+
+        void addAsEffectOfStatement(Collection<? extends StmtContext<?, ?, ?>> ctxs);
+
+        /**
+         * Set identifier of current root context.
+         *
+         * @param identifier
+         *            of current root context, must not be null
+         */
+        void setRootIdentifier(SourceIdentifier identifier);
+
+        void setIsSupportedToBuildEffective(boolean isSupportedToBuild);
+
+        // FIXME: this seems to be unused, but looks useful.
+        void setCompletedPhase(ModelProcessingPhase completedPhase);
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StmtContextUtils.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/StmtContextUtils.java
new file mode 100644 (file)
index 0000000..b21b46f
--- /dev/null
@@ -0,0 +1,651 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.KeyStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.LeafStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MandatoryStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.MinElementsStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.PresenceStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnrecognizedStatement;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public final class StmtContextUtils {
+    private StmtContextUtils() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <A, D extends DeclaredStatement<A>> A firstAttributeOf(
+            final Iterable<? extends StmtContext<?, ?, ?>> contexts, final Class<D> declaredType) {
+        for (final StmtContext<?, ?, ?> ctx : contexts) {
+            if (producesDeclared(ctx, declaredType)) {
+                return (A) ctx.getStatementArgument();
+            }
+        }
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <A, D extends DeclaredStatement<A>> A firstAttributeOf(final StmtContext<?, ?, ?> ctx,
+            final Class<D> declaredType) {
+        return producesDeclared(ctx, declaredType) ? (A) ctx.getStatementArgument() : null;
+    }
+
+    public static <A, D extends DeclaredStatement<A>> A firstSubstatementAttributeOf(
+            final StmtContext<?, ?, ?> ctx, final Class<D> declaredType) {
+        return firstAttributeOf(ctx.allSubstatements(), declaredType);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <A, D extends DeclaredStatement<A>> StmtContext<A, ?, ?> findFirstDeclaredSubstatement(
+            final StmtContext<?, ?, ?> stmtContext, final Class<D> declaredType) {
+        for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
+            if (producesDeclared(subStmtContext, declaredType)) {
+                return (StmtContext<A, ?, ?>) subStmtContext;
+            }
+        }
+        return null;
+    }
+
+    @SafeVarargs
+    public static StmtContext<?, ?, ?> findFirstDeclaredSubstatement(final StmtContext<?, ?, ?> stmtContext,
+            int startIndex, final Class<? extends DeclaredStatement<?>>... types) {
+        if (startIndex >= types.length) {
+            return null;
+        }
+
+        for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
+            if (producesDeclared(subStmtContext, types[startIndex])) {
+                return startIndex + 1 == types.length ? subStmtContext : findFirstDeclaredSubstatement(subStmtContext,
+                        ++startIndex, types);
+            }
+        }
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <A, D extends DeclaredStatement<A>> Collection<StmtContext<A, D, ?>> findAllDeclaredSubstatements(
+            final StmtContext<?, ?, ?> stmtContext, final Class<D> declaredType) {
+        final ImmutableList.Builder<StmtContext<A, D, ?>> listBuilder = ImmutableList.builder();
+        for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
+            if (producesDeclared(subStmtContext, declaredType)) {
+                listBuilder.add((StmtContext<A, D, ?>) subStmtContext);
+            }
+        }
+        return listBuilder.build();
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <A, D extends DeclaredStatement<A>> Collection<StmtContext<A, D, ?>> findAllEffectiveSubstatements(
+            final StmtContext<?, ?, ?> stmtContext, final Class<D> type) {
+        final ImmutableList.Builder<StmtContext<A, D, ?>> listBuilder = ImmutableList.builder();
+        for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.effectiveSubstatements()) {
+            if (producesDeclared(subStmtContext, type)) {
+                listBuilder.add((StmtContext<A, D, ?>) subStmtContext);
+            }
+        }
+        return listBuilder.build();
+    }
+
+    public static <A, D extends DeclaredStatement<A>> Collection<StmtContext<A, D, ?>> findAllSubstatements(
+            final StmtContext<?, ?, ?> stmtContext, final Class<D> type) {
+        final ImmutableList.Builder<StmtContext<A, D, ?>> listBuilder = ImmutableList.builder();
+        listBuilder.addAll(findAllDeclaredSubstatements(stmtContext, type));
+        listBuilder.addAll(findAllEffectiveSubstatements(stmtContext, type));
+        return listBuilder.build();
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <A, D extends DeclaredStatement<A>> StmtContext<A, ?, ?> findFirstEffectiveSubstatement(
+            final StmtContext<?, ?, ?> stmtContext, final Class<D> declaredType) {
+        for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.effectiveSubstatements()) {
+            if (producesDeclared(subStmtContext, declaredType)) {
+                return (StmtContext<A, ?, ?>) subStmtContext;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Searches for the first substatement of the specified type in the specified statement context.
+     * First, it tries to find the substatement in the effective substatements of the statement context.
+     * If it was not found, then it proceeds to search in the declared substatements. If it still was not found,
+     * the method returns null.
+     *
+     * @param stmtContext statement context to search in
+     * @param declaredType substatement type to search for
+     * @param <A> statement argument type
+     * @param <D> declared statement type
+     * @return statement context that was searched for or null if was not found
+     */
+    public static <A, D extends DeclaredStatement<A>> StmtContext<A, ?, ?> findFirstSubstatement(
+            final StmtContext<?, ?, ?> stmtContext, final Class<D> declaredType) {
+        final StmtContext<A, ?, ?> effectiveSubstatement = findFirstEffectiveSubstatement(stmtContext, declaredType);
+        return effectiveSubstatement != null ? effectiveSubstatement : findFirstDeclaredSubstatement(stmtContext,
+                declaredType);
+    }
+
+    public static <D extends DeclaredStatement<?>> StmtContext<?, ?, ?> findFirstDeclaredSubstatementOnSublevel(
+            final StmtContext<?, ?, ?> stmtContext, final Class<D> declaredType, int sublevel) {
+        for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
+            if (sublevel == 1 && producesDeclared(subStmtContext, declaredType)) {
+                return subStmtContext;
+            }
+            if (sublevel > 1) {
+                final StmtContext<?, ?, ?> result = findFirstDeclaredSubstatementOnSublevel(subStmtContext,
+                        declaredType, --sublevel);
+                if (result != null) {
+                    return result;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public static <D extends DeclaredStatement<?>> StmtContext<?, ?, ?> findDeepFirstDeclaredSubstatement(
+            final StmtContext<?, ?, ?> stmtContext, final Class<D> declaredType) {
+        for (final StmtContext<?, ?, ?> subStmtContext : stmtContext.declaredSubstatements()) {
+            if (producesDeclared(subStmtContext, declaredType)) {
+                return subStmtContext;
+            }
+
+            final StmtContext<?, ?, ?> result = findDeepFirstDeclaredSubstatement(subStmtContext, declaredType);
+            if (result != null) {
+                return result;
+            }
+        }
+
+        return null;
+    }
+
+    public static boolean producesDeclared(final StmtContext<?, ?, ?> ctx,
+            final Class<? extends DeclaredStatement<?>> type) {
+        return type.isAssignableFrom(ctx.getPublicDefinition().getDeclaredRepresentationClass());
+    }
+
+    public static boolean isInExtensionBody(final StmtContext<?, ?, ?> stmtCtx) {
+        StmtContext<?, ?, ?> current = stmtCtx;
+        while (current.coerceParentContext().getParentContext() != null) {
+            current = current.getParentContext();
+            if (isUnknownStatement(current)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns true if supplied statement context represents unknown statement,
+     * otherwise returns false.
+     *
+     * @param stmtCtx
+     *            statement context to be checked
+     * @return true if supplied statement context represents unknown statement,
+     *         otherwise false
+     * @throws NullPointerException
+     *             if supplied statement context is null
+     */
+    public static boolean isUnknownStatement(final StmtContext<?, ?, ?> stmtCtx) {
+        return UnknownStatement.class
+                .isAssignableFrom(stmtCtx.getPublicDefinition().getDeclaredRepresentationClass());
+    }
+
+    /**
+     * Returns true if supplied statement context represents unrecognized
+     * statement, otherwise returns false.
+     *
+     * @param stmtCtx
+     *            statement context to be checked
+     * @return true if supplied statement context represents unrecognized
+     *         statement, otherwise false
+     * @throws NullPointerException
+     *             if supplied statement context is null
+     */
+    public static boolean isUnrecognizedStatement(final StmtContext<?, ?, ?> stmtCtx) {
+        return UnrecognizedStatement.class
+                .isAssignableFrom(stmtCtx.getPublicDefinition().getDeclaredRepresentationClass());
+    }
+
+    public static boolean checkFeatureSupport(final StmtContext<?, ?, ?> stmtContext,
+            final Set<QName> supportedFeatures) {
+        boolean isSupported = false;
+        boolean containsIfFeature = false;
+        for (final StmtContext<?, ?, ?> stmt : stmtContext.declaredSubstatements()) {
+            if (YangStmtMapping.IF_FEATURE.equals(stmt.getPublicDefinition())) {
+                containsIfFeature = true;
+                @SuppressWarnings("unchecked")
+                final Predicate<Set<QName>> argument = (Predicate<Set<QName>>) stmt.coerceStatementArgument();
+                if (argument.test(supportedFeatures)) {
+                    isSupported = true;
+                } else {
+                    isSupported = false;
+                    break;
+                }
+            }
+        }
+
+        return !containsIfFeature || isSupported;
+    }
+
+    /**
+     * Checks whether statement context is a presence container or not.
+     *
+     * @param stmtCtx
+     *            statement context
+     * @return true if it is a presence container
+     */
+    public static boolean isPresenceContainer(final StmtContext<?, ?, ?> stmtCtx) {
+        return stmtCtx.getPublicDefinition() == YangStmtMapping.CONTAINER && containsPresenceSubStmt(stmtCtx);
+    }
+
+    /**
+     * Checks whether statement context is a non-presence container or not.
+     *
+     * @param stmtCtx
+     *            statement context
+     * @return true if it is a non-presence container
+     */
+    public static boolean isNonPresenceContainer(final StmtContext<?, ?, ?> stmtCtx) {
+        return stmtCtx.getPublicDefinition() == YangStmtMapping.CONTAINER && !containsPresenceSubStmt(stmtCtx);
+    }
+
+    private static boolean containsPresenceSubStmt(final StmtContext<?, ?, ?> stmtCtx) {
+        return findFirstSubstatement(stmtCtx, PresenceStatement.class) != null;
+    }
+
+    /**
+     * Checks whether statement context is a mandatory leaf, choice, anyxml,
+     * list or leaf-list according to RFC6020 or not.
+     *
+     * @param stmtCtx
+     *            statement context
+     * @return true if it is a mandatory leaf, choice, anyxml, list or leaf-list
+     *         according to RFC6020.
+     */
+    public static boolean isMandatoryNode(final StmtContext<?, ?, ?> stmtCtx) {
+        if (!(stmtCtx.getPublicDefinition() instanceof YangStmtMapping)) {
+            return false;
+        }
+        switch ((YangStmtMapping) stmtCtx.getPublicDefinition()) {
+            case LEAF:
+            case CHOICE:
+            case ANYXML:
+                return Boolean.TRUE.equals(firstSubstatementAttributeOf(stmtCtx, MandatoryStatement.class));
+            case LIST:
+            case LEAF_LIST:
+                final Integer minElements = firstSubstatementAttributeOf(stmtCtx, MinElementsStatement.class);
+                return minElements != null && minElements > 0;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Checks whether a statement context is a statement of supplied statement
+     * definition and whether it is not mandatory leaf, choice, anyxml, list or
+     * leaf-list according to RFC6020.
+     *
+     * @param stmtCtx
+     *            statement context
+     * @param stmtDef
+     *            statement definition
+     * @return true if supplied statement context is a statement of supplied
+     *         statement definition and if it is not mandatory leaf, choice,
+     *         anyxml, list or leaf-list according to RFC6020
+     */
+    public static boolean isNotMandatoryNodeOfType(final StmtContext<?, ?, ?> stmtCtx,
+            final StatementDefinition stmtDef) {
+        return stmtCtx.getPublicDefinition().equals(stmtDef) && !isMandatoryNode(stmtCtx);
+    }
+
+    /**
+     * Checks whether at least one ancestor of a StatementContext matches one from a collection of statement
+     * definitions.
+     *
+     * @param ctx
+     *            StatementContext to be checked
+     * @param ancestorTypes
+     *            collection of statement definitions
+     * @return true if at least one ancestor of a StatementContext matches one
+     *         from collection of statement definitions, otherwise false.
+     */
+    public static boolean hasAncestorOfType(final StmtContext<?, ?, ?> ctx,
+            final Collection<StatementDefinition> ancestorTypes) {
+        requireNonNull(ancestorTypes);
+        StmtContext<?, ?, ?> current = ctx.getParentContext();
+        while (current != null) {
+            if (ancestorTypes.contains(current.getPublicDefinition())) {
+                return true;
+            }
+            current = current.getParentContext();
+        }
+        return false;
+    }
+
+    /**
+     * Checks whether all of StmtContext's ancestors of specified type have a child of specified type.
+     *
+     * @param ctx StmtContext to be checked
+     * @param ancestorType type of ancestor to search for
+     * @param ancestorChildType type of child to search for in the specified ancestor type
+     * @return true if all of StmtContext's ancestors of specified type have a child of specified type, otherwise false
+     */
+    public static <A, D extends DeclaredStatement<A>> boolean hasAncestorOfTypeWithChildOfType(
+            final StmtContext<?, ?, ?> ctx, final StatementDefinition ancestorType,
+            final StatementDefinition ancestorChildType) {
+        requireNonNull(ctx);
+        requireNonNull(ancestorType);
+        requireNonNull(ancestorChildType);
+
+        StmtContext<?, ?, ?> current = ctx.coerceParentContext();
+        StmtContext<?, ?, ?> parent = current.getParentContext();
+        while (parent != null) {
+            if (ancestorType.equals(current.getPublicDefinition())) {
+                @SuppressWarnings("unchecked")
+                final Class<D> ancestorChildTypeClass = (Class<D>) ancestorChildType.getDeclaredRepresentationClass();
+                if (findFirstSubstatement(current, ancestorChildTypeClass) == null) {
+                    return false;
+                }
+            }
+
+            current = parent;
+            parent = current.getParentContext();
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks whether the parent of StmtContext is of specified type.
+     *
+     * @param ctx
+     *            StmtContext to be checked
+     * @param parentType
+     *            type of parent to check
+     * @return true if the parent of StmtContext is of specified type, otherwise false
+     */
+    public static boolean hasParentOfType(final StmtContext<?, ?, ?> ctx, final StatementDefinition parentType) {
+        requireNonNull(parentType);
+        final StmtContext<?, ?, ?> parentContext = ctx.getParentContext();
+        return parentContext != null && parentType.equals(parentContext.getPublicDefinition());
+    }
+
+    /**
+     * Validates the specified statement context with regards to if-feature and when statement on list keys.
+     * The context can either be a leaf which is defined directly in the substatements of a keyed list or a uses
+     * statement defined in a keyed list (a uses statement may add leaves into the list).
+     *
+     * <p>
+     * If one of the list keys contains an if-feature or a when statement in YANG 1.1 model, an exception is thrown.
+     *
+     * @param ctx statement context to be validated
+     */
+    public static void validateIfFeatureAndWhenOnListKeys(final StmtContext<?, ?, ?> ctx) {
+        if (!isRelevantForIfFeatureAndWhenOnListKeysCheck(ctx)) {
+            return;
+        }
+
+        final StmtContext<?, ?, ?> listStmtCtx = ctx.getParentContext();
+        final StmtContext<Collection<SchemaNodeIdentifier>, ?, ?> keyStmtCtx =
+                StmtContextUtils.findFirstDeclaredSubstatement(listStmtCtx, KeyStatement.class);
+
+        if (YangStmtMapping.LEAF.equals(ctx.getPublicDefinition())) {
+            if (isListKey(ctx, keyStmtCtx)) {
+                disallowIfFeatureAndWhenOnListKeys(ctx);
+            }
+        } else if (YangStmtMapping.USES.equals(ctx.getPublicDefinition())) {
+            StmtContextUtils.findAllEffectiveSubstatements(listStmtCtx, LeafStatement.class).forEach(leafStmtCtx -> {
+                if (isListKey(leafStmtCtx, keyStmtCtx)) {
+                    disallowIfFeatureAndWhenOnListKeys(leafStmtCtx);
+                }
+            });
+        }
+    }
+
+    private static boolean isRelevantForIfFeatureAndWhenOnListKeysCheck(final StmtContext<?, ?, ?> ctx) {
+        return YangVersion.VERSION_1_1.equals(ctx.getRootVersion())
+                && StmtContextUtils.hasParentOfType(ctx, YangStmtMapping.LIST)
+                && StmtContextUtils.findFirstDeclaredSubstatement(ctx.coerceParentContext(),
+                    KeyStatement.class) != null;
+    }
+
+    private static boolean isListKey(final StmtContext<?, ?, ?> leafStmtCtx,
+            final StmtContext<Collection<SchemaNodeIdentifier>, ?, ?> keyStmtCtx) {
+        for (final SchemaNodeIdentifier keyIdentifier : keyStmtCtx.coerceStatementArgument()) {
+            if (leafStmtCtx.getStatementArgument().equals(keyIdentifier.getLastComponent())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private static void disallowIfFeatureAndWhenOnListKeys(final StmtContext<?, ?, ?> leafStmtCtx) {
+        leafStmtCtx.allSubstatements().forEach(leafSubstmtCtx -> {
+            final StatementDefinition statementDef = leafSubstmtCtx.getPublicDefinition();
+            SourceException.throwIf(YangStmtMapping.IF_FEATURE.equals(statementDef)
+                    || YangStmtMapping.WHEN.equals(statementDef), leafStmtCtx.getStatementSourceReference(),
+                    "%s statement is not allowed in %s leaf statement which is specified as a list key.",
+                    statementDef.getStatementName(), leafStmtCtx.getStatementArgument());
+        });
+    }
+
+    public static QName qnameFromArgument(StmtContext<?, ?, ?> ctx, final String value) {
+        if (Strings.isNullOrEmpty(value)) {
+            return ctx.getPublicDefinition().getStatementName();
+        }
+
+        String prefix;
+        QNameModule qnameModule = null;
+        String localName = null;
+
+        final String[] namesParts = value.split(":");
+        switch (namesParts.length) {
+            case 1:
+                localName = namesParts[0];
+                qnameModule = StmtContextUtils.getRootModuleQName(ctx);
+                break;
+            default:
+                prefix = namesParts[0];
+                localName = namesParts[1];
+                qnameModule = StmtContextUtils.getModuleQNameByPrefix(ctx, prefix);
+                // in case of unknown statement argument, we're not going to parse it
+                if (qnameModule == null && isUnknownStatement(ctx)) {
+                    localName = value;
+                    qnameModule = StmtContextUtils.getRootModuleQName(ctx);
+                }
+                if (qnameModule == null && ctx.getCopyHistory().getLastOperation() == CopyType.ADDED_BY_AUGMENTATION) {
+                    ctx = ctx.getOriginalCtx().orElse(null);
+                    qnameModule = StmtContextUtils.getModuleQNameByPrefix(ctx, prefix);
+                }
+        }
+
+        return internedQName(ctx,
+            InferenceException.throwIfNull(qnameModule, ctx.getStatementSourceReference(),
+            "Cannot resolve QNameModule for '%s'", value), localName);
+    }
+
+    /**
+     * Parse a YANG identifier string in context of a statement.
+     *
+     * @param ctx Statement context
+     * @param str String to be parsed
+     * @return An interned QName
+     * @throws NullPointerException if any of the arguments are null
+     * @throws SourceException if the string is not a valid YANG identifier
+     */
+    public static QName parseIdentifier(final StmtContext<?, ?, ?> ctx, final String str) {
+        SourceException.throwIf(str.isEmpty(), ctx.getStatementSourceReference(),
+                "Identifier may not be an empty string");
+        return internedQName(ctx, str);
+    }
+
+    public static QName parseNodeIdentifier(StmtContext<?, ?, ?> ctx, final String prefix,
+            final String localName) {
+        final QNameModule module = StmtContextUtils.getModuleQNameByPrefix(ctx, prefix);
+        if (module != null) {
+            return internedQName(ctx, module, localName);
+        }
+
+        if (ctx.getCopyHistory().getLastOperation() == CopyType.ADDED_BY_AUGMENTATION) {
+            final Optional<StmtContext<?, ?, ?>> optOrigCtx = ctx.getOriginalCtx();
+            if (optOrigCtx.isPresent()) {
+                ctx = optOrigCtx.get();
+                final QNameModule origModule = StmtContextUtils.getModuleQNameByPrefix(ctx, prefix);
+                if (origModule != null) {
+                    return internedQName(ctx, origModule, localName);
+                }
+            }
+        }
+
+        throw new InferenceException(ctx.getStatementSourceReference(), "Cannot resolve QNameModule for '%s'", prefix);
+    }
+
+    /**
+     * Parse a YANG node identifier string in context of a statement.
+     *
+     * @param ctx Statement context
+     * @param str String to be parsed
+     * @return An interned QName
+     * @throws NullPointerException if any of the arguments are null
+     * @throws SourceException if the string is not a valid YANG node identifier
+     */
+    public static QName parseNodeIdentifier(final StmtContext<?, ?, ?> ctx, final String str) {
+        SourceException.throwIf(str.isEmpty(), ctx.getStatementSourceReference(),
+                "Node identifier may not be an empty string");
+
+        final int colon = str.indexOf(':');
+        if (colon == -1) {
+            return internedQName(ctx, str);
+        }
+
+        final String prefix = str.substring(0, colon);
+        SourceException.throwIf(prefix.isEmpty(), ctx.getStatementSourceReference(),
+            "String '%s' has an empty prefix", str);
+        final String localName = str.substring(colon + 1);
+        SourceException.throwIf(localName.isEmpty(), ctx.getStatementSourceReference(),
+            "String '%s' has an empty identifier", str);
+
+        return parseNodeIdentifier(ctx, prefix, localName);
+    }
+
+    private static QName internedQName(final StmtContext<?, ?, ?> ctx, final String localName) {
+        return internedQName(ctx, StmtContextUtils.getRootModuleQName(ctx), localName);
+    }
+
+    private static QName internedQName(final StmtContext<?, ?, ?> ctx, final QNameModule module,
+            final String localName) {
+        final QName template;
+        try {
+            template = QName.create(module, localName);
+        } catch (IllegalArgumentException e) {
+            throw new SourceException(ctx.getStatementSourceReference(), e, "Invalid identifier '%s'", localName);
+        }
+
+        return ctx.getFromNamespace(QNameCacheNamespace.class, template);
+    }
+
+    public static QNameModule getRootModuleQName(final StmtContext<?, ?, ?> ctx) {
+        if (ctx == null) {
+            return null;
+        }
+
+        final StmtContext<?, ?, ?> rootCtx = ctx.getRoot();
+        final QNameModule qnameModule;
+
+        if (producesDeclared(rootCtx, ModuleStatement.class)) {
+            qnameModule = rootCtx.getFromNamespace(ModuleCtxToModuleQName.class, rootCtx);
+        } else if (producesDeclared(rootCtx, SubmoduleStatement.class)) {
+            final String belongsToModuleName = firstAttributeOf(rootCtx.declaredSubstatements(),
+                BelongsToStatement.class);
+            qnameModule = rootCtx.getFromNamespace(ModuleNameToModuleQName.class, belongsToModuleName);
+        } else {
+            qnameModule = null;
+        }
+
+        checkArgument(qnameModule != null, "Failed to look up root QNameModule for %s", ctx);
+        return qnameModule;
+    }
+
+    public static QNameModule getModuleQNameByPrefix(final StmtContext<?, ?, ?> ctx, final String prefix) {
+        final StmtContext<?, ?, ?> importedModule = ctx.getRoot().getFromNamespace(ImportPrefixToModuleCtx.class,
+            prefix);
+        final QNameModule qnameModule = ctx.getFromNamespace(ModuleCtxToModuleQName.class, importedModule);
+        if (qnameModule != null) {
+            return qnameModule;
+        }
+
+        if (producesDeclared(ctx.getRoot(), SubmoduleStatement.class)) {
+            final String moduleName = ctx.getRoot().getFromNamespace(BelongsToPrefixToModuleName.class, prefix);
+            return ctx.getFromNamespace(ModuleNameToModuleQName.class, moduleName);
+        }
+
+        return null;
+    }
+
+    public static SourceIdentifier createSourceIdentifier(final StmtContext<?, ?, ?> root) {
+        final QNameModule qNameModule = root.getFromNamespace(ModuleCtxToModuleQName.class, root);
+        if (qNameModule != null) {
+            // creates SourceIdentifier for a module
+            return RevisionSourceIdentifier.create((String) root.getStatementArgument(), qNameModule.getRevision());
+        }
+
+        // creates SourceIdentifier for a submodule
+        final Optional<Revision> revision = getLatestRevision(root.declaredSubstatements());
+        return RevisionSourceIdentifier.create((String) root.getStatementArgument(), revision);
+    }
+
+    public static Optional<Revision> getLatestRevision(final Iterable<? extends StmtContext<?, ?, ?>> subStmts) {
+        Revision revision = null;
+        for (final StmtContext<?, ?, ?> subStmt : subStmts) {
+            if (subStmt.getPublicDefinition().getDeclaredRepresentationClass().isAssignableFrom(
+                    RevisionStatement.class)) {
+                if (revision == null && subStmt.getStatementArgument() != null) {
+                    revision = (Revision) subStmt.getStatementArgument();
+                } else {
+                    final Revision subArg = (Revision) subStmt.getStatementArgument();
+                    if (subArg != null && subArg.compareTo(revision) > 0) {
+                        revision = subArg;
+                    }
+                }
+            }
+        }
+        return Optional.ofNullable(revision);
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SubstatementValidator.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/SubstatementValidator.java
new file mode 100644 (file)
index 0000000..a443102
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2015 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.spi.meta;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.ExtensionNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
+
+public final class SubstatementValidator {
+    private final ImmutableMap<StatementDefinition, Cardinality> cardinalityMap;
+    private final ImmutableMap<StatementDefinition, Cardinality> mandatoryStatements;
+    private final StatementDefinition currentStatement;
+
+    private SubstatementValidator(final Builder builder) {
+        this.cardinalityMap = builder.cardinalityMap.build();
+        this.currentStatement = builder.currentStatement;
+        this.mandatoryStatements = ImmutableMap.copyOf(Maps.filterValues(cardinalityMap, c -> c.getMin() > 0));
+    }
+
+    public static Builder builder(final StatementDefinition currentStatement) {
+        return new Builder(currentStatement);
+    }
+
+    public static final class Builder {
+        private static final Cardinality ONE_MAX = new Cardinality(1, Integer.MAX_VALUE);
+        private static final Cardinality ONE_ONE = new Cardinality(1, 1);
+        private static final Cardinality ZERO_MAX = new Cardinality(0, Integer.MAX_VALUE);
+        private static final Cardinality ZERO_ONE = new Cardinality(0, 1);
+
+        private final ImmutableMap.Builder<StatementDefinition, Cardinality> cardinalityMap = ImmutableMap.builder();
+        private final StatementDefinition currentStatement;
+
+        Builder(final StatementDefinition currentStatement) {
+            this.currentStatement = currentStatement;
+        }
+
+        private Builder add(final StatementDefinition def, final Cardinality card) {
+            cardinalityMap.put(def, card);
+            return this;
+        }
+
+        public Builder add(final StatementDefinition def, final int min, final int max) {
+            if (max == Integer.MAX_VALUE) {
+                return addAtLeast(def, min);
+            } else if (min == 0) {
+                return addAtMost(def, max);
+            } else {
+                return add(def, new Cardinality(min, max));
+            }
+        }
+
+        // Equivalent to min .. Integer.MAX_VALUE
+        public Builder addAtLeast(final StatementDefinition def, final int min) {
+            switch (min) {
+                case 0:
+                    return addAny(def);
+                case 1:
+                    return addMultiple(def);
+                default:
+                    return add(def, new Cardinality(min, Integer.MAX_VALUE));
+            }
+        }
+
+        // Equivalent to 0 .. max
+        public Builder addAtMost(final StatementDefinition def, final int max) {
+            return max == Integer.MAX_VALUE ? addAny(def) : add(def, new Cardinality(0, max));
+        }
+
+        // Equivalent to 0 .. Integer.MAX_VALUE
+        public Builder addAny(final StatementDefinition def) {
+            return add(def, ZERO_MAX);
+        }
+
+        // Equivalent to 1 .. 1
+        public Builder addMandatory(final StatementDefinition def) {
+            return add(def, ONE_ONE);
+        }
+
+        // Equivalent to 1 .. MAX
+        public Builder addMultiple(final StatementDefinition def) {
+            return add(def, ONE_MAX);
+        }
+
+        // Equivalent to 0 .. 1
+        public Builder addOptional(final StatementDefinition def) {
+            return add(def, ZERO_ONE);
+        }
+
+        public SubstatementValidator build() {
+            return new SubstatementValidator(this);
+        }
+    }
+
+    /**
+     * Validate substatements inside a context.
+     *
+     * @param ctx Context to inspect
+     * @throws InvalidSubstatementException when there is a disallowed statement present.
+     * @throws MissingSubstatementException when a mandatory statement is missing.
+     */
+    public void validate(final StmtContext<?, ?, ?> ctx) {
+
+        final Map<StatementDefinition, Counter> stmtCounts = new HashMap<>();
+        for (StmtContext<?, ?, ?> stmtCtx : ctx.allSubstatements()) {
+            stmtCounts.computeIfAbsent(stmtCtx.getPublicDefinition(), key -> new Counter()).increment();
+        }
+
+        // Mark all mandatory statements as not present. We are using a Map instead of a Set, as it provides us with
+        // explicit value in case of failure (which is not important) and a more efficient instantiation performance
+        // (which is important).
+        final Map<StatementDefinition, Cardinality> missingMandatory = new HashMap<>(mandatoryStatements);
+
+        // Iterate over all statements
+        for (Entry<StatementDefinition, Counter> entry : stmtCounts.entrySet()) {
+            final StatementDefinition key = entry.getKey();
+            final Cardinality cardinality = cardinalityMap.get(key);
+            final int value = entry.getValue().getValue();
+
+            if (cardinality == null) {
+                if (ctx.getFromNamespace(ExtensionNamespace.class, key.getStatementName()) == null) {
+                    throw new InvalidSubstatementException(ctx.getStatementSourceReference(),
+                        "%s is not valid for %s. Error in module %s (%s)", key, currentStatement,
+                        ctx.getRoot().getStatementArgument(),
+                        ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
+                }
+
+                continue;
+            }
+
+            if (cardinality.getMin() > 0) {
+                if (cardinality.getMin() > value) {
+                    throw new InvalidSubstatementException(ctx.getStatementSourceReference(),
+                        "Minimal count of %s for %s is %s, detected %s. Error in module %s (%s)", key, currentStatement,
+                        cardinality.getMin(), value, ctx.getRoot().getStatementArgument(),
+                        ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
+                }
+
+                // Encountered a mandatory statement, hence we are not missing it
+                missingMandatory.remove(key);
+            }
+            if (cardinality.getMax() < value) {
+                throw new InvalidSubstatementException(ctx.getStatementSourceReference(),
+                    "Maximal count of %s for %s is %s, detected %s. Error in module %s (%s)", key, currentStatement,
+                    cardinality.getMax(), value, ctx.getRoot().getStatementArgument(),
+                    ctx.getFromNamespace(ModuleCtxToModuleQName.class, ctx.getRoot()));
+            }
+        }
+
+        // Check if there are any mandatory statements we have missed
+        if (!missingMandatory.isEmpty()) {
+            final Entry<StatementDefinition, Cardinality> e = missingMandatory.entrySet().iterator().next();
+            final StmtContext<?, ?, ?> root = ctx.getRoot();
+
+            throw new MissingSubstatementException(ctx.getStatementSourceReference(),
+                "%s is missing %s. Minimal count is %s. Error in module %s (%s)", currentStatement, e.getKey(),
+                e.getValue().getMin(), root.getStatementArgument(), ctx.getFromNamespace(ModuleCtxToModuleQName.class,
+                    root));
+        }
+    }
+
+    private static final class Cardinality {
+        private final int min;
+        private final int max;
+
+        Cardinality(final int min, final int max) {
+            checkArgument(min >= 0, "Min %s cannot be less than 0!", min);
+            checkArgument(min <= max, "Min %s can not be greater than max %s!", min, max);
+            this.min = min;
+            this.max = max;
+        }
+
+        int getMax() {
+            return max;
+        }
+
+        int getMin() {
+            return min;
+        }
+    }
+
+    private static final class Counter {
+        private int value;
+
+        void increment() {
+            value++;
+        }
+
+        int getValue() {
+            return value;
+        }
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/package-info.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/meta/package-info.java
new file mode 100644 (file)
index 0000000..439a35b
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2017 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
+ */
+
+/**
+ * Provides base abstract classes, interfaces and common implementation used by the statement parser.
+ */
+package org.opendaylight.yangtools.yang.parser.spi.meta;
\ No newline at end of file
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/package-info.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/package-info.java
new file mode 100644 (file)
index 0000000..32ecb14
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017 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
+ */
+
+/**
+ * Provides essential namespaces which are filled in the parsing process. Currently this package
+ * contains interfaces which represent essential namespaces according to Rfc6020
+ * and Rfc7950 (for more details see the section 6.2.1. Identifiers and Their Namespaces
+ * in Rfc6020 and Rfc7950).
+ */
+package org.opendaylight.yangtools.yang.parser.spi;
\ No newline at end of file
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/BelongsToModuleContext.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/BelongsToModuleContext.java
new file mode 100644 (file)
index 0000000..54e756a
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ImportedNamespaceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Namespace key class for storing belongs-to statements in YANG model storage.
+ */
+public interface BelongsToModuleContext extends ImportedNamespaceContext<SourceIdentifier> {
+    NamespaceBehaviour<SourceIdentifier, StmtContext<?, ?, ?>, @NonNull BelongsToModuleContext> BEHAVIOUR =
+            NamespaceBehaviour.sourceLocal(BelongsToModuleContext.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/BelongsToPrefixToModuleCtx.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/BelongsToPrefixToModuleCtx.java
new file mode 100644 (file)
index 0000000..d13687e
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ImportedNamespaceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Source-specific mapping of belongsTo prefixes to module identifiers. This mapping allows source-specific context
+ * to correctly populate prefixes map for actual parsing phase and eventually, resolve QName for any valid declared
+ * statement.
+ */
+public interface BelongsToPrefixToModuleCtx extends ImportedNamespaceContext<String> {
+    NamespaceBehaviour<String, StmtContext<?, ?, ?>, @NonNull BelongsToPrefixToModuleCtx> BEHAVIOUR =
+            NamespaceBehaviour.sourceLocal(BelongsToPrefixToModuleCtx.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/BelongsToPrefixToModuleName.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/BelongsToPrefixToModuleName.java
new file mode 100644 (file)
index 0000000..dc65d03
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Source-specific mapping of prefixes to namespaces.
+ */
+public interface BelongsToPrefixToModuleName extends IdentifierNamespace<String, String> {
+    NamespaceBehaviour<String, String, @NonNull BelongsToPrefixToModuleName> BEHAVIOUR =
+            NamespaceBehaviour.sourceLocal(BelongsToPrefixToModuleName.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/DeclarationInTextSource.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/DeclarationInTextSource.java
new file mode 100644 (file)
index 0000000..3d340e0
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+
+/**
+ * Reference of statement source present in textual source format. Utility implementation
+ * of {@link StatementSourceReference} for textual sources, this is prefered {@link StatementSourceReference}
+ * for implementations of YANG / YIN statement stream sources.
+ *
+ * <p>
+ * To create source reference use one of this static factories:
+ * <ul>
+ * <li>{@link #atPosition(String, int, int)} - provides most specific reference of statement location,
+ * this is most prefered since it provides most context to debug YANG model.
+ * </li>
+ * <li>{@link #atLine(String, int)}- provides source and line of statement location.
+ * </li>
+ * <li>{@link #inSource(String)} - least specific reference, should be used only if any of previous
+ * references are unable to create / derive from source.
+ * </li>
+ * </ul>
+ */
+public abstract class DeclarationInTextSource implements StatementSourceReference {
+    private static class InSource extends DeclarationInTextSource {
+        InSource(final String sourceName) {
+            super(sourceName);
+        }
+
+        @Override
+        public String toString() {
+            return getSourceName();
+        }
+    }
+
+    private static class AtLine extends InSource {
+        private final int line;
+
+        AtLine(final String sourceName, final int line) {
+            super(sourceName);
+            this.line = line;
+        }
+
+        @Override
+        public String toString() {
+            return getSourceName() + ':' + line;
+        }
+
+        int getLine() {
+            return line;
+        }
+    }
+
+    private static final class AtPosition extends AtLine {
+        private final int character;
+
+        AtPosition(final String sourceName, final int line, final int character) {
+            super(sourceName, line);
+            this.character = character;
+        }
+
+        @Override
+        public String toString() {
+            return getSourceName() + ':' + getLine() + ':' + character;
+        }
+    }
+
+    private final String sourceName;
+
+    DeclarationInTextSource(final String sourceName) {
+        this.sourceName = sourceName;
+    }
+
+    public static DeclarationInTextSource inSource(final String sourceName) {
+        return new InSource(sourceName);
+    }
+
+    public static DeclarationInTextSource atLine(final String sourceName, final int line) {
+        return new AtLine(sourceName, line);
+    }
+
+    public static DeclarationInTextSource atPosition(final String sourceName, final int line, final int position) {
+        return new AtPosition(sourceName, line, position);
+    }
+
+    public String getSourceName() {
+        return sourceName;
+    }
+
+    @Override
+    public StatementSource getStatementSource() {
+        return StatementSource.DECLARATION;
+    }
+
+    @Override
+    public abstract String toString();
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImpPrefixToNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImpPrefixToNamespace.java
new file mode 100644 (file)
index 0000000..3ef1096
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2016 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.spi.source;
+
+import java.net.URI;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Pre-linkage source-specific mapping of prefixes to module namespaces.
+ */
+public interface ImpPrefixToNamespace extends IdentifierNamespace<String, URI> {
+    NamespaceBehaviour<String, URI, @NonNull ImpPrefixToNamespace> BEHAVIOUR =
+            NamespaceBehaviour.rootStatementLocal(ImpPrefixToNamespace.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImplicitSubstatement.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImplicitSubstatement.java
new file mode 100644 (file)
index 0000000..c1455e0
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017 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.spi.source;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+
+/**
+ * An implicit sub-statement, which is implied to be always present in its parent, even if it does not appear
+ * in model source.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class ImplicitSubstatement implements StatementSourceReference {
+
+    private final StatementSourceReference parentRef;
+
+    private ImplicitSubstatement(final StatementSourceReference parentRef) {
+        this.parentRef = requireNonNull(parentRef);
+    }
+
+    /**
+     * Create a new {@link ImplicitSubstatement}.
+     *
+     * @param parentRef Parent source reference
+     * @return A new reference
+     * @throws NullPointerException if parentRef is null
+     */
+    public static ImplicitSubstatement of(final StatementSourceReference parentRef) {
+        return new ImplicitSubstatement(parentRef);
+    }
+
+    @Override
+    public StatementSource getStatementSource() {
+        return StatementSource.CONTEXT;
+    }
+
+    @Override
+    public int hashCode() {
+        return parentRef.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return obj instanceof ImplicitSubstatement && parentRef.equals(((ImplicitSubstatement) obj).parentRef);
+    }
+
+    @Override
+    public String toString() {
+        return parentRef.toString();
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImportPrefixToModuleCtx.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImportPrefixToModuleCtx.java
new file mode 100644 (file)
index 0000000..f30ab33
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Source-specific mapping of prefix strings to module context.
+ */
+public interface ImportPrefixToModuleCtx extends  IdentifierNamespace<String, StmtContext<?, ?, ?>> {
+    NamespaceBehaviour<String, StmtContext<?, ?, ?>, @NonNull ImportPrefixToModuleCtx> BEHAVIOUR =
+            NamespaceBehaviour.rootStatementLocal(ImportPrefixToModuleCtx.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImportPrefixToSemVerSourceIdentifier.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImportPrefixToSemVerSourceIdentifier.java
new file mode 100644 (file)
index 0000000..d81894c
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 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.spi.source;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Source-specific mapping of prefixes to source identifier with specified semantic version.
+ */
+@Beta
+public interface ImportPrefixToSemVerSourceIdentifier
+    extends IdentifierNamespace<String, SemVerSourceIdentifier> {
+    NamespaceBehaviour<String, SemVerSourceIdentifier, @NonNull ImportPrefixToSemVerSourceIdentifier> BEHAVIOUR =
+        NamespaceBehaviour.rootStatementLocal(ImportPrefixToSemVerSourceIdentifier.class);
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImportedModuleContext.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImportedModuleContext.java
new file mode 100644 (file)
index 0000000..3cabb5e
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ImportedNamespaceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public interface ImportedModuleContext extends ImportedNamespaceContext<SourceIdentifier> {
+    NamespaceBehaviour<SourceIdentifier, StmtContext<?, ?, ?>, @NonNull ImportedModuleContext> BEHAVIOUR =
+            NamespaceBehaviour.sourceLocal(ImportedModuleContext.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/IncludedModuleContext.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/IncludedModuleContext.java
new file mode 100644 (file)
index 0000000..8500dec
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ImportedNamespaceContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+public interface IncludedModuleContext extends ImportedNamespaceContext<SourceIdentifier> {
+    NamespaceBehaviour<SourceIdentifier, StmtContext<?, ?, ?>, @NonNull IncludedModuleContext> BEHAVIOUR =
+            NamespaceBehaviour.sourceLocal(IncludedModuleContext.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/IncludedSubmoduleNameToModuleCtx.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/IncludedSubmoduleNameToModuleCtx.java
new file mode 100644 (file)
index 0000000..2b5f97a
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Source-specific mapping of prefixes to namespaces.
+ */
+public interface IncludedSubmoduleNameToModuleCtx extends IdentifierNamespace<String, StmtContext<?, ?, ?>> {
+    NamespaceBehaviour<String, StmtContext<?, ?, ?>, @NonNull IncludedSubmoduleNameToModuleCtx> BEHAVIOUR =
+            NamespaceBehaviour.sourceLocal(IncludedSubmoduleNameToModuleCtx.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleCtxToModuleQName.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleCtxToModuleQName.java
new file mode 100644 (file)
index 0000000..c0d097f
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Global mapping of modules to QNameModules.
+ */
+public interface ModuleCtxToModuleQName extends IdentifierNamespace<StmtContext<?,?,?>, QNameModule> {
+    NamespaceBehaviour<StmtContext<?, ?, ?>, QNameModule, @NonNull ModuleCtxToModuleQName> BEHAVIOUR =
+            NamespaceBehaviour.global(ModuleCtxToModuleQName.class);
+
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleCtxToSourceIdentifier.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleCtxToSourceIdentifier.java
new file mode 100644 (file)
index 0000000..0403798
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * Global mapping of modules to source identifier.
+ */
+public interface ModuleCtxToSourceIdentifier extends IdentifierNamespace<StmtContext<?, ?, ?>, SourceIdentifier> {
+    NamespaceBehaviour<StmtContext<?, ?, ?>, SourceIdentifier, @NonNull ModuleCtxToSourceIdentifier> BEHAVIOUR =
+            NamespaceBehaviour.global(ModuleCtxToSourceIdentifier.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleNameToModuleQName.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleNameToModuleQName.java
new file mode 100644 (file)
index 0000000..e1ea85a
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Source-specific mapping of prefixes to namespaces.
+ */
+public interface ModuleNameToModuleQName extends IdentifierNamespace<String, QNameModule> {
+    NamespaceBehaviour<String, QNameModule, @NonNull ModuleNameToModuleQName> BEHAVIOUR =
+            NamespaceBehaviour.sourceLocal(ModuleNameToModuleQName.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleNameToNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleNameToNamespace.java
new file mode 100644 (file)
index 0000000..49c0c4f
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2016 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.spi.source;
+
+import java.net.URI;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Pre-linkage global mapping of module names to namespaces.
+ */
+public interface ModuleNameToNamespace extends IdentifierNamespace<String, URI> {
+    NamespaceBehaviour<String, URI, @NonNull ModuleNameToNamespace> BEHAVIOUR =
+            NamespaceBehaviour.global(ModuleNameToNamespace.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleNamespaceForBelongsTo.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleNamespaceForBelongsTo.java
new file mode 100644 (file)
index 0000000..9a520dd
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * namespace class similar to {@link org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace} for storing modules
+ * into Yang model storage but keyed by plain name.
+ */
+public interface ModuleNamespaceForBelongsTo
+        extends StatementNamespace<String, ModuleStatement, ModuleEffectiveStatement> {
+    NamespaceBehaviour<String, StmtContext<?, ModuleStatement, ModuleEffectiveStatement>,
+        @NonNull ModuleNamespaceForBelongsTo> BEHAVIOUR = NamespaceBehaviour.global(ModuleNamespaceForBelongsTo.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleQNameToModuleName.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModuleQNameToModuleName.java
new file mode 100644 (file)
index 0000000..3bce75f
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Source-specific mapping of prefixes to namespaces.
+ */
+public interface ModuleQNameToModuleName extends IdentifierNamespace<QNameModule, String> {
+    NamespaceBehaviour<QNameModule, String, @NonNull ModuleQNameToModuleName> BEHAVIOUR =
+            NamespaceBehaviour.global(ModuleQNameToModuleName.class);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModulesDeviatedByModules.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ModulesDeviatedByModules.java
new file mode 100644 (file)
index 0000000..60e0363
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.spi.source;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.SetMultimap;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Namespace used for storing information about modules that support deviation resolution.
+ * Map key (QNameModule) denotes a module which can be deviated by the modules specified in the Map value.
+ */
+@Beta
+public interface ModulesDeviatedByModules
+        extends IdentifierNamespace<ModulesDeviatedByModules.SupportedModules, SetMultimap<QNameModule, QNameModule>> {
+    NamespaceBehaviour<SupportedModules, SetMultimap<QNameModule, QNameModule>, @NonNull ModulesDeviatedByModules>
+        BEHAVIOUR = NamespaceBehaviour.global(ModulesDeviatedByModules.class);
+
+    enum SupportedModules {
+        SUPPORTED_MODULES
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/PrefixToModule.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/PrefixToModule.java
new file mode 100644 (file)
index 0000000..6b2a187
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import java.net.URISyntaxException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Source-specific mapping of prefixes to namespaces.
+ */
+public interface PrefixToModule extends IdentifierNamespace<String, QNameModule> {
+    NamespaceBehaviour<String, QNameModule, @NonNull PrefixToModule> BEHAVIOUR =
+            NamespaceBehaviour.global(PrefixToModule.class);
+    String DEFAULT_PREFIX = "";
+
+    /**
+     * Returns QNameModule (namespace + revision) associated with supplied
+     * prefix.
+     *
+     * @param prefix
+     *            Prefix
+     * @return QNameModule associated with supplied prefix, or null if prefix is
+     *         not defined.
+     */
+    @Override
+    QNameModule get(String prefix);
+
+    /**
+     * Returns QNameModule (namespace + revision) associated with XML namespace (URI).
+     *
+     * @param namespace
+     *            XML Namespace
+     * @return QNameModule associated with supplied namespace, or null if prefix
+     *         is not defined.
+     * @throws URISyntaxException if the input string is not valid URI
+     */
+    @Nullable QNameModule getByNamespace(String namespace) throws URISyntaxException;
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/PrefixToModuleMap.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/PrefixToModuleMap.java
new file mode 100644 (file)
index 0000000..593da7b
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+/**
+ * Map-based {@link PrefixToModule} namespace. This class is NOT thread-safe.
+ */
+public class PrefixToModuleMap implements PrefixToModule {
+    private final Map<String, QNameModule> prefixToModuleMap = new HashMap<>();
+    private final Map<URI, QNameModule> namespaceToModuleMap = new HashMap<>();
+
+    public void put(final String prefix, final QNameModule module) {
+        prefixToModuleMap.put(prefix, module);
+        namespaceToModuleMap.put(module.getNamespace(), module);
+    }
+
+    @Override
+    public QNameModule get(final String prefix) {
+        return prefixToModuleMap.get(prefix);
+    }
+
+    @Override
+    public QNameModule getByNamespace(final String namespace) throws URISyntaxException {
+        return namespaceToModuleMap.get(new URI(namespace));
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/QNameToStatementDefinition.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/QNameToStatementDefinition.java
new file mode 100644 (file)
index 0000000..8a38c1c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import java.net.URI;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Map of fully qualified statement name to statement definition.
+ */
+public interface QNameToStatementDefinition extends IdentifierNamespace<QName, StatementDefinition> {
+    NamespaceBehaviour<QName, StatementDefinition, @NonNull QNameToStatementDefinition> BEHAVIOUR =
+            NamespaceBehaviour.sourceLocal(QNameToStatementDefinition.class);
+
+    /**
+     * Returns StatementDefinition with specified QName.
+     *
+     * @param identifier
+     *            QName of requested statement
+     * @return StatementDefinition
+     */
+    @Override
+    StatementDefinition get(QName identifier);
+
+    /**
+     * Returns StatementDefinition with specified namespace and localName.
+     *
+     * @param namespace
+     *            namespace of requested statement
+     * @param localName
+     *            localName of requested statement
+     * @return StatementDefinition
+     */
+    @Nullable StatementDefinition getByNamespaceAndLocalName(@NonNull URI namespace, @NonNull String localName);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/QNameToStatementDefinitionMap.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/QNameToStatementDefinitionMap.java
new file mode 100644 (file)
index 0000000..5ddb7d8
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import static java.util.Objects.requireNonNull;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+
+public class QNameToStatementDefinitionMap implements QNameToStatementDefinition {
+    private final Map<QName, StatementSupport<?, ?, ?>> noRevQNameToSupport;
+    private final Map<QName, StatementSupport<?, ?, ?>> qnameToSupport;
+
+    public QNameToStatementDefinitionMap() {
+        noRevQNameToSupport = new HashMap<>();
+        qnameToSupport = new HashMap<>();
+    }
+
+    public QNameToStatementDefinitionMap(final int initialCapacity) {
+        noRevQNameToSupport = new HashMap<>(initialCapacity);
+        qnameToSupport = new HashMap<>(initialCapacity);
+    }
+
+    public void put(final QName qname, final StatementSupport<?, ?, ?> stDef) {
+        // HashMap does not guard against nulls
+        qnameToSupport.put(requireNonNull(qname), requireNonNull(stDef));
+        putNoRev(qname, stDef);
+    }
+
+    public void putAll(final Map<QName, StatementSupport<?, ?, ?>> qnameToStmt) {
+        qnameToSupport.putAll(qnameToStmt);
+        qnameToStmt.forEach(this::putNoRev);
+    }
+
+    public StatementSupport<?, ?, ?> putIfAbsent(final QName qname, final StatementSupport<?, ?, ?> support) {
+        final StatementSupport<?, ?, ?> existing = qnameToSupport.putIfAbsent(qname, support);
+        if (existing != null) {
+            return existing;
+        }
+
+        // XXX: we can (in theory) conflict here if we ever find ourselves needing to have multiple revisions of
+        //      statements. These should be equivalent, so no harm done (?)
+        //      Anyway, this is how it worked before last refactor.
+        putNoRev(qname, support);
+        return null;
+    }
+
+    private void putNoRev(final QName qname, final StatementSupport<?, ?, ?> support) {
+        final QName norev = qname.withoutRevision();
+        noRevQNameToSupport.put(norev != qname ? norev.intern() : qname, support);
+    }
+
+    @Override
+    public StatementSupport<?, ?, ?> get(final QName identifier) {
+        return qnameToSupport.get(identifier);
+    }
+
+    @Override
+    public StatementDefinition getByNamespaceAndLocalName(final URI namespace, final String localName) {
+        return noRevQNameToSupport.get(QName.create(namespace, localName));
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/SourceException.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/SourceException.java
new file mode 100644 (file)
index 0000000..4a277d3
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import static java.util.Objects.requireNonNull;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Thrown to indicate error in YANG model source.
+ */
+public class SourceException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "Interface-specified member")
+    private final @NonNull StatementSourceReference sourceRef;
+
+    /**
+     * Create a new instance with the specified message and source. The message will be appended with
+     * the source reference.
+     *
+     * @param message Context message
+     * @param source Statement source
+     */
+    public SourceException(final @NonNull String message, final @NonNull StatementSourceReference source) {
+        super(createMessage(message, source));
+        sourceRef = source;
+    }
+
+    /**
+     * Create a new instance with the specified message and source. The message will be appended with
+     * the source reference.
+     *
+     * @param message Context message
+     * @param source Statement source
+     * @param cause Underlying cause of this exception
+     */
+    public SourceException(final @NonNull String message, final @NonNull StatementSourceReference source,
+            final Throwable cause) {
+        super(createMessage(message, source), cause);
+        sourceRef = source;
+    }
+
+    /**
+     * Create a new instance with the specified source and a formatted message. The message will be appended with
+     * the source reference.
+     *
+     * @param source Statement source
+     * @param format Format string, according to {@link String#format(String, Object...)}.
+     * @param args Format string arguments, according to {@link String#format(String, Object...)}
+     */
+    public SourceException(final @NonNull StatementSourceReference source, final @NonNull String format,
+            final Object... args) {
+        this(String.format(format, args), source);
+    }
+
+    /**
+     * Create a new instance with the specified source and a formatted message. The message will be appended with
+     * the source reference.
+     *
+     * @param source Statement source
+     * @param cause Underlying cause of this exception
+     * @param format Format string, according to {@link String#format(String, Object...)}.
+     * @param args Format string arguments, according to {@link String#format(String, Object...)}
+     */
+    public SourceException(final @NonNull StatementSourceReference source, final Throwable cause,
+            final @NonNull String format, final Object... args) {
+        this(String.format(format, args), source, cause);
+    }
+
+    /**
+     * Return the reference to the source which caused this exception.
+     *
+     * @return Source reference
+     */
+    public @NonNull StatementSourceReference getSourceReference() {
+        return sourceRef;
+    }
+
+    /**
+     * Throw an instance of this exception if an expression evaluates to true. If the expression evaluates to false,
+     * this method does nothing.
+     *
+     * @param expression Expression to be evaluated
+     * @param source Statement source reference
+     * @param format Format string, according to {@link String#format(String, Object...)}.
+     * @param args Format string arguments, according to {@link String#format(String, Object...)}
+     * @throws SourceException if the expression evaluates to true.
+     */
+    public static void throwIf(final boolean expression, final @NonNull StatementSourceReference source,
+            final @NonNull String format, final Object... args) {
+        if (expression) {
+            throw new SourceException(source, format, args);
+        }
+    }
+
+    /**
+     * Throw an instance of this exception if an object is null. If the object is non-null, it will
+     * be returned as the result of this method.
+     *
+     * @param obj Object reference to be checked
+     * @param source Statement source reference
+     * @param format Format string, according to {@link String#format(String, Object...)}.
+     * @param args Format string arguments, according to {@link String#format(String, Object...)}
+     * @return Object if it is not null
+     * @throws SourceException if object is null
+     */
+    public static <T> @NonNull T throwIfNull(final @Nullable T obj, final @NonNull StatementSourceReference source,
+            final @NonNull String format, final Object... args) {
+        if (obj == null) {
+            throw new SourceException(source, format, args);
+        }
+        return obj;
+    }
+
+    /**
+     * Throw an instance of this exception if an optional is not present. If it is present, this method will return
+     * the unwrapped value.
+     *
+     * @param opt Optional to be checked
+     * @param source Statement source reference
+     * @param format Format string, according to {@link String#format(String, Object...)}.
+     * @param args Format string arguments, according to {@link String#format(String, Object...)}
+     * @return Object unwrapped from the opt optional
+     * @throws SourceException if the optional is not present
+     */
+    public static <T> @NonNull T unwrap(final Optional<T> opt, final @NonNull StatementSourceReference source,
+            final @NonNull String format, final Object... args) {
+        throwIf(opt.isEmpty(), source, format, args);
+        return opt.get();
+    }
+
+    private static String createMessage(final @NonNull String message, final @NonNull StatementSourceReference source) {
+        return requireNonNull(message) + " [at " + requireNonNull(source) + ']';
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StatementSourceReference.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StatementSourceReference.java
new file mode 100644 (file)
index 0000000..59c3dfd
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+
+/**
+ * Reference of statement source. Statement source reference serves to provide information, why a statement was defined
+ * and introduced in model.
+ *
+ * <p>
+ * Reasons for introduction of statement could be various, but most obvious one is explicit declaration in model source
+ * text such as {@link DeclarationInTextSource}.
+ */
+public interface StatementSourceReference {
+    /**
+     * Returns source type.
+     *
+     * @return {@link StatementSource#DECLARATION} if statement was explicitly declared in YANG model source,
+     *         {@link StatementSource#CONTEXT} if statement was inferred.
+     */
+    @NonNull StatementSource getStatementSource();
+
+    /**
+     * Returns human readable representation of statement source.
+     *
+     * <p>
+     * Implementations of this interface should override {@link #toString()}, since it may be used in error reporting
+     * to provide context information for model designer to debug errors in its mode.
+     *
+     * @return human readable representation of statement source.
+     */
+    @Override
+    String toString();
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StatementStreamSource.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StatementStreamSource.java
new file mode 100644 (file)
index 0000000..bed368c
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+
+/**
+ * Statement stream source, which is used for inference of effective model.
+ *
+ * <p>
+ * Statement stream source is required to emit its statements using supplied {@link StatementWriter}.
+ *
+ * <p>
+ * Since YANG allows language extensions defined in sources (which defines how source is serialized), instances of
+ * extensions present anywhere and forward references, each source needs to be processed in three steps, where each step
+ * uses different set of supported statements.
+ *
+ * <p>
+ * Steps (in order of invocation) are:
+ * <ol>
+ * <li>{@link #writePreLinkage(StatementWriter, QNameToStatementDefinition)} -
+ * Source MUST emit only statements related in pre-linkage, which are present in
+ * supplied statement definition map. This step is used as preparatory cross-source
+ * relationship resolution phase which collects available module names and namespaces.
+ * It is necessary in order to correct resolution of unknown statements used by linkage
+ * phase (e.g. semantic version of yang modules).
+ * </li>
+ * <li>{@link #writeLinkage(StatementWriter, QNameToStatementDefinition, PrefixToModule, YangVersion)} -
+ * Source MUST emit only statements related in linkage, which are present in
+ * supplied statement definition map. This step is used to build cross-source
+ * linkage and visibility relationship, and to determine XMl namespaces and
+ * prefixes.</li>
+ * <li>
+ * {@link #writeLinkageAndStatementDefinitions(StatementWriter, QNameToStatementDefinition, PrefixToModule,
+ * YangVersion)}
+ * - Source MUST emit only statements related to linkage and language extensions
+ * definitions, which are present in supplied statement definition map. This
+ * step is used to build statement definitions in order to fully processed
+ * source.</li>
+ * <li>
+ * {@link #writeFull(StatementWriter, QNameToStatementDefinition, PrefixToModule, YangVersion)}
+ * - Source MUST emit all statements present in source. This step is used to
+ * build full declared statement model of source.</li>
+ * </ol>
+ */
+// FIXME: 5.0.0: this is a push parser, essentially traversing the same tree multiple times. Perhaps we should create
+//               a visitor/filter or perform some explicit argument binding?
+public interface StatementStreamSource extends Identifiable<SourceIdentifier> {
+    /**
+     * Emits only pre-linkage-related statements to supplied {@code writer}.
+     *
+     * @param writer
+     *            {@link StatementWriter} which should be used to emit
+     *            statements.
+     * @param stmtDef
+     *            Map of available statement definitions. Only these statements
+     *            may be written to statement writer, source MUST ignore and MUST NOT
+     *            emit any other statements.
+     * @throws SourceException
+     *             If source was is not valid, or provided statement writer
+     *             failed to write statements.
+     */
+    void writePreLinkage(StatementWriter writer, QNameToStatementDefinition stmtDef);
+
+    /**
+     * Emits only linkage-related statements to supplied {@code writer} based on specified YANG version.
+     * Default implementation does not make any differences between versions.
+     *
+     * @param writer
+     *            {@link StatementWriter} which should be used to emit
+     *            statements.
+     * @param stmtDef
+     *            Map of available statement definitions. Only these statements
+     *            may be written to statement writer, source MUST ignore and
+     *            MUST NOT emit any other statements.
+     * @param preLinkagePrefixes
+     *            Pre-linkage map of source-specific prefixes to namespaces
+     * @param yangVersion
+     *            yang version.
+     * @throws SourceException
+     *             If source was is not valid, or provided statement writer
+     *             failed to write statements.
+     */
+    void writeLinkage(StatementWriter writer, QNameToStatementDefinition stmtDef, PrefixToModule preLinkagePrefixes,
+            YangVersion yangVersion);
+
+    /**
+     * Emits only linkage and language extension statements to supplied
+     * {@code writer} based on specified YANG version. Default implementation
+     * does not make any differences between versions.
+     *
+     * @param writer
+     *            {@link StatementWriter} which should be used to emit
+     *            statements.
+     * @param stmtDef
+     *            Map of available statement definitions. Only these statements
+     *            may be written to statement writer, source MUST ignore and
+     *            MUST NOT emit any other statements.
+     * @param prefixes
+     *            Map of source-specific prefixes to namespaces
+     * @param yangVersion
+     *            YANG version.
+     *
+     * @throws SourceException
+     *             If source was is not valid, or provided statement writer
+     *             failed to write statements.
+     */
+    void writeLinkageAndStatementDefinitions(StatementWriter writer, QNameToStatementDefinition stmtDef,
+            PrefixToModule prefixes, YangVersion yangVersion);
+
+    /**
+     * Emits every statements present in this statement source to supplied
+     * {@code writer} based on specified yang version. Default implementation
+     * does not make any differences between versions.
+     *
+     * @param writer
+     *            {@link StatementWriter} which should be used to emit
+     *            statements.
+     * @param stmtDef
+     *            Map of available statement definitions.
+     * @param prefixes
+     *            Map of source-specific prefixes to namespaces
+     * @param yangVersion
+     *            yang version.
+     * @throws SourceException
+     *             If source was is not valid, or provided statement writer
+     *             failed to write statements.
+     */
+    void writeFull(StatementWriter writer, QNameToStatementDefinition stmtDef, PrefixToModule prefixes,
+            YangVersion yangVersion);
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StatementWriter.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StatementWriter.java
new file mode 100644 (file)
index 0000000..db52ffe
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
+
+public interface StatementWriter {
+    /**
+     * Resumed statement state.
+     *
+     * @author Robert Varga
+     */
+    @Beta
+    @NonNullByDefault
+    interface ResumedStatement {
+        /**
+         * Return statement definition.
+         *
+         * @return statement definition.
+         */
+        StatementDefinition getDefinition();
+
+        /**
+         * Return statement source reference.
+         *
+         * @return statement source reference.
+         */
+        StatementSourceReference getSourceReference();
+
+        /**
+         * Check if the statement has been fully defined. This implies that all its children have been fully defined.
+         *
+         * @return True if the statement has been fully defined.
+         */
+        boolean isFullyDefined();
+    }
+
+    /**
+     * Attempt to resume a child statement. If the statement has been previously defined, a {@link ResumedStatement}
+     * instance is returned.
+     *
+     * <p>
+     * If an empty optional is returned, the caller is expected to follow-up with
+     * {@link #startStatement(int, QName, String, StatementSourceReference)} to define the statement.
+     *
+     * <p>
+     * If the returned resumed statement indicates {@link ResumedStatement#isFullyDefined()}, the caller should take
+     * no further action with this or any of the child statements. Otherwise this call is equivalent of issuing
+     * {@link #startStatement(int, QName, String, StatementSourceReference)} and the caller is expected to process
+     * any child statements. The caller should call {@link #storeStatement(int, boolean)} before finishing processing
+     * with {@link #endStatement(StatementSourceReference)}.
+     *
+     * @param childId Child
+     * @return A resumed statement or empty if the statement has not previously been defined.
+     */
+    @Beta
+    default Optional<? extends ResumedStatement> resumeStatement(final int childId) {
+        return Optional.empty();
+    }
+
+    /**
+     * Store a defined statement, hinting at the number of children it is expected to have and indicating whether
+     * it has been fully defined. This method should be called before {@link #endStatement(StatementSourceReference)}
+     * when the caller is taking advantage of {@link #resumeStatement(int)}.
+     *
+     * @param expectedChildren Number of expected children, cannot be negative
+     * @param fullyDefined True if the statement and all its descendants have been defined.
+     */
+    @Beta
+    default void storeStatement(final int expectedChildren, final boolean fullyDefined) {
+        // No-op
+    }
+
+    /**
+     * Starts statement with supplied name and location in source.
+     *
+     * <p>
+     * Each started statement must also be closed by
+     * {@link #endStatement(StatementSourceReference)} in order for stream to be
+     * correct.
+     *
+     * <p>
+     * If statement has substatements, in order to start substatement, call to
+     * {@link #startStatement(int, QName, String, StatementSourceReference)} needs to be done for substatement.
+     *
+     * @param childId Child identifier, unique among siblings
+     * @param name Fully qualified name of statement.
+     * @param argument String representation of value as appeared in source, null if not present
+     * @param ref Identifier of location in source, which will be used for reporting in case of statement processing
+     *            error.
+     * @throws SourceException if statement is not valid according to current context.
+     */
+    void startStatement(int childId, @NonNull QName name, @Nullable String argument,
+            @NonNull StatementSourceReference ref);
+
+    /**
+     * Ends current opened statement.
+     *
+     * @param ref Identifier of location in source, which will be used for reporting in case of statement processing
+     *            error.
+     * @throws SourceException if closed statement is not valid in current context, or there is no such statement
+     */
+    void endStatement(@NonNull StatementSourceReference ref);
+
+    /**
+     * Return current model processing phase.
+     *
+     * @return current processing phase
+     */
+    @NonNull ModelProcessingPhase getPhase();
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StmtOrderingNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StmtOrderingNamespace.java
new file mode 100644 (file)
index 0000000..54cb7cb
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 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.spi.source;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Map of statement order.
+ */
+public interface StmtOrderingNamespace extends IdentifierNamespace<YangStmtMapping, Integer> {
+    NamespaceBehaviour<YangStmtMapping, Integer, @NonNull StmtOrderingNamespace> BEHAVIOUR =
+            NamespaceBehaviour.global(StmtOrderingNamespace.class);
+
+}
\ No newline at end of file
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/SupportedFeaturesNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/SupportedFeaturesNamespace.java
new file mode 100644 (file)
index 0000000..1d937d1
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 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.spi.source;
+
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+public interface SupportedFeaturesNamespace
+        extends IdentifierNamespace<SupportedFeaturesNamespace.SupportedFeatures, Set<QName>> {
+    NamespaceBehaviour<SupportedFeatures, Set<QName>, @NonNull SupportedFeaturesNamespace> BEHAVIOUR =
+            NamespaceBehaviour.global(SupportedFeaturesNamespace.class);
+
+    enum SupportedFeatures {
+        SUPPORTED_FEATURES
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/package-info.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/package-info.java
new file mode 100644 (file)
index 0000000..1ba9498
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2017 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
+ */
+
+/**
+ * Provides API necessary for the processing of statement sources.
+ */
+package org.opendaylight.yangtools.yang.parser.spi.source;
\ No newline at end of file
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/validation/ValidationBundlesNamespace.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/validation/ValidationBundlesNamespace.java
new file mode 100644 (file)
index 0000000..5d7133a
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015 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.spi.validation;
+
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Namespace used for validating whether a node is of some type, e.g. usable target for some operation or has other
+ * significant properties.
+ */
+public interface ValidationBundlesNamespace extends
+        IdentifierNamespace<ValidationBundlesNamespace.ValidationBundleType, Collection<?>> {
+    NamespaceBehaviour<ValidationBundleType, Collection<?>, @NonNull ValidationBundlesNamespace> BEHAVIOUR =
+            NamespaceBehaviour.global(ValidationBundlesNamespace.class);
+
+    enum ValidationBundleType {
+        /**
+         * Whether a node is suitable refine substatement.
+         */
+        SUPPORTED_REFINE_SUBSTATEMENTS,
+
+        /**
+         * Whether a node is suitable target for refine operation.
+         */
+        SUPPORTED_REFINE_TARGETS,
+
+        /**
+         * Whether a node is suitable target for augment operation.
+         */
+        SUPPORTED_AUGMENT_TARGETS,
+
+        /**
+         * Whether a <a href="https://tools.ietf.org/html/rfc6020#section-7.9.2">case shorthand</a> can be created for a
+         * node.
+         */
+        // FIXME: 5.0.0: consider removing this constant, this functionality is part of statement support.
+        SUPPORTED_CASE_SHORTHANDS,
+
+        /**
+         * Whether a node is data node.
+         */
+        SUPPORTED_DATA_NODES
+    }
+}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/validation/package-info.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/validation/package-info.java
new file mode 100644 (file)
index 0000000..1d13f8f
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017 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
+ */
+
+/**
+ * Provides interfaces and enumerations necessary for validation processes.
+ * Currently, this package contains an interface which represents a namespace used
+ * by the validation process. This namespace contains different types of validation
+ * bundles or collections. Allowed types of validation bundles are defined by the
+ * enumeration {@link ValidationBundlesNamespace.ValidationBundleType} which is
+ * provided by this package.
+ */
+package org.opendaylight.yangtools.yang.parser.spi.validation;
\ No newline at end of file
diff --git a/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/NamespaceTest.java b/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/NamespaceTest.java
new file mode 100644 (file)
index 0000000..c8ae439
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.spi;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+
+public class NamespaceTest {
+    @Test
+    public void testNamespaces() {
+        // Touch behaviors
+        // FIXME: add more checks/split this up when behaviours are testable
+        assertNotNull(ExtensionNamespace.BEHAVIOUR);
+        assertNotNull(GroupingNamespace.BEHAVIOUR);
+        assertNotNull(IdentityNamespace.BEHAVIOUR);
+        assertNotNull(ModuleNamespace.BEHAVIOUR);
+        assertNotNull(PreLinkageModuleNamespace.BEHAVIOUR);
+        assertNotNull(SubmoduleNamespace.BEHAVIOUR);
+        assertNotNull(TypeNamespace.BEHAVIOUR);
+
+        assertNotNull(NamespaceToModule.BEHAVIOUR);
+    }
+}
diff --git a/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/meta/CopyHistoryTest.java b/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/meta/CopyHistoryTest.java
new file mode 100644 (file)
index 0000000..6a5c112
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 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.spi.meta;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class CopyHistoryTest {
+
+    @Test
+    public void testSingleton() {
+        final CopyHistory original = CopyHistory.original();
+
+        assertEquals(CopyType.ORIGINAL, original.getLastOperation());
+        assertTrue(original.contains(CopyType.ORIGINAL));
+        assertFalse(original.contains(CopyType.ADDED_BY_USES));
+        assertFalse(original.contains(CopyType.ADDED_BY_AUGMENTATION));
+        assertFalse(original.contains(CopyType.ADDED_BY_USES_AUGMENTATION));
+
+        assertSame(original, CopyHistory.original());
+    }
+
+    @Test
+    public void testAppend() {
+        final CopyHistory original = CopyHistory.original();
+        assertSame(original, original.append(CopyType.ORIGINAL, original));
+
+        final CopyHistory originalUA = original.append(CopyType.ADDED_BY_USES_AUGMENTATION, original);
+        assertEquals(CopyType.ADDED_BY_USES_AUGMENTATION, originalUA.getLastOperation());
+        assertTrue(originalUA.contains(CopyType.ORIGINAL));
+        assertFalse(originalUA.contains(CopyType.ADDED_BY_USES));
+        assertFalse(originalUA.contains(CopyType.ADDED_BY_AUGMENTATION));
+        assertTrue(originalUA.contains(CopyType.ADDED_BY_USES_AUGMENTATION));
+
+        assertSame(originalUA, original.append(CopyType.ADDED_BY_USES_AUGMENTATION, original));
+        assertSame(originalUA, originalUA.append(CopyType.ADDED_BY_USES_AUGMENTATION, original));
+
+        final CopyHistory originalU = original.append(CopyType.ADDED_BY_USES, original);
+        assertEquals(CopyType.ADDED_BY_USES, originalU.getLastOperation());
+        assertTrue(originalU.contains(CopyType.ORIGINAL));
+        assertTrue(originalU.contains(CopyType.ADDED_BY_USES));
+        assertFalse(originalU.contains(CopyType.ADDED_BY_AUGMENTATION));
+        assertFalse(originalU.contains(CopyType.ADDED_BY_USES_AUGMENTATION));
+
+        final CopyHistory uaU = originalUA.append(CopyType.ADDED_BY_USES, original);
+        assertEquals(CopyType.ADDED_BY_USES, uaU.getLastOperation());
+        assertTrue(uaU.contains(CopyType.ORIGINAL));
+        assertTrue(uaU.contains(CopyType.ADDED_BY_USES));
+        assertFalse(uaU.contains(CopyType.ADDED_BY_AUGMENTATION));
+        assertTrue(uaU.contains(CopyType.ADDED_BY_USES_AUGMENTATION));
+
+        assertSame(uaU, originalUA.append(CopyType.ADDED_BY_USES, original));
+
+        final CopyHistory res = originalUA.append(CopyType.ADDED_BY_AUGMENTATION, originalU);
+        assertEquals(CopyType.ADDED_BY_AUGMENTATION, res.getLastOperation());
+        assertTrue(res.contains(CopyType.ORIGINAL));
+        assertTrue(res.contains(CopyType.ADDED_BY_USES));
+        assertTrue(res.contains(CopyType.ADDED_BY_AUGMENTATION));
+        assertTrue(res.contains(CopyType.ADDED_BY_USES_AUGMENTATION));
+    }
+
+}
diff --git a/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/meta/ModelProcessingPhaseTest.java b/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/meta/ModelProcessingPhaseTest.java
new file mode 100644 (file)
index 0000000..c03bfbf
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.spi.meta;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+
+public class ModelProcessingPhaseTest {
+    @Test
+    public void testSequencing() {
+        assertNull(ModelProcessingPhase.INIT.getPreviousPhase());
+        assertEquals(ModelProcessingPhase.INIT, ModelProcessingPhase.SOURCE_PRE_LINKAGE.getPreviousPhase());
+        assertEquals(ModelProcessingPhase.SOURCE_PRE_LINKAGE, ModelProcessingPhase.SOURCE_LINKAGE.getPreviousPhase());
+        assertEquals(ModelProcessingPhase.SOURCE_LINKAGE, ModelProcessingPhase.STATEMENT_DEFINITION.getPreviousPhase());
+        assertEquals(ModelProcessingPhase.STATEMENT_DEFINITION,
+            ModelProcessingPhase.FULL_DECLARATION.getPreviousPhase());
+        assertEquals(ModelProcessingPhase.FULL_DECLARATION,
+            ModelProcessingPhase.EFFECTIVE_MODEL.getPreviousPhase());
+    }
+}
diff --git a/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/meta/NamespaceTest.java b/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/meta/NamespaceTest.java
new file mode 100644 (file)
index 0000000..936f3be
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.spi.meta;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+
+public class NamespaceTest {
+    @Test
+    public void testNamespaces() {
+        // Touch behaviors
+        // FIXME: add more checks/split this up when behaviours are testable
+        assertNotNull(DerivedIdentitiesNamespace.BEHAVIOUR);
+        assertNotNull(SemanticVersionNamespace.BEHAVIOUR);
+        assertNotNull(SemanticVersionModuleNamespace.BEHAVIOUR);
+        assertNotNull(StatementDefinitionNamespace.BEHAVIOUR);
+    }
+}
diff --git a/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/meta/QNameCacheNamespaceTest.java b/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/meta/QNameCacheNamespaceTest.java
new file mode 100644 (file)
index 0000000..964580a
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.spi.meta;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+
+public class QNameCacheNamespaceTest {
+    @Test
+    public void testGetInstance() {
+        assertNotNull(QNameCacheNamespace.getInstance());
+    }
+}
diff --git a/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/DeclarationInTextSourceTest.java b/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/DeclarationInTextSourceTest.java
new file mode 100644 (file)
index 0000000..7197171
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.spi.source;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+
+public class DeclarationInTextSourceTest {
+    @Test
+    public void testStatementSource() {
+        assertEquals(StatementSource.DECLARATION, DeclarationInTextSource.atLine("foo", 5).getStatementSource());
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals("foo", DeclarationInTextSource.inSource("foo").toString());
+        assertEquals("foo:5", DeclarationInTextSource.atLine("foo", 5).toString());
+        assertEquals("foo:5:10", DeclarationInTextSource.atPosition("foo", 5, 10).toString());
+    }
+}
diff --git a/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/ImplicitStatementTest.java b/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/ImplicitStatementTest.java
new file mode 100644 (file)
index 0000000..b06cdd3
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.spi.source;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
+
+public class ImplicitStatementTest {
+    @Test
+    public void testForwarding() {
+        final StatementSourceReference ref = mock(StatementSourceReference.class);
+        final ImplicitSubstatement stmt = ImplicitSubstatement.of(ref);
+        assertEquals(StatementSource.CONTEXT, stmt.getStatementSource());
+
+        doReturn("ref").when(ref).toString();
+        assertEquals("ref", stmt.toString());
+    }
+}
diff --git a/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/NamespaceTest.java b/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/NamespaceTest.java
new file mode 100644 (file)
index 0000000..5b433a5
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.spi.source;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+
+public class NamespaceTest {
+    @Test
+    public void testNamespaces() {
+        // Touch behaviors
+        // FIXME: add more checks/split this up when behaviours are testable
+        assertNotNull(BelongsToModuleContext.BEHAVIOUR);
+        assertNotNull(BelongsToPrefixToModuleCtx.BEHAVIOUR);
+        assertNotNull(BelongsToPrefixToModuleName.BEHAVIOUR);
+        assertNotNull(ImpPrefixToNamespace.BEHAVIOUR);
+        assertNotNull(ImportedModuleContext.BEHAVIOUR);
+        assertNotNull(ImportPrefixToModuleCtx.BEHAVIOUR);
+        assertNotNull(ImportPrefixToSemVerSourceIdentifier.BEHAVIOUR);
+        assertNotNull(IncludedSubmoduleNameToModuleCtx.BEHAVIOUR);
+        assertNotNull(IncludedModuleContext.BEHAVIOUR);
+        assertNotNull(ModuleCtxToModuleQName.BEHAVIOUR);
+        assertNotNull(ModuleNameToNamespace.BEHAVIOUR);
+        assertNotNull(ModuleQNameToModuleName.BEHAVIOUR);
+        assertNotNull(ModuleCtxToSourceIdentifier.BEHAVIOUR);
+        assertNotNull(ModuleNamespaceForBelongsTo.BEHAVIOUR);
+        assertNotNull(ModuleNameToModuleQName.BEHAVIOUR);
+        assertNotNull(ModulesDeviatedByModules.BEHAVIOUR);
+        assertNotNull(PrefixToModule.BEHAVIOUR);
+        assertNotNull(StmtOrderingNamespace.BEHAVIOUR);
+        assertNotNull(SupportedFeaturesNamespace.BEHAVIOUR);
+    }
+}
diff --git a/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/QNameToStatementDefinitionMapTest.java b/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/QNameToStatementDefinitionMapTest.java
new file mode 100644 (file)
index 0000000..f75652c
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.spi.source;
+
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupport;
+
+public class QNameToStatementDefinitionMapTest {
+    private static final QName QNAME = QName.create("", "a");
+
+    private final QNameToStatementDefinitionMap map = new QNameToStatementDefinitionMap();
+    private final StatementSupport<?, ?, ?> support = mock(StatementSupport.class);
+
+    @Test(expected = NullPointerException.class)
+    public void testPutNullNull() {
+        map.put(null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testPutNullSome() {
+        map.put(null, mock(StatementSupport.class));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testPutSomeNull() {
+        map.put(QName.create("", "a"), null);
+    }
+
+    @Test
+    public void testPut() {
+        map.put(QNAME, support);
+        assertSame(support, map.get(QNAME));
+    }
+}
diff --git a/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/SourceExceptionTest.java b/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/SourceExceptionTest.java
new file mode 100644 (file)
index 0000000..70f2dfb
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.spi.source;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SourceExceptionTest {
+    private StatementSourceReference mock;
+
+    @Before
+    public void before() {
+        mock = mock(StatementSourceReference.class);
+        doReturn("mock").when(mock).toString();
+    }
+
+    @Test
+    public void testThrowIfFalse() {
+        SourceException.throwIf(false, mock, "");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testThrowIfTrueMockNull() {
+        SourceException.throwIf(true, mock, null);
+    }
+
+    @Test(expected = SourceException.class)
+    public void testThrowIfTrueMockEmpty() {
+        SourceException.throwIf(true, mock, "");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testThrowIfNullNullMockNull() {
+        SourceException.throwIfNull(null, mock, null);
+    }
+
+    @Test(expected = SourceException.class)
+    public void testThrowIfNullNullMockEmpty() {
+        SourceException.throwIfNull(null, mock, "");
+    }
+
+    @Test
+    public void testThrowIfNullMock() {
+        assertSame(mock, SourceException.throwIfNull(mock, mock, ""));
+    }
+
+    @Test
+    public void testUnwrapPresent() {
+        assertEquals("test", SourceException.unwrap(Optional.of("test"), mock, ""));
+    }
+
+    @Test(expected = SourceException.class)
+    public void testUnwrapAbsent() {
+        SourceException.unwrap(Optional.empty(), mock, "");
+    }
+}
diff --git a/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/validation/ValidationBundlesNamespaceTest.java b/yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/validation/ValidationBundlesNamespaceTest.java
new file mode 100644 (file)
index 0000000..45a9e24
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.spi.validation;
+
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.BEHAVIOUR;
+import static org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType.SUPPORTED_DATA_NODES;
+
+import java.util.Collection;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
+
+public class ValidationBundlesNamespaceTest {
+    @Test
+    public void testBehaviour() {
+        final NamespaceStorageNode node = mock(NamespaceStorageNode.class);
+        doReturn(StorageNodeType.GLOBAL).when(node).getStorageNodeType();
+
+        final Collection<?> result = mock(Collection.class);
+        doReturn(result).when(node).getFromLocalStorage(ValidationBundlesNamespace.class, SUPPORTED_DATA_NODES);
+        assertSame(result, BEHAVIOUR.getFrom(node, SUPPORTED_DATA_NODES));
+    }
+}
diff --git a/yang/yang-test-util/pom.xml b/yang/yang-test-util/pom.xml
new file mode 100644 (file)
index 0000000..201dd71
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2016 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-test-util</artifactId>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/yang/yang-test-util/src/main/java/org/opendaylight/yangtools/yang/test/util/YangParserTestUtils.java b/yang/yang-test-util/src/main/java/org/opendaylight/yangtools/yang/test/util/YangParserTestUtils.java
new file mode 100644 (file)
index 0000000..7a1566f
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2016 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.test.util;
+
+import com.google.common.annotations.Beta;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+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.StatementParserMode;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+/**
+ * Utility class which provides convenience methods for producing effective schema context based on the supplied
+ * yang/yin sources or paths to these sources.
+ */
+@Beta
+public final class YangParserTestUtils {
+
+    private static final FileFilter YANG_FILE_FILTER = file -> {
+        // Locale keeps SpotBugs happy. It should not matter that much anyway.
+        final String name = file.getName().toLowerCase(Locale.ENGLISH);
+        return name.endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION) && file.isFile();
+    };
+
+    private static final @NonNull YangParserFactory PARSER_FACTORY;
+
+    static {
+        final Iterator<@NonNull YangParserFactory> it = ServiceLoader.load(YangParserFactory.class).iterator();
+        if (!it.hasNext()) {
+            throw new IllegalStateException("No YangParserFactory found");
+        }
+        PARSER_FACTORY = it.next();
+    }
+
+    private YangParserTestUtils() {
+        throw new UnsupportedOperationException("Utility class should not be instantiated.");
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG source. Statement parser mode is set to
+     * default mode and all YANG features are supported.
+     *
+     * @param resource relative path to the YANG file to be parsed
+     *
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangResource(final String resource) {
+        return parseYangResource(resource, StatementParserMode.DEFAULT_MODE);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG source. All YANG features are supported.
+     *
+     * @param resource relative path to the YANG file to be parsed
+     * @param parserMode mode of statement parser
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangResource(final String resource, final StatementParserMode parserMode) {
+        return parseYangResource(resource, parserMode, null);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG source. Statement parser mode is set to
+     * default mode.
+     *
+     * @param resource relative path to the YANG file to be parsed
+     * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
+     *                          model are resolved
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangResource(final String resource, final Set<QName> supportedFeatures) {
+        return parseYangResource(resource, StatementParserMode.DEFAULT_MODE, supportedFeatures);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG source.
+     *
+     * @param resource relative path to the YANG file to be parsed
+     * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
+     *                          model are resolved
+     * @param parserMode mode of statement parser
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangResource(final String resource, final StatementParserMode parserMode,
+            final Set<QName> supportedFeatures) {
+        final YangTextSchemaSource source = YangTextSchemaSource.forResource(YangParserTestUtils.class, resource);
+        return parseYangSources(parserMode, supportedFeatures, source);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
+     * default mode and all YANG features are supported.
+     *
+     * @param files YANG files to be parsed
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangFiles(final File... files) {
+        return parseYangFiles(Arrays.asList(files));
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
+     * default mode and all YANG features are supported.
+     *
+     * @param files collection of YANG files to be parsed
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangFiles(final Collection<File> files) {
+        return parseYangFiles(StatementParserMode.DEFAULT_MODE, files);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
+     * default mode.
+     *
+     * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
+     *                          models are resolved
+     * @param files YANG files to be parsed
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangFiles(final Set<QName> supportedFeatures, final File... files) {
+        return parseYangFiles(supportedFeatures, Arrays.asList(files));
+    }
+
+    public static EffectiveModelContext parseYangFiles(final Set<QName> supportedFeatures,
+            final Collection<File> files) {
+        return parseYangFiles(supportedFeatures, StatementParserMode.DEFAULT_MODE, files);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources. All YANG features are supported.
+     *
+     * @param parserMode mode of statement parser
+     * @param files YANG files to be parsed
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangFiles(final StatementParserMode parserMode, final File... files) {
+        return parseYangFiles(parserMode, Arrays.asList(files));
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources. All YANG features are supported.
+     *
+     * @param parserMode mode of statement parser
+     * @param files collection of YANG files to be parsed
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangFiles(final StatementParserMode parserMode,
+            final Collection<File> files) {
+        return parseYangFiles(null, parserMode, files);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources.
+     *
+     * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
+     *                          models are resolved
+     * @param parserMode mode of statement parser
+     * @param files YANG files to be parsed
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangFiles(final Set<QName> supportedFeatures,
+            final StatementParserMode parserMode, final File... files) {
+        return parseYangFiles(supportedFeatures, parserMode, Arrays.asList(files));
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources.
+     *
+     * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
+     *                          models are resolved
+     * @param parserMode mode of statement parser
+     * @param files YANG files to be parsed
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangFiles(final Set<QName> supportedFeatures,
+            final StatementParserMode parserMode, final Collection<File> files) {
+        return parseSources(parserMode, supportedFeatures,
+            files.stream().map(YangTextSchemaSource::forFile).collect(Collectors.toList()));
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
+     * default mode and all YANG features are supported.
+     *
+     * @param resourcePath relative path to the directory with YANG files to be parsed
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangResourceDirectory(final String resourcePath) {
+        return parseYangResourceDirectory(resourcePath, StatementParserMode.DEFAULT_MODE);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources. All YANG features are supported.
+     *
+     * @param resourcePath relative path to the directory with YANG files to be parsed
+     * @param parserMode mode of statement parser
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangResourceDirectory(final String resourcePath,
+            final StatementParserMode parserMode) {
+        return parseYangResourceDirectory(resourcePath, null, parserMode);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
+     * default mode.
+     *
+     * @param resourcePath relative path to the directory with YANG files to be parsed
+     * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
+     *                          models are resolved
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangResourceDirectory(final String resourcePath,
+            final Set<QName> supportedFeatures) {
+        return parseYangResourceDirectory(resourcePath, supportedFeatures, StatementParserMode.DEFAULT_MODE);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources.
+     *
+     * @param resourcePath relative path to the directory with YANG files to be parsed
+     * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
+     *                          models are resolved
+     * @param parserMode mode of statement parser
+     * @return effective schema context
+     */
+    @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Wrong inferent on listFiles")
+    public static EffectiveModelContext parseYangResourceDirectory(final String resourcePath,
+            final Set<QName> supportedFeatures, final StatementParserMode parserMode) {
+        final URI directoryPath;
+        try {
+            directoryPath = YangParserTestUtils.class.getResource(resourcePath).toURI();
+        } catch (URISyntaxException e) {
+            throw new IllegalArgumentException("Failed to open resource " + resourcePath, e);
+        }
+        return parseYangFiles(supportedFeatures, parserMode, new File(directoryPath).listFiles(YANG_FILE_FILTER));
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
+     * default mode and all YANG features are supported.
+     *
+     * @param clazz Resource lookup base
+     * @param resources Resource names to be looked up
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangResources(final Class<?> clazz, final String... resources) {
+        return parseYangResources(clazz, Arrays.asList(resources));
+    }
+
+    public static EffectiveModelContext parseYangResources(final Class<?> clazz, final Collection<String> resources) {
+        final List<YangTextSchemaSource> sources = new ArrayList<>(resources.size());
+        for (final String r : resources) {
+            sources.add(YangTextSchemaSource.forResource(clazz, r));
+        }
+        return parseSources(StatementParserMode.DEFAULT_MODE, null, sources);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources. Statement parser mode is set to
+     * default mode.
+     *
+     * @param yangDirs relative paths to the directories containing YANG files to be parsed
+     * @param yangFiles relative paths to the YANG files to be parsed
+     * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
+     *                          models are resolved
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangResources(final List<String> yangDirs, final List<String> yangFiles,
+            final Set<QName> supportedFeatures) {
+        return parseYangResources(yangDirs, yangFiles, supportedFeatures, StatementParserMode.DEFAULT_MODE);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources. All YANG features are supported.
+     *
+     * @param yangResourceDirs relative paths to the directories containing YANG files to be parsed
+     * @param yangResources relative paths to the YANG files to be parsed
+     * @param statementParserMode mode of statement parser
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangResources(final List<String> yangResourceDirs,
+            final List<String> yangResources, final StatementParserMode statementParserMode) {
+        return parseYangResources(yangResourceDirs, yangResources, null, statementParserMode);
+    }
+
+    /**
+     * Creates a new effective schema context containing the specified YANG sources.
+     *
+     * @param yangResourceDirs relative paths to the directories containing YANG files to be parsed
+     * @param yangResources relative paths to the YANG files to be parsed
+     * @param supportedFeatures set of supported features based on which all if-feature statements in the parsed YANG
+     *                          models are resolved
+     * @param statementParserMode mode of statement parser
+     * @return effective schema context
+     */
+    public static EffectiveModelContext parseYangResources(final List<String> yangResourceDirs,
+            final List<String> yangResources, final Set<QName> supportedFeatures,
+            final StatementParserMode statementParserMode) {
+        final List<File> allYangFiles = new ArrayList<>();
+        for (final String yangDir : yangResourceDirs) {
+            allYangFiles.addAll(getYangFiles(yangDir));
+        }
+
+        for (final String yangFile : yangResources) {
+            try {
+                allYangFiles.add(new File(YangParserTestUtils.class.getResource(yangFile).toURI()));
+            } catch (URISyntaxException e) {
+                throw new IllegalArgumentException("Invalid resource " + yangFile, e);
+            }
+        }
+
+        return parseYangFiles(supportedFeatures, statementParserMode, allYangFiles);
+    }
+
+    public static EffectiveModelContext parseYangSources(final StatementParserMode parserMode,
+            final Set<QName> supportedFeatures, final YangTextSchemaSource... sources) {
+        return parseSources(parserMode, supportedFeatures, Arrays.asList(sources));
+    }
+
+    public static EffectiveModelContext parseSources(final StatementParserMode parserMode,
+            final Set<QName> supportedFeatures, final Collection<? extends SchemaSourceRepresentation> sources) {
+        final YangParser parser = PARSER_FACTORY.createParser(parserMode);
+        if (supportedFeatures != null) {
+            parser.setSupportedFeatures(supportedFeatures);
+        }
+
+        try {
+            parser.addSources(sources);
+        } catch (YangSyntaxErrorException e) {
+            throw new IllegalArgumentException("Malformed source", e);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Failed to read a source", e);
+        }
+
+        try {
+            return parser.buildEffectiveModel();
+        } catch (YangParserException e) {
+            throw new IllegalStateException("Failed to assemble SchemaContext", e);
+        }
+    }
+
+    @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Wrong inferent on listFiles")
+    private static Collection<File> getYangFiles(final String resourcePath) {
+        final URI directoryPath;
+        try {
+            directoryPath = YangParserTestUtils.class.getResource(resourcePath).toURI();
+        } catch (URISyntaxException e) {
+            throw new IllegalArgumentException("Failed to open resource directory " + resourcePath, e);
+        }
+        return Arrays.asList(new File(directoryPath).listFiles(YANG_FILE_FILTER));
+    }
+}
diff --git a/yang/yang-xpath-api/pom.xml b/yang/yang-xpath-api/pom.xml
new file mode 100644 (file)
index 0000000..58bcb7b
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2018 Pantheon Technologies, s.r.o. 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-xpath-api</artifactId>
+    <version>2.0.3-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>YANG XPath ${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.xpath.api</Automatic-Module-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/AbstractYangXPathMathSupport.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/AbstractYangXPathMathSupport.java
new file mode 100644 (file)
index 0000000..f0e756e
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+
+/**
+ * Type-safe shim to ensure concrete {@link YangXPathMathSupport} implementations get handed properly-typed
+ * YangNumberExprs.
+ *
+ * @param <N> Type of YangNumberExpr
+ * @author Robert Varga
+ */
+@Beta
+public abstract class AbstractYangXPathMathSupport<N extends YangNumberExpr> implements YangXPathMathSupport {
+    private static final long serialVersionUID = 1L;
+
+    private final Class<N> numberClass;
+
+    protected AbstractYangXPathMathSupport(final Class<N> numberClass) {
+        this.numberClass = requireNonNull(numberClass);
+    }
+
+    @Override
+    public final N negateNumber(final YangNumberExpr number) {
+        checkArgument(numberClass.isInstance(requireNonNull(number)), "Expected %s have %s", numberClass, number);
+        return doNegateNumber(numberClass.cast(number));
+    }
+
+    @Override
+    public final Optional<YangExpr> tryEvaluate(final YangBinaryOperator operator, final YangNumberExpr left,
+            final YangNumberExpr right) {
+        if (!numberClass.isInstance(left) || !numberClass.isInstance(right)) {
+            requireNonNull(operator);
+            requireNonNull(left);
+            requireNonNull(right);
+            return Optional.empty();
+        }
+
+        return Optional.of(doEvaluate(requireNonNull(operator), numberClass.cast(left), numberClass.cast(right)));
+    }
+
+    /**
+     * Create a {@link YangNumberExpr} representing the negated value of a number.
+     *
+     * @param number input number
+     * @return negated number expression
+     */
+    protected abstract N doNegateNumber(N number);
+
+    /**
+     * Evaluate an  operator and its left- and right-handside.
+     *
+     * @param operator Operator to apply
+     * @param left Left hand-side
+     * @param right Right hand-side
+     * @return Evaluation result
+     */
+    protected abstract YangExpr doEvaluate(YangBinaryOperator operator, N left, N right);
+
+    protected abstract Object readResolve();
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalNumberExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalNumberExpr.java
new file mode 100644 (file)
index 0000000..cc54a44
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import java.math.BigDecimal;
+import org.eclipse.jdt.annotation.Nullable;
+
+final class BigDecimalNumberExpr extends YangNumberExpr {
+    private static final long serialVersionUID = 1L;
+
+    private final BigDecimal number;
+
+    private BigDecimalNumberExpr(final BigDecimal number) {
+        this.number = requireNonNull(number);
+    }
+
+    static BigDecimalNumberExpr of(final BigDecimal number) {
+        return new BigDecimalNumberExpr(number);
+    }
+
+    @Override
+    public BigDecimal getNumber() {
+        return number;
+    }
+
+    @Override
+    public BigDecimalXPathMathSupport getSupport() {
+        return BigDecimalXPathMathSupport.INSTANCE;
+    }
+
+    @Override
+    public int hashCode() {
+        return number.hashCode();
+    }
+
+    @Override
+    public boolean equals(final @Nullable Object obj) {
+        return this == obj || obj instanceof BigDecimalNumberExpr
+                && number.equals(((BigDecimalNumberExpr) obj).number);
+    }
+
+    @Override
+    public String toString() {
+        return number.toString();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalXPathMathSupport.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalXPathMathSupport.java
new file mode 100644 (file)
index 0000000..437ff0d
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import java.math.BigDecimal;
+
+final class BigDecimalXPathMathSupport extends AbstractYangXPathMathSupport<BigDecimalNumberExpr> {
+    static final BigDecimalXPathMathSupport INSTANCE = new BigDecimalXPathMathSupport();
+
+    private static final long serialVersionUID = 1L;
+    private static final BigDecimalNumberExpr ZERO = BigDecimalNumberExpr.of(BigDecimal.ZERO);
+    private static final BigDecimalNumberExpr ONE = BigDecimalNumberExpr.of(BigDecimal.ONE);
+    private static final BigDecimalNumberExpr TEN = BigDecimalNumberExpr.of(BigDecimal.TEN);
+
+    private BigDecimalXPathMathSupport() {
+        super(BigDecimalNumberExpr.class);
+    }
+
+    @Override
+    public BigDecimalNumberExpr createNumber(final String str) {
+        switch (str) {
+            case "0":
+                return ZERO;
+            case "1":
+                return ONE;
+            case "10":
+                return TEN;
+            default:
+                return BigDecimalNumberExpr.of(new BigDecimal(str));
+        }
+    }
+
+    @Override
+    public BigDecimalNumberExpr createNumber(final int value) {
+        switch (value) {
+            case 0:
+                return ZERO;
+            case 1:
+                return ONE;
+            case 10:
+                return TEN;
+            default:
+                return BigDecimalNumberExpr.of(BigDecimal.valueOf(value));
+        }
+    }
+
+    @Override
+    protected BigDecimalNumberExpr doNegateNumber(final BigDecimalNumberExpr number) {
+        return BigDecimalNumberExpr.of(number.getNumber().negate());
+    }
+
+    @Override
+    protected YangExpr doEvaluate(final YangBinaryOperator operator, final BigDecimalNumberExpr left,
+            final BigDecimalNumberExpr right) {
+        final BigDecimal l = left.getNumber();
+        final BigDecimal r = right.getNumber();
+
+        final BigDecimal result;
+        switch (operator) {
+            case DIV:
+                result = l.divide(r);
+                break;
+            case EQUALS:
+                return YangBooleanConstantExpr.of(l.equals(r));
+            case GT:
+                return YangBooleanConstantExpr.of(l.compareTo(r) > 0);
+            case GTE:
+                return YangBooleanConstantExpr.of(l.compareTo(r) >= 0);
+            case LT:
+                return YangBooleanConstantExpr.of(l.compareTo(r) < 0);
+            case LTE:
+                return YangBooleanConstantExpr.of(l.compareTo(r) <= 0);
+            case MINUS:
+                result = l.subtract(r);
+                break;
+            case MOD:
+                result = l.remainder(r);
+                break;
+            case MUL:
+                result = l.multiply(r);
+                break;
+            case NOT_EQUALS:
+                return YangBooleanConstantExpr.of(!l.equals(r));
+            case PLUS:
+                result = l.add(r);
+                break;
+            default:
+                throw new IllegalStateException("Unhandled operator " + operator);
+        }
+
+        return BigDecimalNumberExpr.of(result);
+    }
+
+    @Override
+    protected Object readResolve() {
+        return INSTANCE;
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleNumberExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleNumberExpr.java
new file mode 100644 (file)
index 0000000..56ce14e
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+final class DoubleNumberExpr extends YangNumberExpr {
+    private static final long serialVersionUID = 1L;
+
+    private final double value;
+
+    private DoubleNumberExpr(final double value) {
+        this.value = value;
+    }
+
+    static DoubleNumberExpr of(final double value) {
+        return new DoubleNumberExpr(value);
+    }
+
+    double getValue() {
+        return value;
+    }
+
+    @Override
+    public Double getNumber() {
+        return value;
+    }
+
+    @Override
+    public DoubleXPathMathSupport getSupport() {
+        return DoubleXPathMathSupport.INSTANCE;
+    }
+
+    @Override
+    public int hashCode() {
+        return Double.hashCode(value);
+    }
+
+    @Override
+    public boolean equals(final @Nullable Object obj) {
+        return this == obj || obj instanceof DoubleNumberExpr && bitEqual(((DoubleNumberExpr) obj).value);
+    }
+
+    private boolean bitEqual(final double other) {
+        return Double.doubleToLongBits(value) == Double.doubleToLongBits(other);
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(value);
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleXPathMathSupport.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleXPathMathSupport.java
new file mode 100644 (file)
index 0000000..4662b72
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+final class DoubleXPathMathSupport extends AbstractYangXPathMathSupport<DoubleNumberExpr> {
+    static final DoubleXPathMathSupport INSTANCE = new DoubleXPathMathSupport();
+
+    private static final long serialVersionUID = 1L;
+
+    private DoubleXPathMathSupport() {
+        super(DoubleNumberExpr.class);
+    }
+
+    @Override
+    public DoubleNumberExpr createNumber(final String str) {
+        return DoubleNumberExpr.of(Double.parseDouble(str));
+    }
+
+    @Override
+    public DoubleNumberExpr createNumber(final int value) {
+        return DoubleNumberExpr.of(value);
+    }
+
+    @Override
+    protected DoubleNumberExpr doNegateNumber(final DoubleNumberExpr number) {
+        return DoubleNumberExpr.of(-number.getValue());
+    }
+
+    @Override
+    protected YangExpr doEvaluate(final YangBinaryOperator operator, final DoubleNumberExpr left,
+            final DoubleNumberExpr right) {
+        final double l = left.getValue();
+        final double r = right.getValue();
+
+        final double result;
+        switch (operator) {
+            case DIV:
+                result = l / r;
+                break;
+            case EQUALS:
+                return YangBooleanConstantExpr.of(left.equals(right));
+            case GT:
+                return YangBooleanConstantExpr.of(l > r);
+            case GTE:
+                return YangBooleanConstantExpr.of(l >= r);
+            case LT:
+                return YangBooleanConstantExpr.of(l < r);
+            case LTE:
+                return YangBooleanConstantExpr.of(l <= r);
+            case MINUS:
+                result = l - r;
+                break;
+            case MOD:
+                result = l % r;
+                break;
+            case MUL:
+                result = l * r;
+                break;
+            case NOT_EQUALS:
+                return YangBooleanConstantExpr.of(!left.equals(right));
+            case PLUS:
+                result = l + r;
+                break;
+            default:
+                throw new IllegalStateException("Unhandled operator " + operator);
+        }
+
+        return DoubleNumberExpr.of(result);
+    }
+
+    @Override
+    protected Object readResolve() {
+        return INSTANCE;
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/QNameReferent.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/QNameReferent.java
new file mode 100644 (file)
index 0000000..d21504f
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.xpath.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * An object referencing a QName, either resolved or unresolved.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface QNameReferent extends Immutable {
+    /**
+     * Return local name part of the referenced QName.
+     *
+     * @return Local name string.
+     */
+    String getLocalName();
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/QNameReferentBehavior.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/QNameReferentBehavior.java
new file mode 100644 (file)
index 0000000..94fed8f
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.xpath.api;
+
+/**
+ * Common interface for {@link ResolvedQNameReferent} and {@link UnresolvedQNameReferent}, ensuring that only one of
+ * them is implemented by any class.
+ *
+ * @param <T> {@link QNameReferent} behavior subclass
+ */
+interface QNameReferentBehavior<T extends QNameReferentBehavior<T>> extends QNameReferent {
+
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/ResolvedQNameReferent.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/ResolvedQNameReferent.java
new file mode 100644 (file)
index 0000000..bb98744
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.xpath.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * A {@link QNameReferent} referencing a resolved QName.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface ResolvedQNameReferent extends QNameReferentBehavior<ResolvedQNameReferent> {
+    /**
+     * Return the referenced QName.
+     *
+     * @return A QName
+     */
+    QName getQName();
+
+    @Override
+    default String getLocalName() {
+        return getQName().getLocalName();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/UnresolvedQNameReferent.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/UnresolvedQNameReferent.java
new file mode 100644 (file)
index 0000000..9566733
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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.xpath.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
+
+/**
+ * A {@link QNameReferent} referencing an unresolved QName.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface UnresolvedQNameReferent<T extends ResolvedQNameReferent> extends
+        QNameReferentBehavior<UnresolvedQNameReferent<T>> {
+    /**
+     * Return the referenced {@link AbstractQName}.
+     *
+     * @return An AbstractQName
+     */
+    AbstractQName getQName();
+
+    @Override
+    default String getLocalName() {
+        return getQName().getLocalName();
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangBinaryExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangBinaryExpr.java
new file mode 100644 (file)
index 0000000..373b106
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * A binary expression formed of a {@link #getLeftExpr()}, an {@link #getOperator()} and a {@link #getRightExpr()}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class YangBinaryExpr implements YangExpr {
+    private static final long serialVersionUID = 1L;
+
+    private final YangExpr leftExpr;
+    private final YangExpr rightExpr;
+
+    YangBinaryExpr(final YangExpr leftExpr, final YangExpr rightExpr) {
+        this.leftExpr = requireNonNull(leftExpr);
+        this.rightExpr = requireNonNull(rightExpr);
+    }
+
+    public final YangExpr getLeftExpr() {
+        return leftExpr;
+    }
+
+    public final YangExpr getRightExpr() {
+        return rightExpr;
+    }
+
+    public abstract YangBinaryOperator getOperator();
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(leftExpr, rightExpr, getOperator());
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof YangBinaryExpr)) {
+            return false;
+        }
+        final YangBinaryExpr other = (YangBinaryExpr) obj;
+        return getOperator().equals(other.getOperator()) && leftExpr.equals(other.leftExpr)
+                && rightExpr.equals(other.rightExpr);
+    }
+
+    @Override
+    public final String toString() {
+        return leftExpr.toString() + " " + getOperator() + " " + rightExpr;
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangBinaryOperator.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangBinaryOperator.java
new file mode 100644 (file)
index 0000000..598f5b0
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * YANG XPath binary operator.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public enum YangBinaryOperator {
+    /**
+     * Operands are equal.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-EqualityExpr">EqualityExpr</a>
+     */
+    EQUALS("="),
+    /**
+     * Operands do not equal.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-EqualityExpr">EqualityExpr</a>
+     */
+    NOT_EQUALS("!="),
+
+    /**
+     * Left-hand operand is greater than right-hand operand.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-RelationalExpr">RelationalExpr</a>
+     */
+    GT(">"),
+    /**
+     * Left-hand operand is greater than or equal to right-hand operand.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-RelationalExpr">RelationalExpr</a>
+     */
+    GTE(">="),
+    /**
+     * Left-hand operand is less than right-hand operand.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-RelationalExpr">RelationalExpr</a>
+     */
+    LT("<"),
+    /**
+      * Left-hand operand is less than or equal to right-hand operand.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-RelationalExpr">RelationalExpr</a>
+     */
+    LTE("<="),
+
+    /**
+     * Arithmetic addition.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-AdditiveExpr">AdditiveExpr</a>
+     */
+    PLUS("+"),
+    /**
+     * Arithmetic subtraction.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-AdditiveExpr">AdditiveExpr</a>
+     */
+    MINUS("-"),
+
+    /**
+     * Arithmetic multiplication.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-MultiplicativeExpr">MultiplicativeExpr</a>
+     */
+    MUL("*"),
+    /**
+     * Arithmetic division.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-MultiplicativeExpr">MultiplicativeExpr</a>
+     */
+    DIV("div"),
+    /**
+     * Arithmetic modulus after truncating division.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-MultiplicativeExpr">MultiplicativeExpr</a>
+     */
+    MOD("mod");
+
+    @SuppressFBWarnings(value = "SE_INNER_CLASS", justification = "Outer class is a retained enumeration")
+    private final class Expr extends YangBinaryExpr {
+        private static final long serialVersionUID = 1L;
+
+        Expr(final YangExpr leftExpr, final YangExpr rightExpr) {
+            super(leftExpr, rightExpr);
+        }
+
+        @Override
+        public YangBinaryOperator getOperator() {
+            return YangBinaryOperator.this;
+        }
+    }
+
+    private final String str;
+
+    YangBinaryOperator(final String str) {
+        this.str = requireNonNull(str);
+    }
+
+    @Override
+    public String toString() {
+        return str;
+    }
+
+    public YangBinaryExpr exprWith(final YangExpr leftExpr, final YangExpr rightExpr) {
+        return new Expr(leftExpr, rightExpr);
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangBooleanConstantExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangBooleanConstantExpr.java
new file mode 100644 (file)
index 0000000..85c4327
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Expressions which evaluate to a logical {@code true} or {@code false}. These expressions are equivalent to the result
+ * returned by {@code true()} and {@code false()} functions defined in XPath 1.0.
+ *
+ * <p>
+ * They also map these functions' names to the constant pool under their {@link YangFunctionCallExpr#getName()}
+ * identity. All users should use these constants in favor of their equivalent function calls.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public enum YangBooleanConstantExpr implements YangConstantExpr<Boolean> {
+    /**
+     * A constant {@code false} expression.
+     */
+    FALSE(Boolean.FALSE, YangFunction.FALSE, "false"),
+    /**
+     * A constant {@code true} expression.
+     */
+    TRUE(Boolean.TRUE, YangFunction.TRUE, "true");
+
+    private final YangFunctionCallExpr function;
+    private final YangLiteralExpr literal;
+    private final Boolean value;
+
+    @SuppressWarnings("null")
+    YangBooleanConstantExpr(final @Nullable Boolean value, final YangFunction function, final String literal) {
+        this.value = requireNonNull(value);
+        this.function = YangFunctionCallExpr.of(function.getIdentifier());
+        this.literal = YangLiteralExpr.of(literal);
+    }
+
+    @Override
+    public QName getIdentifier() {
+        return function.getName();
+    }
+
+    @Override
+    public Boolean getValue() {
+        return value;
+    }
+
+    /**
+     * Convert this constant into the equivalent function. This function is provided for bridging purposes only.
+     *
+     * @return Equivalent function invocation.
+     */
+    public YangFunctionCallExpr asFunction() {
+        return function;
+    }
+
+    /**
+     * Convert this constant into a string literal, i.e. the result of calling {@code string(boolean)} function on this
+     * constant.
+     *
+     * @return Literal expression.
+     */
+    public YangLiteralExpr asStringLiteral() {
+        return literal;
+    }
+
+    public static YangBooleanConstantExpr of(final boolean bool) {
+        return bool ? TRUE : FALSE;
+    }
+
+    public static Optional<YangFunctionCallExpr> forFunctionName(final String functionName) {
+        switch (functionName) {
+            case "false":
+                return Optional.of(FALSE.function);
+            case "true":
+                return Optional.of(TRUE.function);
+            default:
+                return Optional.empty();
+        }
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangConstantExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangConstantExpr.java
new file mode 100644 (file)
index 0000000..26336f7
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Common interface for all YANG XPath constant expressions. Each constant has a unique {@link QName}, which acts as its
+ * globally-unique identifier.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface YangConstantExpr<T> extends YangExpr, Identifiable<QName> {
+    /**
+     * Return this constant's value.
+     *
+     * @return this constant's value.
+     */
+    // FIXME: this seems dicey w.r.t. empty nodeset constant.
+    T getValue();
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangExpr.java
new file mode 100644 (file)
index 0000000..6ef826c
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import com.google.common.annotations.Beta;
+import java.io.Serializable;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Common base interface for all YANG Expression components.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface YangExpr extends Immutable, Serializable {
+
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFilterExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFilterExpr.java
new file mode 100644 (file)
index 0000000..a0a8ce3
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+import org.eclipse.jdt.annotation.Nullable;
+
+@Beta
+public class YangFilterExpr implements YangExpr, YangPredicateAware {
+    private static final class WithPredicates extends YangFilterExpr {
+        private static final long serialVersionUID = 1L;
+
+        private final ImmutableSet<YangExpr> predicates;
+
+        WithPredicates(final YangExpr expr, final ImmutableSet<YangExpr> predicates) {
+            super(expr);
+            this.predicates = requireNonNull(predicates);
+        }
+
+        @Override
+        public Set<YangExpr> getPredicates() {
+            return predicates;
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+
+    private final YangExpr expr;
+
+    private YangFilterExpr(final YangExpr expr) {
+        this.expr = requireNonNull(expr);
+    }
+
+    public static YangFilterExpr of(final YangExpr expr) {
+        return new YangFilterExpr(expr);
+    }
+
+    public static YangFilterExpr of(final YangExpr expr, final YangExpr... predicates) {
+        return of(expr, Arrays.asList(predicates));
+    }
+
+    public static YangFilterExpr of(final YangExpr expr, final Collection<YangExpr> predicates) {
+        return predicates.isEmpty() ? of(expr) : new WithPredicates(expr, ImmutableSet.copyOf(predicates));
+    }
+
+    public final YangExpr getExpr() {
+        return expr;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(expr, getPredicates());
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof YangFilterExpr)) {
+            return false;
+        }
+        final YangFilterExpr other = (YangFilterExpr) obj;
+        return expr.equals(((YangFilterExpr) obj).expr) && getPredicates().equals(other.getPredicates());
+    }
+
+    @Override
+    public final String toString() {
+        // FIXME: this is not right
+        return "-(" + expr + ")";
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFunction.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFunction.java
new file mode 100644 (file)
index 0000000..4e82369
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+
+/**
+ * Functions known to a YANG XPath.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public enum YangFunction implements Identifiable<QName> {
+    // XPath 1.0 functions
+    BOOLEAN("boolean"),
+    CEILING("ceiling"),
+    CONCAT("concat"),
+    CONTAINS("contains"),
+    COUNT("count"),
+    FALSE("false"),
+    FLOOR("floor"),
+    ID("id"),
+    LANG("lang"),
+    LAST("last"),
+    LOCAL_NAME("local-name"),
+    NAME("name"),
+    NUMBER("number"),
+    NAMESPACE_URI("namespace-uri"),
+    NORMALIZE_SPACE("normalize-space"),
+    NOT("not"),
+    POSITION("position"),
+    ROUND("round"),
+    STARTS_WITH("starts-with"),
+    STRING("string"),
+    STRING_LENGTH("string-length"),
+    SUM("sum"),
+    SUBSTRING("substring"),
+    SUBSTRING_AFTER("substring-after"),
+    SUBSTRING_BEFORE("substring-before"),
+    TRANSLATE("translate"),
+    TRUE("true"),
+
+    // RFC6020 functions
+    CURRENT("current"),
+
+    // RFC7950 functions
+    BIT_IS_SET("bit-is-set", YangVersion.VERSION_1_1),
+    DEREF("deref", YangVersion.VERSION_1_1),
+    DERIVED_FROM("derived-from", YangVersion.VERSION_1_1),
+    DERIVED_FROM_OR_SELF("derived-from-or-self", YangVersion.VERSION_1_1),
+    ENUM_VALUE("enum-value", YangVersion.VERSION_1_1),
+    RE_MATCH("re-match", YangVersion.VERSION_1_1);
+
+    private final QName identifier;
+    private final YangVersion yangVersion;
+
+    YangFunction(final String localName, final YangVersion yangVersion) {
+        identifier = QName.create(YangConstants.RFC6020_YIN_MODULE, localName).intern();
+        this.yangVersion = requireNonNull(yangVersion);
+    }
+
+    YangFunction(final String localName) {
+        this(localName, YangVersion.VERSION_1);
+    }
+
+    @Override
+    public QName getIdentifier() {
+        return identifier;
+    }
+
+    /**
+     * Return the minimum YANG version where this function is supported.
+     *
+     * @return First YANG version where this function appeared.
+     */
+    public YangVersion getYangVersion() {
+        return yangVersion;
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFunctionCallExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFunctionCallExpr.java
new file mode 100644 (file)
index 0000000..df27ca2
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+
+/**
+ * Function call invocation. Function names without a prefix are mapped into {@link YangConstants#RFC6020_YIN_MODULE},
+ * as they are required to be mapped into YANG as per RFC7950 definitions.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public class YangFunctionCallExpr implements YangExpr {
+    private static final class WithArgs extends YangFunctionCallExpr {
+        private static final long serialVersionUID = 1L;
+
+        private final ImmutableList<YangExpr> arguments;
+
+        WithArgs(final QName name, final List<YangExpr> arguments) {
+            super(name);
+            this.arguments = ImmutableList.copyOf(arguments);
+        }
+
+        @Override
+        public List<YangExpr> getArguments() {
+            return arguments;
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+
+    private final QName name;
+
+    YangFunctionCallExpr(final QName name) {
+        this.name = requireNonNull(name);
+    }
+
+    public static YangFunctionCallExpr of(final QName name) {
+        return new YangFunctionCallExpr(name);
+    }
+
+    public static YangFunctionCallExpr of(final QName name, final List<YangExpr> arguments) {
+        return arguments.isEmpty() ? of(name) : new WithArgs(name, arguments);
+    }
+
+    public final QName getName() {
+        return name;
+    }
+
+    public List<YangExpr> getArguments() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(getName(), getArguments());
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof YangFunctionCallExpr)) {
+            return false;
+        }
+        final YangFunctionCallExpr other = (YangFunctionCallExpr) obj;
+        return getName().equals(other.getName()) && getArguments().equals(other.getArguments());
+    }
+
+    @Override
+    public final String toString() {
+        final StringBuilder sb = new StringBuilder().append(getName()).append('(');
+        final Iterator<YangExpr> it = getArguments().iterator();
+        if (it.hasNext()) {
+            sb.append(it.next());
+            while (it.hasNext()) {
+                sb.append(", ").append(it.next());
+            }
+        }
+        return sb.append(')').toString();
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangLiteralExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangLiteralExpr.java
new file mode 100644 (file)
index 0000000..efff7c6
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * An XPath literal expression.
+ *
+ * <p>
+ * Note that a literal may be required to hold a value of {@code instance-identifier} or {@code identityref} type,
+ * when the corresponding {@link YangXPathExpression} was parsed from reference String specification defined in RFC7950.
+ * When such conversion is required, it should be performed through
+ * {@link YangXPathExpression#interpretAsQName(YangLiteralExpr)} or
+ * {@link YangXPathExpression#interpretAsInstanceIdentifier(YangLiteralExpr)}.
+ *
+ * <p>
+ * A more type-safe alternative is {@link YangQNameExpr}, which should be preferred and used whenever possible.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class YangLiteralExpr implements YangExpr {
+    private static final long serialVersionUID = 1L;
+    private static final YangLiteralExpr EMPTY = new YangLiteralExpr("");
+
+    private final String literal;
+
+    protected YangLiteralExpr(final String literal) {
+        this.literal = requireNonNull(literal);
+    }
+
+    public static YangLiteralExpr empty() {
+        return EMPTY;
+    }
+
+    public static YangLiteralExpr of(final String literal) {
+        return literal.isEmpty() ? EMPTY : new YangLiteralExpr(literal);
+    }
+
+    public String getLiteral() {
+        return literal;
+    }
+
+    @Override
+    public int hashCode() {
+        return literal.hashCode();
+    }
+
+    @Override
+    public boolean equals(final @Nullable Object obj) {
+        return this == obj || obj instanceof YangLiteralExpr && literal.equals(((YangLiteralExpr) obj).literal);
+    }
+
+    @Override
+    public String toString() {
+        return literal;
+    }
+
+    protected Object readResolve() {
+        return literal.isEmpty() ? EMPTY : this;
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangLocationPath.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangLocationPath.java
new file mode 100644 (file)
index 0000000..72806f5
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+@Beta
+public abstract class YangLocationPath implements YangExpr {
+    public abstract static class Step implements Serializable, YangPredicateAware {
+        private static final long serialVersionUID = 1L;
+
+        private final YangXPathAxis axis;
+
+        Step(final YangXPathAxis axis) {
+            this.axis = requireNonNull(axis);
+        }
+
+        public final YangXPathAxis getAxis() {
+            return axis;
+        }
+
+        @Override
+        public abstract int hashCode();
+
+        @Override
+        public abstract boolean equals(@Nullable Object obj);
+
+        @Override
+        public final String toString() {
+            return addToStringAttributes(MoreObjects.toStringHelper(Step.class)).toString();
+        }
+
+        protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            helper.add("axis", axis);
+            final Set<YangExpr> predicates = getPredicates();
+            if (!predicates.isEmpty()) {
+                helper.add("predicates", predicates);
+            }
+            return helper;
+        }
+    }
+
+    public static class AxisStep extends Step {
+        private static final long serialVersionUID = 1L;
+
+        AxisStep(final YangXPathAxis axis) {
+            super(axis);
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(getAxis(), getPredicates());
+        }
+
+        @Override
+        public final boolean equals(@Nullable final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof AxisStep)) {
+                return false;
+            }
+            final AxisStep other = (AxisStep) obj;
+            return getAxis().equals(other.getAxis()) && getPredicates().equals(other.getPredicates());
+        }
+
+        @SuppressFBWarnings(value = "SE_PRIVATE_READ_RESOLVE_NOT_INHERITED",
+                justification = "We have only one subclass, and that does not want to inherit this")
+        private Object readResolve() {
+            return getAxis().asStep();
+        }
+    }
+
+    static final class AxisStepWithPredicates extends AxisStep {
+        private static final long serialVersionUID = 1L;
+
+        private final ImmutableSet<YangExpr> predicates;
+
+        AxisStepWithPredicates(final YangXPathAxis axis, final ImmutableSet<YangExpr> predicates) {
+            super(axis);
+            this.predicates = requireNonNull(predicates);
+        }
+
+        @Override
+        public ImmutableSet<YangExpr> getPredicates() {
+            return predicates;
+        }
+    }
+
+    // match a particular namespace
+    public static class NamespaceStep extends Step {
+        private static final long serialVersionUID = 1L;
+
+        private final QNameModule namespace;
+
+        NamespaceStep(final YangXPathAxis axis, final QNameModule namespace) {
+            super(axis);
+            this.namespace = requireNonNull(namespace);
+        }
+
+        public final QNameModule getNamespace() {
+            return namespace;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(getAxis(), namespace, getPredicates());
+        }
+
+        @Override
+        public final boolean equals(@Nullable final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof NamespaceStep)) {
+                return false;
+            }
+            final NamespaceStep other = (NamespaceStep) obj;
+            return getAxis().equals(other.getAxis()) && namespace.equals(other.namespace)
+                    && getPredicates().equals(other.getPredicates());
+        }
+
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            return super.addToStringAttributes(helper).add("namespace", namespace);
+        }
+    }
+
+    /**
+     * A step along an axis. This may be either a {@link ResolvedQNameStep} or a {@link UnresolvedQNameStep}.
+     *
+     * @author Robert Varga
+     */
+    public abstract static class QNameStep extends Step implements QNameReferent {
+        private static final long serialVersionUID = 1L;
+
+        QNameStep(final YangXPathAxis axis) {
+            super(axis);
+        }
+    }
+
+    public static class ResolvedQNameStep extends QNameStep implements ResolvedQNameReferent {
+        private static final long serialVersionUID = 1L;
+
+        private final QName qname;
+
+        ResolvedQNameStep(final YangXPathAxis axis, final QName qname) {
+            super(axis);
+            this.qname = requireNonNull(qname);
+        }
+
+        static ResolvedQNameStep of(final YangXPathAxis axis, final QName qname,
+                final Collection<YangExpr> predicates) {
+            return predicates.isEmpty() ? new ResolvedQNameStep(axis, qname)
+                    : new ResolvedQNameStepWithPredicates(axis, qname, ImmutableSet.copyOf(predicates));
+        }
+
+        @Override
+        public final QName getQName() {
+            return qname;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(getAxis(), qname, getPredicates());
+        }
+
+        @Override
+        public final boolean equals(@Nullable final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof ResolvedQNameStep)) {
+                return false;
+            }
+            final ResolvedQNameStep other = (ResolvedQNameStep) obj;
+            return getAxis().equals(other.getAxis()) && qname.equals(other.qname)
+                    && getPredicates().equals(other.getPredicates());
+        }
+
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            return super.addToStringAttributes(helper).add("qname", qname);
+        }
+    }
+
+    private static final class ResolvedQNameStepWithPredicates extends ResolvedQNameStep {
+        private static final long serialVersionUID = 1L;
+
+        private final ImmutableSet<YangExpr> predicates;
+
+        ResolvedQNameStepWithPredicates(final YangXPathAxis axis, final QName qname,
+                final ImmutableSet<YangExpr> predicates) {
+            super(axis, qname);
+            this.predicates = requireNonNull(predicates);
+        }
+
+        @Override
+        public ImmutableSet<YangExpr> getPredicates() {
+            return predicates;
+        }
+    }
+
+    // FIXME: 5.0.0: integrate this into QName step once QName is a subclass AbstractQName
+    public static class UnresolvedQNameStep extends QNameStep implements UnresolvedQNameReferent<ResolvedQNameStep> {
+        private static final long serialVersionUID = 1L;
+
+        private final AbstractQName qname;
+
+        UnresolvedQNameStep(final YangXPathAxis axis, final AbstractQName qname) {
+            super(axis);
+            this.qname = requireNonNull(qname);
+        }
+
+        static UnresolvedQNameStep of(final YangXPathAxis axis, final AbstractQName qname,
+                final Collection<YangExpr> predicates) {
+            return predicates.isEmpty() ? new UnresolvedQNameStep(axis, qname)
+                    : new UnresolvedQNameStepWithPredicates(axis, qname, ImmutableSet.copyOf(predicates));
+        }
+
+        @Override
+        public final AbstractQName getQName() {
+            return qname;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(getAxis(), qname, getPredicates());
+        }
+
+        @Override
+        public final boolean equals(final @Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof UnresolvedQNameStep)) {
+                return false;
+            }
+            final UnresolvedQNameStep other = (UnresolvedQNameStep) obj;
+            return getAxis().equals(other.getAxis()) && qname.equals(other.qname)
+                    && getPredicates().equals(other.getPredicates());
+        }
+    }
+
+    private static final class UnresolvedQNameStepWithPredicates extends UnresolvedQNameStep {
+        private static final long serialVersionUID = 1L;
+
+        private final ImmutableSet<YangExpr> predicates;
+
+        UnresolvedQNameStepWithPredicates(final YangXPathAxis axis, final AbstractQName qname,
+                final ImmutableSet<YangExpr> predicates) {
+            super(axis, qname);
+            this.predicates = requireNonNull(predicates);
+        }
+
+        @Override
+        public ImmutableSet<YangExpr> getPredicates() {
+            return predicates;
+        }
+    }
+
+    public static class NodeTypeStep extends Step {
+        private static final long serialVersionUID = 1L;
+
+        private final YangXPathNodeType nodeType;
+
+        NodeTypeStep(final YangXPathAxis axis, final YangXPathNodeType nodeType) {
+            super(axis);
+            this.nodeType = requireNonNull(nodeType);
+        }
+
+        public final YangXPathNodeType getNodeType() {
+            return nodeType;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(getAxis(), nodeType, getPredicates());
+        }
+
+        @Override
+        public boolean equals(@Nullable final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null || !getClass().equals(obj.getClass())) {
+                return false;
+            }
+            final NodeTypeStep other = (NodeTypeStep) obj;
+            return nodeType.equals(other.nodeType) && getAxis().equals(other.getAxis())
+                    && getPredicates().equals(other.getPredicates());
+        }
+
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            return super.addToStringAttributes(helper).add("nodeType", nodeType);
+        }
+    }
+
+    @SuppressFBWarnings(value = "EQ_DOESNT_OVERRIDE_EQUALS",
+            justification = "https://github.com/spotbugs/spotbugs/issues/511")
+    static final class NodeTypeStepWithPredicates extends NodeTypeStep {
+        private static final long serialVersionUID = 1L;
+
+        private final ImmutableSet<YangExpr> predicates;
+
+        NodeTypeStepWithPredicates(final YangXPathAxis axis, final YangXPathNodeType type,
+                final ImmutableSet<YangExpr> predicates) {
+            super(axis, type);
+            this.predicates = requireNonNull(predicates);
+        }
+
+        @Override
+        public ImmutableSet<YangExpr> getPredicates() {
+            return predicates;
+        }
+    }
+
+    public static class ProcessingInstructionStep extends NodeTypeStep {
+        private static final long serialVersionUID = 1L;
+
+        private final String name;
+
+        ProcessingInstructionStep(final YangXPathAxis axis, final String name) {
+            super(axis, YangXPathNodeType.PROCESSING_INSTRUCTION);
+            this.name = requireNonNull(name);
+        }
+
+        public final String getName() {
+            return name;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(getAxis(), getNodeType(), name, getPredicates());
+        }
+
+        @Override
+        public final boolean equals(final @Nullable Object obj) {
+            return obj == this || super.equals(obj) && name.equals(((ProcessingInstructionStep) obj).name);
+        }
+
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            return super.addToStringAttributes(helper).add("name", name);
+        }
+    }
+
+    static final class ProcessingInstructionStepWithPredicates extends ProcessingInstructionStep {
+        private static final long serialVersionUID = 1L;
+
+        private final ImmutableSet<YangExpr> predicates;
+
+        ProcessingInstructionStepWithPredicates(final YangXPathAxis axis, final String name,
+                final ImmutableSet<YangExpr> predicates) {
+            super(axis, name);
+            this.predicates = requireNonNull(predicates);
+        }
+
+        @Override
+        public ImmutableSet<YangExpr> getPredicates() {
+            return predicates;
+        }
+    }
+
+    public static final class Absolute extends YangLocationPath {
+        private static final long serialVersionUID = 1L;
+
+        Absolute(final ImmutableList<Step> steps) {
+            super(steps);
+        }
+
+        @Override
+        public boolean isAbsolute() {
+            return true;
+        }
+    }
+
+    public static final class Relative extends YangLocationPath {
+        private static final long serialVersionUID = 1L;
+
+        Relative(final ImmutableList<Step> steps) {
+            super(steps);
+        }
+
+        @Override
+        public boolean isAbsolute() {
+            return false;
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+    private static final Absolute ROOT = new Absolute(ImmutableList.of());
+    private static final Relative SELF = new Relative(ImmutableList.of());
+
+    private final ImmutableList<Step> steps;
+
+    YangLocationPath(final ImmutableList<Step> steps) {
+        this.steps = requireNonNull(steps);
+    }
+
+    public static final Absolute absolute(final Step... steps) {
+        return absolute(Arrays.asList(steps));
+    }
+
+    public static final Absolute absolute(final Collection<Step> steps) {
+        return steps.isEmpty() ? ROOT : new Absolute(ImmutableList.copyOf(steps));
+    }
+
+    public static final Relative relative(final Step... steps) {
+        return relative(Arrays.asList(steps));
+    }
+
+    public static final Relative relative(final Collection<Step> steps) {
+        return steps.isEmpty() ? SELF : new Relative(ImmutableList.copyOf(steps));
+    }
+
+    /**
+     * The conceptual {@code root} {@link YangLocationPath}. This path is an absolute path and has no steps.
+     *
+     * @return Empty absolute {@link YangLocationPath}
+     */
+    public static final Absolute root() {
+        return ROOT;
+    }
+
+    /**
+     * The conceptual {@code same} {@link YangLocationPath}. This path is a relative path and has no steps and is
+     * equivalent to a step along {@link YangXPathAxis#SELF}.
+     *
+     * @return Empty relative {@link YangLocationPath}
+     */
+    public static final Relative self() {
+        return SELF;
+    }
+
+    public final ImmutableList<Step> getSteps() {
+        return steps;
+    }
+
+    public abstract boolean isAbsolute();
+
+    @Override
+    public final int hashCode() {
+        return Boolean.hashCode(isAbsolute()) * 31 + steps.hashCode();
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof YangLocationPath)) {
+            return false;
+        }
+        final YangLocationPath other = (YangLocationPath) obj;
+        return isAbsolute() == other.isAbsolute() && steps.equals(other.steps);
+    }
+
+    @Override
+    public final String toString() {
+        final ToStringHelper helper = MoreObjects.toStringHelper(YangLocationPath.class).add("absolute", isAbsolute());
+        if (!steps.isEmpty()) {
+            helper.add("steps", steps);
+        }
+        return helper.toString();
+    }
+
+    final Object readSolve() {
+        return steps.isEmpty() ? isAbsolute() ? ROOT : SELF : this;
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryExpr.java
new file mode 100644 (file)
index 0000000..982b338
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import java.util.Objects;
+import java.util.Set;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * A {@link YangExpr} combining a {@link YangNaryOperator} with a set of expressions.
+ */
+@Beta
+public final class YangNaryExpr implements YangExpr {
+    private static final long serialVersionUID = 1L;
+
+    private final YangNaryOperator operator;
+    private final ImmutableSet<YangExpr> expressions;
+
+    YangNaryExpr(final YangNaryOperator operator, final ImmutableSet<YangExpr> expressions) {
+        this.operator = requireNonNull(operator);
+        this.expressions = requireNonNull(expressions);
+    }
+
+    public Set<YangExpr> getExpressions() {
+        return expressions;
+    }
+
+    public YangNaryOperator getOperator() {
+        return operator;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getOperator(), expressions);
+    }
+
+    @Override
+    public boolean equals(final @Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof YangNaryExpr)) {
+            return false;
+        }
+        final YangNaryExpr other = (YangNaryExpr) obj;
+        return getOperator().equals(other.getOperator()) && expressions.equals(other.expressions);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(YangNaryExpr.class)
+                .add("operator", operator)
+                .add("expressions", expressions)
+                .toString();
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryOperator.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryOperator.java
new file mode 100644 (file)
index 0000000..a4fc405
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+
+/**
+ * YANG XPath binary operator.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public enum YangNaryOperator {
+    /**
+     * Logical 'and' operator on operands.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-AndExpr">AndExpr</a>
+     */
+    AND("and"),
+    /**
+     * Logical 'or' operator on operands.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-OrExpr">OrExpr</a>
+     */
+    OR("or"),
+    /**
+     * Set union operator on operands.
+     *
+     * @see <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-UnionExpr">UnionExpr</a>
+     */
+    UNION("|");
+
+    private final String str;
+
+    YangNaryOperator(final String str) {
+        this.str = requireNonNull(str);
+    }
+
+    @Override
+    public String toString() {
+        return str;
+    }
+
+    public YangExpr exprWith(final Collection<YangExpr> exprs) {
+        final ImmutableSet<YangExpr> set = ImmutableSet.copyOf(exprs);
+        return set.size() == 1 ? set.iterator().next() : new YangNaryExpr(this, set);
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNegateExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNegateExpr.java
new file mode 100644 (file)
index 0000000..41fd77f
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.Nullable;
+
+@Beta
+public final class YangNegateExpr implements YangExpr {
+    private static final long serialVersionUID = 1L;
+
+    private final YangExpr subExpr;
+
+    private YangNegateExpr(final YangExpr subExpr) {
+        this.subExpr = requireNonNull(subExpr);
+    }
+
+    public static YangNegateExpr of(final YangExpr subExpr) {
+        return new YangNegateExpr(subExpr);
+    }
+
+    public YangExpr getSubExpr() {
+        return subExpr;
+    }
+
+    @Override
+    public int hashCode() {
+        return subExpr.hashCode();
+    }
+
+    @Override
+    public boolean equals(final @Nullable Object obj) {
+        return this == obj || obj instanceof YangNegateExpr && subExpr.equals(((YangNegateExpr) obj).subExpr);
+    }
+
+    @Override
+    public String toString() {
+        return "-(" + subExpr + ")";
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNumberExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNumberExpr.java
new file mode 100644 (file)
index 0000000..cf355bd
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * A number-bearing expression.
+ */
+@Beta
+public abstract class YangNumberExpr implements YangExpr {
+    private static final long serialVersionUID = 1L;
+
+    YangNumberExpr() {
+        // Hidden to prevent external subclassing
+    }
+
+    public abstract Number getNumber();
+
+    public abstract YangXPathMathSupport getSupport();
+
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals(@Nullable Object obj);
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangPathExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangPathExpr.java
new file mode 100644 (file)
index 0000000..dd7886e
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Relative;
+
+@Beta
+public class YangPathExpr implements YangExpr {
+    private static final class WithLocation extends YangPathExpr {
+        private static final long serialVersionUID = 1L;
+
+        private final Relative locationPath;
+
+        WithLocation(final YangExpr filterExpr, final Relative locationPath) {
+            super(filterExpr);
+            this.locationPath = requireNonNull(locationPath);
+        }
+
+        @Override
+        public Optional<Relative> getLocationPath() {
+            return Optional.of(locationPath);
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+
+    private final YangExpr filterExpr;
+
+    YangPathExpr(final YangExpr filterExpr) {
+        this.filterExpr = requireNonNull(filterExpr);
+    }
+
+    public static YangPathExpr of(final YangExpr filterExpr) {
+        return new YangPathExpr(filterExpr);
+    }
+
+    public static YangPathExpr of(final YangExpr expr, final Relative locationPath) {
+        return new WithLocation(expr, locationPath);
+    }
+
+    public final YangExpr getFilterExpr() {
+        return filterExpr;
+    }
+
+    public Optional<Relative> getLocationPath() {
+        return Optional.empty();
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(filterExpr, getLocationPath());
+    }
+
+    @Override
+    public final boolean equals(final @Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof YangPathExpr)) {
+            return false;
+        }
+        final YangPathExpr other = (YangPathExpr) obj;
+        return filterExpr.equals(((YangPathExpr) obj).filterExpr) && getLocationPath().equals(other.getLocationPath());
+    }
+
+    @Override
+    public final String toString() {
+        // FIXME: this is not right
+        return "-(" + filterExpr + ")";
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangPredicateAware.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangPredicateAware.java
new file mode 100644 (file)
index 0000000..fd07ec2
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
+
+/**
+ * Common interface for {@link YangFilterExpr} and {@link Step}, both of which can contain predicates. Predicates are
+ * expressed in terms of {@link YangExpr}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface YangPredicateAware {
+    default Set<YangExpr> getPredicates() {
+        return ImmutableSet.of();
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangQNameExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangQNameExpr.java
new file mode 100644 (file)
index 0000000..1c90f3c
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * An XPath QName expression. This is an exact QName, which cannot be converted to a string literal compatible with
+ * XPath string representation, because it does not define a prefix/namespace mapping. It represents a strong binding
+ * to a particular namespace at a particular revision.
+ *
+ * <p>
+ * Parsers and users of this package are encouraged to use this class in place of {@link YangLiteralExpr} where
+ * appropriate, as it retains type safety and more semantic context.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class YangQNameExpr implements YangExpr, QNameReferent {
+    public static final class Resolved extends YangQNameExpr implements ResolvedQNameReferent {
+        private static final long serialVersionUID = 1L;
+
+        private final QName qname;
+
+        Resolved(final QName qname) {
+            this.qname = requireNonNull(qname);
+        }
+
+        @Override
+        public QName getQName() {
+            return qname;
+        }
+
+        @Override
+        public int hashCode() {
+            return qname.hashCode();
+        }
+
+        @Override
+        public boolean equals(final @Nullable Object obj) {
+            return this == obj || obj instanceof Resolved && qname.equals(((Resolved) obj).qname);
+        }
+
+        @Override
+        public String toString() {
+            return qname.toString();
+        }
+    }
+
+    public static final class Unresolved extends YangQNameExpr implements UnresolvedQNameReferent<Resolved> {
+        private static final long serialVersionUID = 1L;
+
+        private final AbstractQName qname;
+
+        Unresolved(final AbstractQName qname) {
+            this.qname = requireNonNull(qname);
+        }
+
+        @Override
+        public AbstractQName getQName() {
+            return qname;
+        }
+
+        @Override
+        public int hashCode() {
+            return qname.hashCode();
+        }
+
+        @Override
+        public boolean equals(final @Nullable Object obj) {
+            return this == obj || obj instanceof Unresolved && qname.equals(((Unresolved) obj).qname);
+        }
+
+        @Override
+        public String toString() {
+            return qname.toString();
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+
+    YangQNameExpr() {
+        // Prevent instantiation
+    }
+
+    public static Unresolved of(final AbstractQName qname) {
+        return new Unresolved(qname);
+    }
+
+    public static Resolved of(final QName qname) {
+        return new Resolved(qname);
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangVariableReferenceExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangVariableReferenceExpr.java
new file mode 100644 (file)
index 0000000..ec7c112
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+@Beta
+public final class YangVariableReferenceExpr implements YangExpr {
+    private static final long serialVersionUID = 1L;
+
+    private final QName variableName;
+
+    private YangVariableReferenceExpr(final QName variableName) {
+        this.variableName = requireNonNull(variableName);
+    }
+
+    public static YangVariableReferenceExpr of(final QName variableName) {
+        return new YangVariableReferenceExpr(variableName);
+    }
+
+    public QName getVariableName() {
+        return variableName;
+    }
+
+    @Override
+    public int hashCode() {
+        return variableName.hashCode();
+    }
+
+    @Override
+    public boolean equals(final @Nullable Object obj) {
+        return this == obj || obj instanceof YangVariableReferenceExpr
+                && variableName.equals(((YangVariableReferenceExpr) obj).variableName);
+    }
+
+    @Override
+    public String toString() {
+        return "$" + variableName;
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathAxis.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathAxis.java
new file mode 100644 (file)
index 0000000..3fc7ce5
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.AxisStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.AxisStepWithPredicates;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.NodeTypeStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.NodeTypeStepWithPredicates;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.ProcessingInstructionStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.ProcessingInstructionStepWithPredicates;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.ResolvedQNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.UnresolvedQNameStep;
+
+/**
+ * XPath evaluation axis, as defined in <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#axes">XPath 1.0</a>.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public enum YangXPathAxis {
+    /**
+     * The {@code child} axis.
+     */
+    CHILD("child"),
+    /**
+     * The {@code descendant} axis.
+     */
+    DESCENDANT("descendant"),
+    /**
+     * The {@code parent} axis.
+     */
+    PARENT("parent"),
+    /**
+     * The {@code ancestor} axis.
+     */
+    ANCESTOR("ancestor"),
+    /**
+     * The {@code following-sibling} axis.
+     */
+    FOLLOWING_SIBLING("following-sibling"),
+    /**
+     * The {@code preceding-sibling} axis.
+     */
+    PRECEDING_SIBLING("preceding-sibling"),
+    /**
+     * The {@code following} axis.
+     */
+    FOLLOWING("following"),
+    /**
+     * The {@code preceding} axis.
+     */
+    PRECEDING("preceding"),
+    /**
+     * The {@code attribute} axis.
+     */
+    ATTRIBUTE("attribute"),
+    /**
+     * The {@code namespace} axis.
+     */
+    NAMESPACE("namespace"),
+    /**
+     * The {@code self} axis.
+     */
+    SELF("self"),
+    /**
+     * The {@code descendant-or-self} axis.
+     */
+    DESCENDANT_OR_SELF("descendant-or-self"),
+    /**
+     * The {@code ancestor-or-self} axis.
+     */
+    ANCESTOR_OR_SELF("ancestor-or-self");
+
+    private final AxisStep step = new AxisStep(this);
+    private final String str;
+
+    YangXPathAxis(final String str) {
+        this.str = requireNonNull(str);
+    }
+
+    /**
+     * Return the name-independent {@link AxisStep} along this axis. XPath defines following axis {@code AxisStep}s:
+     * <ul>
+     *     <li>{@link #SELF} axis this equals to the "." step</li>
+     *     <li>{@link #PARENT} axis this equals to the ".." step</li>
+     *     <li>{@link #DESCENDANT_OR_SELF} axis this equals to the "//" separator</li>
+     * </ul>
+     * other axes have these defined as a courtesy.
+     *
+     * @return Name-independent AnyNameStep.
+     */
+    public final AxisStep asStep() {
+        return step;
+    }
+
+    public final AxisStep asStep(final Collection<YangExpr> predicates) {
+        return predicates.isEmpty() ? step : new AxisStepWithPredicates(this, ImmutableSet.copyOf(predicates));
+    }
+
+    public final ResolvedQNameStep asStep(final QName qname) {
+        return new ResolvedQNameStep(this, qname);
+    }
+
+    public final ResolvedQNameStep asStep(final QName qname, final Collection<YangExpr> predicates) {
+        return ResolvedQNameStep.of(this, qname, predicates);
+    }
+
+    public final UnresolvedQNameStep asStep(final AbstractQName qname) {
+        return new UnresolvedQNameStep(this, qname);
+    }
+
+    public final UnresolvedQNameStep asStep(final AbstractQName qname, final Collection<YangExpr> predicates) {
+        return UnresolvedQNameStep.of(this, qname, predicates);
+    }
+
+    public final NodeTypeStep asStep(final YangXPathNodeType type) {
+        return new NodeTypeStep(this, type);
+    }
+
+    public final NodeTypeStep asStep(final YangXPathNodeType type, final Collection<YangExpr> predicates) {
+        return predicates.isEmpty() ? asStep(type) : new NodeTypeStepWithPredicates(this, type,
+            ImmutableSet.copyOf(predicates));
+    }
+
+    public final ProcessingInstructionStep asStep(final String name) {
+        return new ProcessingInstructionStep(this, name);
+    }
+
+    public final ProcessingInstructionStep asStep(final String name, final Collection<YangExpr> predicates) {
+        return predicates.isEmpty() ? asStep(name) : new ProcessingInstructionStepWithPredicates(this, name,
+            ImmutableSet.copyOf(predicates));
+    }
+
+    @Override
+    public String toString() {
+        return str;
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathExpression.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathExpression.java
new file mode 100644 (file)
index 0000000..771b191
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import com.google.common.annotations.Beta;
+import javax.xml.xpath.XPathExpressionException;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QualifiedQName;
+import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+
+/**
+ * An XPath expression. This interface defines a parsed representation of an XPath defined in a YANG context, as
+ * specified in <a href="https://tools.ietf.org/html/rfc7950#section-6.4">RFC7950, Section 6.4</a>.
+ *
+ * <p>
+ * The specification along with rules for {@code path} statement evaluation rules end up defining four incremental
+ * levels to which an XPath expression can be bound:
+ * <ul>
+ * <li>Unbound Expressions, which is a essentially a parse tree. No namespace binding has been performed, i.e. all
+ *     node identifiers are in {@link QualifiedQName} or {@link UnqualifiedQName} form. This level is typically not used
+ *     when dealing with YANG models directly, but can be useful for validating a String conforms to XPath syntax.
+ * </li>
+ * <li>Qualified-bound Expressions, where all {@link QualifiedQName}s are resolved and bound to {@link QName}s, but
+ *     {@link UnqualifiedQName}s are still present. This level corresponds to how far a YANG parser can interpret XPath
+ *     expressions defined in {@code typedef} statements and statements which are not fully instantiated, i.e. are
+ *     descendants of a {@code grouping} statement.
+ * </li>
+ * <li>Namespace-bound Expressions, where all node identifier references are resolved to {@link QName}s. This level
+ *     corresponds to how far a YANG parser can interpret XPath expressions at their place of instantiation, either in
+ *     the data tree or an {@code action}/@{code rpc}/{@code notification} or similar context.
+ * </li>
+ * <li>Context-bound Expressions, where the expression is bound to a {code context node}, i.e. {@code current()}
+ *     function result is know. This API does not handle this state, as it is inherently bound to a particular data
+ *     object model.
+ * </li>
+ * </ul>
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface YangXPathExpression extends Immutable {
+    /**
+     * A Qualified-bound expression. All {@link QualifiedQName}s are eliminated and replaced with {@link QName}s.
+     */
+    interface QualifiedBound extends YangXPathExpression {
+
+    }
+
+    interface UnqualifiedBound extends QualifiedBound {
+        @Override
+        YangQNameExpr.Resolved interpretAsQName(YangLiteralExpr expr) throws XPathExpressionException;
+    }
+
+    /**
+     * Return the root {@link YangExpr}.
+     *
+     * @return Root expression.
+     */
+    YangExpr getRootExpr();
+
+    /**
+     * Return the {@link YangXPathMathMode} used in this expression. All {@link YangNumberExpr} objects used by this
+     * expression are expected to be handled by this mode's {@link YangXPathMathSupport}.
+     *
+     * @return YangXPathMathMode
+     */
+    YangXPathMathMode getMathMode();
+
+    /**
+     * Return the minimum YangVersion runtime required to interpret this expression. The default implementation returns
+     * {@link YangVersion#VERSION_1_1}. Implementations are encouraged to provide a less conservative estimate.
+     *
+     * @return YangVersion runtime version compatibility level required to accurately interpret this expression.
+     */
+    // FIXME: 5.0.0: make this method non-default.
+    default YangVersion getYangVersion() {
+        return YangVersion.VERSION_1_1;
+    }
+
+    /**
+     * Attempt to interpret a {@link YangLiteralExpr} referenced by this expression as a {@link QName}. This method
+     * is required to perform late value binding of the expression when the literal needs to be interpreted as
+     * a reference to an {@code identity}.
+     *
+     * <p>
+     * The syntax of expr is required to conform to
+     * <a href="https://www.w3.org/TR/REC-xml-names/#NT-QName">XML QName format</a>, as further restricted by
+     * <a href="https://tools.ietf.org/html/rfc7950#section-9.10.3">YANG {@code identityref} Lexical Representation</a>.
+     *
+     * <p>
+     * Unfortunately we do not know when a literal will need to be interpreted in this way, as that can only be known
+     * at evaluation.
+     *
+     * @param expr Literal to be reinterpreted
+     * @return YangQNameExpr result of interpretation
+     * @throws XPathExpressionException when the literal cannot be interpreted as a QName
+     */
+    YangQNameExpr interpretAsQName(YangLiteralExpr expr) throws XPathExpressionException;
+
+    // FIXME: this really should be YangInstanceIdentifier without AugmentationIdentifier. Implementations are
+    //        strongly encouraged to validate it as such.
+    YangLocationPath interpretAsInstanceIdentifier(YangLiteralExpr expr) throws XPathExpressionException;
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathMode.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathMode.java
new file mode 100644 (file)
index 0000000..50ecc8b
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * {@link YangXPathParser} number compliance knobs. This enumeration defines what assumptions the parser can make --
+ * affecting its optimization properties around
+ * <a href="https://en.wikipedia.org/wiki/Constant_folding">constant folding</a> when number expressions are
+ * involved.
+ */
+@Beta
+public enum YangXPathMathMode {
+    /**
+     * All number expressions are treated as {@code double}. This in spirit of XPath 1.0 -- any number expression
+     * starts its life as a double, making all operations subject to IEEE754 rounding and range rules.
+     */
+    IEEE754(DoubleXPathMathSupport.INSTANCE),
+
+    /**
+     * All number expressions are treated as infinite-precision numbers. This follows the spirit of YANG 1.1 --
+     * where mostly have integral types and decimal64 mapped to BigDecimal. Non-decimal numbers are mapped either to
+     * {@code int}, {@code long} or {@code BigInteger}.
+     */
+    EXACT(BigDecimalXPathMathSupport.INSTANCE);
+
+    /*
+     * FIXME: 5.0.0: specify and implement this:
+     *
+     * All number expressions are treated either as {@code org.opendaylight.yangtools.yang.common} types with
+     * precision required to hold them. Decimal types are mapped to {@link Decimal64} with all range restrictions
+     * and rounding error implied by it.
+     */
+    // ODL_COMMON;
+
+    private YangXPathMathSupport support;
+
+    YangXPathMathMode(final YangXPathMathSupport support) {
+        this.support = requireNonNull(support);
+    }
+
+    /**
+     * Return {@link YangXPathMathSupport} which provides support for the this mode.
+     *
+     * @return YangXPathMathSupport supporting this mode.
+     */
+    public YangXPathMathSupport getSupport() {
+        return support;
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathSupport.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathSupport.java
new file mode 100644 (file)
index 0000000..e6c2b0d
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import com.google.common.annotations.Beta;
+import java.io.Serializable;
+import java.util.Optional;
+
+/**
+ * Interface supporting mathematical operations. This interface should be implemented by subclassing
+ * {@link AbstractYangXPathMathSupport}, which provides type safety guards.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface YangXPathMathSupport extends Serializable {
+    /**
+     * Create a {@link YangNumberExpr} backed by specified string.
+     *
+     * @param str String
+     * @return number expression
+     * @throws NullPointerException if {@code str} is null
+     * @throws NumberFormatException if the string does not represent a valid number
+     */
+    YangNumberExpr createNumber(String str);
+
+    /**
+     * Create a {@link YangNumberExpr} for specified integer.
+     *
+     * @param value integer value
+     * @return number expression
+     */
+    YangNumberExpr createNumber(int value);
+
+    /**
+     * Create a {@link YangNumberExpr} representing the negated value of a number.
+     *
+     * @param number input number
+     * @return negated number expression
+     * @throws NullPointerException if {@code number} is null
+     * @throws IllegalArgumentException if {@code number} has unrecognized type
+     */
+    YangNumberExpr negateNumber(YangNumberExpr number);
+
+    /**
+     * Attempt to evaluate an  operator and its left- and right-handside.
+     *
+     * @param operator Operator to apply
+     * @param left Left hand-side
+     * @param right Right hand-side
+     * @return Evaluation result, if evaluation succeeded
+     * @throws NullPointerException if any of the arguments is null
+     */
+    Optional<YangExpr> tryEvaluate(YangBinaryOperator operator, YangNumberExpr left, YangNumberExpr right);
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathNodeType.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathNodeType.java
new file mode 100644 (file)
index 0000000..62f40ec
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.xpath.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * XPath node type as defined in <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-NodeType">XPath 1.0</a>.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public enum YangXPathNodeType {
+    /**
+     * A {@code comment}.
+     */
+    COMMENT("comment"),
+    /**
+     * A {@code text}.
+     */
+    TEXT("text"),
+    /**
+     * A {@code processing-instruction}.
+     */
+    PROCESSING_INSTRUCTION("processing-instruction"),
+    /**
+     * A {@code node}.
+     */
+    NODE("node");
+
+    private final String str;
+
+    YangXPathNodeType(final String str) {
+        this.str = requireNonNull(str);
+    }
+
+    @Override
+    public String toString() {
+        return str;
+    }
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParser.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParser.java
new file mode 100644 (file)
index 0000000..7c06db7
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import com.google.common.annotations.Beta;
+import javax.xml.xpath.XPathExpressionException;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+
+/**
+ * Interface for converting a String into a {@link YangXPathExpression}. Implementations of this interface are expected
+ * to be NOT thread-safe.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface YangXPathParser {
+    /**
+     * A {@link YangXPathParser} bound to a {@link YangNamespaceContext}, producing Qualified-bound Expressions.
+     */
+    interface QualifiedBound extends YangXPathParser {
+        @Override
+        YangXPathExpression.QualifiedBound parseExpression(String xpath) throws XPathExpressionException;
+    }
+
+    /**
+     * A {@link YangXPathParser} bound to a {@link YangNamespaceContext} and a default namespace, producing
+     * Unqualified-bound Expressions.
+     */
+    interface UnqualifiedBound extends QualifiedBound {
+        @Override
+        YangXPathExpression.UnqualifiedBound parseExpression(String xpath) throws XPathExpressionException;
+    }
+
+    /**
+     * Parse a string containing an XPath expression.
+     *
+     * @param xpath XPath expression string
+     * @return A parsed {@link YangXPathExpression}
+     * @throws NullPointerException if {@code xpath} is null
+     * @throws XPathExpressionException when the expression cannot be parsed
+     */
+    YangXPathExpression parseExpression(String xpath) throws XPathExpressionException;
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParserFactory.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParserFactory.java
new file mode 100644 (file)
index 0000000..9b5a641
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+
+/**
+ * Factory for creating {@link YangXPathParser}s. Implementations of this interface are expected to be thread-safe.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface YangXPathParserFactory {
+    /**
+     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode#IEEE754}. Returned parser will not
+     * perform any namespace binding.
+     *
+     * @return An XPathParser
+     */
+    default YangXPathParser newParser() {
+        return newParser(YangXPathMathMode.IEEE754);
+    }
+
+    /**
+     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode}. Returned parser will not perform any
+     * namespace binding.
+     *
+     * @param mathMode Requested XPath number compliance
+     * @return An XPathParser
+     * @throws NullPointerException if {@code mathMode} is null
+     */
+    YangXPathParser newParser(YangXPathMathMode mathMode);
+
+    /**
+     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode#IEEE754}. Returned parser will bind
+     * qualified node identifiers to {@link QName}s.
+     *
+     * @param namespaceContext Prefix-to-namespace resolver, used to bind qualified node identifiers
+     * @return An XPathParser
+     * @throws NullPointerException if {@code namespaceContext} is null
+     */
+    default YangXPathParser.QualifiedBound newParser(final YangNamespaceContext namespaceContext) {
+        return newParser(YangXPathMathMode.IEEE754, namespaceContext);
+    }
+
+    /**
+     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode}. Returned parser will bind qualified
+     * node identifiers to {@link QName}s.
+     *
+     * @param mathMode Requested XPath number compliance
+     * @param namespaceContext Prefix-to-namespace resolver, used to bind qualified node identifiers
+     * @return An XPathParser
+     * @throws NullPointerException if any argument is null
+     */
+    YangXPathParser.QualifiedBound newParser(YangXPathMathMode mathMode, YangNamespaceContext namespaceContext);
+
+    /**
+     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode#IEEE754}. Returned parser will bind
+     * qualified and unqualified node identifiers to {@link QName}s.
+     *
+     * @param namespaceContext Prefix-to-namespace resolver, used to bind qualified node identifiers
+     * @param defaultNamespace Default namespace, used to bind unqualified node identifiers
+     * @return An XPathParser
+     * @throws NullPointerException if any argument is null
+     */
+    default YangXPathParser.UnqualifiedBound newParser(final YangNamespaceContext namespaceContext,
+            final QNameModule defaultNamespace) {
+        return newParser(YangXPathMathMode.IEEE754, namespaceContext, defaultNamespace);
+    }
+
+    /**
+     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode}. Returned parser will bind qualified
+     * and unqualified node identifiers to {@link QName}s.
+     *
+     * @param mathMode Requested XPath number compliance
+     * @param namespaceContext Prefix-to-namespace resolver, used to bind qualified node identifiers
+     * @param defaultNamespace Default namespace, used to bind unqualified node identifiers
+     * @return An XPathParser
+     * @throws NullPointerException if any argument is null
+     */
+    YangXPathParser.UnqualifiedBound newParser(YangXPathMathMode mathMode, YangNamespaceContext namespaceContext,
+            QNameModule defaultNamespace);
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/package-info.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/package-info.java
new file mode 100644 (file)
index 0000000..c883d78
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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
+ */
+/**
+ * Model of a RFC7950 XPath Expression. This model is namespace-bound to a particular set of models as conveyed
+ * by their {@link org.opendaylight.yangtools.yang.common.QNameModule}s. Function names are bound either to their
+ * defining {@link org.opendaylight.yangtools.yang.common.QName}, unprefixed functions are bound to
+ * {@link org.opendaylight.yangtools.yang.common.YangConstants#RFC6020_YIN_MODULE} the same way unprefixed statements
+ * are.
+ *
+ * <p>
+ * The model supports multiple number storage and math operations -- with IEEE754 being the default as per XPath 1.0
+ * specifications, but additional exact operations being available.
+ *
+ * @author Robert Varga
+ */
+@NonNullByDefault
+package org.opendaylight.yangtools.yang.xpath.api;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
\ No newline at end of file
diff --git a/yang/yang-xpath-impl/pom.xml b/yang/yang-xpath-impl/pom.xml
new file mode 100644 (file)
index 0000000..53c3402
--- /dev/null
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>bundle-parent</artifactId>
+        <version>4.0.3-SNAPSHOT</version>
+        <relativePath>../../bundle-parent</relativePath>
+    </parent>
+
+    <artifactId>yang-xpath-impl</artifactId>
+    <version>2.0.3-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    <name>${project.artifactId}</name>
+    <description>YANG XPath parser</description>
+
+    <properties>
+        <!-- FIXME: antlr-generated code does not pass SB -->
+        <odlparent.spotbugs.enforce>false</odlparent.spotbugs.enforce>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-xpath-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>antlr4-runtime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kohsuke.metainf-services</groupId>
+            <artifactId>metainf-services</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.antlr</groupId>
+                <artifactId>antlr4-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>antlr4</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <listener>false</listener>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Automatic-Module-Name>org.opendaylight.yangtools.yang.xpath.impl</Automatic-Module-Name>
+                        <Bundle-Activator>org.opendaylight.yangtools.yang.xpath.impl.Activator</Bundle-Activator>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/yang/yang-xpath-impl/src/main/antlr4/org/opendaylight/yangtools/yang/xpath/impl/instanceIdentifier.g4 b/yang/yang-xpath-impl/src/main/antlr4/org/opendaylight/yangtools/yang/xpath/impl/instanceIdentifier.g4
new file mode 100644 (file)
index 0000000..2001305
--- /dev/null
@@ -0,0 +1,85 @@
+grammar instanceIdentifier;
+
+/*
+ * YANG 1.1 instance-identifier grammar, as defined in
+ * https://tools.ietf.org/html/rfc7950#section-9.13
+ *
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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
+ */
+instanceIdentifier : ('/' pathArgument)+
+  ;
+
+pathArgument : nodeIdentifier predicate?
+  ;
+
+nodeIdentifier : Identifier ':' Identifier
+  ;
+
+predicate : keyPredicate+
+  | leafListPredicate
+  | pos
+  ;
+
+keyPredicate : '[' WSP? keyPredicateExpr WSP? ']'
+  ;
+
+keyPredicateExpr  : nodeIdentifier eqQuotedString
+  ;
+
+leafListPredicate : '[' WSP? leafListPredicateExpr WSP? ']'
+  ;
+
+leafListPredicateExpr : '.' eqQuotedString
+  ;
+
+// Common tail of leafListPredicateExpr and keyPredicateExpr
+eqQuotedString : WSP? '=' WSP? quotedString
+  ;
+
+pos : '[' WSP? PositiveIntegerValue WSP? ']'
+  ;
+
+quotedString : '\'' STRING '\''
+  | '"' STRING '"'
+  ;
+
+Identifier : [a-zA-Z][a-zA-Z0-9_\-.]*
+  ;
+
+PositiveIntegerValue : [1-9][0-9]*
+  ;
+
+STRING : YANGCHAR+
+  ;
+
+WSP : [ \t]+
+  ;
+
+fragment
+YANGCHAR : '\t'..'\n'
+  | '\r'
+  | '\u0020'..'\uD7FF'
+  | '\uE000'..'\uFDCF'
+  | '\uFDF0'..'\uFFFD'
+  | '\u{10000}'..'\u{1FFFD}'
+  | '\u{20000}'..'\u{2FFFD}'
+  | '\u{30000}'..'\u{3FFFD}'
+  | '\u{40000}'..'\u{4FFFD}'
+  | '\u{50000}'..'\u{5FFFD}'
+  | '\u{60000}'..'\u{6FFFD}'
+  | '\u{70000}'..'\u{7FFFD}'
+  | '\u{80000}'..'\u{8FFFD}'
+  | '\u{90000}'..'\u{9FFFD}'
+  | '\u{A0000}'..'\u{AFFFD}'
+  | '\u{B0000}'..'\u{BFFFD}'
+  | '\u{C0000}'..'\u{CFFFD}'
+  | '\u{D0000}'..'\u{DFFFD}'
+  | '\u{E0000}'..'\u{EFFFD}'
+  | '\u{F0000}'..'\u{FFFFD}'
+  | '\u{100000}'..'\u{10FFFD}'
+  ;
+
diff --git a/yang/yang-xpath-impl/src/main/antlr4/org/opendaylight/yangtools/yang/xpath/impl/xpath.g4 b/yang/yang-xpath-impl/src/main/antlr4/org/opendaylight/yangtools/yang/xpath/impl/xpath.g4
new file mode 100644 (file)
index 0000000..4bf327c
--- /dev/null
@@ -0,0 +1,258 @@
+grammar xpath;
+
+/*
+XPath 1.0 grammar. Should conform to the official spec at
+http://www.w3.org/TR/1999/REC-xpath-19991116. The grammar
+rules have been kept as close as possible to those in the
+spec, but some adjustmewnts were unavoidable. These were
+mainly removing left recursion (spec seems to be based on
+LR), and to deal with the double nature of the '*' token
+(node wildcard and multiplication operator). See also
+section 3.7 in the spec. These rule changes should make
+no difference to the strings accepted by the grammar.
+
+Written by Jan-Willem van den Broek
+Version 1.0
+
+Do with this code as you will.
+*/
+/*
+    Ported to Antlr4 by Tom Everett <tom@khubla.com>
+*/
+
+
+main  :  expr
+  ;
+
+locationPath 
+  :  relativeLocationPath
+  |  absoluteLocationPathNoroot
+  ;
+
+absoluteLocationPathNoroot
+  :  '/' relativeLocationPath
+  |  '//' relativeLocationPath
+  ;
+
+relativeLocationPath
+  :  step (('/'|'//') step)*
+  ;
+
+step  :  axisSpecifier nodeTest predicate*
+  |  abbreviatedStep
+  ;
+
+axisSpecifier
+  :  AxisName '::'
+  |  '@'?
+  ;
+
+nodeTest:  nameTest
+  |  NodeType '(' ')'
+  |  'processing-instruction' '(' Literal ')'
+  ;
+
+predicate
+  :  '[' expr ']'
+  ;
+
+abbreviatedStep
+  :  '.'
+  |  '..'
+  ;
+
+expr  :  orExpr
+  ;
+
+primaryExpr
+  :  variableReference
+  |  '(' expr ')'
+  |  Literal
+  |  Number  
+  |  functionCall
+  ;
+
+functionCall
+  :  functionName '(' ( expr ( ',' expr )* )? ')'
+  ;
+
+unionExprNoRoot
+  :  pathExprNoRoot ('|' unionExprNoRoot)?
+  |  '/' '|' unionExprNoRoot
+  ;
+
+pathExprNoRoot
+  :  locationPath
+  |  filterExpr (('/'|'//') relativeLocationPath)?
+  ;
+
+filterExpr
+  :  primaryExpr predicate*
+  ;
+
+orExpr  :  andExpr ('or' andExpr)*
+  ;
+
+andExpr  :  equalityExpr ('and' equalityExpr)*
+  ;
+
+equalityExpr
+  :  relationalExpr (('='|'!=') relationalExpr)*
+  ;
+
+relationalExpr
+  :  additiveExpr (('<'|'>'|'<='|'>=') additiveExpr)*
+  ;
+
+additiveExpr
+  :  multiplicativeExpr (('+'|'-') multiplicativeExpr)*
+  ;
+
+multiplicativeExpr
+  :  unaryExprNoRoot (('*'|'div'|'mod') multiplicativeExpr)?
+  |  '/' (('div'|'mod') multiplicativeExpr)?
+  ;
+
+unaryExprNoRoot
+  :  '-'* unionExprNoRoot
+  ;
+
+qName  :  nCName (':' nCName)?
+  ;
+
+// Does not match NodeType, as per spec.
+functionName
+  :  nCName ':' nCName
+  |  NCName
+  |  AxisName
+  ;
+
+variableReference
+  :  '$' qName
+  ;
+
+nameTest:  '*'
+  |  nCName ':' '*'
+  |  qName
+  ;
+
+nCName  :  NCName
+  |  AxisName
+  |  NodeType
+  ;
+
+NodeType:  'comment'
+  |  'text'
+  |  'processing-instruction'
+  |  'node'
+  ;
+  
+Number  :  Digits ('.' Digits?)?
+  |  '.' Digits
+  ;
+
+fragment
+Digits  :  ('0'..'9')+
+  ;
+
+AxisName:  'ancestor'
+  |  'ancestor-or-self'
+  |  'attribute'
+  |  'child'
+  |  'descendant'
+  |  'descendant-or-self'
+  |  'following'
+  |  'following-sibling'
+  |  'namespace'
+  |  'parent'
+  |  'preceding'
+  |  'preceding-sibling'
+  |  'self'
+  ;
+
+
+  PATHSEP 
+       :'/';
+  ABRPATH   
+       : '//';
+  LPAR   
+       : '(';
+  RPAR   
+       : ')';
+  LBRAC   
+       :  '[';
+  RBRAC   
+       :  ']';
+  MINUS   
+       :  '-';
+  PLUS   
+       :  '+';
+  DOT   
+       :  '.';
+  MUL   
+       : '*';
+  DOTDOT   
+       :  '..';
+  AT   
+       : '@';
+  COMMA  
+       : ',';
+  PIPE   
+       :  '|';
+  LESS   
+       :  '<';
+  MORE_ 
+       :  '>';
+  LE   
+       :  '<=';
+  GE   
+       :  '>=';
+  COLON   
+       :  ':';
+  CC   
+       :  '::';
+  APOS   
+       :  '\'';
+  QUOT   
+       :  '"';
+  
+Literal  :  '"' ~'"'* '"'
+  |  '\'' ~'\''* '\''
+  ;
+
+Whitespace
+  :  (' '|'\t'|'\n'|'\r')+ ->skip
+  ;
+
+NCName  :  NCNameStartChar NCNameChar*
+  ;
+
+fragment
+NCNameStartChar
+  :  'A'..'Z'
+  |   '_'
+  |  'a'..'z'
+  |  '\u00C0'..'\u00D6'
+  |  '\u00D8'..'\u00F6'
+  |  '\u00F8'..'\u02FF'
+  |  '\u0370'..'\u037D'
+  |  '\u037F'..'\u1FFF'
+  |  '\u200C'..'\u200D'
+  |  '\u2070'..'\u218F'
+  |  '\u2C00'..'\u2FEF'
+  |  '\u3001'..'\uD7FF'
+  |  '\uF900'..'\uFDCF'
+  |  '\uFDF0'..'\uFFFD'
+// Unfortunately, java escapes can't handle this conveniently,
+// as they're limited to 4 hex digits. TODO.
+//  |  '\U010000'..'\U0EFFFF'
+  ;
+
+fragment
+NCNameChar
+  :  NCNameStartChar | '-' | '.' | '0'..'9'
+  |  '\u00B7' | '\u0300'..'\u036F'
+  |  '\u203F'..'\u2040'
+  ;
+
+
diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/Activator.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/Activator.java
new file mode 100644 (file)
index 0000000..0ab894f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.xpath.impl;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathParserFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * YANG XPath implementation activator. Publishes a {@link YangXPathParserFactory} implementation on bundle start.
+ *
+ * @author Robert Varga
+ */
+public final class Activator implements BundleActivator {
+    @Override
+    public void start(final @Nullable BundleContext context) throws Exception {
+        context.registerService(YangXPathParserFactory.class, new AntlrXPathParserFactory(), null);
+    }
+
+    @Override
+    public void stop(final @Nullable BundleContext context) throws Exception {
+        // No-op, framework will unregister our services
+    }
+}
diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParser.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParser.java
new file mode 100644 (file)
index 0000000..cbec645
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.getChild;
+import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.illegalShape;
+import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyAtLeastChildren;
+import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyChildCount;
+import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyTerminal;
+import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyToken;
+import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyTree;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Maps;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import javax.xml.xpath.XPathExpressionException;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.TerminalNode;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.QualifiedQName;
+import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
+import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangFilterExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
+import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.AxisStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.ResolvedQNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
+import org.opendaylight.yangtools.yang.xpath.api.YangNaryExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangNaryOperator;
+import org.opendaylight.yangtools.yang.xpath.api.YangNegateExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangNumberExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangPathExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangVariableReferenceExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathSupport;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathNodeType;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AbbreviatedStepContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AbsoluteLocationPathNorootContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AdditiveExprContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AndExprContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AxisSpecifierContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.EqualityExprContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.ExprContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.FilterExprContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.FunctionCallContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.FunctionNameContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.LocationPathContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.MultiplicativeExprContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.NCNameContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.NameTestContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.NodeTestContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.OrExprContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.PathExprNoRootContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.PredicateContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.PrimaryExprContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.QNameContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.RelationalExprContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.RelativeLocationPathContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.StepContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.UnaryExprNoRootContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.UnionExprNoRootContext;
+import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.VariableReferenceContext;
+
+/**
+ * ANTLR-based XPath parser. Uses {@code xpath.g4} ANTLR grammar.
+ *
+ * @author Robert Varga
+ */
+abstract class AntlrXPathParser implements YangXPathParser {
+    static class Base extends AntlrXPathParser {
+        Base(final YangXPathMathMode mathMode) {
+            super(mathMode);
+        }
+
+        @Override
+        public YangXPathExpression parseExpression(final String xpath) throws XPathExpressionException {
+            final Entry<YangVersion, YangExpr> result = parseExpr(xpath);
+            return new AntlrYangXPathExpression.Base(mathMode, result.getKey(), result.getValue(), xpath);
+        }
+
+        @Override
+        QNameStep createStep(final YangXPathAxis axis, final String localName,
+                final List<YangExpr> predicates) {
+            return axis.asStep(UnqualifiedQName.of(localName).intern(), predicates);
+        }
+
+        @Override
+        QNameStep createStep(final YangXPathAxis axis, final String prefix, final String localName,
+                final List<YangExpr> predicates) {
+            return axis.asStep(QualifiedQName.of(prefix, localName).intern(), predicates);
+        }
+
+        @Override
+        QName createQName(final String localName) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        QName createQName(final String prefix, final String localName) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    static class Qualified extends Base implements QualifiedBound {
+        final YangNamespaceContext namespaceContext;
+
+        Qualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
+            super(mathMode);
+            this.namespaceContext = requireNonNull(namespaceContext);
+        }
+
+        @Override
+        public YangXPathExpression.QualifiedBound parseExpression(final String xpath) throws XPathExpressionException {
+            final Entry<YangVersion, YangExpr> result = parseExpr(xpath);
+            return new AntlrYangXPathExpression.Qualified(mathMode, result.getKey(), result.getValue(), xpath,
+                namespaceContext);
+        }
+
+        @Override
+        final QName createQName(final String prefix, final String localName) {
+            return namespaceContext.createQName(prefix, localName);
+        }
+
+        @Override
+        ResolvedQNameStep createStep(final YangXPathAxis axis, final String prefix, final String localName,
+                final List<YangExpr> predicates) {
+            return axis.asStep(createQName(prefix, localName), predicates);
+        }
+    }
+
+    static final class Unqualified extends Qualified implements UnqualifiedBound {
+        private final QNameModule defaultNamespace;
+
+        Unqualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext,
+                final QNameModule defaultNamespace) {
+            super(mathMode, namespaceContext);
+            this.defaultNamespace = requireNonNull(defaultNamespace);
+        }
+
+        @Override
+        public YangXPathExpression.UnqualifiedBound parseExpression(final String xpath)
+                throws XPathExpressionException {
+            final Entry<YangVersion, YangExpr> result = parseExpr(xpath);
+
+            return new AntlrYangXPathExpression.Unqualified(mathMode, result.getKey(), result.getValue(), xpath,
+                namespaceContext, defaultNamespace);
+        }
+
+        @Override
+        QName createQName(final String localName) {
+            return QName.create(defaultNamespace, localName);
+        }
+
+        @Override
+        ResolvedQNameStep createStep(final YangXPathAxis axis, final String localName,
+                final List<YangExpr> predicates) {
+            return axis.asStep(QName.create(defaultNamespace, localName), predicates);
+        }
+    }
+
+    private static final Map<String, YangBinaryOperator> BINARY_OPERATORS = Maps.uniqueIndex(
+        Arrays.asList(YangBinaryOperator.values()), YangBinaryOperator::toString);
+    private static final Map<String, YangXPathNodeType> NODE_TYPES = Maps.uniqueIndex(Arrays.asList(
+        YangXPathNodeType.values()), YangXPathNodeType::toString);
+    private static final Map<String, YangXPathAxis> XPATH_AXES = Maps.uniqueIndex(Arrays.asList(YangXPathAxis.values()),
+        YangXPathAxis::toString);
+    private static final Map<QName, YangFunction> YANG_FUNCTIONS = Maps.uniqueIndex(Arrays.asList(
+        YangFunction.values()), YangFunction::getIdentifier);
+
+    // Cached for checks in hot path
+    private static final AxisStep SELF_STEP = YangXPathAxis.SELF.asStep();
+
+    final YangXPathMathMode mathMode;
+    private final YangXPathMathSupport mathSupport;
+    private final FunctionSupport functionSupport;
+
+    private YangVersion minimumYangVersion = YangVersion.VERSION_1;
+
+    AntlrXPathParser(final YangXPathMathMode mathMode) {
+        this.mathMode = requireNonNull(mathMode);
+        this.mathSupport = mathMode.getSupport();
+        this.functionSupport = new FunctionSupport(mathSupport);
+    }
+
+    abstract QName createQName(String localName);
+
+    abstract QName createQName(String prefix, String localName);
+
+    abstract QNameStep createStep(YangXPathAxis axis, String localName, List<YangExpr> predicates);
+
+    abstract QNameStep createStep(YangXPathAxis axis, String prefix, String localName, List<YangExpr> predicates);
+
+    private QNameStep createStep(final YangXPathAxis axis, final QNameContext expr, final List<YangExpr> predicates) {
+        switch (expr.getChildCount()) {
+            case 1:
+                return createStep(axis, getChild(expr, NCNameContext.class, 0).getText(), predicates);
+            case 3:
+                return createStep(axis, getChild(expr, NCNameContext.class, 0).getText(),
+                    getChild(expr, NCNameContext.class, 2).getText(), predicates);
+            default:
+                throw illegalShape(expr);
+        }
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    final Entry<YangVersion, YangExpr> parseExpr(final String xpath) throws XPathExpressionException {
+        // Create a parser and disconnect it from console error output
+        final xpathLexer lexer = new xpathLexer(CharStreams.fromString(xpath));
+        final xpathParser parser = new xpathParser(new CommonTokenStream(lexer));
+
+        final CapturingErrorListener listener = new CapturingErrorListener();
+        lexer.removeErrorListeners();
+        lexer.addErrorListener(listener);
+        parser.removeErrorListeners();
+        parser.addErrorListener(listener);
+        final ExprContext antlr = parser.main().expr();
+        listener.reportError();
+
+        // Reset our internal context
+        minimumYangVersion = YangVersion.VERSION_1;
+
+        final YangExpr expr;
+        try {
+            expr = parseExpr(antlr);
+        } catch (RuntimeException e) {
+            throw new XPathExpressionException(e);
+        }
+        return new SimpleImmutableEntry<>(minimumYangVersion, expr);
+    }
+
+    /**
+     * Parse and simplify an XPath expression in {@link ExprContext} representation.
+     *
+     * @param ctx Current parsing context
+     * @param expr ANTLR ExprContext
+     * @return A {@link YangExpr}
+     * @throws NullPointerException if {@code expr} is null
+     * @throws IllegalArgumentException if {@code expr} references an unbound prefix
+     */
+    private YangExpr parseExpr(final ExprContext expr) {
+        final OrExprContext or = expr.orExpr();
+        final int size = or.getChildCount();
+        if (size == 1) {
+            return parseAnd(getChild(or, AndExprContext.class, 0));
+        }
+        final List<YangExpr> tmp = new ArrayList<>((size + 1) / 2);
+        for (int i = 0; i < size; i += 2) {
+            tmp.add(parseAnd(getChild(or, AndExprContext.class, i)));
+        }
+        return YangNaryOperator.OR.exprWith(tmp);
+    }
+
+    private YangExpr parseAdditive(final AdditiveExprContext expr) {
+        final Iterator<ParseTree> it = expr.children.iterator();
+        final YangExpr first = parseMultiplicative(nextContext(it, MultiplicativeExprContext.class));
+        return it.hasNext() ? parseAdditiveExpr(first, it) : first;
+    }
+
+    private YangExpr parseAnd(final AndExprContext expr) {
+        final int size = expr.getChildCount();
+        if (size == 1) {
+            return parseEquality(getChild(expr, EqualityExprContext.class, 0));
+        }
+        final List<YangExpr> tmp = new ArrayList<>((size + 1) / 2);
+        for (int i = 0; i < size; i += 2) {
+            tmp.add(parseEquality(getChild(expr, EqualityExprContext.class, i)));
+        }
+        return YangNaryOperator.AND.exprWith(tmp);
+    }
+
+    private YangExpr parseEquality(final EqualityExprContext expr) {
+        final Iterator<ParseTree> it = expr.children.iterator();
+        final YangExpr first = parseRelational(nextContext(it, RelationalExprContext.class));
+        return it.hasNext() ? parseEqualityExpr(first, it) : first;
+    }
+
+    private YangExpr parseFilter(final FilterExprContext expr) {
+        final Iterator<ParseTree> it = expr.children.iterator();
+        final YangExpr first = parsePrimary(nextContext(it, PrimaryExprContext.class));
+        return it.hasNext() ? YangFilterExpr.of(first, ImmutableList.copyOf(Iterators.transform(it,
+            tree ->  parsePredicate(verifyTree(PredicateContext.class, tree)))))
+                : first;
+    }
+
+    private YangExpr parseFunctionCall(final FunctionCallContext expr) {
+        // We are mapping functions to RFC7950 YIN namespace, to keep us consistent with type/statement definitions
+
+        final FunctionNameContext name = getChild(expr, FunctionNameContext.class, 0);
+        final QName parsed;
+        switch (name.getChildCount()) {
+            case 1:
+                parsed = QName.create(YangConstants.RFC6020_YIN_MODULE, name.getChild(0).getText());
+                break;
+            case 3:
+                parsed = createQName(name.getChild(0).getText(), name.getChild(2).getText());
+                break;
+            default:
+                throw illegalShape(name);
+        }
+
+        final List<YangExpr> args = expr.expr().stream().map(this::parseExpr).collect(ImmutableList.toImmutableList());
+        final YangFunction func = YANG_FUNCTIONS.get(parsed);
+        if (func != null) {
+            if (minimumYangVersion.compareTo(func.getYangVersion()) < 0) {
+                minimumYangVersion = func.getYangVersion();
+            }
+            return functionSupport.functionToExpr(func, args);
+        }
+
+        checkArgument(!YangConstants.RFC6020_YIN_MODULE.equals(parsed.getModule()), "Unknown default function %s",
+            parsed);
+        return YangFunctionCallExpr.of(parsed, args);
+    }
+
+    private YangLocationPath parseLocationPath(final LocationPathContext expr) {
+        verifyChildCount(expr, 1);
+        final ParseTree first = expr.getChild(0);
+        if (first instanceof RelativeLocationPathContext) {
+            return YangLocationPath.relative(parseLocationPathSteps((RelativeLocationPathContext) first));
+        }
+
+        final AbsoluteLocationPathNorootContext abs = verifyTree(AbsoluteLocationPathNorootContext.class, first);
+        verifyChildCount(abs, 2);
+
+        final Deque<Step> steps = parseLocationPathSteps(getChild(abs, RelativeLocationPathContext.class, 1));
+        parseStepShorthand(abs.getChild(0)).ifPresent(steps::addFirst);
+
+        return YangLocationPath.absolute(steps);
+    }
+
+    private YangExpr parseMultiplicative(final MultiplicativeExprContext expr) {
+        final ParseTree first = expr.getChild(0);
+        final YangExpr left;
+        if (first instanceof UnaryExprNoRootContext) {
+            left = parseUnary((UnaryExprNoRootContext) first);
+        } else {
+            left = YangLocationPath.root();
+        }
+        if (expr.getChildCount() == 1) {
+            return left;
+        }
+
+        verifyChildCount(expr, 3);
+        final YangBinaryOperator operator = parseOperator(expr.getChild(1));
+        final YangExpr right = parseMultiplicative(getChild(expr, MultiplicativeExprContext.class, 2));
+        final Optional<YangExpr> simple = simplifyNumbers(operator, left, right);
+        return simple.isPresent() ? simple.get() : operator.exprWith(left, right);
+    }
+
+    private YangExpr parsePathExpr(final PathExprNoRootContext expr) {
+        final ParseTree first = expr.getChild(0);
+        if (first instanceof LocationPathContext) {
+            return parseLocationPath((LocationPathContext) first);
+        }
+
+        final YangExpr filter = parseFilter(verifyTree(FilterExprContext.class, first));
+        if (expr.getChildCount() == 1) {
+            return filter;
+        }
+
+        verifyChildCount(expr, 3);
+        final Deque<Step> steps = parseLocationPathSteps(getChild(expr, RelativeLocationPathContext.class, 2));
+        parseStepShorthand(expr.getChild(1)).ifPresent(steps::addFirst);
+        return YangPathExpr.of(filter, YangLocationPath.relative(steps));
+    }
+
+    private YangExpr parsePredicate(final PredicateContext expr) {
+        verifyChildCount(expr, 3);
+        return parseExpr(getChild(expr, ExprContext.class, 1));
+    }
+
+    private YangExpr parsePrimary(final PrimaryExprContext expr) {
+        if (expr.getChildCount() == 3) {
+            return parseExpr(getChild(expr, ExprContext.class, 1));
+        }
+
+        verifyChildCount(expr, 1);
+        final ParseTree first = expr.getChild(0);
+        if (first instanceof TerminalNode) {
+            return parseTerminal((TerminalNode) first);
+        }
+        if (first instanceof FunctionCallContext) {
+            return parseFunctionCall((FunctionCallContext) first);
+        }
+        if (first instanceof VariableReferenceContext) {
+            return YangVariableReferenceExpr.of(parseQName(((VariableReferenceContext) first).qName()));
+        }
+        throw illegalShape(first);
+    }
+
+    private YangExpr parseRelational(final RelationalExprContext expr) {
+        final Iterator<ParseTree> it = expr.children.iterator();
+        final YangExpr first = parseAdditive(nextContext(it, AdditiveExprContext.class));
+        return it.hasNext() ? parseRelationalExpr(first, it) : first;
+    }
+
+    private Deque<Step> parseLocationPathSteps(final RelativeLocationPathContext expr) {
+        final Deque<Step> steps = new ArrayDeque<>(expr.getChildCount());
+        final Iterator<ParseTree> it = expr.children.iterator();
+        steps.add(parseStep(nextContext(it, StepContext.class)));
+
+        while (it.hasNext()) {
+            parseStepShorthand(it.next()).ifPresent(steps::add);
+
+            // Parse step and add it if it's not SELF_STEP
+            final Step step = parseStep(nextContext(it, StepContext.class));
+            if (!SELF_STEP.equals(step)) {
+                steps.add(step);
+            }
+        }
+
+        return steps;
+    }
+
+    private YangExpr parseTerminal(final TerminalNode term) {
+        final String text = term.getText();
+        switch (term.getSymbol().getType()) {
+            case xpathParser.Literal:
+                // We have to strip quotes
+                return YangLiteralExpr.of(text.substring(1, text.length() - 1));
+            case xpathParser.Number:
+                return mathSupport.createNumber(text);
+            default:
+                throw illegalShape(term);
+        }
+    }
+
+    private YangExpr parseUnary(final UnaryExprNoRootContext expr) {
+        // any number of '-' and an union expr
+        final int size = verifyAtLeastChildren(expr, 1);
+        final YangExpr ret = parseUnion(getChild(expr, UnionExprNoRootContext.class, size - 1));
+        if (size % 2 != 0) {
+            // Even number of '-' tokens cancel out
+            return ret;
+        }
+        return ret instanceof YangNumberExpr ? mathSupport.negateNumber((YangNumberExpr) ret) : YangNegateExpr.of(ret);
+    }
+
+    private YangExpr parseUnion(final UnionExprNoRootContext expr) {
+        final ParseTree first = expr.getChild(0);
+        final YangExpr path;
+        if (first instanceof PathExprNoRootContext) {
+            path = parsePathExpr((PathExprNoRootContext) first);
+            if (expr.getChildCount() == 1) {
+                return path;
+            }
+        } else {
+            path = YangLocationPath.root();
+        }
+
+        verifyChildCount(expr, 3);
+        final YangExpr union = parseUnion(getChild(expr, UnionExprNoRootContext.class, 2));
+
+        // Deduplicate expressions so we do not perform useless unioning
+        final Set<YangExpr> expressions = new LinkedHashSet<>();
+        expressions.add(path);
+        if (union instanceof YangNaryExpr) {
+            // If the result is a union expression, integrate it into this expression
+            final YangNaryExpr nary = (YangNaryExpr) union;
+            if (nary.getOperator() == YangNaryOperator.UNION) {
+                expressions.addAll(nary.getExpressions());
+            } else {
+                expressions.add(union);
+            }
+        } else {
+            expressions.add(union);
+        }
+
+        return YangNaryOperator.UNION.exprWith(expressions);
+    }
+
+    private YangExpr parseAdditiveExpr(final YangExpr left, final Iterator<ParseTree> it) {
+        YangExpr ret = left;
+        do {
+            final YangBinaryOperator operator = nextOperator(it);
+            final YangExpr right = parseMultiplicative(nextContext(it, MultiplicativeExprContext.class));
+            final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
+            ret = simple.isPresent() ? simple.get() : operator.exprWith(ret, right);
+        } while (it.hasNext());
+
+        return ret;
+    }
+
+    private Optional<YangExpr> simplifyNumbers(final YangBinaryOperator operator, final YangExpr left,
+            final YangExpr right) {
+        if (left instanceof YangNumberExpr && right instanceof YangNumberExpr) {
+            // Constant folding on numbers -- precision plays a role here
+            return mathSupport.tryEvaluate(operator, (YangNumberExpr)left, (YangNumberExpr)right);
+        }
+        return Optional.empty();
+    }
+
+    private YangExpr parseEqualityExpr(final YangExpr left, final Iterator<ParseTree> it) {
+        YangExpr ret = left;
+        do {
+            final YangBinaryOperator operator = nextOperator(it);
+            final YangExpr right = parseRelational(nextContext(it, RelationalExprContext.class));
+
+            if (left.equals(right)) {
+                // Constant folding on expression level: equal expressions are result in equal results
+                switch (operator) {
+                    case EQUALS:
+                        return YangBooleanConstantExpr.TRUE;
+                    case NOT_EQUALS:
+                        return YangBooleanConstantExpr.FALSE;
+                    default:
+                        break;
+                }
+            }
+
+            final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
+            ret = simple.isPresent() ? simple.get() : operator.exprWith(ret, right);
+        } while (it.hasNext());
+
+        return ret;
+    }
+
+    private YangExpr parseRelationalExpr(final YangExpr left, final Iterator<ParseTree> it) {
+        YangExpr ret = left;
+        do {
+            final YangBinaryOperator operator = nextOperator(it);
+            final YangExpr right = parseAdditive(nextContext(it, AdditiveExprContext.class));
+            final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
+            ret = simple.isPresent() ? simple.get() : nextOperator(it).exprWith(ret, right);
+        } while (it.hasNext());
+
+        return ret;
+    }
+
+    private Step parseStep(final StepContext expr) {
+        if (expr.getChildCount() == 1) {
+            final AbbreviatedStepContext abbrev = getChild(expr, AbbreviatedStepContext.class, 0);
+            verifyChildCount(abbrev, 1);
+            switch (getTerminalType(abbrev, 0)) {
+                case xpathParser.DOT:
+                    return YangXPathAxis.SELF.asStep();
+                case xpathParser.DOTDOT:
+                    return YangXPathAxis.PARENT.asStep();
+                default:
+                    throw illegalShape(abbrev);
+            }
+        }
+
+        final int size = verifyAtLeastChildren(expr, 2);
+        final List<YangExpr> predicates = new ArrayList<>(size - 2);
+        for (int i = 2; i < size; ++i) {
+            predicates.add(parsePredicate(getChild(expr, PredicateContext.class, i)));
+        }
+
+        final YangXPathAxis axis = parseAxis(getChild(expr, AxisSpecifierContext.class, 0));
+        final NodeTestContext nodeTest = getChild(expr, NodeTestContext.class, 1);
+        switch (nodeTest.getChildCount()) {
+            case 1:
+                final NameTestContext nameChild = getChild(nodeTest, NameTestContext.class, 0);
+                final ParseTree first = nameChild.getChild(0);
+                if (first instanceof TerminalNode) {
+                    verify(((TerminalNode) first).getSymbol().getType() == xpathParser.MUL);
+                    return axis.asStep(predicates);
+                }
+                return createStep(axis, verifyTree(QNameContext.class, first), predicates);
+            case 3:
+                return axis.asStep(parseNodeType(nodeTest.getChild(0)), predicates);
+            case 4:
+                final String text = verifyToken(nodeTest, 2, xpathParser.Literal).getText();
+                return axis.asStep(text.substring(1, text.length() - 1), predicates);
+            default:
+                throw illegalShape(nodeTest);
+        }
+    }
+
+    private static YangXPathAxis parseAxis(final AxisSpecifierContext expr) {
+        switch (expr.getChildCount()) {
+            case 0:
+                return YangXPathAxis.CHILD;
+            case 1:
+                verify(getTerminalType(expr, 0) == xpathParser.AT, "Unhandled axis specifier shape %s", expr);
+                return YangXPathAxis.ATTRIBUTE;
+            case 2:
+                final String str = verifyTerminal(expr.getChild(0)).getText();
+                return verifyNotNull(XPATH_AXES.get(str), "Unhandled axis %s", str);
+            default:
+                throw illegalShape(expr);
+        }
+    }
+
+    private QName parseQName(final QNameContext expr) {
+        switch (expr.getChildCount()) {
+            case 1:
+                return createQName(getChild(expr, NCNameContext.class, 0).getText());
+            case 3:
+                return createQName(getChild(expr, NCNameContext.class, 0).getText(),
+                    getChild(expr, NCNameContext.class, 2).getText());
+            default:
+                throw illegalShape(expr);
+        }
+    }
+
+    private static <T extends ParserRuleContext> T nextContext(final Iterator<ParseTree> it, final Class<T> type) {
+        return verifyTree(type, it.next());
+    }
+
+    private static YangBinaryOperator nextOperator(final Iterator<ParseTree> it) {
+        return parseOperator(it.next());
+    }
+
+    private static int getTerminalType(final ParseTree parent, final int offset) {
+        return verifyTerminal(parent.getChild(offset)).getSymbol().getType();
+    }
+
+    private static YangXPathNodeType parseNodeType(final ParseTree tree) {
+        final String str = verifyTerminal(tree).getText();
+        return verifyNotNull(NODE_TYPES.get(str), "Unhandled node type %s", str);
+    }
+
+    private static YangBinaryOperator parseOperator(final ParseTree tree) {
+        final String str = verifyTerminal(tree).getText();
+        return verifyNotNull(BINARY_OPERATORS.get(str), "Unhandled operator %s", str);
+    }
+
+    private static Optional<Step> parseStepShorthand(final ParseTree tree) {
+        switch (verifyTerminal(tree).getSymbol().getType()) {
+            case xpathParser.PATHSEP:
+                return Optional.empty();
+            case xpathParser.ABRPATH:
+                return Optional.of(YangXPathAxis.DESCENDANT_OR_SELF.asStep());
+            default:
+                throw illegalShape(tree);
+        }
+    }
+}
diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParserFactory.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParserFactory.java
new file mode 100644 (file)
index 0000000..d061bfc
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.impl;
+
+import org.kohsuke.MetaInfServices;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser.QualifiedBound;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser.UnqualifiedBound;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathParserFactory;
+
+@MetaInfServices
+public final class AntlrXPathParserFactory implements YangXPathParserFactory {
+    @Override
+    public YangXPathParser newParser(final YangXPathMathMode mathMode) {
+        return new AntlrXPathParser.Base(mathMode);
+    }
+
+    @Override
+    public QualifiedBound newParser(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
+        return new AntlrXPathParser.Qualified(mathMode, namespaceContext);
+    }
+
+    @Override
+    public UnqualifiedBound newParser(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext,
+            final QNameModule defaultNamespace) {
+        return new AntlrXPathParser.Unqualified(mathMode, namespaceContext, defaultNamespace);
+    }
+}
diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrYangXPathExpression.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrYangXPathExpression.java
new file mode 100644 (file)
index 0000000..9021238
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import javax.xml.xpath.XPathExpressionException;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr.Resolved;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
+
+abstract class AntlrYangXPathExpression implements YangXPathExpression {
+    static final class Base extends AntlrYangXPathExpression {
+        Base(final YangXPathMathMode mathMode, final YangVersion yangVersion, final YangExpr rootExpr,
+                final String origStr) {
+            super(mathMode, yangVersion, rootExpr, origStr);
+        }
+
+        @Override
+        public YangQNameExpr interpretAsQName(final YangLiteralExpr expr) throws XPathExpressionException {
+            return Utils.interpretAsQName(expr);
+        }
+
+        @Override
+        InstanceIdentifierParser createInstanceIdentifierParser() {
+            return new InstanceIdentifierParser.Base(getMathMode());
+        }
+    }
+
+    static class Qualified extends AntlrYangXPathExpression implements QualifiedBound {
+        final YangNamespaceContext namespaceContext;
+
+        Qualified(final YangXPathMathMode mathMode, final YangVersion yangVersion, final YangExpr rootExpr,
+                final String origStr, final YangNamespaceContext namespaceContext) {
+            super(mathMode, yangVersion, rootExpr, origStr);
+            this.namespaceContext = requireNonNull(namespaceContext);
+        }
+
+        YangNamespaceContext getNamespaceContext() {
+            return namespaceContext;
+        }
+
+        @Override
+        public YangQNameExpr interpretAsQName(final YangLiteralExpr expr) throws XPathExpressionException {
+            return Utils.interpretAsQName(namespaceContext, expr);
+        }
+
+        @Override
+        final InstanceIdentifierParser createInstanceIdentifierParser() {
+            return new InstanceIdentifierParser.Qualified(getMathMode(), namespaceContext);
+        }
+    }
+
+    static final class Unqualified extends Qualified implements UnqualifiedBound {
+        private final QNameModule defaultNamespace;
+
+        Unqualified(final YangXPathMathMode mathMode, final YangVersion yangVersion, final YangExpr rootExpr,
+                final String origStr, final YangNamespaceContext namespaceContext, final QNameModule defaultNamespace) {
+            super(mathMode, yangVersion, rootExpr, origStr, namespaceContext);
+            this.defaultNamespace = requireNonNull(defaultNamespace);
+        }
+
+        @Override
+        public Resolved interpretAsQName(final YangLiteralExpr expr) throws XPathExpressionException {
+            return Utils.interpretAsQName(getNamespaceContext(), defaultNamespace, expr);
+        }
+    }
+
+    private final YangXPathMathMode mathMode;
+    private final YangVersion yangVersion;
+    private final YangExpr rootExpr;
+    private final String origStr;
+
+    AntlrYangXPathExpression(final YangXPathMathMode mathMode, final YangVersion yangVersion, final YangExpr rootExpr,
+            final String origStr) {
+        this.mathMode = requireNonNull(mathMode);
+        this.yangVersion = requireNonNull(yangVersion);
+        this.rootExpr = requireNonNull(rootExpr);
+        this.origStr = requireNonNull(origStr);
+    }
+
+    @Override
+    public final YangXPathMathMode getMathMode() {
+        return mathMode;
+    }
+
+    @Override
+    public final YangVersion getYangVersion() {
+        return yangVersion;
+    }
+
+    @Override
+    public final YangExpr getRootExpr() {
+        return rootExpr;
+    }
+
+    @Override
+    public final YangLocationPath interpretAsInstanceIdentifier(final YangLiteralExpr expr)
+            throws XPathExpressionException {
+        return createInstanceIdentifierParser().interpretAsInstanceIdentifier(expr);
+    }
+
+    @Override
+    public final String toString() {
+        return origStr;
+    }
+
+    abstract InstanceIdentifierParser createInstanceIdentifierParser();
+}
diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/CapturingErrorListener.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/CapturingErrorListener.java
new file mode 100644 (file)
index 0000000..d6a5d31
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.xpath.impl;
+
+import javax.xml.xpath.XPathExpressionException;
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.eclipse.jdt.annotation.Nullable;
+
+final class CapturingErrorListener extends BaseErrorListener {
+    private @Nullable XPathExpressionException error;
+
+    @Override
+    public void syntaxError(final @Nullable Recognizer<?, ?> recognizer, final @Nullable Object offendingSymbol,
+            final int line, final int charPositionInLine, final @Nullable String msg,
+            final @Nullable RecognitionException cause) {
+        final XPathExpressionException ex = Utils.wrapException(cause, "%s", msg);
+        if (error == null) {
+            error = ex;
+        } else {
+            error.addSuppressed(ex);
+        }
+    }
+
+    void reportError() throws XPathExpressionException {
+        if (error != null) {
+            throw error;
+        }
+    }
+}
diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/FunctionSupport.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/FunctionSupport.java
new file mode 100644 (file)
index 0000000..74f2dce
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
+import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangNumberExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathSupport;
+
+/**
+ * This class provides support for validating function call arguments as well as compile-time evaluation.
+ */
+final class FunctionSupport {
+    static final YangFunctionCallExpr POSITION = YangFunctionCallExpr.of(YangFunction.POSITION.getIdentifier());
+
+    private static final YangFunctionCallExpr CURRENT = YangFunctionCallExpr.of(YangFunction.CURRENT.getIdentifier());
+    private static final YangFunctionCallExpr LAST = YangFunctionCallExpr.of(YangFunction.LAST.getIdentifier());
+    private static final YangFunctionCallExpr LOCAL_NAME = YangFunctionCallExpr.of(
+        YangFunction.LOCAL_NAME.getIdentifier());
+    private static final YangFunctionCallExpr NAME = YangFunctionCallExpr.of(YangFunction.NAME.getIdentifier());
+    private static final YangFunctionCallExpr NAMESPACE_URI = YangFunctionCallExpr.of(
+        YangFunction.NAMESPACE_URI.getIdentifier());
+    private static final YangFunctionCallExpr NORMALIZE_SPACE = YangFunctionCallExpr.of(
+        YangFunction.NORMALIZE_SPACE.getIdentifier());
+    private static final YangFunctionCallExpr NUMBER = YangFunctionCallExpr.of(YangFunction.NUMBER.getIdentifier());
+    private static final YangFunctionCallExpr STRING = YangFunctionCallExpr.of(YangFunction.STRING.getIdentifier());
+    private static final YangFunctionCallExpr STRING_LENGTH = YangFunctionCallExpr.of(
+        YangFunction.STRING_LENGTH.getIdentifier());
+
+    private final YangXPathMathSupport mathSupport;
+
+    FunctionSupport(final YangXPathMathSupport mathSupport) {
+        this.mathSupport = requireNonNull(mathSupport);
+    }
+
+    YangExpr functionToExpr(final YangFunction func, final List<YangExpr> args) {
+        switch (func) {
+            case BIT_IS_SET:
+                checkArgument(args.size() == 2, "bit-is-set(node-set, string) takes two arguments");
+                break;
+            case BOOLEAN:
+                return booleanExpr(args);
+            case CEILING:
+                checkArgument(args.size() == 1, "ceiling(number) takes one argument");
+                // TODO: constant folding requires math support
+                break;
+            case CONCAT:
+                return concatExpr(args);
+            case CONTAINS:
+                return containsExpr(args);
+            case COUNT:
+                checkArgument(args.size() == 1, "count(node-set) takes one argument");
+                // TODO: constant folding requires math support
+                break;
+            case CURRENT:
+                checkArgument(args.isEmpty(), "current() does not take any arguments");
+                return CURRENT;
+            case DEREF:
+                checkArgument(args.size() == 1, "deref(node-set) takes one argument");
+                break;
+            case DERIVED_FROM:
+                return derivedFromExpr(args);
+            case DERIVED_FROM_OR_SELF:
+                return derivedFromOrSelfExpr(args);
+            case ENUM_VALUE:
+                checkArgument(args.size() == 1, "enum-value(node-set) takes one argument");
+                break;
+            case FALSE:
+                checkArgument(args.isEmpty(), "false() does not take any arguments");
+                return YangBooleanConstantExpr.FALSE;
+            case FLOOR:
+                checkArgument(args.size() == 1, "floor(number) takes one argument");
+                // TODO: constant folding requires math support
+                break;
+            case ID:
+                checkArgument(args.size() == 1, "id(object) takes one argument");
+                break;
+            case LANG:
+                checkArgument(args.size() == 1, "lang(string) takes one argument");
+                break;
+            case LAST:
+                checkArgument(args.isEmpty(), "last() does not take any arguments");
+                return LAST;
+            case LOCAL_NAME:
+                checkArgument(args.size() <= 1, "local-name(node-set?) takes at most one argument");
+                if (args.isEmpty()) {
+                    return LOCAL_NAME;
+                }
+                break;
+            case NAME:
+                checkArgument(args.size() <= 1, "name(node-set?) takes at most one argument");
+                if (args.isEmpty()) {
+                    return NAME;
+                }
+                break;
+            case NAMESPACE_URI:
+                checkArgument(args.size() <= 1, "namespace-uri(node-set?) takes at most one argument");
+                if (args.isEmpty()) {
+                    return NAMESPACE_URI;
+                }
+                break;
+            case NORMALIZE_SPACE:
+                return normalizeSpaceExpr(args);
+            case NOT:
+                return notExpr(args);
+            case NUMBER:
+                return numberExpr(args);
+            case POSITION:
+                checkArgument(args.isEmpty(), "position() does not take any arguments");
+                return POSITION;
+            case RE_MATCH:
+                checkArgument(args.size() == 2, "re-match(string, string) takes two arguments");
+                // TODO: static analysis requires XSD regex support -- we should validate args[1] and match it to
+                //       args[0] if that is a literal
+                break;
+            case ROUND:
+                checkArgument(args.size() == 1, "round(number) takes one argument");
+                // TODO: constant folding requires math support
+                break;
+            case STARTS_WITH:
+                return startsWithExpr(args);
+            case STRING:
+                return stringExpr(args);
+            case STRING_LENGTH:
+                return stringLengthExpr(args);
+            case SUBSTRING:
+                return substringExpr(args);
+            case SUBSTRING_AFTER:
+                return substringAfterExpr(args);
+            case SUBSTRING_BEFORE:
+                return substringBeforeExpr(args);
+            case SUM:
+                checkArgument(args.size() == 1, "sub(node-set) takes one argument");
+                // TODO: constant folding requires math support
+                break;
+            case TRANSLATE:
+                checkArgument(args.size() == 3, "translate(string, string, string) takes three arguments");
+                // TODO: constant folding?
+                break;
+            case TRUE:
+                checkArgument(args.isEmpty(), "true() does not take any arguments");
+                return YangBooleanConstantExpr.TRUE;
+            default:
+                throw new IllegalStateException("Unhandled function " + func);
+        }
+
+        return YangFunctionCallExpr.of(func.getIdentifier(), args);
+    }
+
+    private static YangExpr booleanExpr(final List<YangExpr> args) {
+        checkArgument(args.size() == 1, "boolean(object) takes one argument");
+        final YangExpr arg = args.get(0);
+        if (arg instanceof YangBooleanConstantExpr) {
+            return arg;
+        }
+        if (arg instanceof YangLiteralExpr) {
+            return YangBooleanConstantExpr.of(!((YangLiteralExpr) arg).getLiteral().isEmpty());
+        }
+
+        // TODO: handling YangNumberExpr requires math support
+
+        return YangFunctionCallExpr.of(YangFunction.BOOLEAN.getIdentifier(), args);
+    }
+
+    private static YangExpr concatExpr(final List<YangExpr> args) {
+        checkArgument(args.size() >= 2, "concat(string, string, string*) takes at least two arguments");
+
+        // TODO: constant folding
+
+        return YangFunctionCallExpr.of(YangFunction.CONCAT.getIdentifier(), args);
+    }
+
+    private static YangExpr containsExpr(final List<YangExpr> args) {
+        checkArgument(args.size() == 2, "contains(string, string) takes two arguments");
+        final YangExpr first = args.get(0);
+        if (first instanceof YangLiteralExpr) {
+            final YangExpr second = args.get(1);
+            if (second instanceof YangLiteralExpr) {
+                return YangBooleanConstantExpr.of(
+                    ((YangLiteralExpr) first).getLiteral().contains(((YangLiteralExpr) second).getLiteral()));
+            }
+        }
+
+        // TODO: handling YangNumberExpr requires math support
+        return YangFunctionCallExpr.of(YangFunction.CONTAINS.getIdentifier(), args);
+    }
+
+    private static YangExpr derivedFromExpr(final List<YangExpr> args) {
+        checkArgument(args.size() == 2, "derived-from(node-set, string) takes two arguments");
+        // FIXME: coerce second arg to a QName
+        return YangFunctionCallExpr.of(YangFunction.DERIVED_FROM.getIdentifier(), args);
+    }
+
+    private static YangExpr derivedFromOrSelfExpr(final List<YangExpr> args) {
+        checkArgument(args.size() == 2, "derived-from-or-self(node-set, string) takes two arguments");
+        // FIXME: coerce second arg to a QName
+        return YangFunctionCallExpr.of(YangFunction.DERIVED_FROM_OR_SELF.getIdentifier(), args);
+    }
+
+    private static YangExpr notExpr(final List<YangExpr> args) {
+        checkArgument(args.size() == 1, "not(boolean) takes one argument");
+        final YangExpr arg = args.get(0);
+        if (arg instanceof YangBooleanConstantExpr) {
+            return YangBooleanConstantExpr.of(((YangBooleanConstantExpr) arg).getValue());
+        }
+
+        return YangFunctionCallExpr.of(YangFunction.NOT.getIdentifier(), args);
+    }
+
+    private static YangExpr normalizeSpaceExpr(final List<YangExpr> args) {
+        checkArgument(args.size() <= 1, "normalize-space(object?) takes at most one argument");
+        if (args.isEmpty()) {
+            return NORMALIZE_SPACE;
+        }
+        final YangExpr arg = args.get(0);
+        if (arg instanceof YangLiteralExpr) {
+            // TODO: normalize value
+        }
+
+        return YangFunctionCallExpr.of(YangFunction.NORMALIZE_SPACE.getIdentifier(), args);
+    }
+
+    private YangExpr numberExpr(final List<YangExpr> args) {
+        checkArgument(args.size() <= 1, "number(object?) takes at most one argument");
+        if (args.isEmpty()) {
+            return NUMBER;
+        }
+
+        final YangExpr arg = args.get(0);
+        if (arg instanceof YangNumberExpr) {
+            return arg;
+        }
+        if (arg instanceof YangLiteralExpr) {
+            return mathSupport.createNumber(((YangLiteralExpr) arg).getLiteral());
+        }
+        if (arg instanceof YangBooleanConstantExpr) {
+            final boolean value = ((YangBooleanConstantExpr) arg).getValue();
+            return mathSupport.createNumber(value ? 1 : 0);
+        }
+
+        return YangFunctionCallExpr.of(YangFunction.NUMBER.getIdentifier(), args);
+    }
+
+    private static YangExpr startsWithExpr(final List<YangExpr> args) {
+        checkArgument(args.size() == 2, "starts-with(string, string) takes two arguments");
+
+        // TODO: constant folding
+
+        return YangFunctionCallExpr.of(YangFunction.STARTS_WITH.getIdentifier(), args);
+    }
+
+    private static YangExpr substringBeforeExpr(final List<YangExpr> args) {
+        checkArgument(args.size() == 2, "substring-before(string, string) takes two arguments");
+
+        // TODO: constant folding
+
+        return YangFunctionCallExpr.of(YangFunction.SUBSTRING_BEFORE.getIdentifier(), args);
+    }
+
+    private static YangExpr substringAfterExpr(final List<YangExpr> args) {
+        checkArgument(args.size() == 2, "substring-after(string, string) takes two arguments");
+
+        // TODO: constant folding
+
+        return YangFunctionCallExpr.of(YangFunction.SUBSTRING_AFTER.getIdentifier(), args);
+    }
+
+    private static YangExpr substringExpr(final List<YangExpr> args) {
+        final int size = args.size();
+        checkArgument(size == 2 || size == 3, "substring-(string, number, number?) takes two or three arguments");
+
+        // TODO: constant folding
+
+        return YangFunctionCallExpr.of(YangFunction.SUBSTRING.getIdentifier(), args);
+    }
+
+    private static YangExpr stringExpr(final List<YangExpr> args) {
+        checkArgument(args.size() <= 1, "string(object?) takes at most one argument");
+        if (args.isEmpty()) {
+            return STRING;
+        }
+
+        final YangExpr arg = args.get(0);
+        if (arg instanceof YangLiteralExpr) {
+            return arg;
+        }
+        if (arg instanceof YangBooleanConstantExpr) {
+            return ((YangBooleanConstantExpr) arg).asStringLiteral();
+        }
+
+        // TODO: handling YangNumberExpr requires math support
+        return YangFunctionCallExpr.of(YangFunction.STRING.getIdentifier(), args);
+    }
+
+    private YangExpr stringLengthExpr(final List<YangExpr> args) {
+        checkArgument(args.size() <= 1, "string-length(object?) takes at most one argument");
+        if (args.isEmpty()) {
+            return STRING_LENGTH;
+        }
+
+        YangExpr first = args.get(0);
+        if (first instanceof YangBooleanConstantExpr) {
+            first = ((YangBooleanConstantExpr) first).asStringLiteral();
+        }
+        if (first instanceof YangLiteralExpr) {
+            return mathSupport.createNumber(((YangLiteralExpr) first).getLiteral().length());
+        }
+
+        return YangFunctionCallExpr.of(YangFunction.STRING_LENGTH.getIdentifier(), args);
+    }
+}
diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/InstanceIdentifierParser.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/InstanceIdentifierParser.java
new file mode 100644 (file)
index 0000000..3c28a1f
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.xpath.impl;
+
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.getChild;
+import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.illegalShape;
+import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyToken;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.xml.xpath.XPathExpressionException;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.opendaylight.yangtools.yang.common.QualifiedQName;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
+import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Absolute;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
+import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathSupport;
+import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.EqQuotedStringContext;
+import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.InstanceIdentifierContext;
+import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.KeyPredicateContext;
+import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.KeyPredicateExprContext;
+import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.LeafListPredicateContext;
+import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.LeafListPredicateExprContext;
+import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.NodeIdentifierContext;
+import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PathArgumentContext;
+import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PosContext;
+import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PredicateContext;
+import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.QuotedStringContext;
+
+abstract class InstanceIdentifierParser {
+    static final class Base extends InstanceIdentifierParser {
+        Base(final YangXPathMathMode mathMode) {
+            super(mathMode);
+        }
+
+        @Override
+        YangQNameExpr createExpr(final String prefix, final String localName) {
+            return YangQNameExpr.of(QualifiedQName.of(prefix, localName));
+        }
+
+        @Override
+        QNameStep createChildStep(final String prefix, final String localName, final Collection<YangExpr> predicates) {
+            return YangXPathAxis.CHILD.asStep(QualifiedQName.of(prefix, localName), predicates);
+        }
+    }
+
+    static final class Qualified extends InstanceIdentifierParser {
+        final YangNamespaceContext namespaceContext;
+
+        Qualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
+            super(mathMode);
+            this.namespaceContext = requireNonNull(namespaceContext);
+        }
+
+        @Override
+        QNameStep createChildStep(final String prefix, final String localName, final Collection<YangExpr> predicates) {
+            return YangXPathAxis.CHILD.asStep(namespaceContext.createQName(prefix, localName), predicates);
+        }
+
+
+        @Override
+        YangQNameExpr createExpr(final String prefix, final String localName) {
+            return YangQNameExpr.of(namespaceContext.createQName(prefix, localName));
+        }
+
+    }
+
+    private final YangXPathMathSupport mathSupport;
+
+    InstanceIdentifierParser(final YangXPathMathMode mathMode) {
+        this.mathSupport = mathMode.getSupport();
+    }
+
+    final Absolute interpretAsInstanceIdentifier(final YangLiteralExpr expr) throws XPathExpressionException {
+        final xpathLexer lexer = new xpathLexer(CharStreams.fromString(expr.getLiteral()));
+        final instanceIdentifierParser parser = new instanceIdentifierParser(new CommonTokenStream(lexer));
+        final CapturingErrorListener listener = new CapturingErrorListener();
+        lexer.removeErrorListeners();
+        lexer.addErrorListener(listener);
+        parser.removeErrorListeners();
+        parser.addErrorListener(listener);
+
+        final InstanceIdentifierContext id = parser.instanceIdentifier();
+        listener.reportError();
+
+        final int length = id.getChildCount();
+        final List<Step> steps = new ArrayList<>(length / 2);
+        for (int i = 1; i < length; i += 2) {
+            steps.add(parsePathArgument(getChild(id, PathArgumentContext.class, i)));
+        }
+
+        return YangLocationPath.absolute(steps);
+    }
+
+    abstract YangQNameExpr createExpr(String prefix, String localName);
+
+    abstract QNameStep createChildStep(String prefix, String localName, Collection<YangExpr> predicates);
+
+    private QNameStep parsePathArgument(final PathArgumentContext expr) {
+        final NodeIdentifierContext childExpr = getChild(expr, NodeIdentifierContext.class, 0);
+        final String prefix = verifyIdentifier(childExpr, 0);
+        final String localName = verifyIdentifier(childExpr, 2);
+
+        switch (expr.getChildCount()) {
+            case 1:
+                return createChildStep(prefix, localName, ImmutableSet.of());
+            case 2:
+                return createChildStep(prefix, localName, parsePredicate(getChild(expr, PredicateContext.class, 1)));
+            default:
+                throw illegalShape(expr);
+        }
+    }
+
+    private Collection<YangExpr> parsePredicate(final PredicateContext expr) {
+        final ParseTree first = expr.getChild(0);
+        if (first instanceof LeafListPredicateContext) {
+            return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(YangLocationPath.self(),
+                parseEqStringValue(getChild(((LeafListPredicateContext) first)
+                    .getChild(LeafListPredicateExprContext.class, 0), EqQuotedStringContext.class, 1))));
+        } else if (first instanceof PosContext) {
+            return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(FunctionSupport.POSITION,
+                mathSupport.createNumber(((PosContext) first).getToken(instanceIdentifierParser.PositiveIntegerValue, 0)
+                    .getText())));
+        }
+
+        final int length = expr.getChildCount();
+        final List<YangExpr> ret = new ArrayList<>(length);
+        for (int i = 0; i < length; ++i) {
+            final KeyPredicateExprContext pred = getChild(expr, KeyPredicateContext.class, i)
+                    .getChild(KeyPredicateExprContext.class, 0);
+            ret.add(YangBinaryOperator.EQUALS.exprWith(
+                createChildExpr(getChild(pred, NodeIdentifierContext.class, 0)),
+                parseEqStringValue(getChild(pred, EqQuotedStringContext.class, 1))));
+
+        }
+
+        return ret;
+    }
+
+    private YangQNameExpr createChildExpr(final NodeIdentifierContext expr) {
+        return createExpr(verifyIdentifier(expr, 0), verifyIdentifier(expr, 2));
+    }
+
+    private static String verifyIdentifier(final NodeIdentifierContext expr, final int child) {
+        return verifyToken(expr, child, instanceIdentifierParser.Identifier).getText();
+    }
+
+    private static YangLiteralExpr parseEqStringValue(final EqQuotedStringContext expr) {
+        return YangLiteralExpr.of(verifyToken(getChild(expr, QuotedStringContext.class, expr.getChildCount() - 1), 1,
+            instanceIdentifierParser.STRING).getText());
+    }
+}
diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/ParseTreeUtils.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/ParseTreeUtils.java
new file mode 100644 (file)
index 0000000..8e6c035
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.xpath.impl;
+
+import static com.google.common.base.Verify.verify;
+
+import com.google.common.base.VerifyException;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.TerminalNode;
+
+/**
+ * Utility methods for dealing with {@link ParseTree}s.
+ */
+final class ParseTreeUtils {
+    private ParseTreeUtils() {
+
+    }
+
+    static <T extends ParseTree> T getChild(final ParseTree parent, final Class<T> type, final int offset) {
+        return verifyTree(type, parent.getChild(offset));
+    }
+
+    static void verifyChildCount(final ParseTree tree, final int expected) {
+        if (tree.getChildCount() != expected) {
+            throw illegalShape(tree);
+        }
+    }
+
+    static int verifyAtLeastChildren(final ParseTree tree, final int expected) {
+        final int count = tree.getChildCount();
+        if (count < expected) {
+            throw illegalShape(tree);
+        }
+        return count;
+    }
+
+    static TerminalNode verifyTerminal(final ParseTree tree) {
+        if (tree instanceof TerminalNode) {
+            return (TerminalNode) tree;
+        }
+        throw new VerifyException(String.format("'%s' is not a terminal node", tree.getText()));
+    }
+
+    static Token verifyToken(final ParseTree parent, final int offset, final int expected) {
+        final TerminalNode node = verifyTerminal(parent.getChild(offset));
+        final Token ret = node.getSymbol();
+        final int type = ret.getType();
+        verify(type == expected, "Item %s has type %s, expected %s", node, type, expected);
+        return ret;
+    }
+
+    static <T extends ParseTree> T verifyTree(final Class<T> type, final ParseTree tree) {
+        if (type.isInstance(tree)) {
+            return type.cast(tree);
+        }
+        throw new VerifyException(String.format("'%s' does not have expected type %s", tree.getText(), type));
+    }
+
+    static VerifyException illegalShape(final ParseTree tree) {
+        return new VerifyException(String.format("Invalid parser shape of '%s'", tree.getText()));
+    }
+}
diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/Utils.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/Utils.java
new file mode 100644 (file)
index 0000000..74ebb6a
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, s.r.o.  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.xpath.impl;
+
+import javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.QualifiedQName;
+import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr.Resolved;
+import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr.Unresolved;
+
+/**
+ * Various simplistic utilities shared across classes.
+ */
+final class Utils {
+    private Utils() {
+
+    }
+
+    static Unresolved interpretAsQName(final YangLiteralExpr expr) throws XPathExpressionException {
+        final String text = expr.getLiteral();
+        final int colon = text.indexOf(':');
+
+        final AbstractQName qname;
+        try {
+            qname = colon != -1 ? QualifiedQName.of(text.substring(0, colon), text.substring(colon + 1))
+                    : UnqualifiedQName.of(text);
+        } catch (IllegalArgumentException e) {
+            throw wrapException(e, "Cannot interpret %s as a QName", expr);
+        }
+
+        return YangQNameExpr.of(qname.intern());
+    }
+
+    static YangQNameExpr interpretAsQName(final YangNamespaceContext namespaceContext, final YangLiteralExpr expr)
+            throws XPathExpressionException {
+        final String text = expr.getLiteral();
+        final int colon = text.indexOf(':');
+        try {
+            if (colon == -1) {
+                return YangQNameExpr.of(UnqualifiedQName.of(text).intern());
+            }
+
+            return YangQNameExpr.of(namespaceContext.createQName(text.substring(0, colon), text.substring(colon + 1)));
+        } catch (IllegalArgumentException e) {
+            throw wrapException(e, "Cannot interpret %s as a QName", expr);
+        }
+    }
+
+    static Resolved interpretAsQName(final YangNamespaceContext namespaceContext,
+            final QNameModule defaultNamespace, final YangLiteralExpr expr) throws XPathExpressionException {
+        final String text = expr.getLiteral();
+        final int colon = text.indexOf(':');
+        final QName qname;
+
+        try {
+            qname = colon == -1 ? QName.create(defaultNamespace, text).intern()
+                    : namespaceContext.createQName(text.substring(0, colon), text.substring(colon + 1));
+        } catch (IllegalArgumentException e) {
+            throw wrapException(e, "Cannot interpret %s as a QName", expr);
+        }
+
+        return YangQNameExpr.of(qname);
+    }
+
+    static XPathExpressionException wrapException(final @Nullable Throwable cause, final String format,
+            final Object... args) {
+        final XPathExpressionException ret = new XPathExpressionException(String.format(format, args));
+        ret.initCause(cause);
+        return ret;
+    }
+}
diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/package-info.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/package-info.java
new file mode 100644 (file)
index 0000000..5f36861
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2016 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
+ */
+/**
+ * XPath parsing which results in a {@link org.opendaylight.yangtools.yang.xpath.api.YangExpr}. This is based
+ * on the XPath grammar from <a href="https://github.com/antlr/grammars-v4/blob/master/xpath/xpath.g4">grammars-v4</a>.
+ *
+ * @author Robert Varga
+ */
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.opendaylight.yangtools.yang.xpath.impl;
\ No newline at end of file
diff --git a/yang/yang-xpath-impl/src/test/java/org/opendaylight/yangtools/yang/xpath/impl/XPathParserTest.java b/yang/yang-xpath-impl/src/test/java/org/opendaylight/yangtools/yang/xpath/impl/XPathParserTest.java
new file mode 100644 (file)
index 0000000..c4196b5
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.xpath.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableBiMap;
+import java.net.URI;
+import javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.BiMapYangNamespaceContext;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
+
+@SuppressWarnings("null")
+public class XPathParserTest {
+    private static final QNameModule DEFNS = QNameModule.create(URI.create("defaultns"));
+    private static final YangNamespaceContext CONTEXT = new BiMapYangNamespaceContext(ImmutableBiMap.of(
+        "def", DEFNS,
+        "foo", QNameModule.create(URI.create("foo")),
+        "bar", QNameModule.create(URI.create("bar"))));
+
+    private @Nullable AntlrXPathParser parser;
+
+    @Before
+    public void before() {
+        parser = new AntlrXPathParser.Unqualified(YangXPathMathMode.IEEE754, CONTEXT, DEFNS);
+    }
+
+    @Test
+    public void testSmoke() throws XPathExpressionException {
+        parseExpr("../a[foo = current()/foo]");
+
+        parseExpr("3 + 5");
+        parseExpr("/a/b");
+        parseExpr("a/b");
+        parseExpr("./a/b");
+        parseExpr("../a/b");
+        parseExpr("foo:foo");
+        parseExpr("@foo");
+        parseExpr("@foo:foo");
+        parseExpr("current()");
+        parseExpr("foo:self()");
+        parseExpr("foo:comment()");
+        parseExpr("/a[foo = 2 and bar = 3]");
+        parseExpr("/a[foo = \"2\" and bar = '3']");
+        parseExpr("/foo:a[foo = 2 and bar = 3]");
+        parseExpr("//a[foo = 2 and bar = 3]");
+        parseExpr("//foo:a[foo=2 and bar:bar=3]");
+        parseExpr("a//b[foo = 2]");
+        parseExpr("foo:a//b[foo = 2]");
+        parseExpr("$foo:comment");
+        parseExpr("$comment");
+        parseExpr("$self");
+    }
+
+    @Test
+    public void testUnionSquashing() throws XPathExpressionException {
+        final YangExpr a = parseExpr("a");
+        assertEquals(a, parseExpr("a|a"));
+        assertEquals(a, parseExpr("a|a|a"));
+        assertEquals(a, parseExpr("a|a|a|a"));
+
+        final YangExpr ab = parseExpr("a|b");
+        assertEquals(ab, parseExpr("a|b|a"));
+        assertEquals(ab, parseExpr("a|b|a|b"));
+        assertEquals(ab, parseExpr("a|a|b|a|b"));
+        assertEquals(ab, parseExpr("a|b|b|a|b"));
+        assertEquals(ab, parseExpr("a|b|a|a|b"));
+    }
+
+    @Test
+    public void testNumberSquashing() throws XPathExpressionException {
+        final YangExpr two = parseExpr("2");
+        assertEquals(two, parseExpr("1 + 1"));
+        assertEquals(two, parseExpr("3 - 1"));
+        assertEquals(two, parseExpr("2 * 1"));
+        assertEquals(two, parseExpr("4 div 2"));
+        assertEquals(two, parseExpr("6 mod 4"));
+    }
+
+    @Test
+    public void testSameExprSquashing() throws XPathExpressionException {
+        // Expressions
+        assertEquals(YangBooleanConstantExpr.FALSE, parseExpr("/a != /a"));
+        assertEquals(YangBooleanConstantExpr.TRUE, parseExpr("/a = /a"));
+
+        // Numbers
+        assertEquals(YangBooleanConstantExpr.FALSE, parseExpr("2 != 2"));
+        assertEquals(YangBooleanConstantExpr.FALSE, parseExpr("2 != (1 + 1)"));
+        assertEquals(YangBooleanConstantExpr.TRUE, parseExpr("2 = 2"));
+        assertEquals(YangBooleanConstantExpr.TRUE, parseExpr("2 = (1 + 1)"));
+    }
+
+    private YangExpr parseExpr(final String xpath) throws XPathExpressionException {
+        return parser.parseExpression(xpath).getRootExpr();
+    }
+}